4 ms·
You can do it with QEMU using its emulation backend to emulate the entire x86 boot chain. It will be dozens (hundreds?) of times slower. Hypervisors work effic
by aseipp 3y ago
You can do it with QEMU using its emulation backend to emulate the entire x86 boot chain. It will be dozens (hundreds?) of times slower.
Hypervisors work efficiently because they allow you to "nest" certain functions inside of other userspace processes, which are typically only available to the operating system (with a higher privilege level), things like delivering interrupts efficiently, or managing page tables. The nesting means that the nested operating system is running on the same hardware in practice, and so it inherits many of the constraints of the host architecture. So, virtualization extensions can be seen as a kind of generalization of existing features, designed so that nesting them is efficient and secure.
For example, different architectures have different rules about how page tables are set up and how virtual memory page faults are handled in that environment. The entire memory mapped layout of the system is completely different. The entire memory model (TSO vs weak ordering) is different. There are strict correctness requirements. A linux kernel for x86 has specific x86 code to manage that and perform those sequences correctly. You cannot just translate x86 code to ARM code and hope the same sequence works; you have to emulate the entire processor environment, so that x86 code works as it should.
Rosetta does not emulate code; it is a binary translator. It translates x86 code to ARM code up front, then runs that. The only reason this works is because normal userspace programs have an ABI that dictates how they interoperate with each other across process and address space and function boundaries. When CrossOver runs an x86 game for example, it translates the x86 to ARM. That program then calls Metal APIs, libmetal.dylib or something, but that .dylib itself isn't x86-to-ARM translated. It is simply "pass through" shim to your system-native Metal APIs. So the graphics stack achieves native performance; the application overhead comes from the x86-to-ARM translation, which needs to preserve the semantics of the original code.
Rosetta-for-Linux works the same way, because there is a stable ABI that exists between processes and function calls, and the Linux kernel ABI is considered stable (though not between architectures in some cases). It translates the x86-to-ARM, and then that binary is run and it gets to call into the native Linux kernel, which is not translated, etc. This basically works well in practice. It is also how Windows' x86-on-ARM emulation works.
If you want to emulate an entire x86 processor, including an x86 Linux kernel, you have to do exactly that, emulate it. Which includes the entire boot process, the memory model, CPU instructions that may not have efficient 1-to-1 translations, etc.
Unfortunately, what you are asking is not actually reasonably possible, in a technical sense. Your options are either to use Rosetta-on-Linux to translate your binary, or get an actual x86 Linux machine.
- astrange 3y ago> Rosetta does not emulate code; it is a binary translator. It translates x86 code to ARM code up front, then runs that. The only reason this works is because normal userspace programs have an ABI that dictates how they interoperate with each other across process and address space and function boundaries. When CrossOver runs an x86 game for example, it translates the x86 to ARM. I don't think this is true. I think code run under WINE is always JITted because it's too much unlike a Mac binary.
- olliej 3y agoHonestly I feel like ([edit: removed personal detail that in hindsight isn't really relevant in this specific discussion]) translation vs emulation is very close to a purely marketing distinction. I feel like the only thing that makes it a semantically valid distinction is if you take an interpretation of translation being explicitly the "translated" code is run as native code on the cpu, rather than as a loop written in native code that manually evaluating each original instruction. But that's still iffy, as many things that people would absolutely call emulators nowadays are generating native code as well. JIT vs non-JIT also isn't really sufficient. Rosetta necessarily does runtime code generation as well[1], because Rosetta needs to support running code generated by x86_64 JITs at runtime (note that this is actually often slower than simply not using a JIT in the first place, to the extent that JavaScriptCore at least has a mechanism to ensure it does not use the JITs if its running under rosetta for this reason). Then as you say programs like WINE are definitionally not using platform binaries, so any code that they load and start to execute looks basically identical to "this program generated code at runtime". I do wonder if there's something that could be done either as some kind of Rosetta API to register a block of code as being logically static to allow some kind of caching, or by wine doing some fairly butchered rewriting to convert the windows objects into something that looks like "normal" machos to essentially trick rosetta into the AOT and/or caching path. [1] Until the 2023 rosetta update the linux VM support only supported JIT translation, that update added support for a more Mac like model where there's a central daemon to handle loading binaries and libraries so they can be cached and shared