Skip to content

Commit

Permalink
ARM64EC: Handle calls into ARM64EC code with an 8-byte-aligned SP
Browse files Browse the repository at this point in the history
ARM64 requires that SP is always 16-byte aligned for memory accesses,
but ARM64EC shares the SP between x64 code and ARM64 code, the former
of which doesn't enforce such a restriction. This causes crashes in
programs such as HITMAN 3 that don't correctly follow the Windows ABI
and call into system library functions with SP only 8-byte-aligned.
Fixup stack alignment in such cases by leaving the 8-byte return
address on the stack and returning to a lone 'ret' instruction instead.
  • Loading branch information
bylaws committed Jul 12, 2024
1 parent 5dc8530 commit 1059279
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Source/Windows/ARM64EC/Module.S
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,17 @@ ExitFunctionEC:
add x17, x17, x23 // Resolve entry thunk address.

mov x4, sp
tbz x4, #3, ret_sp_misaligned
ldr lr, [x4], #0x8 // Pop the return address into lr.
mov sp, x4

ret_sp_aligned:
br x17

ret_sp_misaligned:
// In the case of the x64 caller leaving sp only 8-byte aligned, leave the return address on the stack to keep 16-byte
// alignment and have the callee return to an x86 ret instruction. FEX can then return to the actual caller keeping
// the misaligned RSP.
adrp lr, X64ReturnInstr
ldr lr, [lr, #:lo12:X64ReturnInstr]
br x17
4 changes: 4 additions & 0 deletions Source/Windows/ARM64EC/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ desc: Implements the ARM64EC BT module API using FEXCore
#include <wine/debug.h>

class ECSyscallHandler;
void* X64ReturnInstr; // See Module.S
extern void* ExitFunctionEC;

struct ThreadCPUArea {
Expand Down Expand Up @@ -166,6 +167,9 @@ void ProcessInit() {
CTX->InitCore();
InvalidationTracker.emplace(*CTX, Threads);
CPUFeatures.emplace(*CTX);

X64ReturnInstr = ::VirtualAlloc(nullptr, FEXCore::Utils::FEX_PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
*reinterpret_cast<uint8_t*>(X64ReturnInstr) = 0xc3;
}

void ProcessTerm() {}
Expand Down

0 comments on commit 1059279

Please sign in to comment.