From 2f635c3ce9be74f9e9ffb71324f7ba51bd2987f4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 4 Aug 2022 18:09:10 -0700 Subject: [PATCH] update libunwind to LLVM 15 release/15.x commit 134fd359a5d884f16662a9edd22ab24feeb1498c --- lib/libunwind/include/__libunwind_config.h | 11 +- lib/libunwind/include/libunwind.h | 47 +- lib/libunwind/include/unwind.h | 2 +- lib/libunwind/src/AddressSpace.hpp | 23 +- lib/libunwind/src/DwarfInstructions.hpp | 28 +- lib/libunwind/src/EHHeaderParser.hpp | 3 +- lib/libunwind/src/Registers.hpp | 348 +++++++++- lib/libunwind/src/Unwind-EHABI.cpp | 9 +- lib/libunwind/src/Unwind-seh.cpp | 6 +- lib/libunwind/src/UnwindCursor.hpp | 733 ++++++++++++++++++++- lib/libunwind/src/UnwindLevel1-gcc-ext.c | 7 +- lib/libunwind/src/UnwindLevel1.c | 8 +- lib/libunwind/src/UnwindRegistersRestore.S | 199 ++++-- lib/libunwind/src/UnwindRegistersSave.S | 56 +- lib/libunwind/src/Unwind_AIXExtras.cpp | 63 ++ lib/libunwind/src/assembly.h | 49 +- lib/libunwind/src/cet_unwind.h | 2 +- lib/libunwind/src/config.h | 15 +- lib/libunwind/src/libunwind.cpp | 12 + lib/libunwind/src/libunwind_ext.h | 4 + src/libunwind.zig | 24 +- 21 files changed, 1515 insertions(+), 134 deletions(-) create mode 100644 lib/libunwind/src/Unwind_AIXExtras.cpp diff --git a/lib/libunwind/include/__libunwind_config.h b/lib/libunwind/include/__libunwind_config.h index e87bcf4003..5e9de90f64 100644 --- a/lib/libunwind/include/__libunwind_config.h +++ b/lib/libunwind/include/__libunwind_config.h @@ -9,8 +9,10 @@ #ifndef ____LIBUNWIND_CONFIG_H__ #define ____LIBUNWIND_CONFIG_H__ +#define _LIBUNWIND_VERSION 15000 + #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ - !defined(__ARM_DWARF_EH__) + !defined(__ARM_DWARF_EH__) && !defined(__SEH__) #define _LIBUNWIND_ARM_EHABI #endif @@ -27,6 +29,7 @@ #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON 34 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE 143 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X 83 #if defined(_LIBUNWIND_IS_NATIVE_ONLY) # if defined(__linux__) @@ -158,6 +161,11 @@ # define _LIBUNWIND_CONTEXT_SIZE 67 # define _LIBUNWIND_CURSOR_SIZE 79 # define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE +# elif defined(__s390x__) +# define _LIBUNWIND_TARGET_S390X 1 +# define _LIBUNWIND_CONTEXT_SIZE 34 +# define _LIBUNWIND_CURSOR_SIZE 46 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X # else # error "Unsupported architecture." # endif @@ -176,6 +184,7 @@ # define _LIBUNWIND_TARGET_HEXAGON 1 # define _LIBUNWIND_TARGET_RISCV 1 # define _LIBUNWIND_TARGET_VE 1 +# define _LIBUNWIND_TARGET_S390X 1 # define _LIBUNWIND_CONTEXT_SIZE 167 # define _LIBUNWIND_CURSOR_SIZE 179 # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 diff --git a/lib/libunwind/include/libunwind.h b/lib/libunwind/include/libunwind.h index 9a74faa48d..f878b46f03 100644 --- a/lib/libunwind/include/libunwind.h +++ b/lib/libunwind/include/libunwind.h @@ -81,7 +81,7 @@ typedef struct unw_addr_space *unw_addr_space_t; typedef int unw_regnum_t; typedef uintptr_t unw_word_t; -#if defined(__arm__) && !defined(__ARM_DWARF_EH__) +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) && !defined(__SEH__) typedef uint64_t unw_fpreg_t; #else typedef double unw_fpreg_t; @@ -120,6 +120,9 @@ extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; #endif +#ifdef _AIX +extern uintptr_t unw_get_data_rel_base(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; @@ -1174,4 +1177,46 @@ enum { UNW_VE_VL = 145, }; +// s390x register numbers +enum { + UNW_S390X_R0 = 0, + UNW_S390X_R1 = 1, + UNW_S390X_R2 = 2, + UNW_S390X_R3 = 3, + UNW_S390X_R4 = 4, + UNW_S390X_R5 = 5, + UNW_S390X_R6 = 6, + UNW_S390X_R7 = 7, + UNW_S390X_R8 = 8, + UNW_S390X_R9 = 9, + UNW_S390X_R10 = 10, + UNW_S390X_R11 = 11, + UNW_S390X_R12 = 12, + UNW_S390X_R13 = 13, + UNW_S390X_R14 = 14, + UNW_S390X_R15 = 15, + UNW_S390X_F0 = 16, + UNW_S390X_F2 = 17, + UNW_S390X_F4 = 18, + UNW_S390X_F6 = 19, + UNW_S390X_F1 = 20, + UNW_S390X_F3 = 21, + UNW_S390X_F5 = 22, + UNW_S390X_F7 = 23, + UNW_S390X_F8 = 24, + UNW_S390X_F10 = 25, + UNW_S390X_F12 = 26, + UNW_S390X_F14 = 27, + UNW_S390X_F9 = 28, + UNW_S390X_F11 = 29, + UNW_S390X_F13 = 30, + UNW_S390X_F15 = 31, + // 32-47 Control Registers + // 48-63 Access Registers + UNW_S390X_PSWM = 64, + UNW_S390X_PSWA = 65, + // 66-67 Reserved + // 68-83 Vector Registers %v16-%v31 +}; + #endif diff --git a/lib/libunwind/include/unwind.h b/lib/libunwind/include/unwind.h index b8d6020a33..6557374fa9 100644 --- a/lib/libunwind/include/unwind.h +++ b/lib/libunwind/include/unwind.h @@ -160,7 +160,7 @@ extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *); extern void *_Unwind_FindEnclosingFunction(void *pc); // Mac OS X does not support text-rel and data-rel addressing so these functions -// are unimplemented +// are unimplemented. extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) LIBUNWIND_UNAVAIL; extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context) diff --git a/lib/libunwind/src/AddressSpace.hpp b/lib/libunwind/src/AddressSpace.hpp index 0c4dfeb4e6..36c9f5a9e3 100644 --- a/lib/libunwind/src/AddressSpace.hpp +++ b/lib/libunwind/src/AddressSpace.hpp @@ -24,7 +24,7 @@ #include "Registers.hpp" #ifndef _LIBUNWIND_USE_DLADDR - #if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32) + #if !(defined(_LIBUNWIND_IS_BAREMETAL) || defined(_WIN32) || defined(_AIX)) #define _LIBUNWIND_USE_DLADDR 1 #else #define _LIBUNWIND_USE_DLADDR 0 @@ -45,6 +45,13 @@ struct EHABIIndexEntry { }; #endif +#if defined(_AIX) +namespace libunwind { +char *getFuncNameFromTBTable(uintptr_t pc, uint16_t &NameLen, + unw_word_t *offset); +} +#endif + #ifdef __APPLE__ struct dyld_unwind_sections @@ -544,6 +551,7 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, DWORD err = GetLastError(); _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: EnumProcessModules failed, " "returned error %d", (int)err); + (void)err; return false; } @@ -580,6 +588,11 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, (void)targetAddr; (void)info; return true; +#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + // The traceback table is used for unwinding. + (void)targetAddr; + (void)info; + return true; #elif defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX) int length = 0; info.arm_section = @@ -596,7 +609,6 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, return false; } - inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) { // TO DO: if OS has way to dynamically register FDEs, check that. (void)targetAddr; @@ -616,6 +628,13 @@ inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, return true; } } +#elif defined(_AIX) + uint16_t nameLen; + char *funcName = getFuncNameFromTBTable(addr, nameLen, offset); + if (funcName != NULL) { + snprintf(buf, bufLen, "%.*s", nameLen, funcName); + return true; + } #else (void)addr; (void)buf; diff --git a/lib/libunwind/src/DwarfInstructions.hpp b/lib/libunwind/src/DwarfInstructions.hpp index ab83b0c87a..cee4ea53da 100644 --- a/lib/libunwind/src/DwarfInstructions.hpp +++ b/lib/libunwind/src/DwarfInstructions.hpp @@ -72,6 +72,10 @@ private: assert(0 && "getCFA(): unknown location"); __builtin_unreachable(); } +#if defined(_LIBUNWIND_TARGET_AARCH64) + static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa, + PrologInfo &prolog); +#endif }; template @@ -166,6 +170,21 @@ v128 DwarfInstructions::getSavedVectorRegister( } _LIBUNWIND_ABORT("unsupported restore location for vector register"); } +#if defined(_LIBUNWIND_TARGET_AARCH64) +template +bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, + pint_t cfa, PrologInfo &prolog) { + pint_t raSignState; + auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE]; + if (regloc.location == CFI_Parser::kRegisterUnused) + raSignState = static_cast(regloc.value); + else + raSignState = getSavedRegister(addressSpace, registers, cfa, regloc); + + // Only bit[0] is meaningful. + return raSignState & 0x01; +} +#endif template int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, @@ -194,9 +213,10 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, newRegisters.setSP(cfa); pint_t returnAddress = 0; - const int lastReg = R::lastDwarfRegNum(); - assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= lastReg && - "register range too large"); + constexpr int lastReg = R::lastDwarfRegNum(); + static_assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= + lastReg, + "register range too large"); assert(lastReg >= (int)cieInfo.returnAddressRegister && "register range does not contain return address register"); for (int i = 0; i <= lastReg; ++i) { @@ -235,7 +255,7 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, // restored. autia1716 is used instead of autia as autia1716 assembles // to a NOP on pre-v8.3a architectures. if ((R::getArch() == REGISTERS_ARM64) && - prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value && + getRA_SIGN_STATE(addressSpace, registers, cfa, prolog) && returnAddress != 0) { #if !defined(_LIBUNWIND_IS_NATIVE_ONLY) return UNW_ECROSSRASIGNING; diff --git a/lib/libunwind/src/EHHeaderParser.hpp b/lib/libunwind/src/EHHeaderParser.hpp index 9a38070fab..ed4317c890 100644 --- a/lib/libunwind/src/EHHeaderParser.hpp +++ b/lib/libunwind/src/EHHeaderParser.hpp @@ -57,7 +57,8 @@ bool EHHeaderParser::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t p = ehHdrStart; uint8_t version = addressSpace.get8(p++); if (version != 1) { - _LIBUNWIND_LOG0("Unsupported .eh_frame_hdr version"); + _LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64, + version, static_cast(ehHdrStart)); return false; } diff --git a/lib/libunwind/src/Registers.hpp b/lib/libunwind/src/Registers.hpp index cbc3876d67..98adb78940 100644 --- a/lib/libunwind/src/Registers.hpp +++ b/lib/libunwind/src/Registers.hpp @@ -39,6 +39,7 @@ enum { REGISTERS_HEXAGON, REGISTERS_RISCV, REGISTERS_VE, + REGISTERS_S390X, }; #if defined(_LIBUNWIND_TARGET_I386) @@ -69,7 +70,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto() { __libunwind_Registers_x86_jumpto(this); } - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86; + } static int getArch() { return REGISTERS_X86; } uint32_t getSP() const { return _registers.__esp; } @@ -285,7 +288,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto() { __libunwind_Registers_x86_64_jumpto(this); } - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64; + } static int getArch() { return REGISTERS_X86_64; } uint64_t getSP() const { return _registers.__rsp; } @@ -602,13 +607,17 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC; + } static int getArch() { return REGISTERS_PPC; } uint64_t getSP() const { return _registers.__r1; } void setSP(uint32_t value) { _registers.__r1 = value; } uint64_t getIP() const { return _registers.__srr0; } void setIP(uint32_t value) { _registers.__srr0 = value; } + uint64_t getCR() const { return _registers.__cr; } + void setCR(uint32_t value) { _registers.__cr = value; } private: struct ppc_thread_state_t { @@ -1168,13 +1177,17 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64; + } static int getArch() { return REGISTERS_PPC64; } uint64_t getSP() const { return _registers.__r1; } void setSP(uint64_t value) { _registers.__r1 = value; } uint64_t getIP() const { return _registers.__srr0; } void setIP(uint64_t value) { _registers.__srr0 = value; } + uint64_t getCR() const { return _registers.__cr; } + void setCR(uint64_t value) { _registers.__cr = value; } private: struct ppc64_thread_state_t { @@ -1813,7 +1826,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto() { __libunwind_Registers_arm64_jumpto(this); } - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; + } static int getArch() { return REGISTERS_ARM64; } uint64_t getSP() const { return _registers.__sp; } @@ -2103,7 +2118,9 @@ public: restoreSavedFloatRegisters(); restoreCoreAndJumpTo(); } - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; + } static int getArch() { return REGISTERS_ARM; } uint32_t getSP() const { return _registers.__sp; } @@ -2603,7 +2620,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K; + } static int getArch() { return REGISTERS_OR1K; } uint64_t getSP() const { return _registers.__r[1]; } @@ -2800,7 +2819,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; + } static int getArch() { return REGISTERS_MIPS_O32; } uint32_t getSP() const { return _registers.__r[29]; } @@ -3127,7 +3148,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; + } static int getArch() { return REGISTERS_MIPS_NEWABI; } uint64_t getSP() const { return _registers.__r[29]; } @@ -3422,7 +3445,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC; + } static int getArch() { return REGISTERS_SPARC; } uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6]; } @@ -3606,7 +3631,7 @@ public: void setVectorRegister(int num, v128 value); const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { + static constexpr int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64; } static int getArch() { return REGISTERS_SPARC64; } @@ -3791,7 +3816,9 @@ public: void setVectorRegister(int num, v128 value); const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON; + } static int getArch() { return REGISTERS_HEXAGON; } uint32_t getSP() const { return _registers.__r[UNW_HEXAGON_R29]; } @@ -4004,7 +4031,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV; + } static int getArch() { return REGISTERS_RISCV; } reg_t getSP() const { return _registers[2]; } @@ -4290,7 +4319,9 @@ public: void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto(); - static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE; } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE; + } static int getArch() { return REGISTERS_VE; } uint64_t getSP() const { return _registers.__s[11]; } @@ -4712,6 +4743,295 @@ inline const char *Registers_ve::getRegisterName(int regNum) { } #endif // _LIBUNWIND_TARGET_VE +#if defined(_LIBUNWIND_TARGET_S390X) +/// Registers_s390x holds the register state of a thread in a +/// 64-bit Linux on IBM zSystems process. +class _LIBUNWIND_HIDDEN Registers_s390x { +public: + Registers_s390x(); + Registers_s390x(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X; + } + static int getArch() { return REGISTERS_S390X; } + + uint64_t getSP() const { return _registers.__gpr[15]; } + void setSP(uint64_t value) { _registers.__gpr[15] = value; } + uint64_t getIP() const { return _registers.__pswa; } + void setIP(uint64_t value) { _registers.__pswa = value; } + +private: + struct s390x_thread_state_t { + uint64_t __pswm; // Problem Status Word: Mask + uint64_t __pswa; // Problem Status Word: Address (PC) + uint64_t __gpr[16]; // General Purpose Registers + double __fpr[16]; // Floating-Point Registers + }; + + s390x_thread_state_t _registers; +}; + +inline Registers_s390x::Registers_s390x(const void *registers) { + static_assert((check_fit::does_fit), + "s390x registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_s390x::Registers_s390x() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_s390x::validRegister(int regNum) const { + switch (regNum) { + case UNW_S390X_PSWM: + case UNW_S390X_PSWA: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + } + + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) + return true; + + return false; +} + +inline uint64_t Registers_s390x::getRegister(int regNum) const { + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) + return _registers.__gpr[regNum - UNW_S390X_R0]; + + switch (regNum) { + case UNW_S390X_PSWM: + return _registers.__pswm; + case UNW_S390X_PSWA: + case UNW_REG_IP: + return _registers.__pswa; + case UNW_REG_SP: + return _registers.__gpr[15]; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline void Registers_s390x::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) { + _registers.__gpr[regNum - UNW_S390X_R0] = value; + return; + } + + switch (regNum) { + case UNW_S390X_PSWM: + _registers.__pswm = value; + return; + case UNW_S390X_PSWA: + case UNW_REG_IP: + _registers.__pswa = value; + return; + case UNW_REG_SP: + _registers.__gpr[15] = value; + return; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline bool Registers_s390x::validFloatRegister(int regNum) const { + return regNum >= UNW_S390X_F0 && regNum <= UNW_S390X_F15; +} + +inline double Registers_s390x::getFloatRegister(int regNum) const { + // NOTE: FPR DWARF register numbers are not consecutive. + switch (regNum) { + case UNW_S390X_F0: + return _registers.__fpr[0]; + case UNW_S390X_F1: + return _registers.__fpr[1]; + case UNW_S390X_F2: + return _registers.__fpr[2]; + case UNW_S390X_F3: + return _registers.__fpr[3]; + case UNW_S390X_F4: + return _registers.__fpr[4]; + case UNW_S390X_F5: + return _registers.__fpr[5]; + case UNW_S390X_F6: + return _registers.__fpr[6]; + case UNW_S390X_F7: + return _registers.__fpr[7]; + case UNW_S390X_F8: + return _registers.__fpr[8]; + case UNW_S390X_F9: + return _registers.__fpr[9]; + case UNW_S390X_F10: + return _registers.__fpr[10]; + case UNW_S390X_F11: + return _registers.__fpr[11]; + case UNW_S390X_F12: + return _registers.__fpr[12]; + case UNW_S390X_F13: + return _registers.__fpr[13]; + case UNW_S390X_F14: + return _registers.__fpr[14]; + case UNW_S390X_F15: + return _registers.__fpr[15]; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline void Registers_s390x::setFloatRegister(int regNum, double value) { + // NOTE: FPR DWARF register numbers are not consecutive. + switch (regNum) { + case UNW_S390X_F0: + _registers.__fpr[0] = value; + return; + case UNW_S390X_F1: + _registers.__fpr[1] = value; + return; + case UNW_S390X_F2: + _registers.__fpr[2] = value; + return; + case UNW_S390X_F3: + _registers.__fpr[3] = value; + return; + case UNW_S390X_F4: + _registers.__fpr[4] = value; + return; + case UNW_S390X_F5: + _registers.__fpr[5] = value; + return; + case UNW_S390X_F6: + _registers.__fpr[6] = value; + return; + case UNW_S390X_F7: + _registers.__fpr[7] = value; + return; + case UNW_S390X_F8: + _registers.__fpr[8] = value; + return; + case UNW_S390X_F9: + _registers.__fpr[9] = value; + return; + case UNW_S390X_F10: + _registers.__fpr[10] = value; + return; + case UNW_S390X_F11: + _registers.__fpr[11] = value; + return; + case UNW_S390X_F12: + _registers.__fpr[12] = value; + return; + case UNW_S390X_F13: + _registers.__fpr[13] = value; + return; + case UNW_S390X_F14: + _registers.__fpr[14] = value; + return; + case UNW_S390X_F15: + _registers.__fpr[15] = value; + return; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline bool Registers_s390x::validVectorRegister(int /*regNum*/) const { + return false; +} + +inline v128 Registers_s390x::getVectorRegister(int /*regNum*/) const { + _LIBUNWIND_ABORT("s390x vector support not implemented"); +} + +inline void Registers_s390x::setVectorRegister(int /*regNum*/, v128 /*value*/) { + _LIBUNWIND_ABORT("s390x vector support not implemented"); +} + +inline const char *Registers_s390x::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_S390X_R0: + return "r0"; + case UNW_S390X_R1: + return "r1"; + case UNW_S390X_R2: + return "r2"; + case UNW_S390X_R3: + return "r3"; + case UNW_S390X_R4: + return "r4"; + case UNW_S390X_R5: + return "r5"; + case UNW_S390X_R6: + return "r6"; + case UNW_S390X_R7: + return "r7"; + case UNW_S390X_R8: + return "r8"; + case UNW_S390X_R9: + return "r9"; + case UNW_S390X_R10: + return "r10"; + case UNW_S390X_R11: + return "r11"; + case UNW_S390X_R12: + return "r12"; + case UNW_S390X_R13: + return "r13"; + case UNW_S390X_R14: + return "r14"; + case UNW_S390X_R15: + return "r15"; + case UNW_S390X_F0: + return "f0"; + case UNW_S390X_F1: + return "f1"; + case UNW_S390X_F2: + return "f2"; + case UNW_S390X_F3: + return "f3"; + case UNW_S390X_F4: + return "f4"; + case UNW_S390X_F5: + return "f5"; + case UNW_S390X_F6: + return "f6"; + case UNW_S390X_F7: + return "f7"; + case UNW_S390X_F8: + return "f8"; + case UNW_S390X_F9: + return "f9"; + case UNW_S390X_F10: + return "f10"; + case UNW_S390X_F11: + return "f11"; + case UNW_S390X_F12: + return "f12"; + case UNW_S390X_F13: + return "f13"; + case UNW_S390X_F14: + return "f14"; + case UNW_S390X_F15: + return "f15"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_S390X + + } // namespace libunwind #endif // __REGISTERS_HPP__ diff --git a/lib/libunwind/src/Unwind-EHABI.cpp b/lib/libunwind/src/Unwind-EHABI.cpp index 257db724c2..f203887567 100644 --- a/lib/libunwind/src/Unwind-EHABI.cpp +++ b/lib/libunwind/src/Unwind-EHABI.cpp @@ -432,10 +432,11 @@ _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, uint32_t sp; uint32_t pac; _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); - _Unwind_VRS_Get(context, _UVRSC_PSEUDO, UNW_ARM_RA_AUTH_CODE, - _UVRSD_UINT32, &pac); + _Unwind_VRS_Get(context, _UVRSC_PSEUDO, 0, _UVRSD_UINT32, &pac); __asm__ __volatile__("autg %0, %1, %2" : : "r"(pac), "r"(lr), "r"(sp) :); } +#else + (void)hasReturnAddrAuthCode; #endif _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr); } @@ -1136,8 +1137,7 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, } uint32_t pac = *sp++; _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); - return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_RA_AUTH_CODE, - _UVRSD_UINT32, &pac); + return _Unwind_VRS_Set(context, _UVRSC_PSEUDO, 0, _UVRSD_UINT32, &pac); } } _LIBUNWIND_ABORT("unsupported register class"); @@ -1193,6 +1193,7 @@ _Unwind_DeleteException(_Unwind_Exception *exception_object) { extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception *exception_object, struct _Unwind_Context *context) { + (void)exception_object; unw_cursor_t *cursor = (unw_cursor_t *)context; switch (__unw_step(cursor)) { case UNW_STEP_SUCCESS: diff --git a/lib/libunwind/src/Unwind-seh.cpp b/lib/libunwind/src/Unwind-seh.cpp index f00bc4721b..c2c31045a3 100644 --- a/lib/libunwind/src/Unwind-seh.cpp +++ b/lib/libunwind/src/Unwind-seh.cpp @@ -104,7 +104,7 @@ _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, if (!ctx) { __unw_init_seh(&cursor, disp->ContextRecord); __unw_seh_set_disp_ctx(&cursor, disp); - __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1); + __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc); ctx = (struct _Unwind_Context *)&cursor; if (!IS_UNWINDING(ms_exc->ExceptionFlags)) { @@ -255,8 +255,8 @@ unwind_phase2_forced(unw_context_t *uc, (frameInfo.start_ip + offset > frameInfo.end_ip)) functionName = ".anonymous."; _LIBUNWIND_TRACE_UNWINDING( - "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64 - ", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64, + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, (void *)exception_object, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler); } diff --git a/lib/libunwind/src/UnwindCursor.hpp b/lib/libunwind/src/UnwindCursor.hpp index 1ca842f33a..b8bd9bc590 100644 --- a/lib/libunwind/src/UnwindCursor.hpp +++ b/lib/libunwind/src/UnwindCursor.hpp @@ -24,6 +24,19 @@ #ifdef __APPLE__ #include #endif +#ifdef _AIX +#include +#include +#include +#endif + +#if defined(_LIBUNWIND_TARGET_LINUX) && \ + (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_S390X)) +#include +#include +#include +#define _LIBUNWIND_CHECK_LINUX_SIGRETURN 1 +#endif #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) // Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and @@ -451,6 +464,12 @@ public: virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); } #endif +#ifdef _AIX + virtual uintptr_t getDataRelBase() { + _LIBUNWIND_ABORT("getDataRelBase not implemented"); + } +#endif + #if defined(_LIBUNWIND_USE_CET) virtual void *get_registers() { _LIBUNWIND_ABORT("get_registers not implemented"); @@ -499,6 +518,14 @@ private: pint_t getLastPC() const { return _dispContext.ControlPc; } void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; } RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { +#ifdef __arm__ + // Remove the thumb bit; FunctionEntry ranges don't include the thumb bit. + pc &= ~1U; +#endif + // If pc points exactly at the end of the range, we might resolve the + // next function instead. Decrement pc by 1 to fit inside the current + // function. + pc -= 1; _dispContext.FunctionEntry = RtlLookupFunctionEntry(pc, &_dispContext.ImageBase, _dispContext.HistoryTable); @@ -844,7 +871,7 @@ void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { uint32_t w; float f; } d; - d.f = value; + d.f = (float)value; _msContext.S[regNum - UNW_ARM_S0] = d.w; } if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { @@ -910,9 +937,14 @@ public: virtual void saveVFPAsX(); #endif +#ifdef _AIX + virtual uintptr_t getDataRelBase(); +#endif + #if defined(_LIBUNWIND_USE_CET) virtual void *get_registers() { return &_registers; } #endif + // libunwind does not and should not depend on C++ library which means that we // need our own defition of inline placement new. static void *operator new(size_t, UnwindCursor *p) { return p; } @@ -937,7 +969,7 @@ private: } #endif -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) bool setInfoForSigReturn() { R dummy; return setInfoForSigReturn(dummy); @@ -946,8 +978,14 @@ private: R dummy; return stepThroughSigReturn(dummy); } +#if defined(_LIBUNWIND_TARGET_AARCH64) bool setInfoForSigReturn(Registers_arm64 &); int stepThroughSigReturn(Registers_arm64 &); +#endif +#if defined(_LIBUNWIND_TARGET_S390X) + bool setInfoForSigReturn(Registers_s390x &); + int stepThroughSigReturn(Registers_s390x &); +#endif template bool setInfoForSigReturn(Registers &) { return false; } @@ -1204,6 +1242,12 @@ private: } #endif +#if defined (_LIBUNWIND_TARGET_S390X) + compact_unwind_encoding_t dwarfEncoding(Registers_s390x &) const { + return 0; + } +#endif + #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) @@ -1220,13 +1264,23 @@ private: int stepWithSEHData() { /* FIXME: Implement */ return 0; } #endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + bool getInfoFromTBTable(pint_t pc, R ®isters); + int stepWithTBTable(pint_t pc, tbtable *TBTable, R ®isters, + bool &isSignalFrame); + int stepWithTBTableData() { + return stepWithTBTable(reinterpret_cast(this->getReg(UNW_REG_IP)), + reinterpret_cast(_info.unwind_info), + _registers, _isSignalFrame); + } +#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) A &_addressSpace; R _registers; unw_proc_info_t _info; bool _unwindInfoMissing; bool _isSignalFrame; -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) bool _isSigReturn = false; #endif }; @@ -1292,6 +1346,13 @@ template void UnwindCursor::saveVFPAsX() { } #endif +#ifdef _AIX +template +uintptr_t UnwindCursor::getDataRelBase() { + return reinterpret_cast(_info.extra); +} +#endif + template const char *UnwindCursor::getRegisterName(int regNum) { return _registers.getRegisterName(regNum); @@ -1912,20 +1973,517 @@ bool UnwindCursor::getInfoFromSEH(pint_t pc) { _info.handler = 0; } } -#elif defined(_LIBUNWIND_TARGET_ARM) - _info.end_ip = _info.start_ip + unwindEntry->FunctionLength; - _info.lsda = 0; // FIXME - _info.handler = 0; // FIXME #endif setLastPC(pc); return true; } #endif +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) +// Masks for traceback table field xtbtable. +enum xTBTableMask : uint8_t { + reservedBit = 0x02, // The traceback table was incorrectly generated if set + // (see comments in function getInfoFromTBTable(). + ehInfoBit = 0x08 // Exception handling info is present if set +}; + +enum frameType : unw_word_t { + frameWithXLEHStateTable = 0, + frameWithEHInfo = 1 +}; + +extern "C" { +typedef _Unwind_Reason_Code __xlcxx_personality_v0_t(int, _Unwind_Action, + uint64_t, + _Unwind_Exception *, + struct _Unwind_Context *); +__attribute__((__weak__)) __xlcxx_personality_v0_t __xlcxx_personality_v0; +} + +static __xlcxx_personality_v0_t *xlcPersonalityV0; +static RWMutex xlcPersonalityV0InitLock; + +template +bool UnwindCursor::getInfoFromTBTable(pint_t pc, R ®isters) { + uint32_t *p = reinterpret_cast(pc); + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable *TBTable = reinterpret_cast(p + 1); + + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) { + functionName = ".anonymous."; + } + _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p", + __func__, functionName, + reinterpret_cast(TBTable)); + } + + // If the traceback table does not contain necessary info, bypass this frame. + if (!TBTable->tb.has_tboff) + return false; + + // Structure tbtable_ext contains important data we are looking for. + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // p now points to tb_offset, the offset from start of function to TB table. + unw_word_t start_ip = + reinterpret_cast(TBTable) - *p - sizeof(uint32_t); + unw_word_t end_ip = reinterpret_cast(TBTable); + ++p; + + _LIBUNWIND_TRACE_UNWINDING("start_ip=%p, end_ip=%p\n", + reinterpret_cast(start_ip), + reinterpret_cast(end_ip)); + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + ++p; + + unw_word_t lsda = 0; + unw_word_t handler = 0; + unw_word_t flags = frameType::frameWithXLEHStateTable; + + if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl) { + // State table info is available. The ctl_info field indicates the + // number of CTL anchors. There should be only one entry for the C++ + // state table. + assert(*p == 1 && "libunwind: there must be only one ctl_info entry"); + ++p; + // p points to the offset of the state table into the stack. + pint_t stateTableOffset = *p++; + + int framePointerReg; + + // Skip fields name_len and name if exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(p)); + p = reinterpret_cast(reinterpret_cast(p) + name_len + + sizeof(uint16_t)); + } + + if (TBTable->tb.uses_alloca) + framePointerReg = *(reinterpret_cast(p)); + else + framePointerReg = 1; // default frame pointer == SP + + _LIBUNWIND_TRACE_UNWINDING( + "framePointerReg=%d, framePointer=%p, " + "stateTableOffset=%#lx\n", + framePointerReg, + reinterpret_cast(_registers.getRegister(framePointerReg)), + stateTableOffset); + lsda = _registers.getRegister(framePointerReg) + stateTableOffset; + + // Since the traceback table generated by the legacy XLC++ does not + // provide the location of the personality for the state table, + // function __xlcxx_personality_v0(), which is the personality for the state + // table and is exported from libc++abi, is directly assigned as the + // handler here. When a legacy XLC++ frame is encountered, the symbol + // is resolved dynamically using dlopen() to avoid hard dependency from + // libunwind on libc++abi. + + // Resolve the function pointer to the state table personality if it has + // not already. + if (xlcPersonalityV0 == NULL) { + xlcPersonalityV0InitLock.lock(); + if (xlcPersonalityV0 == NULL) { + // If libc++abi is statically linked in, symbol __xlcxx_personality_v0 + // has been resolved at the link time. + xlcPersonalityV0 = &__xlcxx_personality_v0; + if (xlcPersonalityV0 == NULL) { + // libc++abi is dynamically linked. Resolve __xlcxx_personality_v0 + // using dlopen(). + const char libcxxabi[] = "libc++abi.a(libc++abi.so.1)"; + void *libHandle; + libHandle = dlopen(libcxxabi, RTLD_MEMBER | RTLD_NOW); + if (libHandle == NULL) { + _LIBUNWIND_TRACE_UNWINDING("dlopen() failed with errno=%d\n", + errno); + assert(0 && "dlopen() failed"); + } + xlcPersonalityV0 = reinterpret_cast<__xlcxx_personality_v0_t *>( + dlsym(libHandle, "__xlcxx_personality_v0")); + if (xlcPersonalityV0 == NULL) { + _LIBUNWIND_TRACE_UNWINDING("dlsym() failed with errno=%d\n", errno); + assert(0 && "dlsym() failed"); + } + dlclose(libHandle); + } + } + xlcPersonalityV0InitLock.unlock(); + } + handler = reinterpret_cast(xlcPersonalityV0); + _LIBUNWIND_TRACE_UNWINDING("State table: LSDA=%p, Personality=%p\n", + reinterpret_cast(lsda), + reinterpret_cast(handler)); + } else if (TBTable->tb.longtbtable) { + // This frame has the traceback table extension. Possible cases are + // 1) a C++ frame that has the 'eh_info' structure; 2) a C++ frame that + // is not EH aware; or, 3) a frame of other languages. We need to figure out + // if the traceback table extension contains the 'eh_info' structure. + // + // We also need to deal with the complexity arising from some XL compiler + // versions use the wrong ordering of 'longtbtable' and 'has_vec' bits + // where the 'longtbtable' bit is meant to be the 'has_vec' bit and vice + // versa. For frames of code generated by those compilers, the 'longtbtable' + // bit may be set but there isn't really a traceback table extension. + // + // In , there is the following definition of + // 'struct tbtable_ext'. It is not really a structure but a dummy to + // collect the description of optional parts of the traceback table. + // + // struct tbtable_ext { + // ... + // char alloca_reg; /* Register for alloca automatic storage */ + // struct vec_ext vec_ext; /* Vector extension (if has_vec is set) */ + // unsigned char xtbtable; /* More tbtable fields, if longtbtable is set*/ + // }; + // + // Depending on how the 'has_vec'/'longtbtable' bit is interpreted, the data + // following 'alloca_reg' can be treated either as 'struct vec_ext' or + // 'unsigned char xtbtable'. 'xtbtable' bits are defined in + // as flags. The 7th bit '0x02' is currently + // unused and should not be set. 'struct vec_ext' is defined in + // as follows: + // + // struct vec_ext { + // unsigned vr_saved:6; /* Number of non-volatile vector regs saved + // */ + // /* first register saved is assumed to be */ + // /* 32 - vr_saved */ + // unsigned saves_vrsave:1; /* Set if vrsave is saved on the stack */ + // unsigned has_varargs:1; + // ... + // }; + // + // Here, the 7th bit is used as 'saves_vrsave'. To determine whether it + // is 'struct vec_ext' or 'xtbtable' that follows 'alloca_reg', + // we checks if the 7th bit is set or not because 'xtbtable' should + // never have the 7th bit set. The 7th bit of 'xtbtable' will be reserved + // in the future to make sure the mitigation works. This mitigation + // is not 100% bullet proof because 'struct vec_ext' may not always have + // 'saves_vrsave' bit set. + // + // 'reservedBit' is defined in enum 'xTBTableMask' above as the mask for + // checking the 7th bit. + + // p points to field name len. + uint8_t *charPtr = reinterpret_cast(p); + + // Skip fields name_len and name if they exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(charPtr)); + charPtr = charPtr + name_len + sizeof(uint16_t); + } + + // Skip field alloc_reg if it exists. + if (TBTable->tb.uses_alloca) + ++charPtr; + + // Check traceback table bit has_vec. Skip struct vec_ext if it exists. + if (TBTable->tb.has_vec) + // Note struct vec_ext does exist at this point because whether the + // ordering of longtbtable and has_vec bits is correct or not, both + // are set. + charPtr += sizeof(struct vec_ext); + + // charPtr points to field 'xtbtable'. Check if the EH info is available. + // Also check if the reserved bit of the extended traceback table field + // 'xtbtable' is set. If it is, the traceback table was incorrectly + // generated by an XL compiler that uses the wrong ordering of 'longtbtable' + // and 'has_vec' bits and this is in fact 'struct vec_ext'. So skip the + // frame. + if ((*charPtr & xTBTableMask::ehInfoBit) && + !(*charPtr & xTBTableMask::reservedBit)) { + // Mark this frame has the new EH info. + flags = frameType::frameWithEHInfo; + + // eh_info is available. + charPtr++; + // The pointer is 4-byte aligned. + if (reinterpret_cast(charPtr) % 4) + charPtr += 4 - reinterpret_cast(charPtr) % 4; + uintptr_t *ehInfo = + reinterpret_cast(*(reinterpret_cast( + registers.getRegister(2) + + *(reinterpret_cast(charPtr))))); + + // ehInfo points to structure en_info. The first member is version. + // Only version 0 is currently supported. + assert(*(reinterpret_cast(ehInfo)) == 0 && + "libunwind: ehInfo version other than 0 is not supported"); + + // Increment ehInfo to point to member lsda. + ++ehInfo; + lsda = *ehInfo++; + + // enInfo now points to member personality. + handler = *ehInfo; + + _LIBUNWIND_TRACE_UNWINDING("Range table: LSDA=%#lx, Personality=%#lx\n", + lsda, handler); + } + } + + _info.start_ip = start_ip; + _info.end_ip = end_ip; + _info.lsda = lsda; + _info.handler = handler; + _info.gp = 0; + _info.flags = flags; + _info.format = 0; + _info.unwind_info = reinterpret_cast(TBTable); + _info.unwind_info_size = 0; + _info.extra = registers.getRegister(2); + + return true; +} + +// Step back up the stack following the frame back link. +template +int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, + R ®isters, bool &isSignalFrame) { + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) { + functionName = ".anonymous."; + } + _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p", + __func__, functionName, + reinterpret_cast(TBTable)); + } + +#if defined(__powerpc64__) + // Instruction to reload TOC register "l r2,40(r1)" + const uint32_t loadTOCRegInst = 0xe8410028; + const int32_t unwPPCF0Index = UNW_PPC64_F0; + const int32_t unwPPCV0Index = UNW_PPC64_V0; +#else + // Instruction to reload TOC register "l r2,20(r1)" + const uint32_t loadTOCRegInst = 0x80410014; + const int32_t unwPPCF0Index = UNW_PPC_F0; + const int32_t unwPPCV0Index = UNW_PPC_V0; +#endif + + R newRegisters = registers; + + // lastStack points to the stack frame of the next routine up. + pint_t lastStack = *(reinterpret_cast(registers.getSP())); + + // Return address is the address after call site instruction. + pint_t returnAddress; + + if (isSignalFrame) { + _LIBUNWIND_TRACE_UNWINDING("Possible signal handler frame: lastStack=%p", + reinterpret_cast(lastStack)); + + sigcontext *sigContext = reinterpret_cast( + reinterpret_cast(lastStack) + STKMIN); + returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; + + _LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n", + reinterpret_cast(sigContext), + reinterpret_cast(returnAddress)); + + if (returnAddress < 0x10000000) { + // Try again using STKMINALIGN + sigContext = reinterpret_cast( + reinterpret_cast(lastStack) + STKMINALIGN); + returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; + if (returnAddress < 0x10000000) { + _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n", + reinterpret_cast(returnAddress)); + return UNW_EBADFRAME; + } else { + _LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: " + "sigContext=%p, returnAddress=%p. " + "Seems to be a valid address\n", + reinterpret_cast(sigContext), + reinterpret_cast(returnAddress)); + } + } + // Restore the condition register from sigcontext. + newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr); + + // Restore GPRs from sigcontext. + for (int i = 0; i < 32; ++i) + newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]); + + // Restore FPRs from sigcontext. + for (int i = 0; i < 32; ++i) + newRegisters.setFloatRegister(i + unwPPCF0Index, + sigContext->sc_jmpbuf.jmp_context.fpr[i]); + + // Restore vector registers if there is an associated extended context + // structure. + if (sigContext->sc_jmpbuf.jmp_context.msr & __EXTCTX) { + ucontext_t *uContext = reinterpret_cast(sigContext); + if (uContext->__extctx->__extctx_magic == __EXTCTX_MAGIC) { + for (int i = 0; i < 32; ++i) + newRegisters.setVectorRegister( + i + unwPPCV0Index, *(reinterpret_cast( + &(uContext->__extctx->__vmx.__vr[i])))); + } + } + } else { + // Step up a normal frame. + returnAddress = reinterpret_cast(lastStack)[2]; + + _LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, " + "returnAddress=%p\n", + reinterpret_cast(lastStack), + reinterpret_cast(returnAddress)); + _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n", + TBTable->tb.fpr_saved, TBTable->tb.gpr_saved, + TBTable->tb.saves_cr); + + // Restore FP registers. + char *ptrToRegs = reinterpret_cast(lastStack); + double *FPRegs = reinterpret_cast( + ptrToRegs - (TBTable->tb.fpr_saved * sizeof(double))); + for (int i = 0; i < TBTable->tb.fpr_saved; ++i) + newRegisters.setFloatRegister( + 32 - TBTable->tb.fpr_saved + i + unwPPCF0Index, FPRegs[i]); + + // Restore GP registers. + ptrToRegs = reinterpret_cast(FPRegs); + uintptr_t *GPRegs = reinterpret_cast( + ptrToRegs - (TBTable->tb.gpr_saved * sizeof(uintptr_t))); + for (int i = 0; i < TBTable->tb.gpr_saved; ++i) + newRegisters.setRegister(32 - TBTable->tb.gpr_saved + i, GPRegs[i]); + + // Restore Vector registers. + ptrToRegs = reinterpret_cast(GPRegs); + + // Restore vector registers only if this is a Clang frame. Also + // check if traceback table bit has_vec is set. If it is, structure + // vec_ext is available. + if (_info.flags == frameType::frameWithEHInfo && TBTable->tb.has_vec) { + + // Get to the vec_ext structure to check if vector registers are saved. + uint32_t *p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // Skip field tb_offset if exists. + if (TBTable->tb.has_tboff) + ++p; + + // Skip field hand_mask if exists. + if (TBTable->tb.int_hndl) + ++p; + + // Skip fields ctl_info and ctl_info_disp if exist. + if (TBTable->tb.has_ctl) { + // Skip field ctl_info. + ++p; + // Skip field ctl_info_disp. + ++p; + } + + // Skip fields name_len and name if exist. + // p is supposed to point to field name_len now. + uint8_t *charPtr = reinterpret_cast(p); + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(charPtr)); + charPtr = charPtr + name_len + sizeof(uint16_t); + } + + // Skip field alloc_reg if it exists. + if (TBTable->tb.uses_alloca) + ++charPtr; + + struct vec_ext *vec_ext = reinterpret_cast(charPtr); + + _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved); + + // Restore vector register(s) if saved on the stack. + if (vec_ext->vr_saved) { + // Saved vector registers are 16-byte aligned. + if (reinterpret_cast(ptrToRegs) % 16) + ptrToRegs -= reinterpret_cast(ptrToRegs) % 16; + v128 *VecRegs = reinterpret_cast(ptrToRegs - vec_ext->vr_saved * + sizeof(v128)); + for (int i = 0; i < vec_ext->vr_saved; ++i) { + newRegisters.setVectorRegister( + 32 - vec_ext->vr_saved + i + unwPPCV0Index, VecRegs[i]); + } + } + } + if (TBTable->tb.saves_cr) { + // Get the saved condition register. The condition register is only + // a single word. + newRegisters.setCR( + *(reinterpret_cast(lastStack + sizeof(uintptr_t)))); + } + + // Restore the SP. + newRegisters.setSP(lastStack); + + // The first instruction after return. + uint32_t firstInstruction = *(reinterpret_cast(returnAddress)); + + // Do we need to set the TOC register? + _LIBUNWIND_TRACE_UNWINDING( + "Current gpr2=%p\n", + reinterpret_cast(newRegisters.getRegister(2))); + if (firstInstruction == loadTOCRegInst) { + _LIBUNWIND_TRACE_UNWINDING( + "Set gpr2=%p from frame\n", + reinterpret_cast(reinterpret_cast(lastStack)[5])); + newRegisters.setRegister(2, reinterpret_cast(lastStack)[5]); + } + } + _LIBUNWIND_TRACE_UNWINDING("lastStack=%p, returnAddress=%p, pc=%p\n", + reinterpret_cast(lastStack), + reinterpret_cast(returnAddress), + reinterpret_cast(pc)); + + // The return address is the address after call site instruction, so + // setting IP to that simualates a return. + newRegisters.setIP(reinterpret_cast(returnAddress)); + + // Simulate the step by replacing the register set with the new ones. + registers = newRegisters; + + // Check if the next frame is a signal frame. + pint_t nextStack = *(reinterpret_cast(registers.getSP())); + + // Return address is the address after call site instruction. + pint_t nextReturnAddress = reinterpret_cast(nextStack)[2]; + + if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) { + _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: " + "nextStack=%p, next return address=%p\n", + reinterpret_cast(nextStack), + reinterpret_cast(nextReturnAddress)); + isSignalFrame = true; + } else { + isSignalFrame = false; + } + + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) template void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) _isSigReturn = false; #endif @@ -1948,7 +2506,14 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { // To disambiguate this, back up the pc when we know it is a return // address. if (isReturnAddress) +#if defined(_AIX) + // PC needs to be a 4-byte aligned address to be able to look for a + // word of 0 that indicates the start of the traceback table at the end + // of a function on AIX. + pc -= 4; +#else --pc; +#endif // Ask address space object to find unwind sections for this pc. UnwindInfoSections sects; @@ -1982,6 +2547,12 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { return; #endif +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + // If there is unwind info in the traceback table, look there next. + if (this->getInfoFromTBTable(pc, _registers)) + return; +#endif + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) // If there is dwarf unwind info, look there next. if (sects.dwarf_section != 0) { @@ -2027,7 +2598,7 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { } #endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) if (setInfoForSigReturn()) return; #endif @@ -2036,7 +2607,8 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { _unwindInfoMissing = true; } -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_AARCH64) template bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // Look for the sigreturn trampoline. The trampoline's body is two @@ -2055,14 +2627,28 @@ bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { // // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a segfault. Use process_vm_readv to read + // the memory safely instead. process_vm_readv was added in Linux 3.2, and + // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to + // be present. Unfortunately, there are Linux AArch64 environments where the + // libc wrapper for the syscall might not be present (e.g. Android 5), so call + // the syscall directly instead. + uint32_t instructions[2]; + struct iovec local_iov = {&instructions, sizeof instructions}; + struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; + long bytesRead = + syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); // Look for instructions: mov x8, #0x8b; svc #0x0 - if (_addressSpace.get32(pc) == 0xd2801168 && - _addressSpace.get32(pc + 4) == 0xd4000001) { - _info = {}; - _isSigReturn = true; - return true; - } - return false; + if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + instructions[1] != 0xd4000001) + return false; + + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 4; + _isSigReturn = true; + return true; } template @@ -2096,7 +2682,114 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { _isSignalFrame = true; return UNW_STEP_SUCCESS; } -#endif // defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_AARCH64) + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_S390X) +template +bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { + // Look for the sigreturn trampoline. The trampoline's body is a + // specific instruction (see below). Typically the trampoline comes from the + // vDSO (i.e. the __kernel_[rt_]sigreturn function). A libc might provide its + // own restorer function, though, or user-mode QEMU might write a trampoline + // onto the stack. + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a segfault. Use process_vm_readv to + // read the memory safely instead. + uint16_t inst; + struct iovec local_iov = {&inst, sizeof inst}; + struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; + long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0); + if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) { + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 2; + _isSigReturn = true; + return true; + } + return false; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_s390x &) { + // Determine current SP. + const pint_t sp = static_cast(this->getReg(UNW_REG_SP)); + // According to the s390x ABI, the CFA is at (incoming) SP + 160. + const pint_t cfa = sp + 160; + + // Determine current PC and instruction there (this must be either + // a "svc __NR_sigreturn" or "svc __NR_rt_sigreturn"). + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + const uint16_t inst = _addressSpace.get16(pc); + + // Find the addresses of the signo and sigcontext in the frame. + pint_t pSigctx = 0; + pint_t pSigno = 0; + + // "svc __NR_sigreturn" uses a non-RT signal trampoline frame. + if (inst == 0x0a77) { + // Layout of a non-RT signal trampoline frame, starting at the CFA: + // - 8-byte signal mask + // - 8-byte pointer to sigcontext, followed by signo + // - 4-byte signo + pSigctx = _addressSpace.get64(cfa + 8); + pSigno = pSigctx + 344; + } + + // "svc __NR_rt_sigreturn" uses a RT signal trampoline frame. + if (inst == 0x0aad) { + // Layout of a RT signal trampoline frame, starting at the CFA: + // - 8-byte retcode (+ alignment) + // - 128-byte siginfo struct (starts with signo) + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + pSigctx = cfa + 8 + 128 + 8 + 8 + 24 + 8; + pSigno = cfa + 8; + } + + assert(pSigctx != 0); + assert(pSigno != 0); + + // Offsets from sigcontext to each register. + const pint_t kOffsetPc = 8; + const pint_t kOffsetGprs = 16; + const pint_t kOffsetFprs = 216; + + // Restore all registers. + for (int i = 0; i < 16; ++i) { + uint64_t value = _addressSpace.get64(pSigctx + kOffsetGprs + + static_cast(i * 8)); + _registers.setRegister(UNW_S390X_R0 + i, value); + } + for (int i = 0; i < 16; ++i) { + static const int fpr[16] = { + UNW_S390X_F0, UNW_S390X_F1, UNW_S390X_F2, UNW_S390X_F3, + UNW_S390X_F4, UNW_S390X_F5, UNW_S390X_F6, UNW_S390X_F7, + UNW_S390X_F8, UNW_S390X_F9, UNW_S390X_F10, UNW_S390X_F11, + UNW_S390X_F12, UNW_S390X_F13, UNW_S390X_F14, UNW_S390X_F15 + }; + double value = _addressSpace.getDouble(pSigctx + kOffsetFprs + + static_cast(i * 8)); + _registers.setFloatRegister(fpr[i], value); + } + _registers.setIP(_addressSpace.get64(pSigctx + kOffsetPc)); + + // SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr + // after the faulting instruction rather than before it. + // Do not set _isSignalFrame in that case. + uint32_t signo = _addressSpace.get32(pSigno); + _isSignalFrame = (signo != 4 && signo != 5 && signo != 8); + + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_S390X) template int UnwindCursor::step() { @@ -2106,7 +2799,7 @@ int UnwindCursor::step() { // Use unwinding info to modify register set as if function returned. int result; -#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) if (_isSigReturn) { result = this->stepThroughSigReturn(); } else @@ -2116,6 +2809,8 @@ int UnwindCursor::step() { result = this->stepWithCompactEncoding(); #elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) result = this->stepWithSEHData(); +#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + result = this->stepWithTBTableData(); #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) result = this->stepWithDwarfFDE(); #elif defined(_LIBUNWIND_ARM_EHABI) diff --git a/lib/libunwind/src/UnwindLevel1-gcc-ext.c b/lib/libunwind/src/UnwindLevel1-gcc-ext.c index 951d5d219a..0250664bbc 100644 --- a/lib/libunwind/src/UnwindLevel1-gcc-ext.c +++ b/lib/libunwind/src/UnwindLevel1-gcc-ext.c @@ -59,12 +59,15 @@ _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { /// relative encodings. _LIBUNWIND_EXPORT uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) { - (void)context; _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context); +#if defined(_AIX) + return unw_get_data_rel_base((unw_cursor_t *)context); +#else + (void)context; _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +#endif } - /// Called by personality handler during phase 2 to get base address for text /// relative encodings. _LIBUNWIND_EXPORT uintptr_t diff --git a/lib/libunwind/src/UnwindLevel1.c b/lib/libunwind/src/UnwindLevel1.c index 5c1f99d4bf..84f865e591 100644 --- a/lib/libunwind/src/UnwindLevel1.c +++ b/lib/libunwind/src/UnwindLevel1.c @@ -44,7 +44,11 @@ // directly jump to __libunwind_Registerts_x86/x86_64_jumpto instead of using // a regular function call to avoid pushing to CET shadow stack again. #if !defined(_LIBUNWIND_USE_CET) -#define __unw_phase2_resume(cursor, fn) __unw_resume((cursor)) +#define __unw_phase2_resume(cursor, fn) \ + do { \ + (void)fn; \ + __unw_resume((cursor)); \ + } while (0) #elif defined(_LIBUNWIND_TARGET_I386) #define __unw_phase2_resume(cursor, fn) \ do { \ @@ -480,11 +484,13 @@ _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { _LIBUNWIND_TRACE_API( "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, (void *)context, result); +#if !defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) if (result != 0) { if (*((uint8_t *)result) != 0xFF) _LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF", result); } +#endif return result; } diff --git a/lib/libunwind/src/UnwindRegistersRestore.S b/lib/libunwind/src/UnwindRegistersRestore.S index 1df97f5fc4..eeb6453496 100644 --- a/lib/libunwind/src/UnwindRegistersRestore.S +++ b/lib/libunwind/src/UnwindRegistersRestore.S @@ -8,7 +8,11 @@ #include "assembly.h" +#if defined(_AIX) + .toc +#else .text +#endif #if !defined(__USING_SJLJ_EXCEPTIONS__) @@ -222,27 +226,36 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv) PPC64_LVS(30) PPC64_LVS(31) - // use VRSAVE to conditionally restore the remaining VS regs, - // that are where the V regs are mapped +#define PPC64_CLVS_RESTORE(n) \ + addi 4, 3, PPC64_OFFS_FP + n * 16 ;\ + lxvd2x n, 0, 4 +#if !defined(_AIX) + // use VRSAVE to conditionally restore the remaining VS regs, that are + // where the V regs are mapped. In the AIX ABI, VRSAVE is not used. ld 5, PPC64_OFFS_VRSAVE(3) // test VRsave cmpwi 5, 0 beq Lnovec // conditionally load VS -#define PPC64_CLVS_BOTTOM(n) \ - beq Ldone##n ;\ - addi 4, 3, PPC64_OFFS_FP + n * 16 ;\ - lxvd2x n, 0, 4 ;\ +#define PPC64_CLVSl(n) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(47-n)) ;\ + beq Ldone##n ;\ + PPC64_CLVS_RESTORE(n) ;\ Ldone##n: -#define PPC64_CLVSl(n) \ - andis. 0, 5, (1 PPC_LEFT_SHIFT(47-n)) ;\ -PPC64_CLVS_BOTTOM(n) +#define PPC64_CLVSh(n) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(63-n)) ;\ + beq Ldone##n ;\ + PPC64_CLVS_RESTORE(n) ;\ +Ldone##n: -#define PPC64_CLVSh(n) \ - andi. 0, 5, (1 PPC_LEFT_SHIFT(63-n)) ;\ -PPC64_CLVS_BOTTOM(n) +#else + +#define PPC64_CLVSl(n) PPC64_CLVS_RESTORE(n) +#define PPC64_CLVSh(n) PPC64_CLVS_RESTORE(n) + +#endif // !defined(_AIX) PPC64_CLVSl(32) PPC64_CLVSl(33) @@ -318,33 +331,45 @@ PPC64_CLVS_BOTTOM(n) PPC64_LF(31) #if defined(__ALTIVEC__) - // restore vector registers if any are in use + +#define PPC64_CLV_UNALIGNED_RESTORE(n) \ + ld 0, (PPC64_OFFS_V + n * 16)(3) ;\ + std 0, 0(4) ;\ + ld 0, (PPC64_OFFS_V + n * 16 + 8)(3) ;\ + std 0, 8(4) ;\ + lvx n, 0, 4 + +#if !defined(_AIX) + // restore vector registers if any are in use. In the AIX ABI, VRSAVE is + // not used. ld 5, PPC64_OFFS_VRSAVE(3) // test VRsave cmpwi 5, 0 beq Lnovec +#define PPC64_CLV_UNALIGNEDl(n) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(15-n)) ;\ + beq Ldone##n ;\ + PPC64_CLV_UNALIGNED_RESTORE(n) ;\ +Ldone ## n: + +#define PPC64_CLV_UNALIGNEDh(n) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(31-n)) ;\ + beq Ldone##n ;\ + PPC64_CLV_UNALIGNED_RESTORE(n) ;\ +Ldone ## n: + +#else + +#define PPC64_CLV_UNALIGNEDl(n) PPC64_CLV_UNALIGNED_RESTORE(n) +#define PPC64_CLV_UNALIGNEDh(n) PPC64_CLV_UNALIGNED_RESTORE(n) + +#endif // !defined(_AIX) + subi 4, 1, 16 // r4 is now a 16-byte aligned pointer into the red zone // the _vectorScalarRegisters may not be 16-byte aligned // so copy via red zone temp buffer -#define PPC64_CLV_UNALIGNED_BOTTOM(n) \ - beq Ldone##n ;\ - ld 0, (PPC64_OFFS_V + n * 16)(3) ;\ - std 0, 0(4) ;\ - ld 0, (PPC64_OFFS_V + n * 16 + 8)(3) ;\ - std 0, 8(4) ;\ - lvx n, 0, 4 ;\ -Ldone ## n: - -#define PPC64_CLV_UNALIGNEDl(n) \ - andis. 0, 5, (1 PPC_LEFT_SHIFT(15-n)) ;\ -PPC64_CLV_UNALIGNED_BOTTOM(n) - -#define PPC64_CLV_UNALIGNEDh(n) \ - andi. 0, 5, (1 PPC_LEFT_SHIFT(31-n)) ;\ -PPC64_CLV_UNALIGNED_BOTTOM(n) - PPC64_CLV_UNALIGNEDl(0) PPC64_CLV_UNALIGNEDl(1) PPC64_CLV_UNALIGNEDl(2) @@ -387,11 +412,23 @@ Lnovec: ld 0, PPC64_OFFS_SRR0(3) mtctr 0 +#if defined(_AIX) + // After setting GPR1 to a higher address, AIX wipes out the original + // stack space below that address invalidated by the new GPR1 value. Use + // GPR0 to save the value of GPR3 in the context before it is wiped out. + // This compromises the content of GPR0 which is a volatile register. + ld 0, (8 * (3 + 2))(3) +#else PPC64_LR(0) +#endif PPC64_LR(5) PPC64_LR(4) PPC64_LR(1) +#if defined(_AIX) + mr 3, 0 +#else PPC64_LR(3) +#endif bctr #elif defined(__powerpc__) @@ -475,46 +512,49 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv) #endif #if defined(__ALTIVEC__) - // restore vector registers if any are in use + +#define LOAD_VECTOR_RESTORE(_index) \ + lwz 0, 424+_index*16(3) SEPARATOR \ + stw 0, 0(4) SEPARATOR \ + lwz 0, 424+_index*16+4(3) SEPARATOR \ + stw 0, 4(4) SEPARATOR \ + lwz 0, 424+_index*16+8(3) SEPARATOR \ + stw 0, 8(4) SEPARATOR \ + lwz 0, 424+_index*16+12(3) SEPARATOR \ + stw 0, 12(4) SEPARATOR \ + lvx _index, 0, 4 + +#if !defined(_AIX) + // restore vector registers if any are in use. In the AIX ABI, VRSAVE + // is not used. lwz 5, 156(3) // test VRsave cmpwi 5, 0 beq Lnovec +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(15-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + LOAD_VECTOR_RESTORE(_index) SEPARATOR \ + Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(31-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + LOAD_VECTOR_RESTORE(_index) SEPARATOR \ + Ldone ## _index: + +#else + +#define LOAD_VECTOR_UNALIGNEDl(_index) LOAD_VECTOR_RESTORE(_index) +#define LOAD_VECTOR_UNALIGNEDh(_index) LOAD_VECTOR_RESTORE(_index) + +#endif // !defined(_AIX) + subi 4, 1, 16 rlwinm 4, 4, 0, 0, 27 // mask low 4-bits // r4 is now a 16-byte aligned pointer into the red zone // the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer - -#define LOAD_VECTOR_UNALIGNEDl(_index) \ - andis. 0, 5, (1 PPC_LEFT_SHIFT(15-_index)) SEPARATOR \ - beq Ldone ## _index SEPARATOR \ - lwz 0, 424+_index*16(3) SEPARATOR \ - stw 0, 0(%r4) SEPARATOR \ - lwz 0, 424+_index*16+4(%r3) SEPARATOR \ - stw 0, 4(%r4) SEPARATOR \ - lwz 0, 424+_index*16+8(%r3) SEPARATOR \ - stw 0, 8(%r4) SEPARATOR \ - lwz 0, 424+_index*16+12(%r3) SEPARATOR \ - stw 0, 12(%r4) SEPARATOR \ - lvx _index, 0, 4 SEPARATOR \ - Ldone ## _index: - -#define LOAD_VECTOR_UNALIGNEDh(_index) \ - andi. 0, 5, (1 PPC_LEFT_SHIFT(31-_index)) SEPARATOR \ - beq Ldone ## _index SEPARATOR \ - lwz 0, 424+_index*16(3) SEPARATOR \ - stw 0, 0(4) SEPARATOR \ - lwz 0, 424+_index*16+4(3) SEPARATOR \ - stw 0, 4(4) SEPARATOR \ - lwz 0, 424+_index*16+8(3) SEPARATOR \ - stw 0, 8(%r4) SEPARATOR \ - lwz 0, 424+_index*16+12(3) SEPARATOR \ - stw 0, 12(4) SEPARATOR \ - lvx _index, 0, 4 SEPARATOR \ - Ldone ## _index: - - LOAD_VECTOR_UNALIGNEDl(0) LOAD_VECTOR_UNALIGNEDl(1) LOAD_VECTOR_UNALIGNEDl(2) @@ -1212,6 +1252,43 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_riscv6jumptoEv) ret // jump to ra +#elif defined(__s390x__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_s390x6jumptoEv) +// +// void libunwind::Registers_s390x::jumpto() +// +// On entry: +// thread_state pointer is in r2 +// + + // Skip PSWM, but load PSWA into r1 + lg %r1, 8(%r2) + + // Restore FPRs + ld %f0, 144(%r2) + ld %f1, 152(%r2) + ld %f2, 160(%r2) + ld %f3, 168(%r2) + ld %f4, 176(%r2) + ld %f5, 184(%r2) + ld %f6, 192(%r2) + ld %f7, 200(%r2) + ld %f8, 208(%r2) + ld %f9, 216(%r2) + ld %f10, 224(%r2) + ld %f11, 232(%r2) + ld %f12, 240(%r2) + ld %f13, 248(%r2) + ld %f14, 256(%r2) + ld %f15, 264(%r2) + + // Restore GPRs - skipping %r0 and %r1 + lmg %r2, %r15, 32(%r2) + + // Return to PSWA (was loaded into %r1 above) + br %r1 + #endif #endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */ diff --git a/lib/libunwind/src/UnwindRegistersSave.S b/lib/libunwind/src/UnwindRegistersSave.S index b39489235c..f57dd637dd 100644 --- a/lib/libunwind/src/UnwindRegistersSave.S +++ b/lib/libunwind/src/UnwindRegistersSave.S @@ -8,7 +8,11 @@ #include "assembly.h" +#if defined(_AIX) + .toc +#else .text +#endif #if !defined(__USING_SJLJ_EXCEPTIONS__) @@ -334,8 +338,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) // On entry: // thread_state pointer is in r3 // +#if defined(_AIX) +DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(__unw_getcontext, unw_getcontext) +#else DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) - +#endif // store register (GPR) #define PPC64_STR(n) \ std n, (8 * (n + 2))(3) @@ -567,7 +574,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) // On entry: // thread_state pointer is in r3 // +#if defined(_AIX) +DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(__unw_getcontext, unw_getcontext) +#else DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#endif stw 0, 8(3) mflr 0 stw 0, 0(3) // store lr as ssr0 @@ -1168,6 +1179,49 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) li a0, 0 // return UNW_ESUCCESS ret // jump to ra + +#elif defined(__s390x__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in r2 +// +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + + // Save GPRs + stmg %r0, %r15, 16(%r2) + + // Save PSWM + epsw %r0, %r1 + stm %r0, %r1, 0(%r2) + + // Store return address as PSWA + stg %r14, 8(%r2) + + // Save FPRs + std %f0, 144(%r2) + std %f1, 152(%r2) + std %f2, 160(%r2) + std %f3, 168(%r2) + std %f4, 176(%r2) + std %f5, 184(%r2) + std %f6, 192(%r2) + std %f7, 200(%r2) + std %f8, 208(%r2) + std %f9, 216(%r2) + std %f10, 224(%r2) + std %f11, 232(%r2) + std %f12, 240(%r2) + std %f13, 248(%r2) + std %f14, 256(%r2) + std %f15, 264(%r2) + + // Return UNW_ESUCCESS + lghi %r2, 0 + br %r14 + #endif WEAK_ALIAS(__unw_getcontext, unw_getcontext) diff --git a/lib/libunwind/src/Unwind_AIXExtras.cpp b/lib/libunwind/src/Unwind_AIXExtras.cpp new file mode 100644 index 0000000000..7e47f70186 --- /dev/null +++ b/lib/libunwind/src/Unwind_AIXExtras.cpp @@ -0,0 +1,63 @@ +//===--------------------- Unwind_AIXExtras.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +// This file is only used for AIX. +#if defined(_AIX) + +#include "config.h" +#include "libunwind_ext.h" +#include + +namespace libunwind { +// getFuncNameFromTBTable +// Get the function name from its traceback table. +char *getFuncNameFromTBTable(uintptr_t Pc, uint16_t &NameLen, + unw_word_t *Offset) { + uint32_t *p = reinterpret_cast(Pc); + *Offset = 0; + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + p++; + tbtable *TBTable = reinterpret_cast(p + 1); + + if (!TBTable->tb.name_present) + return NULL; + + // Get to the name of the function. + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + p++; + + // If the tb_offset field exisits, get the offset from the start of + // the function to pc. Skip the field. + if (TBTable->tb.has_tboff) { + unw_word_t StartIp = + reinterpret_cast(TBTable) - *p - sizeof(uint32_t); + *Offset = Pc - StartIp; + p++; + } + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + p++; + + // Skip fields ctl_info and ctl_info_disp if they exist. + if (TBTable->tb.has_ctl) { + p += 1 + *p; + } + + NameLen = *(reinterpret_cast(p)); + return reinterpret_cast(p) + sizeof(uint16_t); +} +} // namespace libunwind +#endif // defined(_AIX) diff --git a/lib/libunwind/src/assembly.h b/lib/libunwind/src/assembly.h index 89293a555b..fb07d04071 100644 --- a/lib/libunwind/src/assembly.h +++ b/lib/libunwind/src/assembly.h @@ -67,7 +67,8 @@ #define SEPARATOR ; #endif -#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) +#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) && \ + !defined(_AIX) #define PPC64_OPD1 .section .opd,"aw",@progbits SEPARATOR #define PPC64_OPD2 SEPARATOR \ .p2align 3 SEPARATOR \ @@ -203,12 +204,57 @@ #elif defined(__sparc__) +#elif defined(_AIX) + +#if defined(__powerpc64__) +#define VBYTE_LEN 8 +#define CSECT_ALIGN 3 +#else +#define VBYTE_LEN 4 +#define CSECT_ALIGN 2 +#endif + +// clang-format off +#define DEFINE_LIBUNWIND_FUNCTION_AND_WEAK_ALIAS(name, aliasname) \ + .csect .text[PR], 2 SEPARATOR \ + .csect .name[PR], 2 SEPARATOR \ + .globl name[DS] SEPARATOR \ + .globl .name[PR] SEPARATOR \ + .align 4 SEPARATOR \ + .csect name[DS], CSECT_ALIGN SEPARATOR \ +aliasname: \ + .vbyte VBYTE_LEN, .name[PR] SEPARATOR \ + .vbyte VBYTE_LEN, TOC[TC0] SEPARATOR \ + .vbyte VBYTE_LEN, 0 SEPARATOR \ + .weak aliasname SEPARATOR \ + .weak .aliasname SEPARATOR \ + .csect .name[PR], 2 SEPARATOR \ +.aliasname: \ + +#define WEAK_ALIAS(name, aliasname) +#define NO_EXEC_STACK_DIRECTIVE + +// clang-format on #else #error Unsupported target #endif +#if defined(_AIX) + // clang-format off +#define DEFINE_LIBUNWIND_FUNCTION(name) \ + .globl name[DS] SEPARATOR \ + .globl .name SEPARATOR \ + .align 4 SEPARATOR \ + .csect name[DS], CSECT_ALIGN SEPARATOR \ + .vbyte VBYTE_LEN, .name SEPARATOR \ + .vbyte VBYTE_LEN, TOC[TC0] SEPARATOR \ + .vbyte VBYTE_LEN, 0 SEPARATOR \ + .csect .text[PR], 2 SEPARATOR \ +.name: + // clang-format on +#else #define DEFINE_LIBUNWIND_FUNCTION(name) \ .globl SYMBOL_NAME(name) SEPARATOR \ HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR \ @@ -217,6 +263,7 @@ SYMBOL_NAME(name): \ PPC64_OPD2 \ AARCH64_BTI +#endif #if defined(__arm__) #if !defined(__ARM_ARCH) diff --git a/lib/libunwind/src/cet_unwind.h b/lib/libunwind/src/cet_unwind.h index e371be20c4..c364ed3e12 100644 --- a/lib/libunwind/src/cet_unwind.h +++ b/lib/libunwind/src/cet_unwind.h @@ -36,6 +36,6 @@ #endif extern void *__libunwind_cet_get_registers(unw_cursor_t *); -extern void *__libunwind_cet_get_jump_target(); +extern void *__libunwind_cet_get_jump_target(void); #endif diff --git a/lib/libunwind/src/config.h b/lib/libunwind/src/config.h index 5ae1604f65..cc41b817ac 100644 --- a/lib/libunwind/src/config.h +++ b/lib/libunwind/src/config.h @@ -43,6 +43,9 @@ // For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After // API 21, dl_iterate_phdr exists, but dl_unwind_find_exidx is much faster. #define _LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX 1 +#elif defined(_AIX) +// The traceback table at the end of each function is used for unwinding. +#define _LIBUNWIND_SUPPORT_TBTAB_UNWIND 1 #else // Assume an ELF system with a dl_iterate_phdr function. #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1 @@ -57,7 +60,7 @@ #define _LIBUNWIND_EXPORT #define _LIBUNWIND_HIDDEN #else - #if !defined(__ELF__) && !defined(__MACH__) + #if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX) #define _LIBUNWIND_EXPORT __declspec(dllexport) #define _LIBUNWIND_HIDDEN #else @@ -80,7 +83,7 @@ __asm__(".globl " SYMBOL_NAME(aliasname)); \ __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \ _LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname)) -#elif defined(__ELF__) +#elif defined(__ELF__) || defined(_AIX) #define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ __attribute__((weak, alias(#name))); @@ -112,7 +115,7 @@ #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \ (!defined(__APPLE__) && defined(__arm__)) || defined(__aarch64__) || \ defined(__mips__) || defined(__riscv) || defined(__hexagon__) || \ - defined(__sparc__) + defined(__sparc__) || defined(__s390x__) #if !defined(_LIBUNWIND_BUILD_SJLJ_APIS) #define _LIBUNWIND_BUILD_ZERO_COST_APIS #endif @@ -188,9 +191,9 @@ #ifdef __cplusplus extern "C" { #endif - extern bool logAPIs(); - extern bool logUnwinding(); - extern bool logDWARF(); + extern bool logAPIs(void); + extern bool logUnwinding(void); + extern bool logDWARF(void); #ifdef __cplusplus } #endif diff --git a/lib/libunwind/src/libunwind.cpp b/lib/libunwind/src/libunwind.cpp index 03f8b75b5b..b8b41ff25e 100644 --- a/lib/libunwind/src/libunwind.cpp +++ b/lib/libunwind/src/libunwind.cpp @@ -75,6 +75,8 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, # define REGISTER_KIND Registers_riscv #elif defined(__ve__) # define REGISTER_KIND Registers_ve +#elif defined(__s390x__) +# define REGISTER_KIND Registers_s390x #else # error Architecture not supported #endif @@ -247,6 +249,16 @@ _LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) { } _LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame) +#ifdef _AIX +_LIBUNWIND_EXPORT uintptr_t __unw_get_data_rel_base(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("unw_get_data_rel_base(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = reinterpret_cast(cursor); + return co->getDataRelBase(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_data_rel_base, unw_get_data_rel_base) +#endif + #ifdef __arm__ // Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD _LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) { diff --git a/lib/libunwind/src/libunwind_ext.h b/lib/libunwind/src/libunwind_ext.h index 7065ffcdae..fdc533cb89 100644 --- a/lib/libunwind/src/libunwind_ext.h +++ b/lib/libunwind/src/libunwind_ext.h @@ -43,6 +43,10 @@ extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t); extern int __unw_is_signal_frame(unw_cursor_t *); extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *); +#if defined(_AIX) +extern uintptr_t __unw_get_data_rel_base(unw_cursor_t *); +#endif + // SPI extern void __unw_iterate_dwarf_unwind_cache(void (*func)( unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); diff --git a/src/libunwind.zig b/src/libunwind.zig index 0d030be329..ea09959c1c 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -33,17 +33,6 @@ pub fn buildStaticLib(comp: *Compilation) !void { .directory = null, // Put it in the cache directory. .basename = basename, }; - const unwind_src_list = [_][]const u8{ - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S", - "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "gcc_personality_v0.c", - }; var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined; for (unwind_src_list) |unwind_src, i| { var cflags = std.ArrayList([]const u8).init(arena); @@ -150,3 +139,16 @@ pub fn buildStaticLib(comp: *Compilation) !void { .lock = sub_compilation.bin_file.toOwnedLock(), }; } + +const unwind_src_list = [_][]const u8{ + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind_AIXExtras.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "gcc_personality_v0.c", +};