From 2d4bf97cac366e07d00091cb64085d18f4bca9f8 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 4 May 2023 14:23:11 -0700 Subject: [PATCH] FEXCore: Moves SIGBUS handler to FEXCore/Utils This can be done in an OS agnostic fashion. FEXCore knows the details of its JIT and should be done in FEXCore itself. The frontend is only necessary to inform FEXCore where the fault occured and provide the array of GPRs for accessing and modifying the signal state. This is necessary for supporting both Linux and Wine signal contexts with their unaligned access handlers. --- External/FEXCore/Source/CMakeLists.txt | 6 + .../Source/Utils}/ArchHelpers/Arm64.cpp | 354 +++++++----------- .../Source/Utils}/ArchHelpers/Arm64_stubs.cpp | 2 +- .../FEXCore/Utils}/ArchHelpers/Arm64.h | 33 +- .../ArchHelpers/MContext.h | 10 +- .../{LinuxSyscalls => ArchHelpers}/UContext.h | 0 .../Tools/FEXLoader/ArchHelpers/WinContext.h | 65 ++++ Source/Tools/FEXLoader/ELFCodeLoader.h | 2 +- .../FEXLoader/LinuxSyscalls/CMakeLists.txt | 6 - .../LinuxSyscalls/SignalDelegator.cpp | 32 +- .../FEXLoader/LinuxSyscalls/SignalDelegator.h | 2 +- .../FEXLoader/LinuxSyscalls/x32/Signals.cpp | 2 +- .../FEXLoader/LinuxSyscalls/x32/Thread.cpp | 2 +- .../TestHarnessRunner/HostRunner.cpp | 2 +- 14 files changed, 260 insertions(+), 258 deletions(-) rename {Source/Tools/FEXLoader/LinuxSyscalls => External/FEXCore/Source/Utils}/ArchHelpers/Arm64.cpp (86%) rename {Source/Tools/FEXLoader/LinuxSyscalls => External/FEXCore/Source/Utils}/ArchHelpers/Arm64_stubs.cpp (93%) rename {Source/Tools/FEXLoader/LinuxSyscalls => External/FEXCore/include/FEXCore/Utils}/ArchHelpers/Arm64.h (72%) rename Source/Tools/FEXLoader/{LinuxSyscalls => }/ArchHelpers/MContext.h (97%) rename Source/Tools/FEXLoader/{LinuxSyscalls => ArchHelpers}/UContext.h (100%) create mode 100644 Source/Tools/FEXLoader/ArchHelpers/WinContext.h diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index cbe6ebc93..fd46bd14e 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -148,6 +148,12 @@ set (SRCS Utils/Profiler.cpp ) +if (_M_ARM_64) + list(APPEND SRCS Utils/ArchHelpers/Arm64.cpp) +else() + list(APPEND SRCS Utils/ArchHelpers/Arm64_stubs.cpp) +endif() + if (ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT) list(APPEND FEXCORE_BASE_SRCS Utils/AllocatorOverride.cpp) diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.cpp b/External/FEXCore/Source/Utils/ArchHelpers/Arm64.cpp similarity index 86% rename from Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.cpp rename to External/FEXCore/Source/Utils/ArchHelpers/Arm64.cpp index 008e48d5d..1dd2c1b80 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.cpp +++ b/External/FEXCore/Source/Utils/ArchHelpers/Arm64.cpp @@ -1,15 +1,13 @@ -#include "LinuxSyscalls/ArchHelpers/Arm64.h" -#include "LinuxSyscalls/ArchHelpers/MContext.h" - #include #include #include +#include #include #include #include -namespace FEX::ArchHelpers::Arm64 { +namespace FEXCore::ArchHelpers::Arm64 { FEXCORE_TELEMETRY_STATIC_INIT(SplitLock, TYPE_HAS_SPLIT_LOCKS); FEXCORE_TELEMETRY_STATIC_INIT(SplitLock16B, TYPE_16BYTE_SPLIT); FEXCORE_TELEMETRY_STATIC_INIT(Cas16Tear, TYPE_CAS_16BIT_TEAR); @@ -241,20 +239,16 @@ std::pair DoLoad128(uint64_t Addr) { return {ResultLower, ResultUpper}; } -static bool RunCASPAL(void *_ucontext, void *_info, uint32_t Size, uint32_t DesiredReg1, uint32_t DesiredReg2, uint32_t ExpectedReg1, uint32_t ExpectedReg2, uint32_t AddressReg) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - - //Bus_ADRALN check happens in HandleCASPAL and HandleCASPAL_ARMv8 - +static bool RunCASPAL(uint64_t *GPRs, uint32_t Size, uint32_t DesiredReg1, uint32_t DesiredReg2, uint32_t ExpectedReg1, uint32_t ExpectedReg2, uint32_t AddressReg) { if (Size == 0) { // 32bit - uint64_t Addr = mcontext->regs[AddressReg]; + uint64_t Addr = GPRs[AddressReg]; - uint32_t DesiredLower = mcontext->regs[DesiredReg1]; - uint32_t DesiredUpper = mcontext->regs[DesiredReg2]; + uint32_t DesiredLower = GPRs[DesiredReg1]; + uint32_t DesiredUpper = GPRs[DesiredReg2]; - uint32_t ExpectedLower = mcontext->regs[ExpectedReg1]; - uint32_t ExpectedUpper = mcontext->regs[ExpectedReg2]; + uint32_t ExpectedLower = GPRs[ExpectedReg1]; + uint32_t ExpectedUpper = GPRs[ExpectedReg2]; // Cross-cacheline CAS doesn't work on ARM // It isn't even guaranteed to work on x86 @@ -352,16 +346,16 @@ static bool RunCASPAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desi // If the bits changed that we were wanting to change then we have failed and can return // We need to extract the bits and return them in EXPECTED uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg1] = FailedResult & ~0U; - mcontext->regs[ExpectedReg2] = FailedResult >> 32; + GPRs[ExpectedReg1] = FailedResult & ~0U; + GPRs[ExpectedReg2] = FailedResult >> 32; return true; } // This happens in the case that between Load and CAS that something has store our desired in to the memory location // This means our CAS fails because what we wanted to store was already stored uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg1] = FailedResult & ~0U; - mcontext->regs[ExpectedReg2] = FailedResult >> 32; + GPRs[ExpectedReg1] = FailedResult & ~0U; + GPRs[ExpectedReg2] = FailedResult >> 32; return true; } } @@ -415,8 +409,8 @@ static bool RunCASPAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desi // This happens in the case that between Load and CAS that something has store our desired in to the memory location // This means our CAS fails because what we wanted to store was already stored uint64_t FailedResult = FailedResultOurBits >> (Alignment * 8); - mcontext->regs[ExpectedReg1] = FailedResult & ~0U; - mcontext->regs[ExpectedReg2] = FailedResult >> 32; + GPRs[ExpectedReg1] = FailedResult & ~0U; + GPRs[ExpectedReg2] = FailedResult >> 32; return true; } } @@ -425,14 +419,7 @@ static bool RunCASPAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desi return false; } -bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) { - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } - +bool HandleCASPAL(uint32_t Instr, uint64_t *GPRs) { uint32_t Size = (Instr >> 30) & 1; uint32_t DesiredReg1 = Instr & 0b11111; @@ -441,18 +428,10 @@ bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr) { uint32_t ExpectedReg2 = ExpectedReg1 + 1; uint32_t AddressReg = (Instr >> 5) & 0b11111; - return RunCASPAL(_ucontext, _info, Size, DesiredReg1, DesiredReg2, ExpectedReg1, ExpectedReg2, AddressReg); + return RunCASPAL(GPRs, Size, DesiredReg1, DesiredReg2, ExpectedReg1, ExpectedReg2, AddressReg); } -uint64_t HandleCASPAL_ARMv8(void *_ucontext, void *_info, uint32_t Instr) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return 0; - } - +uint64_t HandleCASPAL_ARMv8(uint32_t Instr, uintptr_t ProgramCounter, uint64_t *GPRs) { // caspair // [1] ldaxp(TMP2.W(), TMP3.W(), MemOperand(MemSrc)); <-- DataReg & AddrReg // [2] cmp(TMP2.W(), Expected.first.W()); <-- ExpectedReg1 @@ -467,7 +446,7 @@ uint64_t HandleCASPAL_ARMv8(void *_ucontext, void *_info, uint32_t Instr) { // [11] mov(Dst.second.W(), TMP3.W()); // [12] clrex(); - uint32_t *PC = (uint32_t*)ArchHelpers::Context::GetPc(_ucontext); + uint32_t *PC = (uint32_t*)ProgramCounter; uint32_t Size = (Instr >> 30) & 1; uint32_t AddrReg = (Instr >> 5) & 0x1F; @@ -495,15 +474,15 @@ uint64_t HandleCASPAL_ARMv8(void *_ucontext, void *_info, uint32_t Instr) { else { uint32_t NextInstr = PC[1]; if ((NextInstr & ArchHelpers::Arm64::CLREX_MASK) == ArchHelpers::Arm64::CLREX_INST) { - uint64_t Addr = mcontext->regs[AddrReg]; + uint64_t Addr = GPRs[AddrReg]; auto Res = DoLoad128(Addr); // We set the result register if it isn't a zero register if (DataReg != 31) { - mcontext->regs[DataReg] = std::get<0>(Res); + GPRs[DataReg] = std::get<0>(Res); } if (DataReg2 != 31) { - mcontext->regs[DataReg2] = std::get<1>(Res); + GPRs[DataReg2] = std::get<1>(Res); } // Skip ldaxp and clrex @@ -528,25 +507,18 @@ uint64_t HandleCASPAL_ARMv8(void *_ucontext, void *_info, uint32_t Instr) { } //mov expected into the temp registers used by JIT - mcontext->regs[DataReg] = mcontext->regs[ExpectedReg1]; - mcontext->regs[DataReg2] = mcontext->regs[ExpectedReg2]; + GPRs[DataReg] = GPRs[ExpectedReg1]; + GPRs[DataReg2] = GPRs[ExpectedReg2]; - if(RunCASPAL(_ucontext, _info, Size, DesiredReg1, DesiredReg2, DataReg, DataReg2, AddrReg)) { + if(RunCASPAL(GPRs, Size, DesiredReg1, DesiredReg2, DataReg, DataReg2, AddrReg)) { return 9 * sizeof(uint32_t); // skip to mov + clrex } else { return 0; } } -bool HandleAtomicVectorStore(void *_ucontext, void *_info, uint32_t Instr) { - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return 0; - } - - uint32_t *PC = (uint32_t*)ArchHelpers::Context::GetPc(_ucontext); +static bool HandleAtomicVectorStore(uint32_t Instr, uintptr_t ProgramCounter) { + uint32_t *PC = (uint32_t*)ProgramCounter; uint32_t Size = (Instr >> 30) & 1; uint32_t AddrReg = (Instr >> 5) & 0x1F; @@ -1280,10 +1252,8 @@ uint64_t DoCAS64( } } -static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t DesiredReg, uint32_t ExpectedReg, uint32_t AddressReg) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - - uint64_t Addr = mcontext->regs[AddressReg]; +static bool RunCASAL(uint64_t *GPRs, uint32_t Size, uint32_t DesiredReg, uint32_t ExpectedReg, uint32_t AddressReg) { + uint64_t Addr = GPRs[AddressReg]; // Cross-cacheline CAS doesn't work on ARM // It isn't even guaranteed to work on x86 @@ -1297,8 +1267,8 @@ static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desir // Only need to handle 16, 32, 64 if (Size == 2) { auto Res = DoCAS16( - mcontext->regs[DesiredReg], - mcontext->regs[ExpectedReg], + GPRs[DesiredReg], + GPRs[ExpectedReg], Addr, [](uint16_t, uint16_t Expected) -> uint16_t { // Expected is just Expected @@ -1312,14 +1282,14 @@ static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desir // Regardless of pass or fail // We set the result register if it isn't a zero register if (ExpectedReg != 31) { - mcontext->regs[ExpectedReg] = Res; + GPRs[ExpectedReg] = Res; } return true; } else if (Size == 4) { auto Res = DoCAS32( - mcontext->regs[DesiredReg], - mcontext->regs[ExpectedReg], + GPRs[DesiredReg], + GPRs[ExpectedReg], Addr, [](uint32_t, uint32_t Expected) -> uint32_t { // Expected is just Expected @@ -1333,14 +1303,14 @@ static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desir // Regardless of pass or fail // We set the result register if it isn't a zero register if (ExpectedReg != 31) { - mcontext->regs[ExpectedReg] = Res; + GPRs[ExpectedReg] = Res; } return true; } else if (Size == 8) { auto Res = DoCAS64( - mcontext->regs[DesiredReg], - mcontext->regs[ExpectedReg], + GPRs[DesiredReg], + GPRs[ExpectedReg], Addr, [](uint64_t, uint64_t Expected) -> uint64_t { // Expected is just Expected @@ -1354,7 +1324,7 @@ static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desir // Regardless of pass or fail // We set the result register if it isn't a zero register if (ExpectedReg != 31) { - mcontext->regs[ExpectedReg] = Res; + GPRs[ExpectedReg] = Res; } return true; } @@ -1362,37 +1332,22 @@ static bool RunCASAL(void *_ucontext, void *_info, uint32_t Size, uint32_t Desir return false; } -bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr) { - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } - +static bool HandleCASAL(uint64_t *GPRs, uint32_t Instr) { uint32_t Size = 1 << (Instr >> 30); uint32_t DesiredReg = Instr & 0b11111; uint32_t ExpectedReg = (Instr >> 16) & 0b11111; uint32_t AddressReg = (Instr >> 5) & 0b11111; - return RunCASAL(_ucontext, _info, Size, DesiredReg, ExpectedReg, AddressReg); + return RunCASAL(GPRs, Size, DesiredReg, ExpectedReg, AddressReg); } -bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } - +static bool HandleAtomicMemOp(uint32_t Instr, uint64_t *GPRs) { uint32_t Size = 1 << (Instr >> 30); uint32_t ResultReg = Instr & 0b11111; uint32_t SourceReg = (Instr >> 16) & 0b11111; uint32_t AddressReg = (Instr >> 5) & 0b11111; - uint64_t Addr = mcontext->regs[AddressReg]; + uint64_t Addr = GPRs[AddressReg]; uint8_t Op = (Instr >> 12) & 0xF; @@ -1445,7 +1400,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { } auto Res = DoCAS16( - mcontext->regs[SourceReg], + GPRs[SourceReg], 0, // Unused Addr, NOPExpected, @@ -1453,7 +1408,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { // If we passed and our destination register is not zero // Then we need to update the result register with what was in memory if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1506,7 +1461,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { } auto Res = DoCAS32( - mcontext->regs[SourceReg], + GPRs[SourceReg], 0, // Unused Addr, NOPExpected, @@ -1514,7 +1469,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { // If we passed and our destination register is not zero // Then we need to update the result register with what was in memory if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1567,7 +1522,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { } auto Res = DoCAS64( - mcontext->regs[SourceReg], + GPRs[SourceReg], 0, // Unused Addr, NOPExpected, @@ -1575,7 +1530,7 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { // If we passed and our destination register is not zero // Then we need to update the result register with what was in memory if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1583,26 +1538,19 @@ bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr) { return false; } -bool HandleAtomicLoad(void *_ucontext, void *_info, uint32_t Instr, int64_t Offset) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } +static bool HandleAtomicLoad(uint32_t Instr, uint64_t *GPRs, int64_t Offset) { uint32_t Size = 1 << (Instr >> 30); uint32_t ResultReg = Instr & 0b11111; uint32_t AddressReg = (Instr >> 5) & 0b11111; - uint64_t Addr = mcontext->regs[AddressReg] + Offset; + uint64_t Addr = GPRs[AddressReg] + Offset; if (Size == 2) { auto Res = DoLoad16(Addr); // We set the result register if it isn't a zero register if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1610,7 +1558,7 @@ bool HandleAtomicLoad(void *_ucontext, void *_info, uint32_t Instr, int64_t Offs auto Res = DoLoad32(Addr); // We set the result register if it isn't a zero register if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1618,7 +1566,7 @@ bool HandleAtomicLoad(void *_ucontext, void *_info, uint32_t Instr, int64_t Offs auto Res = DoLoad64(Addr); // We set the result register if it isn't a zero register if (ResultReg != 31) { - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } return true; } @@ -1626,25 +1574,18 @@ bool HandleAtomicLoad(void *_ucontext, void *_info, uint32_t Instr, int64_t Offs return false; } -bool HandleAtomicStore(void *_ucontext, void *_info, uint32_t Instr, int64_t Offset) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } +static bool HandleAtomicStore(uint32_t Instr, uint64_t *GPRs, int64_t Offset) { uint32_t Size = 1 << (Instr >> 30); uint32_t DataReg = Instr & 0x1F; uint32_t AddressReg = (Instr >> 5) & 0b11111; - uint64_t Addr = mcontext->regs[AddressReg] + Offset; + uint64_t Addr = GPRs[AddressReg] + Offset; constexpr bool DoRetry = false; if (Size == 2) { DoCAS16( - mcontext->regs[DataReg], + GPRs[DataReg], 0, // Unused Addr, [](uint16_t SrcVal, uint16_t) -> uint16_t { @@ -1659,7 +1600,7 @@ bool HandleAtomicStore(void *_ucontext, void *_info, uint32_t Instr, int64_t Off } else if (Size == 4) { DoCAS32( - mcontext->regs[DataReg], + GPRs[DataReg], 0, // Unused Addr, [](uint32_t SrcVal, uint32_t) -> uint32_t { @@ -1674,7 +1615,7 @@ bool HandleAtomicStore(void *_ucontext, void *_info, uint32_t Instr, int64_t Off } else if (Size == 8) { DoCAS64( - mcontext->regs[DataReg], + GPRs[DataReg], 0, // Unused Addr, [](uint64_t SrcVal, uint64_t) -> uint64_t { @@ -1691,36 +1632,8 @@ bool HandleAtomicStore(void *_ucontext, void *_info, uint32_t Instr, int64_t Off return false; } -bool HandleAtomicLoad128(void *_ucontext, void *_info, uint32_t Instr) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return false; - } - uint32_t ResultReg = Instr & 0b11111; - uint32_t ResultReg2 = (Instr >> 10) & 0x1F; - uint32_t AddressReg = (Instr >> 5) & 0b11111; - - uint64_t Addr = mcontext->regs[AddressReg]; - - auto Res = DoLoad128(Addr); - // We set the result register if it isn't a zero register - if (ResultReg != 31) { - mcontext->regs[ResultReg] = std::get<0>(Res); - } - if (ResultReg2 != 31) { - mcontext->regs[ResultReg2] = std::get<1>(Res); - } - - return true; -} - -static uint64_t HandleCAS_NoAtomics(void *_ucontext, void *_info) +static uint64_t HandleCAS_NoAtomics(uintptr_t ProgramCounter, uint64_t *GPRs) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - // ARMv8.0 CAS // [1] ldaxrb(TMP2.W(), MemOperand(MemSrc)) // [2] cmp (TMP2.W(), Expected.W()) @@ -1732,7 +1645,7 @@ static uint64_t HandleCAS_NoAtomics(void *_ucontext, void *_info) // [8] mov (.., TMP2.W()); // [9] clrex - uint32_t *PC = (uint32_t*)ArchHelpers::Context::GetPc(_ucontext); + uint32_t *PC = (uint32_t*)ProgramCounter; uint32_t Instr = PC[0]; uint32_t Size = 1 << (Instr >> 30); uint32_t AddressReg = GetRnReg(Instr); @@ -1755,25 +1668,17 @@ static uint64_t HandleCAS_NoAtomics(void *_ucontext, void *_info) } } //set up CASAL by doing mov(TMP2, Expected) - mcontext->regs[ResultReg] = mcontext->regs[ExpectedReg]; + GPRs[ResultReg] = GPRs[ExpectedReg]; - if(RunCASAL(_ucontext, _info, Size, DesiredReg, ResultReg, AddressReg)) { + if(RunCASAL(GPRs, Size, DesiredReg, ResultReg, AddressReg)) { return 7 * sizeof(uint32_t); //jump to mov to allocated register } else { return 0; } } -uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { - mcontext_t* mcontext = &reinterpret_cast(_ucontext)->uc_mcontext; - siginfo_t* info = reinterpret_cast(_info); - - if (info->si_code != BUS_ADRALN) { - // This only handles alignment problems - return 0; - } - - uint32_t *PC = (uint32_t*)ArchHelpers::Context::GetPc(_ucontext); +static uint64_t HandleAtomicLoadstoreExclusive(uintptr_t ProgramCounter, uint64_t *GPRs) { + uint32_t *PC = (uint32_t*)ProgramCounter; uint32_t Instr = PC[0]; // Atomic Add @@ -1815,7 +1720,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { // - The [5]mov instruction source is always the destination register from [1] ldaxr* uint32_t ResultReg = GetRdReg(Instr); uint32_t AddressReg = GetRnReg(Instr); - uint64_t Addr = mcontext->regs[AddressReg]; + uint64_t Addr = GPRs[AddressReg]; size_t NumInstructionsToSkip = 0; @@ -1852,7 +1757,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { } else if ((NextInstr & ArchHelpers::Arm64::ALU_OP_MASK) == ArchHelpers::Arm64::CMP_INST || (NextInstr & ArchHelpers::Arm64::ALU_OP_MASK) == ArchHelpers::Arm64::CMP_SHIFT_INST ) { - return HandleCAS_NoAtomics(_ucontext, _info); //ARMv8.0 CAS + return HandleCAS_NoAtomics(ProgramCounter, GPRs); //ARMv8.0 CAS } else if ((NextInstr & ArchHelpers::Arm64::ALU_OP_MASK) == ArchHelpers::Arm64::AND_INST) { AtomicOp = ExclusiveAtomicPairType::TYPE_AND; @@ -1995,7 +1900,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { } auto Res = DoCAS16( - mcontext->regs[DataSourceReg], + GPRs[DataSourceReg], 0, // Unused Addr, NOPExpected, @@ -2004,7 +1909,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { if (AtomicFetch && ResultReg != 31) { // On atomic fetch then we store the resulting value back in to the loadacquire destination register // We want the memory value BEFORE the ALU op - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } } else if (Size == 4) { @@ -2049,7 +1954,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { } auto Res = DoCAS32( - mcontext->regs[DataSourceReg], + GPRs[DataSourceReg], 0, // Unused Addr, NOPExpected, @@ -2058,7 +1963,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { if (AtomicFetch && ResultReg != 31) { // On atomic fetch then we store the resulting value back in to the loadacquire destination register // We want the memory value BEFORE the ALU op - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } } else if (Size == 8) { @@ -2103,7 +2008,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { } auto Res = DoCAS64( - mcontext->regs[DataSourceReg], + GPRs[DataSourceReg], 0, // Unused Addr, NOPExpected, @@ -2111,7 +2016,7 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { if (AtomicFetch && ResultReg != 31) { // On atomic fetch then we store the resulting value back in to the loadacquire destination register // We want the memory value BEFORE the ALU op - mcontext->regs[ResultReg] = Res; + GPRs[ResultReg] = Res; } } @@ -2119,15 +2024,17 @@ uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info) { return NumInstructionsToSkip * 4; } -bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { +[[nodiscard]] std::pair HandleUnalignedAccess(bool ParanoidTSO, uintptr_t ProgramCounter, uint64_t *GPRs) { #ifdef _M_ARM_64 constexpr bool is_arm64 = true; #else constexpr bool is_arm64 = false; #endif + constexpr auto NotHandled = std::make_pair(false, 0); + if constexpr (is_arm64) { - uint32_t *PC = (uint32_t*)ArchHelpers::Context::GetPc(ucontext); + uint32_t *PC = (uint32_t*)ProgramCounter; uint32_t Instr = PC[0]; // 1 = 16bit @@ -2139,14 +2046,13 @@ bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { if ((Instr & 0x3F'FF'FC'00) == 0x08'DF'FC'00 || // LDAR* (Instr & 0x3F'FF'FC'00) == 0x38'BF'C0'00) { // LDAPR* if (ParanoidTSO) { - if (ArchHelpers::Arm64::HandleAtomicLoad(ucontext, info, Instr, 0)) { + if (ArchHelpers::Arm64::HandleAtomicLoad(Instr, GPRs, 0)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAR*: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAR*: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else { @@ -2157,20 +2063,20 @@ bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { PC[-1] = DMB; PC[0] = LDR; PC[1] = DMB; + ClearICache(&PC[-1], 16); // Back up one instruction and have another go - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) - 4); + return std::make_pair(true, -4); } } else if ( (Instr & 0x3F'FF'FC'00) == 0x08'9F'FC'00) { // STLR* if (ParanoidTSO) { - if (ArchHelpers::Arm64::HandleAtomicStore(ucontext, info, Instr, 0)) { + if (ArchHelpers::Arm64::HandleAtomicStore(Instr, GPRs, 0)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS STLR*: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS STLR*: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else { @@ -2181,22 +2087,22 @@ bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { PC[-1] = DMB; PC[0] = STR; PC[1] = DMB; + ClearICache(&PC[-1], 16); // Back up one instruction and have another go - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) - 4); + return std::make_pair(true, -4); } } else if ((Instr & RCPC2_MASK) == LDAPUR_INST) { // LDAPUR* // Extract the 9-bit offset from the instruction int32_t Offset = static_cast(Instr) << 11 >> 23; if (ParanoidTSO) { - if (ArchHelpers::Arm64::HandleAtomicLoad(ucontext, info, Instr, Offset)) { + if (ArchHelpers::Arm64::HandleAtomicLoad(Instr, GPRs, Offset)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAPUR*: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAPUR*: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else { @@ -2208,22 +2114,22 @@ bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { PC[-1] = DMB; PC[0] = LDUR; PC[1] = DMB; + ClearICache(&PC[-1], 16); // Back up one instruction and have another go - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) - 4); + return std::make_pair(true, -4); } } else if ((Instr & RCPC2_MASK) == STLUR_INST) { // STLUR* // Extract the 9-bit offset from the instruction int32_t Offset = static_cast(Instr) << 11 >> 23; if (ParanoidTSO) { - if (ArchHelpers::Arm64::HandleAtomicStore(ucontext, info, Instr, Offset)) { + if (ArchHelpers::Arm64::HandleAtomicStore(Instr, GPRs, Offset)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDLUR*: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDLUR*: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else { @@ -2235,88 +2141,82 @@ bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext) { PC[-1] = DMB; PC[0] = STUR; PC[1] = DMB; + ClearICache(&PC[-1], 16); // Back up one instruction and have another go - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) - 4); + return std::make_pair(true, -4); } } else if ((Instr & ArchHelpers::Arm64::LDAXP_MASK) == ArchHelpers::Arm64::LDAXP_INST) { // LDAXP //Should be compare and swap pair only. LDAXP not used elsewhere - uint64_t BytesToSkip = ArchHelpers::Arm64::HandleCASPAL_ARMv8(ucontext, info, Instr); + uint64_t BytesToSkip = ArchHelpers::Arm64::HandleCASPAL_ARMv8(Instr, ProgramCounter, GPRs); if (BytesToSkip) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + BytesToSkip); - return true; + return std::make_pair(true, BytesToSkip); } else { - if (ArchHelpers::Arm64::HandleAtomicVectorStore(ucontext, info, Instr)) { - return true; + if (ArchHelpers::Arm64::HandleAtomicVectorStore(Instr, ProgramCounter)) { + return std::make_pair(true, 0); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAXP: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAXP: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } } else if ((Instr & ArchHelpers::Arm64::STLXP_MASK) == ArchHelpers::Arm64::STLXP_INST) { // STLXP //Should not trigger - middle of an LDAXP/STAXP pair. - LogMan::Msg::EFmt("Unhandled JIT SIGBUS STLXP: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS STLXP: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } else if ((Instr & ArchHelpers::Arm64::CASPAL_MASK) == ArchHelpers::Arm64::CASPAL_INST) { // CASPAL - if (ArchHelpers::Arm64::HandleCASPAL(ucontext, info, Instr)) { + if (ArchHelpers::Arm64::HandleCASPAL(Instr, GPRs)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS CASPAL: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS CASPAL: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else if ((Instr & ArchHelpers::Arm64::CASAL_MASK) == ArchHelpers::Arm64::CASAL_INST) { // CASAL - if (ArchHelpers::Arm64::HandleCASAL(ucontext, info, Instr)) { + if (ArchHelpers::Arm64::HandleCASAL(GPRs, Instr)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS CASAL: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS CASAL: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else if ((Instr & ArchHelpers::Arm64::ATOMIC_MEM_MASK) == ArchHelpers::Arm64::ATOMIC_MEM_INST) { // Atomic memory op - if (ArchHelpers::Arm64::HandleAtomicMemOp(ucontext, info, Instr)) { + if (ArchHelpers::Arm64::HandleAtomicMemOp(Instr, GPRs)) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + 4); - return true; + return std::make_pair(true, 4); } else { uint8_t Op = (PC[0] >> 12) & 0xF; - LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}: PC: {} Instruction: 0x{:08x}\n", Op, fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS Atomic mem op 0x{:02x}: PC: 0x{:x} Instruction: 0x{:08x}\n", Op, ProgramCounter, PC[0]); + return NotHandled; } } else if ((Instr & ArchHelpers::Arm64::LDAXR_MASK) == ArchHelpers::Arm64::LDAXR_INST) { // LDAXR* - uint64_t BytesToSkip = ArchHelpers::Arm64::HandleAtomicLoadstoreExclusive(ucontext, info); + uint64_t BytesToSkip = ArchHelpers::Arm64::HandleAtomicLoadstoreExclusive(ProgramCounter, GPRs); if (BytesToSkip) { // Skip this instruction now - ArchHelpers::Context::SetPc(ucontext, ArchHelpers::Context::GetPc(ucontext) + BytesToSkip); - return true; + return std::make_pair(true, BytesToSkip); } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAXR: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAXR: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } } else { - LogMan::Msg::EFmt("Unhandled JIT SIGBUS: PC: {} Instruction: 0x{:08x}\n", fmt::ptr(PC), PC[0]); - return false; + LogMan::Msg::EFmt("Unhandled JIT SIGBUS: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]); + return NotHandled; } - - ClearICache(&PC[-1], 16); - return true; } - return false; + return NotHandled; } + } diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64_stubs.cpp b/External/FEXCore/Source/Utils/ArchHelpers/Arm64_stubs.cpp similarity index 93% rename from Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64_stubs.cpp rename to External/FEXCore/Source/Utils/ArchHelpers/Arm64_stubs.cpp index 4a4806960..8ed629f23 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64_stubs.cpp +++ b/External/FEXCore/Source/Utils/ArchHelpers/Arm64_stubs.cpp @@ -1,6 +1,6 @@ -#include "LinuxSyscalls/ArchHelpers/Arm64.h" #include +#include #include namespace FEXCore::ArchHelpers::Arm64 { diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.h b/External/FEXCore/include/FEXCore/Utils/ArchHelpers/Arm64.h similarity index 72% rename from Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.h rename to External/FEXCore/include/FEXCore/Utils/ArchHelpers/Arm64.h index bd00d0b01..d65dfe0d9 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/Arm64.h +++ b/External/FEXCore/include/FEXCore/Utils/ArchHelpers/Arm64.h @@ -1,8 +1,11 @@ #pragma once -#include +#include -namespace FEX::ArchHelpers::Arm64 { +#include +#include + +namespace FEXCore::ArchHelpers::Arm64 { constexpr uint32_t CASPAL_MASK = 0xBF'E0'FC'00; constexpr uint32_t CASPAL_INST = 0x08'60'FC'00; @@ -96,14 +99,20 @@ namespace FEX::ArchHelpers::Arm64 { return (Instr >> RM_OFFSET) & REGISTER_MASK; } - bool HandleAtomicLoad(void *_ucontext, void *_info, uint32_t Instr, int64_t Offset); - bool HandleAtomicStore(void *_ucontext, void *_info, uint32_t Instr, int64_t Offset); - bool HandleAtomicLoad128(void *_ucontext, void *_info, uint32_t Instr); - uint64_t HandleAtomicLoadstoreExclusive(void *_ucontext, void *_info); - bool HandleCASPAL(void *_ucontext, void *_info, uint32_t Instr); - uint64_t HandleCASPAL_ARMv8(void *_ucontext, void *_info, uint32_t Instr); - bool HandleAtomicVectorStore(void *_ucontext, void *_info, uint32_t Instr); - bool HandleCASAL(void *_ucontext, void *_info, uint32_t Instr); - bool HandleAtomicMemOp(void *_ucontext, void *_info, uint32_t Instr); - [[nodiscard]] bool HandleSIGBUS(bool ParanoidTSO, int Signal, void *info, void *ucontext); + /** + * @brief On ARM64 handles an unaligned memory access that the JIT has done. + * + * This is an OS agnostic handler where the frontend must provide FEXCore with the information necessary to know if this is safe. + * This does not check if the PC is within a JIT code buffer, the frontend must provide that safety with `CPUBackend::IsAddressInCodeBuffer`. + * + * @param ParanoidTSO If the unaligned fault needs to handled directly or can be backpatched. + * @param ProgramCounter The location in memory for the instruction that did the access + * @param GPRs The array of GPRs from the signal context. This will be modified and the host context needs to be updated on signal return. + * + * @return A pair where the first element is if the unaligned access has been handle and the second element is how many bytes to modify the host PC + * by. FEXCore will return a positive or negative offset depending on internal handling. + */ + [[nodiscard]] + FEX_DEFAULT_VISIBILITY + std::pair HandleUnalignedAccess(bool ParanoidTSO, uintptr_t ProgramCounter, uint64_t *GPRs); } diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/MContext.h b/Source/Tools/FEXLoader/ArchHelpers/MContext.h similarity index 97% rename from Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/MContext.h rename to Source/Tools/FEXLoader/ArchHelpers/MContext.h index 1026942b1..7c9b39ba1 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/ArchHelpers/MContext.h +++ b/Source/Tools/FEXLoader/ArchHelpers/MContext.h @@ -1,6 +1,6 @@ #pragma once -#include "LinuxSyscalls/UContext.h" +#include "UContext.h" #include #include @@ -142,6 +142,10 @@ static inline uint64_t GetArmReg(void* ucontext, uint32_t id) { return GetMContext(ucontext)->regs[id]; } +static inline uint64_t *GetArmGPRs(void* ucontext) { + return reinterpret_cast(GetMContext(ucontext)->regs); +} + static inline void SetArmReg(void* ucontext, uint32_t id, uint64_t val) { GetMContext(ucontext)->regs[id] = val; } @@ -308,6 +312,10 @@ static inline __uint128_t GetArmFPR(void* ucontext, uint32_t id) { ERROR_AND_DIE_FMT("Not implemented for x86 host"); } +static inline uint64_t *GetArmGPRs(void* ucontext) { + ERROR_AND_DIE_FMT("Not implemented for x86 host"); +} + static inline uint32_t GetProtectFlags(void* ucontext) { return GetMContext(ucontext)->gregs[REG_ERR]; } diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/UContext.h b/Source/Tools/FEXLoader/ArchHelpers/UContext.h similarity index 100% rename from Source/Tools/FEXLoader/LinuxSyscalls/UContext.h rename to Source/Tools/FEXLoader/ArchHelpers/UContext.h diff --git a/Source/Tools/FEXLoader/ArchHelpers/WinContext.h b/Source/Tools/FEXLoader/ArchHelpers/WinContext.h new file mode 100644 index 000000000..96412c1dc --- /dev/null +++ b/Source/Tools/FEXLoader/ArchHelpers/WinContext.h @@ -0,0 +1,65 @@ +#pragma once +#ifdef _WIN32 +#include + +namespace FEX::ArchHelpers::Context { +#ifdef _M_ARM_64 +static inline uint64_t GetSp(PCONTEXT Context) { + return Context->Sp; +} + +static inline uint64_t GetPc(PCONTEXT Context) { + return Context->Pc; +} + +static inline void SetSp(PCONTEXT Context, uint64_t val) { + Context->Sp = val; +} + +static inline void SetPc(PCONTEXT Context, uint64_t val) { + Context->Pc = val; +} + +static inline uint64_t GetState(PCONTEXT Context) { + return Context->X28; +} + +static inline void SetState(PCONTEXT Context, uint64_t val) { + Context->X28 = val; +} + +static inline uint64_t *GetArmGPRs(PCONTEXT Context) { + return Context->X; +} +#endif + +#ifdef _M_X86_64 +static inline uint64_t GetSp(PCONTEXT Context) { + return Context->Rsp; +} + +static inline uint64_t GetPc(PCONTEXT Context) { + return Context->Rip; +} + +static inline void SetSp(PCONTEXT Context, uint64_t val) { + Context->Rsp = val; +} + +static inline void SetPc(PCONTEXT Context, uint64_t val) { + Context->Rip = val; +} + +static inline uint64_t GetState(PCONTEXT Context) { + return Context->R14; +} + +static inline void SetState(PCONTEXT Context, uint64_t val) { + Context->R14 = val; +} + +#endif + +} + +#endif diff --git a/Source/Tools/FEXLoader/ELFCodeLoader.h b/Source/Tools/FEXLoader/ELFCodeLoader.h index 78705f761..a44cc4f75 100644 --- a/Source/Tools/FEXLoader/ELFCodeLoader.h +++ b/Source/Tools/FEXLoader/ELFCodeLoader.h @@ -1,11 +1,11 @@ #pragma once +#include "ArchHelpers/UContext.h" #include "Common/Config.h" #include "Common/FDUtils.h" #include "FEXCore/Utils/Allocator.h" #include "LinuxSyscalls/Syscalls.h" -#include "LinuxSyscalls/UContext.h" #include "VDSO_Emulation.h" #include "Linux/Utils/ELFParser.h" #include "Linux/Utils/ELFSymbolDatabase.h" diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/CMakeLists.txt b/Source/Tools/FEXLoader/LinuxSyscalls/CMakeLists.txt index e954528c3..a57dcd2fb 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/CMakeLists.txt +++ b/Source/Tools/FEXLoader/LinuxSyscalls/CMakeLists.txt @@ -1,7 +1,6 @@ add_compile_options(-fno-operator-names) set (SRCS - ArchHelpers/Arm64_stubs.cpp EmulatedFiles/EmulatedFiles.cpp FileManagement.cpp LinuxAllocator.cpp @@ -64,11 +63,6 @@ set (SRCS Syscalls/NotImplemented.cpp Syscalls/Stubs.cpp) -if (_M_ARM_64) - list(APPEND SRCS - ArchHelpers/Arm64.cpp) -endif() - add_library(LinuxEmulation STATIC ${SRCS}) target_compile_options(LinuxEmulation diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.cpp b/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.cpp index 905bb2ab7..2b7f3963d 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.cpp +++ b/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.cpp @@ -5,7 +5,6 @@ desc: Handles host -> host and host -> guest signal routing, emulates procmask & $end_info$ */ -#include "LinuxSyscalls/ArchHelpers/Arm64.h" #include "LinuxSyscalls/SignalDelegator.h" #include @@ -17,6 +16,7 @@ $end_info$ #include #include #include +#include #include #include @@ -1621,16 +1621,36 @@ namespace FEX::HLE { #ifdef _M_ARM_64 // Register SIGBUS signal handler. - const auto SigbusHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool { - if (!Thread->CPUBackend->IsAddressInCodeBuffer(ArchHelpers::Context::GetPc(ucontext))) { + const auto SigbusHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *_info, void *ucontext) -> bool { + const auto PC = ArchHelpers::Context::GetPc(ucontext); + if (!Thread->CPUBackend->IsAddressInCodeBuffer(PC)) { // Wasn't a sigbus in JIT code return false; } + siginfo_t* info = reinterpret_cast(_info); - return FEX::ArchHelpers::Arm64::HandleSIGBUS(GlobalDelegator->ParanoidTSO(), Signal, info, ucontext); + if (info->si_code != BUS_ADRALN) { + // This only handles alignment problems + return false; + } + + const auto Result = FEXCore::ArchHelpers::Arm64::HandleUnalignedAccess(GlobalDelegator->ParanoidTSO(), PC, ArchHelpers::Context::GetArmGPRs(ucontext)); + ArchHelpers::Context::SetPc(ucontext, PC + Result.second); + return Result.first; }; - const auto SigbusHandlerInterpreter = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool { - return FEX::ArchHelpers::Arm64::HandleSIGBUS(true, Signal, info, ucontext); + + const auto SigbusHandlerInterpreter = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *_info, void *ucontext) -> bool { + const auto PC = ArchHelpers::Context::GetPc(ucontext); + siginfo_t* info = reinterpret_cast(_info); + + if (info->si_code != BUS_ADRALN) { + // This only handles alignment problems + return false; + } + + const auto Result = FEXCore::ArchHelpers::Arm64::HandleUnalignedAccess(true, PC, ArchHelpers::Context::GetArmGPRs(ucontext)); + ArchHelpers::Context::SetPc(ucontext, PC + Result.second); + return Result.first; }; if (Core == FEXCore::Config::CONFIG_INTERPRETER) { diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.h b/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.h index b292b13b9..48a2075d5 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.h +++ b/Source/Tools/FEXLoader/LinuxSyscalls/SignalDelegator.h @@ -8,7 +8,7 @@ $end_info$ #pragma once #include "LinuxSyscalls/Types.h" -#include "LinuxSyscalls/ArchHelpers/MContext.h" +#include "ArchHelpers/MContext.h" #include "VDSO_Emulation.h" #include diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/x32/Signals.cpp b/Source/Tools/FEXLoader/LinuxSyscalls/x32/Signals.cpp index fbe237264..c31b51636 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/x32/Signals.cpp +++ b/Source/Tools/FEXLoader/LinuxSyscalls/x32/Signals.cpp @@ -4,9 +4,9 @@ tags: LinuxSyscalls|syscalls-x86-32 $end_info$ */ +#include "ArchHelpers/UContext.h" #include "LinuxSyscalls/SignalDelegator.h" #include "LinuxSyscalls/Syscalls.h" -#include "LinuxSyscalls/UContext.h" #include "LinuxSyscalls/x64/Syscalls.h" #include "LinuxSyscalls/x32/Syscalls.h" #include "LinuxSyscalls/x32/Types.h" diff --git a/Source/Tools/FEXLoader/LinuxSyscalls/x32/Thread.cpp b/Source/Tools/FEXLoader/LinuxSyscalls/x32/Thread.cpp index 77406e456..bfbe4e8ef 100644 --- a/Source/Tools/FEXLoader/LinuxSyscalls/x32/Thread.cpp +++ b/Source/Tools/FEXLoader/LinuxSyscalls/x32/Thread.cpp @@ -4,9 +4,9 @@ tags: LinuxSyscalls|syscalls-x86-32 $end_info$ */ +#include "ArchHelpers/UContext.h" #include "LinuxSyscalls/SignalDelegator.h" #include "LinuxSyscalls/Syscalls.h" -#include "LinuxSyscalls/UContext.h" #include "LinuxSyscalls/x32/Syscalls.h" #include "LinuxSyscalls/x32/Thread.h" #include "LinuxSyscalls/x32/Types.h" diff --git a/Source/Tools/FEXLoader/TestHarnessRunner/HostRunner.cpp b/Source/Tools/FEXLoader/TestHarnessRunner/HostRunner.cpp index 3f648053f..5ffa80bb0 100644 --- a/Source/Tools/FEXLoader/TestHarnessRunner/HostRunner.cpp +++ b/Source/Tools/FEXLoader/TestHarnessRunner/HostRunner.cpp @@ -1,5 +1,5 @@ +#include "ArchHelpers/UContext.h" #include "LinuxSyscalls/SignalDelegator.h" -#include "LinuxSyscalls/UContext.h" #include #include #include