Skip to content

Commit f3e4787

Browse files
committed
[libunwind][X86-64] Handle Linux sigreturn trampoline when DWARF info is missing
1 parent 101acff commit f3e4787

File tree

1 file changed

+95
-1
lines changed

1 file changed

+95
-1
lines changed

libunwind/src/UnwindCursor.hpp

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
#if defined(_LIBUNWIND_TARGET_LINUX) && \
3434
(defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \
35-
defined(_LIBUNWIND_TARGET_S390X))
35+
defined(_LIBUNWIND_TARGET_S390X) || defined(_LIBUNWIND_TARGET_X86_64))
3636
#include <errno.h>
3737
#include <signal.h>
3838
#include <sys/syscall.h>
@@ -1003,6 +1003,10 @@ class UnwindCursor : public AbstractUnwindCursor{
10031003
#if defined(_LIBUNWIND_TARGET_S390X)
10041004
bool setInfoForSigReturn(Registers_s390x &);
10051005
int stepThroughSigReturn(Registers_s390x &);
1006+
#endif
1007+
#if defined(_LIBUNWIND_TARGET_X86_64)
1008+
bool setInfoForSigReturn(Registers_x86_64 &);
1009+
int stepThroughSigReturn(Registers_x86_64 &);
10061010
#endif
10071011
template <typename Registers> bool setInfoForSigReturn(Registers &) {
10081012
return false;
@@ -2917,6 +2921,96 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) {
29172921
#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) &&
29182922
// defined(_LIBUNWIND_TARGET_S390X)
29192923

2924+
#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \
2925+
defined(_LIBUNWIND_TARGET_X86_64)
2926+
template <typename A, typename R>
2927+
bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_x86_64 &) {
2928+
// Look for the sigreturn trampoline. The trampoline's body is two
2929+
// specific instructions (see below). Typically the trampoline comes from the
2930+
// vDSO or from libc.
2931+
//
2932+
// This special code path is a fallback that is only used if the trampoline
2933+
// lacks proper (e.g. DWARF) unwind info.
2934+
const uint8_t amd64_linux_sigtramp_code[9] = {
2935+
0x48, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00, // mov rax, 15
2936+
0x0f, 0x05 // syscall
2937+
};
2938+
const size_t code_size = sizeof(amd64_linux_sigtramp_code);
2939+
2940+
// The PC might contain an invalid address if the unwind info is bad, so
2941+
// directly accessing it could cause a SIGSEGV.
2942+
unw_word_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
2943+
if (!isReadableAddr(pc))
2944+
return false;
2945+
// If near page boundary, check the next page too.
2946+
if (((pc + code_size - 1) & 4095) != (pc & 4095) && !isReadableAddr(pc + code_size - 1))
2947+
return false;
2948+
2949+
const uint8_t *pc_ptr = reinterpret_cast<const uint8_t *>(pc);
2950+
if (memcmp(pc_ptr, amd64_linux_sigtramp_code, code_size))
2951+
return false;
2952+
2953+
_info = {};
2954+
_info.start_ip = pc;
2955+
_info.end_ip = pc + code_size;
2956+
_isSigReturn = true;
2957+
return true;
2958+
}
2959+
2960+
template <typename A, typename R>
2961+
int UnwindCursor<A, R>::stepThroughSigReturn(Registers_x86_64 &) {
2962+
// In the signal trampoline frame, sp points to ucontext:
2963+
// struct ucontext {
2964+
// unsigned long uc_flags;
2965+
// struct ucontext *uc_link;
2966+
// stack_t uc_stack; // 24 bytes
2967+
// struct sigcontext uc_mcontext;
2968+
// ...
2969+
// };
2970+
const pint_t kOffsetSpToSigcontext = (8 + 8 + 24);
2971+
pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext;
2972+
2973+
// UNW_X86_64_* -> field in struct sigcontext_64.
2974+
// struct sigcontext_64 {
2975+
// __u64 r8; // 0
2976+
// __u64 r9; // 1
2977+
// __u64 r10; // 2
2978+
// __u64 r11; // 3
2979+
// __u64 r12; // 4
2980+
// __u64 r13; // 5
2981+
// __u64 r14; // 6
2982+
// __u64 r15; // 7
2983+
// __u64 di; // 8
2984+
// __u64 si; // 9
2985+
// __u64 bp; // 10
2986+
// __u64 bx; // 11
2987+
// __u64 dx; // 12
2988+
// __u64 ax; // 13
2989+
// __u64 cx; // 14
2990+
// __u64 sp; // 15
2991+
// __u64 ip; // 16
2992+
// ...
2993+
// };
2994+
const size_t idx_map[17] = {13, 12, 14, 11, 9, 8, 10, 15, 0, 1, 2, 3, 4, 5, 6, 7, 16};
2995+
2996+
for (int i = 0; i < 17; ++i) {
2997+
uint64_t value = _addressSpace.get64(sigctx + idx_map[i] * 8);
2998+
_registers.setRegister(i, value);
2999+
}
3000+
3001+
// The +1 story is the same as in DwarfInstructions::stepWithDwarf()
3002+
// (search for "returnAddress + cieInfo.isSignalFrame" or "Return address points to the next instruction").
3003+
// This is probably not the right place for this because this function is not necessarily used
3004+
// with DWARF. Need to research whether the other unwind methods have the same +-1 situation or
3005+
// are off by one.
3006+
_registers.setIP(_registers.getIP() + 1);
3007+
3008+
_isSignalFrame = true;
3009+
return UNW_STEP_SUCCESS;
3010+
}
3011+
#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) &&
3012+
// defined(_LIBUNWIND_TARGET_X86_64)
3013+
29203014
template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) {
29213015
(void)stage2;
29223016
// Bottom of stack is defined is when unwind info cannot be found.

0 commit comments

Comments
 (0)