4 ms·
It probably just hooks mmap() (or mprotect()) and looks for when the JIT sets PROT_EXEC on memory it hasn't translated yet. Here's a tutorial for a simple JIT
by lsllc 6y ago
It probably just hooks mmap() (or mprotect()) and looks for when the JIT sets PROT_EXEC on memory it hasn't translated yet.
Here's a tutorial for a simple JIT that uses mmap() with PROT_EXEC to write machine code to before executing:
https://github.com/spencertipping/jit-tutorial https://github.com/spencertipping/jit-tutorial
- monocasa 6y agoSince they don't enforce W^X you need to go a little deeper than that since some JITs reuse the same memory later for new traces. So what you'll do is silently enforce W^X to trap code modifications, but not propagate those traps to the emulated code.
- lsllc 6y agoRight, if you look at the example JIT project I posted, they start by creating the memory with mmap() and PROT_EXEC before generating any code. So yes, you'd need to trap subsequent writes to allow for retranslation [I assume they have hooks in the Darwin kernel for this].
- monocasa 6y agoOr they just enforce W^X in the rosetta runtime by intercepting client mmap calls and fix it up as far as the client code is concerned by catching SIGBUS first. I don't see anything here that requires extra kernel hooks.
- anyfoo 6y agoPages containing x86 code are never actually being marked executable in the (ARM) pagetables, because that would be nonsensical: The CPU itself does not know how to run x86 code. mmap/mprotect from x86 with the exec bit therefore does something else than native code.
- monocasa 6y agoRight, but the x86 code _thinks_ it does, and those semantics have to be maintained. What that practically means is that x86 executable pages get mapped as RO by default as seen by the ARM side even if they would normally be RWX. Modifications trap, the handler mprotects the pages as writeable, flush the translation cache for that region, then returns from the trap. Then on an x86 jump to that region, the JIT cache sees no traces for that region, marks the page as RO again so that modifications will trap, and starts recompiling (or maybe just interpreting; they may be using a tiered approach in the JIT).
- anyfoo 6y agoYeah, I specifically said they don't get marked executable in the page tables. From the x86 code's perspective, they of course look executable.
- api 6y ago"Just" This is some serious black magic, especially if it actually works. Even if it only works some of the time I give it an A for effort, since they could easily have just said "JITs will not work with Rosetta." Most applications do not contain JITs. My guess is there are enough important Mac apps that contain JITs of some form to merit this work, probably high performance math and graphics kernels that JIT processing pipelines or something like that. I wonder if some pro studio editing applications do this.
- needusername 6y ago> Most applications do not contain JITs. Java apps and JavaScript apps (Electron) come to mind.
- api 6y agoTrue, but those are trivial to port as there are already mature JDKs and JavaScript VMs for ARM64. Porting an electron app should be a matter of checking it out and building it with ARM support. Applications with internal JITs custom-built for X64 are going to be a lot tougher.
- monocasa 6y agoElectron doesn't support aarch64 builds compiled from aarch64 hosts yet (so you have to cross compile), and doesn't support cross compiling if you have any native code addons. Some apps out there are between a rock and a hard place if it weren't for rosetta.
- dtech 6y agoI suspect this will change fairly soon now that there will be a significant non-mobile aarch64 market in the future
- monocasa 6y agoThere's still a non trivial amount of time to get that propagated. First merged into chromium, the merged into electron, then actually used by the client programs.