[libunwind] [sparc] Add SPARCv9 support

Adds libunwind support for SPARCv9 (aka sparc64). This is a rebase of @kettenis' patch D32450, which I created (with his permission) because the original review has become inactive.
The changes are of a cosmetic nature to make it fit better with the new code style, and to reuse the existing SPARCv8 code, whenever possible.

Please let me know if I posted this on the wrong place. Also, the summary of the original review is reproduced below:

> This adds unwinder support for 64-bit SPARC (aka SPARCv9). The implementation was done on OpenBSD/sparc64, so it takes StackGhost into account:
>
> https://www.usenix.org/legacy/publications/library/proceedings/sec01/full_papers/frantzen/frantzen_html/index.html
>
> Since StackGhost xor's return addresses with a random cookie before storing them on the stack, the unwinder has to do some extra work to recover those. This is done by introducing a new kRegisterInCFADecrypt "location" type that is used to implement the DW_CFA_GNU_window_save opcode. That implementation is SPARC-specific, but should work for 32-bit SPARC as well. DW_CFA_GNU_window_save is only ever generated on SPARC as far as I know.

Co-authored-by: Mark Kettenis
Reviewed By: #libunwind, thesamesam, MaskRay, Arfrever

Differential Revision: https://reviews.llvm.org/D116857
This commit is contained in:
Koakuma 2022-02-05 13:08:26 -08:00 committed by Fangrui Song
parent 527654dceb
commit 2b9554b885
8 changed files with 362 additions and 1 deletions

View File

@ -23,6 +23,7 @@
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64 31
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON 34
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE 143
@ -125,6 +126,12 @@
# error "Unsupported MIPS ABI and/or environment"
# endif
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS
#elif defined(__sparc__) && defined(__arch64__)
#define _LIBUNWIND_TARGET_SPARC64 1
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER \
_LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64
#define _LIBUNWIND_CONTEXT_SIZE 33
#define _LIBUNWIND_CURSOR_SIZE 45
# elif defined(__sparc__)
#define _LIBUNWIND_TARGET_SPARC 1
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC
@ -165,6 +172,7 @@
# define _LIBUNWIND_TARGET_MIPS_O32 1
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
# define _LIBUNWIND_TARGET_SPARC 1
# define _LIBUNWIND_TARGET_SPARC64 1
# define _LIBUNWIND_TARGET_HEXAGON 1
# define _LIBUNWIND_TARGET_RISCV 1
# define _LIBUNWIND_TARGET_VE 1

View File

@ -74,6 +74,13 @@ private:
}
};
template <typename R>
auto getSparcWCookie(const R &r, int) -> decltype(r.getWCookie()) {
return r.getWCookie();
}
template <typename R> uint64_t getSparcWCookie(const R &, long) {
return 0;
}
template <typename A, typename R>
typename A::pint_t DwarfInstructions<A, R>::getSavedRegister(
@ -83,6 +90,10 @@ typename A::pint_t DwarfInstructions<A, R>::getSavedRegister(
case CFI_Parser<A>::kRegisterInCFA:
return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value);
case CFI_Parser<A>::kRegisterInCFADecrypt: // sparc64 specific
return addressSpace.getP(cfa + (pint_t)savedReg.value) ^
getSparcWCookie(registers, 0);
case CFI_Parser<A>::kRegisterAtExpression:
return (pint_t)addressSpace.getRegister(evaluateExpression(
(pint_t)savedReg.value, addressSpace, registers, cfa));
@ -124,6 +135,7 @@ double DwarfInstructions<A, R>::getSavedFloatRegister(
case CFI_Parser<A>::kRegisterIsExpression:
case CFI_Parser<A>::kRegisterUnused:
case CFI_Parser<A>::kRegisterOffsetFromCFA:
case CFI_Parser<A>::kRegisterInCFADecrypt:
// FIX ME
break;
}
@ -148,6 +160,7 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
case CFI_Parser<A>::kRegisterUndefined:
case CFI_Parser<A>::kRegisterOffsetFromCFA:
case CFI_Parser<A>::kRegisterInRegister:
case CFI_Parser<A>::kRegisterInCFADecrypt:
// FIX ME
break;
}
@ -266,6 +279,12 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
}
#endif
#if defined(_LIBUNWIND_TARGET_SPARC64)
// Skip call site instruction and delay slot.
if (R::getArch() == REGISTERS_SPARC64)
returnAddress += 8;
#endif
#if defined(_LIBUNWIND_TARGET_PPC64)
#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1)
#define PPC64_ELFV1_R2_OFFSET 40

