Rosetta 2 is a translation layer from x86-64 to ARM64 that Apple launched in 2020 alongside Apple Silicon Macs, designed to facilitate the migration of software from Intel Macs. Later, Apple integrated a Rosetta implementation specifically designed for Linux VMs into the Virtualization.framework, enabling x86-64 Linux binaries to run directly when running Linux VMs on Apple Silicon Macs. This article provides a brief introduction to this Rosetta translation layer for Linux, helping you understand how it works and some interesting phenomena you might encounter during use.
Motivation: Why I’m Researching This
Recently, I’ve been creating a Virtualization.framework backend for libvirt (the work in progress can be found here). The explicit goal of this project is to allow running x86-64 Linux VMs on Apple Silicon Macs through libvirt — because libvirt already has a QEMU backend that has long used Apple’s Hypervisor.framework for virtualization, but on Apple Silicon, Hypervisor.framework can only run ARM64 guest systems. Otherwise, running x86-64 Linux would require emulation rather than virtualization, which comes with certain performance penalties.
Apple’s Virtualization.framework offers a better alternative — by using Rosetta 2 for Linux VMs, we can run x86-64 Linux binaries on ARM hardware with near-native performance.
This article documents my discoveries and thoughts during this process, hoping to help those with similar needs understand how this technology works.
Rosetta 2 in Linux VMs
OrbStack and Docker Desktop, which I frequently use, both provide the ability to run x86-64 containers/VMs on Apple Silicon Macs, leveraging this very technology. When you pull an amd64/x86-64 architecture container image and run it on Apple Silicon:
- OrbStack/Docker creates a lightweight Linux VM
- The VM transparently translates x86-64 instructions through Rosetta
- x86 binaries inside the container are executed seamlessly
This is why you can docker run --platform=linux/amd64 on M-series Macs without any additional configuration.
But here’s an interesting phenomenon: the Linux kernel in these VMs and containers is actually still ARM64 architecture, but paired with an x86-64 userspace environment. For Docker, this is natural because it always needs to run a VM on Macs, and Docker images themselves don’t include a kernel — when we pull a Docker image, we’re actually pulling a userspace filesystem layer, not an entire operating system. For OrbStack, its VM also boots an ARM64 Linux kernel, but paired with an x86-64 userspace filesystem, using Rosetta 2 to support running x86-64 userspace programs. Generally, this is accomplished by mounting Apple’s Rosetta 2 implementation for Linux VMs (the corresponding implementation can be found in /Library/Apple/usr/libexec/oah/RosettaLinux on the Apple host after installing Rosetta 2) into the VM — for example, through VZLinuxRosettaDirectoryShare, and configuring corresponding binfmts to achieve automatic translation.
1 | tree /Library/Apple/usr/libexec/oah/ |
This differs from the VMs we normally run — in traditional VMs, we can install a complete operating system via an ISO image, where both kernel and userspace are of the same architecture. In Linux VMs that use Rosetta 2, the kernel is ARM64, but the userspace filesystem is x86-64, which is the unique characteristic of Rosetta 2 in Linux VMs.
Don’t Be Confused by What You See
But if you execute uname or cat /proc/cpuinfo in this Linux VM, you’ll discover an interesting phenomenon:
1 | alpine:/proc/self$ uname -a |
Notice several key points:
- vendor_id is
VirtualAppleinstead ofGenuineIntelorAuthenticAMD - model name shows
VirtualApple @ 2.50GHz - cpu family is
6, which is the Intel architecture family identifier - flags lists x86 instruction set features
This doesn’t match the description that “the VM is using an ARM64 kernel,” right? Actually, this is because Rosetta 2 intercepts certain system calls and file read operations in the VM, and modifies the CPU information returned to userspace, making it appear as if it’s running on real x86-64 hardware.
What Rosetta 2 Does
The core work of Rosetta 2 is instruction set translation, including but not limited to:
- Static translation: When a program loads, translating x86_64 binary code into ARM64 code
- Dynamic translation: Handling code paths that cannot be statically translated at runtime
- System call mapping: Converting Linux x86_64 system calls into Linux ARM64 kernel system calls
- And more…
I’m currently having AI automatically analyze Rosetta 2’s implementation for Linux VMs in the Attesor project. Let’s look at the /proc/cpuinfo output as an example.
When you run strings /Library/Apple/usr/libexec/oah/RosettaLinux/rosetta | grep "VirtualApple", you’ll discover it contains a string template. In the code, it operates something like this:
1 | iVar7 = 0; |
In a loop, Rosetta 2 creates a temporary file in memory to output a predefined CPU information string containing the VirtualApple identifier, adjusting fields like processor and siblings based on the actual number of CPU cores. When there’s a read access to the normalized path /proc/cpuinfo, this string is returned, which is why we see the output shown in the cat /proc/cpuinfo example above. Interestingly, fields like model name, cpu MHz, and cache size are all hardcoded and not dynamically generated based on actual CPU information.
For Rosetta 2’s interception of the uname system call (I might be wrong about this), deeper investigation is needed, but anyway, Rosetta 2 is responsible for changing the machine type from the actual ARM to the x86_64 we see above.
If you examine the device tree in sysfs where kernel objects are mounted (a solution adopted by the Linux community to simplify fragmented ARM device descriptions), the truth comes to light:
1 | alpine:/proc/self$ cat /sys/firmware/devicetree/base/cpus/cpu@0/compatible |
The device tree honestly tells you: This is actually an ARM v8-compatible processor, so the kernel it boots should be ARM64 architecture.
Summary
Apple Rosetta 2 used in virtualization is an ingenious technology that makes running x86-64 Linux binaries on Apple Silicon Macs possible through instruction set translation and system call mapping. Although the VM’s kernel is actually ARM64 architecture, the userspace environment is translated by Rosetta 2 to appear as x86-64, which is why you see uname reporting the architecture as x86_64 while the device tree shows ARM.
Next time when you run docker run --platform=linux/amd64 on an M-series Mac, you might want to think about everything happening behind the scenes.
References: