diff --git a/ChangeLog.md b/ChangeLog.md index cb31869484a70..2b0649e3fc5ac 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,7 @@ See docs/process.md for more on how version tagging works. 4.0.23 (in development) ----------------------- +- libunwind was updated to LLVM 21.1.8. (#26035) - The inconsistency of incrementing / decrementing refcounts between Wasm EH and Emscripten EH has been fixed. See `test_EXPORT_EXCEPTION_HANDLING_HELPERS` in `test_core.py` to see the usage. (#25988) diff --git a/system/lib/libunwind/readme.txt b/system/lib/libunwind/readme.txt index 9fe2331add639..fd0ee7c24a74d 100644 --- a/system/lib/libunwind/readme.txt +++ b/system/lib/libunwind/readme.txt @@ -1,14 +1,14 @@ LLVM's libunwind ---------------- -These files are from the llvm-project based on release 20.1.8. +These files are from the llvm-project based on release 21.1.8. We maintain a local fork of llvm-project that contains any Emscripten specific patches: https://github.com/emscripten-core/llvm-project -The current patch is based on the emscripten-libs-20 branch. +The current patch is based on the emscripten-libs-21 branch. Update Instructions ------------------- @@ -20,4 +20,4 @@ Modifications For a list of changes from upstream see the libunwind files that are part of: -https://github.com/llvm/llvm-project/compare/llvmorg-20.1.8...emscripten-core:emscripten-libs-20 +https://github.com/llvm/llvm-project/compare/llvmorg-21.1.8...emscripten-core:emscripten-libs-21 diff --git a/system/lib/libunwind/src/Registers.hpp b/system/lib/libunwind/src/Registers.hpp index 861e6b5f6f2c5..2c3bfb7e8428a 100644 --- a/system/lib/libunwind/src/Registers.hpp +++ b/system/lib/libunwind/src/Registers.hpp @@ -15,9 +15,9 @@ #include #include -#include "cet_unwind.h" #include "config.h" #include "libunwind.h" +#include "shadow_stack_unwind.h" namespace libunwind { @@ -48,7 +48,7 @@ class _LIBUNWIND_HIDDEN Registers_x86; extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *); #if defined(_LIBUNWIND_USE_CET) -extern "C" void *__libunwind_cet_get_jump_target() { +extern "C" void *__libunwind_shstk_get_jump_target() { return reinterpret_cast(&__libunwind_Registers_x86_jumpto); } #endif @@ -268,7 +268,7 @@ class _LIBUNWIND_HIDDEN Registers_x86_64; extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *); #if defined(_LIBUNWIND_USE_CET) -extern "C" void *__libunwind_cet_get_jump_target() { +extern "C" void *__libunwind_shstk_get_jump_target() { return reinterpret_cast(&__libunwind_Registers_x86_64_jumpto); } #endif @@ -1817,7 +1817,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64; extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); #if defined(_LIBUNWIND_USE_GCS) -extern "C" void *__libunwind_cet_get_jump_target() { +extern "C" void *__libunwind_shstk_get_jump_target() { return reinterpret_cast(&__libunwind_Registers_arm64_jumpto); } #endif @@ -4126,7 +4126,7 @@ inline reg_t Registers_riscv::getRegister(int regNum) const { return _registers[regNum]; if (regNum == UNW_RISCV_VLENB) { reg_t vlenb; - __asm__("csrr %0, 0xC22" : "=r"(vlenb)); + __asm__ volatile("csrr %0, 0xC22" : "=r"(vlenb)); return vlenb; } _LIBUNWIND_ABORT("unsupported riscv register"); diff --git a/system/lib/libunwind/src/Unwind-seh.cpp b/system/lib/libunwind/src/Unwind-seh.cpp index b2bb119ed6d29..110c5987c3f1a 100644 --- a/system/lib/libunwind/src/Unwind-seh.cpp +++ b/system/lib/libunwind/src/Unwind-seh.cpp @@ -51,6 +51,32 @@ static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor); static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, DISPATCHER_CONTEXT *disp); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +// Local redefinition of this type; mingw-w64 headers lack the +// DISPATCHER_CONTEXT_NONVOLREG_ARM64 type as of May 2025, so locally redefine +// it and use that definition, to avoid needing to test/guess whether the real +// type is available of not. +union LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM64 { + BYTE Buffer[11 * sizeof(DWORD64) + 8 * sizeof(double)]; + + struct { + DWORD64 GpNvRegs[11]; + double FpNvRegs[8]; + }; +}; + +// Custom data type definition; this type is not defined in WinSDK. +union LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM { + BYTE Buffer[8 * sizeof(DWORD) + 8 * sizeof(double)]; + + struct { + DWORD GpNvRegs[8]; + double FpNvRegs[8]; + }; +}; +#pragma clang diagnostic pop + /// Common implementation of SEH-style handler functions used by Itanium- /// style frames. Depending on how and why it was called, it may do one of: /// a) Delegate to the given Itanium-style personality function; or @@ -148,7 +174,8 @@ _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, } // FIXME: Indicate target frame in foreign case! // phase 2: the clean up phase - RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); + RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, disp->ContextRecord, + disp->HistoryTable); _LIBUNWIND_ABORT("RtlUnwindEx() failed"); case _URC_INSTALL_CONTEXT: { // If we were called by __libunwind_seh_personality(), indicate that @@ -212,6 +239,21 @@ __libunwind_seh_personality(int version, _Unwind_Action state, ms_exc.ExceptionInformation[2] = state; DISPATCHER_CONTEXT *disp_ctx = __unw_seh_get_disp_ctx((unw_cursor_t *)context); +#if defined(__aarch64__) + LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM64 nonvol; + memcpy(&nonvol.GpNvRegs, &disp_ctx->ContextRecord->X19, + sizeof(nonvol.GpNvRegs)); + for (int i = 0; i < 8; i++) + nonvol.FpNvRegs[i] = disp_ctx->ContextRecord->V[i + 8].D[0]; + disp_ctx->NonVolatileRegisters = nonvol.Buffer; +#elif defined(__arm__) + LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM nonvol; + memcpy(&nonvol.GpNvRegs, &disp_ctx->ContextRecord->R4, + sizeof(nonvol.GpNvRegs)); + memcpy(&nonvol.FpNvRegs, &disp_ctx->ContextRecord->D[8], + sizeof(nonvol.FpNvRegs)); + disp_ctx->NonVolatileRegisters = nonvol.Buffer; +#endif _LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() calling " "LanguageHandler %p(%p, %p, %p, %p)", (void *)disp_ctx->LanguageHandler, (void *)&ms_exc, diff --git a/system/lib/libunwind/src/Unwind-wasm.c b/system/lib/libunwind/src/Unwind-wasm.c index 51c363736feaa..bcb9a6a303779 100644 --- a/system/lib/libunwind/src/Unwind-wasm.c +++ b/system/lib/libunwind/src/Unwind-wasm.c @@ -102,8 +102,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { } /// Not used in Wasm. -_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, - uintptr_t value) {} +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t) {} /// Called by personality handler to get LSDA for current frame. _LIBUNWIND_EXPORT uintptr_t @@ -115,8 +114,7 @@ _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { } /// Not used in Wasm. -_LIBUNWIND_EXPORT uintptr_t -_Unwind_GetRegionStart(struct _Unwind_Context *context) { +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *) { return 0; } diff --git a/system/lib/libunwind/src/UnwindCursor.hpp b/system/lib/libunwind/src/UnwindCursor.hpp index 3831d8e071ef3..9a1afd3721f5a 100644 --- a/system/lib/libunwind/src/UnwindCursor.hpp +++ b/system/lib/libunwind/src/UnwindCursor.hpp @@ -11,7 +11,7 @@ #ifndef __UNWINDCURSOR_HPP__ #define __UNWINDCURSOR_HPP__ -#include "cet_unwind.h" +#include "shadow_stack_unwind.h" #include #include #include @@ -31,8 +31,9 @@ #endif #if defined(_LIBUNWIND_TARGET_LINUX) && \ - (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ - defined(_LIBUNWIND_TARGET_S390X)) + (defined(_LIBUNWIND_TARGET_AARCH64) || \ + defined(_LIBUNWIND_TARGET_LOONGARCH) || \ + defined(_LIBUNWIND_TARGET_RISCV) || defined(_LIBUNWIND_TARGET_S390X)) #include #include #include @@ -40,6 +41,12 @@ #define _LIBUNWIND_CHECK_LINUX_SIGRETURN 1 #endif +#if defined(_LIBUNWIND_TARGET_HAIKU) && defined(_LIBUNWIND_TARGET_X86_64) +#include +#include +#define _LIBUNWIND_CHECK_HAIKU_SIGRETURN 1 +#endif + #include "AddressSpace.hpp" #include "CompactUnwinder.hpp" #include "config.h" @@ -82,6 +89,22 @@ struct UNWIND_INFO { uint16_t UnwindCodes[2]; }; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +union UNWIND_INFO_ARM { + DWORD HeaderData; + struct { + DWORD FunctionLength : 18; + DWORD Version : 2; + DWORD ExceptionDataPresent : 1; + DWORD EpilogInHeader : 1; + DWORD FunctionFragment : 1; + DWORD EpilogCount : 5; + DWORD CodeWords : 4; + }; +}; +#pragma clang diagnostic pop + extern "C" _Unwind_Reason_Code __libunwind_seh_personality( int, _Unwind_Action, uint64_t, _Unwind_Exception *, struct _Unwind_Context *); @@ -150,7 +173,8 @@ bool DwarfFDECache::_registeredForDyldUnloads = false; #endif template -typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { +typename DwarfFDECache::pint_t DwarfFDECache::findFDE(pint_t mh, + pint_t pc) { pint_t result = 0; _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); for (entry *p = _buffer; p < _bufferUsed; ++p) { @@ -996,6 +1020,10 @@ class UnwindCursor : public AbstractUnwindCursor{ bool setInfoForSigReturn(Registers_arm64 &); int stepThroughSigReturn(Registers_arm64 &); #endif +#if defined(_LIBUNWIND_TARGET_LOONGARCH) + bool setInfoForSigReturn(Registers_loongarch &); + int stepThroughSigReturn(Registers_loongarch &); +#endif #if defined(_LIBUNWIND_TARGET_RISCV) bool setInfoForSigReturn(Registers_riscv &); int stepThroughSigReturn(Registers_riscv &); @@ -1010,7 +1038,7 @@ class UnwindCursor : public AbstractUnwindCursor{ template int stepThroughSigReturn(Registers &) { return UNW_STEP_END; } -#elif defined(_LIBUNWIND_TARGET_HAIKU) +#elif defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) bool setInfoForSigReturn(); int stepThroughSigReturn(); #endif @@ -2013,6 +2041,61 @@ bool UnwindCursor::getInfoFromSEH(pint_t pc) { _info.handler = 0; } } +#elif defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_ARM) + +#if defined(_LIBUNWIND_TARGET_AARCH64) +#define FUNC_LENGTH_UNIT 4 +#define XDATA_TYPE IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY_XDATA +#else +#define FUNC_LENGTH_UNIT 2 +#define XDATA_TYPE UNWIND_INFO_ARM +#endif + if (unwindEntry->Flag != 0) { // Packed unwind info + _info.end_ip = + _info.start_ip + unwindEntry->FunctionLength * FUNC_LENGTH_UNIT; + // Only fill in the handler and LSDA if they're stale. + if (pc != getLastPC()) { + // Packed unwind info doesn't have an exception handler. + _info.lsda = 0; + _info.handler = 0; + } + } else { + XDATA_TYPE *xdata = + reinterpret_cast(base + unwindEntry->UnwindData); + _info.end_ip = _info.start_ip + xdata->FunctionLength * FUNC_LENGTH_UNIT; + // Only fill in the handler and LSDA if they're stale. + if (pc != getLastPC()) { + if (xdata->ExceptionDataPresent) { + uint32_t offset = 1; // The main xdata + uint32_t codeWords = xdata->CodeWords; + uint32_t epilogScopes = xdata->EpilogCount; + if (xdata->EpilogCount == 0 && xdata->CodeWords == 0) { + // The extension word has got the same layout for both ARM and ARM64 + uint32_t extensionWord = reinterpret_cast(xdata)[1]; + codeWords = (extensionWord >> 16) & 0xff; + epilogScopes = extensionWord & 0xffff; + offset++; + } + if (!xdata->EpilogInHeader) + offset += epilogScopes; + offset += codeWords; + uint32_t *exceptionHandlerInfo = + reinterpret_cast(xdata) + offset; + _dispContext.HandlerData = &exceptionHandlerInfo[1]; + _dispContext.LanguageHandler = reinterpret_cast( + base + exceptionHandlerInfo[0]); + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (exceptionHandlerInfo[0]) + _info.handler = + reinterpret_cast(__libunwind_seh_personality); + else + _info.handler = 0; + } else { + _info.lsda = 0; + _info.handler = 0; + } + } + } #endif setLastPC(pc); return true; @@ -2554,7 +2637,7 @@ int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, template void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ - defined(_LIBUNWIND_TARGET_HAIKU) + defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) _isSigReturn = false; #endif @@ -2679,7 +2762,7 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ - defined(_LIBUNWIND_TARGET_HAIKU) + defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) if (setInfoForSigReturn()) return; #endif @@ -2755,65 +2838,63 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { _isSignalFrame = true; return UNW_STEP_SUCCESS; } +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_AARCH64) -#elif defined(_LIBUNWIND_TARGET_HAIKU) && defined(_LIBUNWIND_TARGET_X86_64) -#include -#include +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_LOONGARCH) +template +bool UnwindCursor::setInfoForSigReturn(Registers_loongarch &) { + const pint_t pc = static_cast(getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a SIGSEGV. + if (!isReadableAddr(pc)) + return false; + const auto *instructions = reinterpret_cast(pc); + // Look for the two instructions used in the sigreturn trampoline + // __vdso_rt_sigreturn: + // + // 0x03822c0b li a7,0x8b + // 0x002b0000 syscall 0 + if (instructions[0] != 0x03822c0b || instructions[1] != 0x002b0000) + return false; -extern "C" { -extern void *__gCommPageAddress; + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 4; + _isSigReturn = true; + return true; } template -bool UnwindCursor::setInfoForSigReturn() { -#if defined(_LIBUNWIND_TARGET_X86_64) - addr_t signal_handler = - (((addr_t *)__gCommPageAddress)[COMMPAGE_ENTRY_X86_SIGNAL_HANDLER] + - (addr_t)__gCommPageAddress); - addr_t signal_handler_ret = signal_handler + 45; -#endif - pint_t pc = static_cast(this->getReg(UNW_REG_IP)); - if (pc == signal_handler_ret) { - _info = {}; - _info.start_ip = signal_handler; - _info.end_ip = signal_handler_ret; - _isSigReturn = true; - return true; - } - return false; -} +int UnwindCursor::stepThroughSigReturn(Registers_loongarch &) { + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext_t struct: + // - 8-byte long (__uc_flags) + // - 8-byte pointer (*uc_link) + // - 24-byte uc_stack + // - 8-byte uc_sigmask + // - 120-byte of padding to allow sigset_t to be expanded in the future + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - struct sigcontext uc_mcontext + // [1] + // https://github.com/torvalds/linux/blob/master/arch/loongarch/kernel/signal.c + const pint_t kOffsetSpToSigcontext = 128 + 8 + 8 + 24 + 8 + 128; -template -int UnwindCursor::stepThroughSigReturn() { + const pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + _registers.setIP(_addressSpace.get64(sigctx)); + for (int i = UNW_LOONGARCH_R1; i <= UNW_LOONGARCH_R31; ++i) { + // skip R0 + uint64_t value = + _addressSpace.get64(sigctx + static_cast((i + 1) * 8)); + _registers.setRegister(i, value); + } _isSignalFrame = true; - pint_t sp = _registers.getSP(); -#if defined(_LIBUNWIND_TARGET_X86_64) - vregs *regs = (vregs *)(sp + 0x70); - - _registers.setRegister(UNW_REG_IP, regs->rip); - _registers.setRegister(UNW_REG_SP, regs->rsp); - _registers.setRegister(UNW_X86_64_RAX, regs->rax); - _registers.setRegister(UNW_X86_64_RDX, regs->rdx); - _registers.setRegister(UNW_X86_64_RCX, regs->rcx); - _registers.setRegister(UNW_X86_64_RBX, regs->rbx); - _registers.setRegister(UNW_X86_64_RSI, regs->rsi); - _registers.setRegister(UNW_X86_64_RDI, regs->rdi); - _registers.setRegister(UNW_X86_64_RBP, regs->rbp); - _registers.setRegister(UNW_X86_64_R8, regs->r8); - _registers.setRegister(UNW_X86_64_R9, regs->r9); - _registers.setRegister(UNW_X86_64_R10, regs->r10); - _registers.setRegister(UNW_X86_64_R11, regs->r11); - _registers.setRegister(UNW_X86_64_R12, regs->r12); - _registers.setRegister(UNW_X86_64_R13, regs->r13); - _registers.setRegister(UNW_X86_64_R14, regs->r14); - _registers.setRegister(UNW_X86_64_R15, regs->r15); - // TODO: XMM -#endif - return UNW_STEP_SUCCESS; } #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && - // defined(_LIBUNWIND_TARGET_AARCH64) + // defined(_LIBUNWIND_TARGET_LOONGARCH) #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ defined(_LIBUNWIND_TARGET_RISCV) @@ -2972,6 +3053,96 @@ int UnwindCursor::stepThroughSigReturn(Registers_s390x &) { #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && // defined(_LIBUNWIND_TARGET_S390X) +#if defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) +template +bool UnwindCursor::setInfoForSigReturn() { + Dl_info dlinfo; + const auto isSignalHandler = [&](pint_t addr) { + if (!dladdr(reinterpret_cast(addr), &dlinfo)) + return false; + if (strcmp(dlinfo.dli_fname, "commpage")) + return false; + if (dlinfo.dli_sname == NULL || + strcmp(dlinfo.dli_sname, "commpage_signal_handler")) + return false; + return true; + }; + + pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + if (!isSignalHandler(pc)) + return false; + + pint_t start = reinterpret_cast(dlinfo.dli_saddr); + + static size_t signalHandlerSize = 0; + if (signalHandlerSize == 0) { + size_t boundLow = 0; + size_t boundHigh = static_cast(-1); + + area_info areaInfo; + if (get_area_info(area_for(dlinfo.dli_saddr), &areaInfo) == B_OK) + boundHigh = areaInfo.size; + + while (boundLow < boundHigh) { + size_t boundMid = boundLow + ((boundHigh - boundLow) / 2); + pint_t test = start + boundMid; + if (test >= start && isSignalHandler(test)) + boundLow = boundMid + 1; + else + boundHigh = boundMid; + } + + signalHandlerSize = boundHigh; + } + + _info = {}; + _info.start_ip = start; + _info.end_ip = start + signalHandlerSize; + _isSigReturn = true; + + return true; +} + +template +int UnwindCursor::stepThroughSigReturn() { + _isSignalFrame = true; + +#if defined(_LIBUNWIND_TARGET_X86_64) + // Layout of the stack before function call: + // - signal_frame_data + // + siginfo_t (public struct, fairly stable) + // + ucontext_t (public struct, fairly stable) + // - mcontext_t -> Offset 0x70, this is what we want. + // - frame->ip (8 bytes) + // - frame->bp (8 bytes). Not written by the kernel, + // but the signal handler has a "push %rbp" instruction. + pint_t bp = this->getReg(UNW_X86_64_RBP); + vregs *regs = (vregs *)(bp + 0x70); + + _registers.setRegister(UNW_REG_IP, regs->rip); + _registers.setRegister(UNW_REG_SP, regs->rsp); + _registers.setRegister(UNW_X86_64_RAX, regs->rax); + _registers.setRegister(UNW_X86_64_RDX, regs->rdx); + _registers.setRegister(UNW_X86_64_RCX, regs->rcx); + _registers.setRegister(UNW_X86_64_RBX, regs->rbx); + _registers.setRegister(UNW_X86_64_RSI, regs->rsi); + _registers.setRegister(UNW_X86_64_RDI, regs->rdi); + _registers.setRegister(UNW_X86_64_RBP, regs->rbp); + _registers.setRegister(UNW_X86_64_R8, regs->r8); + _registers.setRegister(UNW_X86_64_R9, regs->r9); + _registers.setRegister(UNW_X86_64_R10, regs->r10); + _registers.setRegister(UNW_X86_64_R11, regs->r11); + _registers.setRegister(UNW_X86_64_R12, regs->r12); + _registers.setRegister(UNW_X86_64_R13, regs->r13); + _registers.setRegister(UNW_X86_64_R14, regs->r14); + _registers.setRegister(UNW_X86_64_R15, regs->r15); + // TODO: XMM +#endif // defined(_LIBUNWIND_TARGET_X86_64) + + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) + template int UnwindCursor::step(bool stage2) { (void)stage2; // Bottom of stack is defined is when unwind info cannot be found. @@ -2981,7 +3152,7 @@ template int UnwindCursor::step(bool stage2) { // Use unwinding info to modify register set as if function returned. int result; #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) || \ - defined(_LIBUNWIND_TARGET_HAIKU) + defined(_LIBUNWIND_CHECK_HAIKU_SIGRETURN) if (_isSigReturn) { result = this->stepThroughSigReturn(); } else @@ -3062,7 +3233,7 @@ bool UnwindCursor::isReadableAddr(const pint_t addr) const { #endif #if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS) -extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) { +extern "C" void *__libunwind_shstk_get_registers(unw_cursor_t *cursor) { AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; return co->get_registers(); } diff --git a/system/lib/libunwind/src/UnwindLevel1.c b/system/lib/libunwind/src/UnwindLevel1.c index 7e785f4d31e71..f3b451ad9b730 100644 --- a/system/lib/libunwind/src/UnwindLevel1.c +++ b/system/lib/libunwind/src/UnwindLevel1.c @@ -25,10 +25,10 @@ #include #include -#include "cet_unwind.h" #include "config.h" #include "libunwind.h" #include "libunwind_ext.h" +#include "shadow_stack_unwind.h" #include "unwind.h" #if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ @@ -36,14 +36,17 @@ #ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND -// When CET is enabled, each "call" instruction will push return address to -// CET shadow stack, each "ret" instruction will pop current CET shadow stack -// top and compare it with target address which program will return. -// In exception handing, some stack frames will be skipped before jumping to -// landing pad and we must adjust CET shadow stack accordingly. -// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we -// directly jump to __libunwind_Registers_x86/x86_64_jumpto instead of using -// a regular function call to avoid pushing to CET shadow stack again. +// When shadow stack is enabled, a separate stack containing only return +// addresses would be maintained. On function return, the return address would +// be compared to the popped address from shadow stack to ensure the return +// target is not tempered with. When unwinding, we're skipping the normal return +// procedure for multiple frames and thus need to pop the return addresses of +// the skipped frames from shadow stack to avoid triggering an exception (using +// `_LIBUNWIND_POP_SHSTK_SSP()`). Also, some architectures, like the x86-family +// CET, push the return adddresses onto shadow stack with common call +// instructions, so for these architectures, normal function calls should be +// avoided when invoking the `jumpto()` function. To do this, we use inline +// assemblies to "goto" the `jumpto()` for these architectures. #if !defined(_LIBUNWIND_USE_CET) && !defined(_LIBUNWIND_USE_GCS) #define __unw_phase2_resume(cursor, fn) \ do { \ @@ -51,38 +54,38 @@ __unw_resume((cursor)); \ } while (0) #elif defined(_LIBUNWIND_TARGET_I386) -#define __cet_ss_step_size 4 +#define __shstk_step_size (4) #define __unw_phase2_resume(cursor, fn) \ do { \ - _LIBUNWIND_POP_CET_SSP((fn)); \ - void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ - void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ + _LIBUNWIND_POP_SHSTK_SSP((fn)); \ + void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \ + void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \ __asm__ volatile("push %%edi\n\t" \ "sub $4, %%esp\n\t" \ - "jmp *%%edx\n\t" :: "D"(cetRegContext), \ - "d"(cetJumpAddress)); \ + "jmp *%%edx\n\t" ::"D"(shstkRegContext), \ + "d"(shstkJumpAddress)); \ } while (0) #elif defined(_LIBUNWIND_TARGET_X86_64) -#define __cet_ss_step_size 8 +#define __shstk_step_size (8) #define __unw_phase2_resume(cursor, fn) \ do { \ - _LIBUNWIND_POP_CET_SSP((fn)); \ - void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ - void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ - __asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \ - "d"(cetJumpAddress)); \ + _LIBUNWIND_POP_SHSTK_SSP((fn)); \ + void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \ + void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \ + __asm__ volatile("jmpq *%%rdx\n\t" ::"D"(shstkRegContext), \ + "d"(shstkJumpAddress)); \ } while (0) #elif defined(_LIBUNWIND_TARGET_AARCH64) -#define __cet_ss_step_size 8 +#define __shstk_step_size (8) #define __unw_phase2_resume(cursor, fn) \ do { \ - _LIBUNWIND_POP_CET_SSP((fn)); \ - void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ - void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ + _LIBUNWIND_POP_SHSTK_SSP((fn)); \ + void *shstkRegContext = __libunwind_shstk_get_registers((cursor)); \ + void *shstkJumpAddress = __libunwind_shstk_get_jump_target(); \ __asm__ volatile("mov x0, %0\n\t" \ "br %1\n\t" \ : \ - : "r"(cetRegContext), "r"(cetJumpAddress) \ + : "r"(shstkRegContext), "r"(shstkJumpAddress) \ : "x0"); \ } while (0) #endif @@ -185,10 +188,11 @@ extern int __unw_step_stage2(unw_cursor_t *); #if defined(_LIBUNWIND_USE_GCS) // Enable the GCS target feature to permit gcspop instructions to be used. -__attribute__((target("gcs"))) +__attribute__((target("+gcs"))) #endif static _Unwind_Reason_Code -unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { +unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object) { __unw_init_local(cursor, uc); _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p)", @@ -255,16 +259,16 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except } #endif -// In CET enabled environment, we check return address stored in normal stack -// against return address stored in CET shadow stack, if the 2 addresses don't +// In shadow stack enabled environment, we check return address stored in normal +// stack against return address stored in shadow stack, if the 2 addresses don't // match, it means return address in normal stack has been corrupted, we return // _URC_FATAL_PHASE2_ERROR. #if defined(_LIBUNWIND_USE_CET) || defined(_LIBUNWIND_USE_GCS) if (shadowStackTop != 0) { unw_word_t retInNormalStack; __unw_get_reg(cursor, UNW_REG_IP, &retInNormalStack); - unsigned long retInShadowStack = *( - unsigned long *)(shadowStackTop + __cet_ss_step_size * framesWalked); + unsigned long retInShadowStack = + *(unsigned long *)(shadowStackTop + __shstk_step_size * framesWalked); if (retInNormalStack != retInShadowStack) return _URC_FATAL_PHASE2_ERROR; } @@ -329,12 +333,12 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except #if defined(_LIBUNWIND_USE_GCS) // Enable the GCS target feature to permit gcspop instructions to be used. -__attribute__((target("gcs"))) +__attribute__((target("+gcs"))) #endif static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, - _Unwind_Exception *exception_object, - _Unwind_Stop_Fn stop, void *stop_parameter) { + _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, + void *stop_parameter) { __unw_init_local(cursor, uc); // uc is initialized by __unw_getcontext in the parent frame. The first stack @@ -440,7 +444,6 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, return _URC_FATAL_PHASE2_ERROR; } - /// Called by __cxa_throw. Only returns if there is a fatal error. _LIBUNWIND_EXPORT _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *exception_object) { diff --git a/system/lib/libunwind/src/UnwindRegistersRestore.S b/system/lib/libunwind/src/UnwindRegistersRestore.S index 1702d016c368b..1bcd205be260d 100644 --- a/system/lib/libunwind/src/UnwindRegistersRestore.S +++ b/system/lib/libunwind/src/UnwindRegistersRestore.S @@ -66,7 +66,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_jumpto) # skip fs # skip gs -#elif defined(__x86_64__) +#elif defined(__x86_64__) && !defined(__arm64ec__) DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto) # @@ -1044,9 +1044,10 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv) lw $27, (4 * 27)($4) lw $28, (4 * 28)($4) lw $29, (4 * 29)($4) - lw $30, (4 * 30)($4) // load new pc into ra lw $31, (4 * 32)($4) + // MIPS 1 has load delay slot. Ensure lw $31 and jr are separated by an instruction. + lw $30, (4 * 30)($4) // jump to ra, load a0 in the delay slot jr $31 lw $4, (4 * 4)($4) @@ -1082,11 +1083,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) ld $2, (8 * 2)($4) ld $3, (8 * 3)($4) // skip a0 for now - .irp i,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + .irp i,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 ld $\i, (8 * \i)($4) .endr // load new pc into ra ld $31, (8 * 32)($4) + // MIPS 1 has load delay slot. Ensure lw $31 and jr are separated by an instruction. + ld $30, (8 * 30)($4) // jump to ra, load a0 in the delay slot jr $31 ld $4, (8 * 4)($4) diff --git a/system/lib/libunwind/src/UnwindRegistersSave.S b/system/lib/libunwind/src/UnwindRegistersSave.S index a489a8ba6df15..5139a551ad245 100644 --- a/system/lib/libunwind/src/UnwindRegistersSave.S +++ b/system/lib/libunwind/src/UnwindRegistersSave.S @@ -65,6 +65,47 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) xorl %eax, %eax # return UNW_ESUCCESS ret +#elif defined(__arm64ec__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in x0 +// + .section .text,"xr",discard,"#__unw_getcontext" + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION("#__unw_getcontext") + stp x8, x27, [x0, #0x000] // rax, rbx + stp x0, x1, [x0, #0x010] // rcx, rdx + stp x26,x25, [x0, #0x020] // rdi, rsi + mov x1, sp + stp fp, x1, [x0, #0x030] // rbp, rsp + stp x2, x3, [x0, #0x040] // r8, r9 + stp x4, x5, [x0, #0x050] // r10, r11 + stp x19,x20, [x0, #0x060] // r12, r13 + stp x21,x22, [x0, #0x070] // r14, r15 + str x30, [x0, #0x080] // store return address as pc + stp q0, q1, [x0, #0x0b0] // xmm0, xmm1 + stp q2, q3, [x0, #0x0d0] // xmm2, xmm3 + stp q4, q5, [x0, #0x0f0] // xmm4, xmm5 + stp q6, q7, [x0, #0x110] // xmm6, xmm7 + stp q8, q9, [x0, #0x130] // xmm8, xmm9 + stp q10,q11, [x0, #0x150] // xmm10,xmm11 + stp q12,q13, [x0, #0x170] // xmm12,xmm13 + stp q14,q15, [x0, #0x190] // xmm14,xmm15 + mov x0, #0 // return UNW_ESUCCESS + ret + + .weak_anti_dep __unw_getcontext + .set __unw_getcontext, "#__unw_getcontext" + + .section .hybmp$x,"yi" + .symidx "#__unw_getcontext" + .symidx $ientry_thunk$cdecl$i8$i8 + .word 1 + .text + #elif defined(__x86_64__) # @@ -1181,7 +1222,15 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #endif +#ifdef __arm64ec__ + .globl "#unw_getcontext" + .set "#unw_getcontext", "#__unw_getcontext" + .weak_anti_dep unw_getcontext + .set unw_getcontext, "#unw_getcontext" + EXPORT_SYMBOL(unw_getcontext) +#else WEAK_ALIAS(__unw_getcontext, unw_getcontext) +#endif #endif /* !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__) */ diff --git a/system/lib/libunwind/src/cet_unwind.h b/system/lib/libunwind/src/shadow_stack_unwind.h similarity index 88% rename from system/lib/libunwind/src/cet_unwind.h rename to system/lib/libunwind/src/shadow_stack_unwind.h index 47d7616a7322c..1f229d8317116 100644 --- a/system/lib/libunwind/src/cet_unwind.h +++ b/system/lib/libunwind/src/shadow_stack_unwind.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LIBUNWIND_CET_UNWIND_H -#define LIBUNWIND_CET_UNWIND_H +#ifndef LIBUNWIND_SHADOW_STACK_UNWIND_H +#define LIBUNWIND_SHADOW_STACK_UNWIND_H #include "libunwind.h" @@ -21,7 +21,7 @@ #include #include -#define _LIBUNWIND_POP_CET_SSP(x) \ +#define _LIBUNWIND_POP_SHSTK_SSP(x) \ do { \ unsigned long ssp = _get_ssp(); \ if (ssp != 0) { \ @@ -46,7 +46,7 @@ #define _LIBUNWIND_USE_GCS 1 #endif -#define _LIBUNWIND_POP_CET_SSP(x) \ +#define _LIBUNWIND_POP_SHSTK_SSP(x) \ do { \ if (__chkfeat(_CHKFEAT_GCS)) { \ unsigned tmp = (x); \ @@ -57,7 +57,7 @@ #endif -extern void *__libunwind_cet_get_registers(unw_cursor_t *); -extern void *__libunwind_cet_get_jump_target(void); +extern void *__libunwind_shstk_get_registers(unw_cursor_t *); +extern void *__libunwind_shstk_get_jump_target(void); #endif diff --git a/tools/system_libs.py b/tools/system_libs.py index 3cdf76d9af161..eb917cc935158 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1734,7 +1734,8 @@ class libunwind(ExceptionLibrary, MTLibrary): # See https://bugs.llvm.org/show_bug.cgi?id=44353 force_object_files = True - cflags = ['-Oz', '-fno-inline-functions', '-D_LIBUNWIND_HIDE_SYMBOLS'] + cflags = ['-Oz', '-fno-inline-functions', '-D_LIBUNWIND_HIDE_SYMBOLS', + '-Wno-c23-extensions'] src_dir = 'system/lib/libunwind/src' # Without this we can't build libunwind since it will pickup the unwind.h # that is part of llvm (which is not compatible for some reason).