View File

@ -71,6 +71,7 @@ public:
kRegisterUnused,
kRegisterUndefined,
kRegisterInCFA,
kRegisterInCFADecrypt, // sparc64 specific
kRegisterOffsetFromCFA,
kRegisterInRegister,
kRegisterAtExpression,
@ -733,7 +734,8 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
"DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
break;
#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC)
#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) || \
defined(_LIBUNWIND_TARGET_SPARC64)
// The same constant is used to represent different instructions on
// AArch64 (negate_ra_state) and SPARC (window_save).
static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
@ -767,8 +769,31 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
}
break;
#endif
#if defined(_LIBUNWIND_TARGET_SPARC64)
// case DW_CFA_GNU_window_save:
case REGISTERS_SPARC64:
// Don't save %o0-%o7 on sparc64.
// https://reviews.llvm.org/D32450#736405
for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
if (reg == UNW_SPARC_I7)
results->setRegister(
reg, kRegisterInCFADecrypt,
static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
initialState);
else
results->setRegister(
reg, kRegisterInCFA,
static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
initialState);
}
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save\n");
break;
#endif
}
break;
#else
(void)arch;
#endif

View File

@ -35,6 +35,7 @@ enum {
REGISTERS_MIPS_O32,
REGISTERS_MIPS_NEWABI,
REGISTERS_SPARC,
REGISTERS_SPARC64,
REGISTERS_HEXAGON,
REGISTERS_RISCV,
REGISTERS_VE,
@ -3586,6 +3587,191 @@ inline const char *Registers_sparc::getRegisterName(int regNum) {
}
#endif // _LIBUNWIND_TARGET_SPARC
#if defined(_LIBUNWIND_TARGET_SPARC64)
/// Registers_sparc64 holds the register state of a thread in a 64-bit
/// sparc process.
class _LIBUNWIND_HIDDEN Registers_sparc64 {
public:
Registers_sparc64() = default;
Registers_sparc64(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);
const char *getRegisterName(int num);
void jumpto();
static int lastDwarfRegNum() {
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64;
}
static int getArch() { return REGISTERS_SPARC64; }
uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6] + 2047; }
void setSP(uint64_t value) { _registers.__regs[UNW_SPARC_O6] = value - 2047; }
uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; }
void setIP(uint64_t value) { _registers.__regs[UNW_SPARC_O7] = value; }
uint64_t getWCookie() const { return _wcookie; }
private:
struct sparc64_thread_state_t {
uint64_t __regs[32];
};
sparc64_thread_state_t _registers{};
uint64_t _wcookie = 0;
};
inline Registers_sparc64::Registers_sparc64(const void *registers) {
static_assert((check_fit<Registers_sparc64, unw_context_t>::does_fit),
"sparc64 registers do not fit into unw_context_t");
memcpy(&_registers, registers, sizeof(_registers));
memcpy(&_wcookie,
static_cast<const uint8_t *>(registers) + sizeof(_registers),
sizeof(_wcookie));
}
inline bool Registers_sparc64::validRegister(int regNum) const {
if (regNum == UNW_REG_IP)
return true;
if (regNum == UNW_REG_SP)
return true;
if (regNum < 0)
return false;
if (regNum <= UNW_SPARC_I7)
return true;
return false;
}
inline uint64_t Registers_sparc64::getRegister(int regNum) const {
if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7)
return _registers.__regs[regNum];
switch (regNum) {
case UNW_REG_IP:
return _registers.__regs[UNW_SPARC_O7];
case UNW_REG_SP:
return _registers.__regs[UNW_SPARC_O6] + 2047;
}
_LIBUNWIND_ABORT("unsupported sparc64 register");
}
inline void Registers_sparc64::setRegister(int regNum, uint64_t value) {
if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7) {
_registers.__regs[regNum] = value;
return;
}
switch (regNum) {
case UNW_REG_IP:
_registers.__regs[UNW_SPARC_O7] = value;
return;
case UNW_REG_SP:
_registers.__regs[UNW_SPARC_O6] = value - 2047;
return;
}
_LIBUNWIND_ABORT("unsupported sparc64 register");
}
inline bool Registers_sparc64::validFloatRegister(int) const { return false; }
inline double Registers_sparc64::getFloatRegister(int) const {
_LIBUNWIND_ABORT("no sparc64 float registers");
}
inline void Registers_sparc64::setFloatRegister(int, double) {
_LIBUNWIND_ABORT("no sparc64 float registers");
}
inline bool Registers_sparc64::validVectorRegister(int) const { return false; }
inline v128 Registers_sparc64::getVectorRegister(int) const {
_LIBUNWIND_ABORT("no sparc64 vector registers");
}
inline void Registers_sparc64::setVectorRegister(int, v128) {
_LIBUNWIND_ABORT("no sparc64 vector registers");
}
inline const char *Registers_sparc64::getRegisterName(int regNum) {
switch (regNum) {
case UNW_REG_IP:
return "pc";
case UNW_SPARC_G0:
return "g0";
case UNW_SPARC_G1:
return "g1";
case UNW_SPARC_G2:
return "g2";
case UNW_SPARC_G3:
return "g3";
case UNW_SPARC_G4:
return "g4";
case UNW_SPARC_G5:
return "g5";
case UNW_SPARC_G6:
return "g6";
case UNW_SPARC_G7:
return "g7";
case UNW_SPARC_O0:
return "o0";
case UNW_SPARC_O1:
return "o1";
case UNW_SPARC_O2:
return "o2";
case UNW_SPARC_O3:
return "o3";
case UNW_SPARC_O4:
return "o4";
case UNW_SPARC_O5:
return "o5";
case UNW_REG_SP:
case UNW_SPARC_O6:
return "o6";
case UNW_SPARC_O7:
return "o7";
case UNW_SPARC_L0:
return "l0";
case UNW_SPARC_L1:
return "l1";
case UNW_SPARC_L2:
return "l2";
case UNW_SPARC_L3:
return "l3";
case UNW_SPARC_L4:
return "l4";
case UNW_SPARC_L5:
return "l5";
case UNW_SPARC_L6:
return "l6";
case UNW_SPARC_L7:
return "l7";
case UNW_SPARC_I0:
return "i0";
case UNW_SPARC_I1:
return "i1";
case UNW_SPARC_I2:
return "i2";
case UNW_SPARC_I3:
return "i3";
case UNW_SPARC_I4:
return "i4";
case UNW_SPARC_I5:
return "i5";
case UNW_SPARC_I6:
return "i6";
case UNW_SPARC_I7:
return "i7";
default:
return "unknown register";
}
}
#endif // _LIBUNWIND_TARGET_SPARC64
#if defined(_LIBUNWIND_TARGET_HEXAGON)
/// Registers_hexagon holds the register state of a thread in a Hexagon QDSP6
/// process.

