Binary translation

On this page 7

Binary translation is the technique of converting compiled machine code written for one instruction set into equivalent machine code for another, so that a program built for one processor architecture can run on a different one without its source code. In OS compatibility, it is the main way modern operating systems run x86 applications on Arm-based computers, as with Rosetta 2 on Apple silicon Macs and the Prism translator in Windows on Arm.

Unlike classic emulation, which interprets every instruction one at a time, binary translation rewrites whole blocks of code into native instructions and reuses the result. This makes translated programs fast enough for everyday use, often reaching a substantial share of native performance, although the exact figure depends heavily on the workload.

How binary translation converts machine code

Binary translation works by decoding guest instructions, mapping them to an intermediate representation, optimizing that representation and then emitting native host instructions. The translator must also reproduce the guest’s registers, condition flags, memory model and calling conventions, because the program expects the exact behavior of its original processor.

The unit of work is usually a basic block or a larger trace of frequently executed code. Translated blocks are stored in a translation cache, so a loop that runs a million times is translated once and then executed natively. Blocks are linked directly to each other, which avoids returning to the translator after every jump.

The two main strategies differ in when translation happens, and modern systems often combine them. The table compares them.

StrategyWhen code is translatedStrengthsWeaknesses
Static (ahead-of-time)Before the program starts, often at installNo translation delay at run timeCannot handle code generated at run time
Dynamic (just-in-time)While the program runsHandles self-modifying and generated codeStartup delay and memory for the cache
HybridAhead of time, with dynamic fallbackFast common path, full coverageMore complex to implement
Static and dynamic binary translation

Where binary translation is used

Binary translation appears wherever a processor architecture transition would otherwise break existing software. Operating system vendors use it to keep legacy software working while developers port their applications to the new architecture.

TranslatorTranslates fromTranslates toApproach
Rosetta 2x86-64 macOS appsArm64 on Apple siliconAhead-of-time with just-in-time fallback
Prismx86 and x64 Windows appsArm64 WindowsDynamic with cached translations
FEX and Box64x86 Linux programsArm64 LinuxDynamic, user-space translation
QEMU user modeMany guest architecturesMany host architecturesDynamic via an intermediate code
Notable uses of binary translation

The same idea is used inside virtual machines and CPU simulators, in some processors that translate their public instruction set into an internal one, and in security tools that rewrite programs to insert monitoring code. In each case, the program never needs to be recompiled.

Binary translation compared with neighboring techniques

Binary translation is easily confused with emulation and compatibility layers, but it solves a narrower problem: a different processor instruction set. A compatibility layer such as Wine or Proton solves a different operating system interface on the same processor, and the two can be stacked when both differ.

TechniqueWhat differsHow it bridges the gapTypical performance
Binary translationProcessor instruction setRewrites machine code into host codeModerate to near-native
Interpreting emulationProcessor and often hardwareExecutes each instruction in softwareMuch slower than native
Compatibility layerOperating system interfacesMaps system calls and librariesNear-native
Software portingArchitecture or platformRecompiles the source codeNative
How binary translation relates to other compatibility techniques

A practical rule follows from the table: recompiling to native code is always best when source code and developer support exist, and binary translation is the bridge for everything that cannot be ported yet. Full system emulators often use binary translation internally, so the boundary between the terms is not perfectly sharp.

Performance factors and hard cases

Translated code runs slower than native code mainly because the translator must reproduce guest semantics that the host processor does not have. Some features map cheaply, while others cost several host instructions for each guest instruction.

  • Memory ordering: x86 guarantees stronger ordering than Arm, so translators must add barriers. Apple silicon includes a hardware mode for x86-style memory ordering to remove most of that cost.
  • Condition flags: x86 updates flags after most arithmetic, and translators compute them lazily, only when later code reads them.
  • Self-modifying and generated code: JIT engines inside browsers or games force retranslation and invalidation of cached blocks.
  • Vector extensions: instructions such as AVX may be translated slowly or not supported, so some programs refuse to start.
  • Kernel-mode code: user-space translators cannot run drivers or kernel extensions, so hardware drivers must be native.

Because of these factors, a startup delay on the first launch, higher memory use and lower performance in vector-heavy work are normal. Anti-cheat systems and copy protection that inspect the processor or kernel are the most common reasons a translated game fails entirely.

Practical guidance for users and developers

For users, the most useful habit is to prefer native builds when they exist and to treat translation as a compatibility safety net. The table summarizes typical decisions.

SituationRecommended choiceReason
Native Arm build availableInstall the native buildBest speed and battery life
Only an x86 app existsRun it through the translatorUsually works without changes
App needs a kernel driverLook for a native version or alternativeDrivers cannot be translated in user space
Heavy vector workloadsBenchmark before relying on itTranslation overhead is highest here
Choosing between translated and native software

The simple explanation

Binary translation is like a skilled interpreter who translates a whole speech in advance. Each program speaks the language of one kind of processor. The translator rewrites it into the language of another kind. It then keeps the translation, so the work is done only once.

The key takeaway is simple. Old programs keep working on new kinds of computers. Most people never notice the translation at all.

How it is used in practice

An architecture studio replaces its aging x86 laptops with Arm-based laptops that offer longer battery life. Most of its tools, including the office suite and browser, already have native Arm versions, but one specialized structural calculation program exists only as an older x64 build, and its vendor has no plans to port it.

The IT team tests the program under the operating system’s built-in binary translator. The first launch takes noticeably longer while code is translated and cached, and later launches start normally. Calculations run somewhat slower than on the old machines, which the engineers accept because the program is used a few times a day.

One plug-in that installs a kernel driver for a USB license dongle fails to work, so the studio switches to the vendor’s network license server. The result is a successful migration with one native exception list and one workaround, without waiting years for the vendor to port its software.