diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h index 30f5e0a23d08..e626567d4e59 100644 --- a/libunwind/include/__libunwind_config.h +++ b/libunwind/include/__libunwind_config.h @@ -29,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__) @@ -160,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 @@ -178,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/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index a69e72fc132d..3d8fd2146ebb 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -1177,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/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index 32c13e6c9a24..28c617f34999 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -39,6 +39,7 @@ enum { REGISTERS_HEXAGON, REGISTERS_RISCV, REGISTERS_VE, + REGISTERS_S390X, }; #if defined(_LIBUNWIND_TARGET_I386) @@ -4716,6 +4717,293 @@ 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 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/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 29ded5c4e78e..655e41d81d5e 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -1220,6 +1220,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) diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index b4843c912b0b..eeb64534966d 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -1252,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/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 0cfc7b565863..f57dd637dd9d 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -1179,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/libunwind/src/config.h b/libunwind/src/config.h index e751860bd936..cc41b817acf6 100644 --- a/libunwind/src/config.h +++ b/libunwind/src/config.h @@ -115,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 diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index 21aa42680e5b..b8b41ff25e54 100644 --- a/libunwind/src/libunwind.cpp +++ b/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