Chen frames the emulator team's decision to patch buggy guest code at JIT-translation time as a continuation of a 40-year tradition of quietly fixing user software so it keeps running. The specifics of the broken x86 sequence matter less than the principle: emulators translate intent, not instructions, because shipping software is what users actually depend on.
By submitting the post and titling it around code 'so bad they fixed it during emulation,' the submitter highlights this as a noteworthy and admirable engineering practice rather than a hack. The 284-point reception suggests broad agreement that this kind of invisible compatibility work deserves recognition.
The editorial argues that the spec is a lie and the real interface is whatever the largest existing user base depends on. The Microsoft anecdote is presented as Hyrum's Law 'with teeth' — once enough software depends on incidental behavior, that behavior becomes the contract, and emulators, OSes, and runtimes must absorb the cost of preserving it.
The editorial catalogs parallel cases — DOSBox patching Sierra adventure games, QEMU shimming buggy BIOS calls, Rosetta 2 reportedly carrying Adobe-specific paths, and Windows' AppCompat database — to argue this is a universal pattern across emulators and runtimes. The Microsoft x86-on-ARM fix is just the latest visible instance of a quiet 40-year practice.
Raymond Chen's latest Old New Thing post (284 points on Hacker News) tells a small story with an outsized lesson. While building the x86 emulator that lets legacy Windows apps run on ARM-based Windows machines, Microsoft's emulation team ran into a binary whose generated code was, in Chen's framing, simply wrong — wrong enough that a faithful translation to ARM instructions would have faulted or produced garbage. Rather than declare the app incompatible, the team did what emulator teams have quietly done for forty years: they detected the broken pattern at JIT-translation time and emitted ARM code that did what the original author *meant*, not what they wrote.
The specifics matter less than the shape of the fix. The emulator wasn't translating instructions; it was translating intent. A buggy sequence on real x86 silicon happened to work because of some incidental behavior — a flag that lingered, a register that was preserved across a call, a memory access that landed on a page that happened to be mapped. On ARM, none of those incidental truths hold. So the team pattern-matched the broken idiom in the guest binary and substituted a corrected translation. The app vendor never knew. The user never knew. The bug shipped, ran on millions of machines, and was quietly euthanized inside a translation cache.
The HN thread filled up with the expected war stories: DOSBox patching Sierra adventure games, QEMU shimming around buggy BIOS calls, Apple's Rosetta 2 reportedly carrying special cases for specific Adobe binaries, and the granddaddy of them all — Windows itself, where the AppCompat database is a 25-year-old monument to one company's refusal to let user software break across OS upgrades.
This is the part of systems engineering that rarely makes the conference slides: the spec is a lie, and the real interface is whatever the largest existing user base happens to depend on. Hyrum's Law, but with teeth. When you're shipping an emulator, a JIT, a browser, or a kernel, you don't get to be right in the abstract. You get to be right in a way that doesn't break Photoshop, AutoCAD, or some bespoke factory-floor app written in 1998 by a contractor who has since retired to Florida.
The deeper point is about where the *correctness boundary* lives. A naive emulator author thinks correctness means "faithfully execute the x86 ISA." A shipping emulator author learns that correctness means "produce the output the user expected from this specific binary on this specific day, including the parts where the binary was wrong." Those are different jobs. The first is a CS exercise. The second is what Microsoft, Apple, Intel (with their own x86 microcode that quietly works around buggy AMD assumptions in some apps), and the JVM team have all been doing for decades.
It's also worth naming the dual-use risk. A translation layer that silently corrects guest bugs is, structurally, a translation layer that can silently change guest behavior — and the line between "compatibility shim" and "vendor patching my software without consent" is thinner than anyone wants to admit. Microsoft's AppCompat shims have, on occasion, masked security-relevant bugs in third-party software, letting vulnerable code keep running past the point where the vendor would have been forced to patch. The same is true of every JIT that smooths over UB in compiled C. Compatibility is a feature; it is also a way to never fix anything.
Finally, this story is a small artifact of why ARM-on-Windows took twenty years to become usable. Apple did the same translation work for Rosetta 2 in roughly two years, but Apple controls the platform end-to-end and could pressure top-100 vendors to ship native binaries on a tight timeline. Microsoft can't. Its emulator has to handle a 30-year tail of binaries from vendors who are dead, dissolved, or uninterested. The fact that any of it works is a tribute to thousands of small Raymond-Chen-style stories that never get told.
If you maintain a runtime — a language VM, a transpiler, a database with a SQL dialect, an API gateway that rewrites requests, or anything that sits between user code and the thing user code thinks it's talking to — your real test suite is not your test suite. It's the production traffic of your largest users, including their bugs. Two practical implications:
First, instrument for divergence, not just failure. When you ship a new version of your runtime, you want to know not just "did anything crash" but "did the *output* change for any large customer's workload." Diff-based canarying — running old and new in parallel and comparing results — catches the kind of silent regressions that pattern-matched emulator fixes were created to prevent. If you don't have this, your refactors are blind.
Second, write down your shims. Every runtime accumulates an undocumented folklore of "we handle this weird case for $bigcustomer." That folklore is institutional memory and it walks out the door when engineers leave. The AppCompat database is ugly, but it is *written down*. Your shim list should be too, with the binary fingerprint, the symptom, the corrected behavior, and the date you can probably remove it (spoiler: you can't).
The interesting next chapter is what happens when the buggy guest code is itself AI-generated. We are about to enter a decade in which a meaningful fraction of new binaries will be compiled from LLM-written source, with subtler bugs than human-written code — confidently wrong rather than obviously wrong. Emulator and runtime teams will be the canary: the first to notice that the bug distribution has shifted from "the contractor in 1998 forgot to zero a register" to "the model in 2026 hallucinated a calling convention." The shims will keep getting written. They just won't have a human author to blame anymore.
SimCity had a read-after-free bug that Microsoft patched in Windows 95. That was a lot easier for customers than having Maxis fix it, which could have required exchanging copies of the game.
I think we're starting to see more of this sort of thing happening now with Proton and Wine gaining prominence in the Linux community. Some games (Elden Ring comes to mind) have bad enough PC ports when they come out that the compatibility layer can incorporate a hotfix to improve performance,
> Anyway, my colleague found that there was one program that needed to allocate around 64KB of memory on the stack and initialize it. The standard way of doing this is to perform a stack probe to ensure that 64KB of memory is available, then subtracting 65536 from the stack pointer, and then init
To be fair it is possible that the developer enabled a special "unroll all loops, no matter what" optimisation flag during compilation.I agree it would be stupid for a compiler to even support such a flag, but those were the 1980s/90s.
Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.
This reminds me of a story from 15 years ago, where I was developing a technology to download games on demand by hooking into the OS calls.There was a particular game that was superslow when this tech was applied. Original game loading took around 15-20 seconds, whereas once the tech was applied it