mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-09 04:04:21 +02:00
The constraints introduced by shared code buffers make supporting calls with the previous layout impossible. The main additional constraint imposed by call-ret that if a host location is ever pushed onto the call-ret stack, then it must forever be a valid jump target. While this is reasonable in the: unlinked, direct linked, unlinked, direct linked case; it's almost impossible to achieve in the: unlinked, indirect linked, unlinked, direct linked case while ensuring all backpatching cases are valid with the current approach. To solve this introduce an additional layer of indirection, jump thunks, these are emitted at the end of a multiblock and are used to handle the two cases of calling the initial linker, and calling an indirect linked block. Initially at the ExitFunction location a branch/call to a unique jump thunk will be emitted, which will have the code layout: 00: b 0x8 04: br TMP1 08: ldr TMP1, <Shared exit linker> 0c: blr TMP1 10: HostCode 18: GuestRIP 20: CallerOffset If a direct link can be performed, then the initial branch/call to the jump thunk can be linked/unlinked to point to the jump thunk in a single 32-bit atomic operation. For an indirect link, the HostCode member is updated with a 64 bit atomic operation, and then a 32 bit atomic operation is used to replace the branch at 00 with a load of HostCode. Indirect unlinks are done by placing back the b 0x8 at 00. Safety: (1) Sequential link (e.g. one waiting to lock, one locked and linking): Linking is idempotent, would just rewrite the same data atomically. (2) Simultaneous link or simultaneous delink: Impossible due to LookupCache locking. (3) Simultaneous link and execute: (3.1) Direct link: Either the direct link is observed at the thunk callsite, or it is not observed and the linker is entered - this is then just (1). (3.2) Indirect link: Either the branch at 00 in the thunk is observed to be replaced with an ldr, in which case the modified HostCode must be observed due to the cache flush. Alternatively the branch replacement isn't observed and it's just (1). (4) Simultaneous unlink and execute: (4.1) Direct link: Either the jump to the jump thunk is seen, which must be in its base unlinked state with the branch at 00 as that would be inserted by any previous indirect unlink. In such a case the linker would just be entered, giving (5). Alternatively the modified jump isn't seen and it calls the original host code (which is fine). (4.2) Indirect link: If an ldr is seen at 00, then the rest of that sequence will function fine as HostCode is left untouched. If a branch is seen at 00, then it will just call the linker giving (5). (5) Sequential unlink then link: Unlinking restores the callsite and jump thunk to their original contents (aside from a modified HostCode). Linking then works as usual.