View File

@ -1032,6 +1032,10 @@ private:
int stepWithCompactEncoding(Registers_sparc &) { return UNW_EINVAL; }
#endif
#if defined(_LIBUNWIND_TARGET_SPARC64)
int stepWithCompactEncoding(Registers_sparc64 &) { return UNW_EINVAL; }
#endif
#if defined (_LIBUNWIND_TARGET_RISCV)
int stepWithCompactEncoding(Registers_riscv &) {
return UNW_EINVAL;
@ -1104,6 +1108,12 @@ private:
bool compactSaysUseDwarf(Registers_sparc &, uint32_t *) const { return true; }
#endif
#if defined(_LIBUNWIND_TARGET_SPARC64)
bool compactSaysUseDwarf(Registers_sparc64 &, uint32_t *) const {
return true;
}
#endif
#if defined (_LIBUNWIND_TARGET_RISCV)
bool compactSaysUseDwarf(Registers_riscv &, uint32_t *) const {
return true;
@ -1182,6 +1192,12 @@ private:
compact_unwind_encoding_t dwarfEncoding(Registers_sparc &) const { return 0; }
#endif
#if defined(_LIBUNWIND_TARGET_SPARC64)
compact_unwind_encoding_t dwarfEncoding(Registers_sparc64 &) const {
return 0;
}
#endif
#if defined (_LIBUNWIND_TARGET_RISCV)
compact_unwind_encoding_t dwarfEncoding(Registers_riscv &) const {
return 0;

View File

@ -1062,6 +1062,53 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv)
ld $4, (8 * 4)($4)
.set pop
#elif defined(__sparc__) && defined(__arch64__)
DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind17Registers_sparc646jumptoEv)
//
// void libunwind::Registers_sparc64::jumpto()
//
// On entry:
// thread_state pointer is in %o0
//
.register %g2, #scratch
.register %g3, #scratch
.register %g6, #scratch
.register %g7, #scratch
flushw
ldx [%o0 + 0x08], %g1
ldx [%o0 + 0x10], %g2
ldx [%o0 + 0x18], %g3
ldx [%o0 + 0x20], %g4
ldx [%o0 + 0x28], %g5
ldx [%o0 + 0x30], %g6
ldx [%o0 + 0x38], %g7
ldx [%o0 + 0x48], %o1
ldx [%o0 + 0x50], %o2
ldx [%o0 + 0x58], %o3
ldx [%o0 + 0x60], %o4
ldx [%o0 + 0x68], %o5
ldx [%o0 + 0x70], %o6
ldx [%o0 + 0x78], %o7
ldx [%o0 + 0x80], %l0
ldx [%o0 + 0x88], %l1
ldx [%o0 + 0x90], %l2
ldx [%o0 + 0x98], %l3
ldx [%o0 + 0xa0], %l4
ldx [%o0 + 0xa8], %l5
ldx [%o0 + 0xb0], %l6
ldx [%o0 + 0xb8], %l7
ldx [%o0 + 0xc0], %i0
ldx [%o0 + 0xc8], %i1
ldx [%o0 + 0xd0], %i2
ldx [%o0 + 0xd8], %i3
ldx [%o0 + 0xe0], %i4
ldx [%o0 + 0xe8], %i5
ldx [%o0 + 0xf0], %i6
ldx [%o0 + 0xf8], %i7
jmp %o7
ldx [%o0 + 0x40], %o0
#elif defined(__sparc__)
//

View File

@ -999,6 +999,64 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
jumpr r31
#elif defined(__sparc__) && defined(__arch64__)
#
# extern int __unw_getcontext(unw_context_t* thread_state)
#
# On entry:
# thread_state pointer is in %o0
#
DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
.register %g2, #scratch
.register %g3, #scratch
.register %g6, #scratch
.register %g7, #scratch
stx %g1, [%o0 + 0x08]
stx %g2, [%o0 + 0x10]
stx %g3, [%o0 + 0x18]
stx %g4, [%o0 + 0x20]
stx %g5, [%o0 + 0x28]
stx %g6, [%o0 + 0x30]
stx %g7, [%o0 + 0x38]
stx %o0, [%o0 + 0x40]
stx %o1, [%o0 + 0x48]
stx %o2, [%o0 + 0x50]
stx %o3, [%o0 + 0x58]
stx %o4, [%o0 + 0x60]
stx %o5, [%o0 + 0x68]
stx %o6, [%o0 + 0x70]
stx %o7, [%o0 + 0x78]
stx %l0, [%o0 + 0x80]
stx %l1, [%o0 + 0x88]
stx %l2, [%o0 + 0x90]
stx %l3, [%o0 + 0x98]
stx %l4, [%o0 + 0xa0]
stx %l5, [%o0 + 0xa8]
stx %l6, [%o0 + 0xb0]
stx %l7, [%o0 + 0xb8]
stx %i0, [%o0 + 0xc0]
stx %i1, [%o0 + 0xc8]
stx %i2, [%o0 + 0xd0]
stx %i3, [%o0 + 0xd8]
stx %i4, [%o0 + 0xe0]
stx %i5, [%o0 + 0xe8]
stx %i6, [%o0 + 0xf0]
stx %i7, [%o0 + 0xf8]
# save StackGhost cookie
mov %i7, %g4
save %sp, -176, %sp
# register window flush necessary even without StackGhost
flushw
restore
ldx [%sp + 2047 + 0x78], %g5
xor %g4, %g5, %g4
stx %g4, [%o0 + 0x100]
retl
# return UNW_ESUCCESS
clr %o0
#elif defined(__sparc__)
#

View File

@ -67,6 +67,8 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor,
# define REGISTER_KIND Registers_mips_newabi
#elif defined(__mips__)
# warning The MIPS architecture is not supported with this ABI and environment!
#elif defined(__sparc__) && defined(__arch64__)
#define REGISTER_KIND Registers_sparc64
#elif defined(__sparc__)
# define REGISTER_KIND Registers_sparc
#elif defined(__riscv)