Skip to content

Commit

Permalink
[unwind] Handle UNW_X86_64_RIP register
Browse files Browse the repository at this point in the history
In some binaries, built with clang/lld, libunwind crashes
with "unsupported x86_64 register" for regNum == 16:

Differential Revision: https://reviews.llvm.org/D107919
  • Loading branch information
vitalybuka authored and mbautin committed Oct 1, 2021
1 parent fed4134 commit bdb147e
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 1 deletion.
5 changes: 4 additions & 1 deletion libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,15 @@ inline bool Registers_x86_64::validRegister(int regNum) const {
return true;
if (regNum < 0)
return false;
if (regNum > 15)
if (regNum > 16)
return false;
return true;
}

inline uint64_t Registers_x86_64::getRegister(int regNum) const {
switch (regNum) {
case UNW_REG_IP:
case UNW_X86_64_RIP:
return _registers.__rip;
case UNW_REG_SP:
return _registers.__rsp;
Expand Down Expand Up @@ -389,6 +390,7 @@ inline uint64_t Registers_x86_64::getRegister(int regNum) const {
inline void Registers_x86_64::setRegister(int regNum, uint64_t value) {
switch (regNum) {
case UNW_REG_IP:
case UNW_X86_64_RIP:
_registers.__rip = value;
return;
case UNW_REG_SP:
Expand Down Expand Up @@ -449,6 +451,7 @@ inline void Registers_x86_64::setRegister(int regNum, uint64_t value) {
inline const char *Registers_x86_64::getRegisterName(int regNum) {
switch (regNum) {
case UNW_REG_IP:
case UNW_X86_64_RIP:
return "rip";
case UNW_REG_SP:
return "rsp";
Expand Down
74 changes: 74 additions & 0 deletions libunwind/test/libunwind_01.pass.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <libunwind.h>
#include <stdlib.h>
#include <string.h>

void backtrace(int lower_bound) {
unw_context_t context;
Expand Down Expand Up @@ -55,10 +56,83 @@ void test_no_info() {
abort();
}

void test_reg_names() {
unw_context_t context;
unw_getcontext(&context);

unw_cursor_t cursor;
unw_init_local(&cursor, &context);

int max_reg_num = -100;
#if defined(__i386__)
max_reg_num = 7;
#elif defined(__x86_64__)
max_reg_num = 32;
#endif

const char prefix[] = "unknown";
for (int i = -2; i < max_reg_num; ++i) {
if (strncmp(prefix, unw_regname(&cursor, i), sizeof(prefix) - 1) == 0)
abort();
}

if (strncmp(prefix, unw_regname(&cursor, max_reg_num + 1),
sizeof(prefix) - 1) != 0)
abort();
}

#if defined(__x86_64__)
void test_reg_get_set() {
unw_context_t context;
unw_getcontext(&context);

unw_cursor_t cursor;
unw_init_local(&cursor, &context);

for (int i = 0; i < 17; ++i) {
const unw_word_t set_value = 7;
if (unw_set_reg(&cursor, i, set_value) != UNW_ESUCCESS)
abort();

unw_word_t get_value = 0;
if (unw_get_reg(&cursor, i, &get_value) != UNW_ESUCCESS)
abort();

if (set_value != get_value)
abort();
}
}

void test_fpreg_get_set() {
unw_context_t context;
unw_getcontext(&context);

unw_cursor_t cursor;
unw_init_local(&cursor, &context);

// get/set is not implemented for x86_64 fpregs.
for (int i = 17; i < 33; ++i) {
const unw_fpreg_t set_value = 7;
if (unw_set_fpreg(&cursor, i, set_value) != UNW_EBADREG)
abort();

unw_fpreg_t get_value = 0;
if (unw_get_fpreg(&cursor, i, &get_value) != UNW_EBADREG)
abort();
}
}
#else
void test_reg_get_set() {}
void test_fpreg_get_set() {}
#endif

int main(int, char**) {
test1(1);
test2(1, 2);
test3(1, 2, 3);
test_no_info();
test_reg_names();
test_reg_get_set();
test_fpreg_get_set();
return 0;
}

0 comments on commit bdb147e

Please sign in to comment.