Skip to content

Commit

Permalink
[libunwind] [sparc] Add SPARCv9 support
Browse files Browse the repository at this point in the history
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
  • Loading branch information
koachan authored and MaskRay committed Feb 5, 2022
1 parent 527654d commit 2b9554b
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 1 deletion.
8 changes: 8 additions & 0 deletions libunwind/include/__libunwind_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions libunwind/src/DwarfInstructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ class DwarfInstructions {
}
};

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(
Expand All @@ -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));
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion libunwind/src/DwarfParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class CFI_Parser {
kRegisterUnused,
kRegisterUndefined,
kRegisterInCFA,
kRegisterInCFADecrypt, // sparc64 specific
kRegisterOffsetFromCFA,
kRegisterInRegister,
kRegisterAtExpression,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
186 changes: 186 additions & 0 deletions libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum {
REGISTERS_MIPS_O32,
REGISTERS_MIPS_NEWABI,
REGISTERS_SPARC,
REGISTERS_SPARC64,
REGISTERS_HEXAGON,
REGISTERS_RISCV,
REGISTERS_VE,
Expand Down Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions libunwind/src/UnwindCursor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,10 @@ class UnwindCursor : public AbstractUnwindCursor{
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;
Expand Down Expand Up @@ -1104,6 +1108,12 @@ class UnwindCursor : public AbstractUnwindCursor{
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;
Expand Down Expand Up @@ -1182,6 +1192,12 @@ class UnwindCursor : public AbstractUnwindCursor{
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;
Expand Down
Loading

0 comments on commit 2b9554b

Please sign in to comment.