Skip to content

Commit 3beb6fc

Browse files
committed
[libunwind][X86-64] Handle Linux sigreturn trampoline when DWARF info is missing
1 parent 6038fd4 commit 3beb6fc

File tree

1 file changed

+99
-1
lines changed

1 file changed

+99
-1
lines changed

libunwind/src/UnwindCursor.hpp

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
#if defined(_LIBUNWIND_TARGET_LINUX) && \
3434
(defined(_LIBUNWIND_TARGET_AARCH64) || \
3535
defined(_LIBUNWIND_TARGET_LOONGARCH) || \
36-
defined(_LIBUNWIND_TARGET_RISCV) || defined(_LIBUNWIND_TARGET_S390X))
36+
defined(_LIBUNWIND_TARGET_RISCV) || defined(_LIBUNWIND_TARGET_S390X) || \
37+
defined(_LIBUNWIND_TARGET_S390X) || defined(_LIBUNWIND_TARGET_X86_64))
3738
#include <errno.h>
3839
#include <signal.h>
3940
#include <sys/syscall.h>
@@ -1008,6 +1009,10 @@ class UnwindCursor : public AbstractUnwindCursor{
10081009
#if defined(_LIBUNWIND_TARGET_S390X)
10091010
bool setInfoForSigReturn(Registers_s390x &);
10101011
int stepThroughSigReturn(Registers_s390x &);
1012+
#endif
1013+
#if defined(_LIBUNWIND_TARGET_X86_64)
1014+
bool setInfoForSigReturn(Registers_x86_64 &);
1015+
int stepThroughSigReturn(Registers_x86_64 &);
10111016
#endif
10121017
template <typename Registers> bool setInfoForSigReturn(Registers &) {
10131018
return false;
@@ -3032,6 +3037,99 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) {
30323037
#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) &&
30333038
// defined(_LIBUNWIND_TARGET_S390X)
30343039

3040+
#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \
3041+
defined(_LIBUNWIND_TARGET_X86_64)
3042+
template <typename A, typename R>
3043+
bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_x86_64 &) {
3044+
// Look for the sigreturn trampoline. The trampoline's body is two
3045+
// specific instructions (see below). Typically the trampoline comes from the
3046+
// vDSO or from libc.
3047+
//
3048+
// This special code path is a fallback that is only used if the trampoline
3049+
// lacks proper (e.g. DWARF) unwind info.
3050+
const uint8_t amd64_linux_sigtramp_code[9] = {
3051+
0x48, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00, // mov rax, 15
3052+
0x0f, 0x05 // syscall
3053+
};
3054+
const size_t code_size = sizeof(amd64_linux_sigtramp_code);
3055+
3056+
// The PC might contain an invalid address if the unwind info is bad, so
3057+
// directly accessing it could cause a SIGSEGV.
3058+
unw_word_t pc = static_cast<pint_t>(this->getReg(UNW_REG_IP));
3059+
if (!isReadableAddr(pc))
3060+
return false;
3061+
// If near page boundary, check the next page too.
3062+
if (((pc + code_size - 1) & 4095) != (pc & 4095) &&
3063+
!isReadableAddr(pc + code_size - 1))
3064+
return false;
3065+
3066+
const uint8_t *pc_ptr = reinterpret_cast<const uint8_t *>(pc);
3067+
if (memcmp(pc_ptr, amd64_linux_sigtramp_code, code_size))
3068+
return false;
3069+
3070+
_info = {};
3071+
_info.start_ip = pc;
3072+
_info.end_ip = pc + code_size;
3073+
_isSigReturn = true;
3074+
return true;
3075+
}
3076+
3077+
template <typename A, typename R>
3078+
int UnwindCursor<A, R>::stepThroughSigReturn(Registers_x86_64 &) {
3079+
// In the signal trampoline frame, sp points to ucontext:
3080+
// struct ucontext {
3081+
// unsigned long uc_flags;
3082+
// struct ucontext *uc_link;
3083+
// stack_t uc_stack; // 24 bytes
3084+
// struct sigcontext uc_mcontext;
3085+
// ...
3086+
// };
3087+
const pint_t kOffsetSpToSigcontext = (8 + 8 + 24);
3088+
pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext;
3089+
3090+
// UNW_X86_64_* -> field in struct sigcontext_64.
3091+
// struct sigcontext_64 {
3092+
// __u64 r8; // 0
3093+
// __u64 r9; // 1
3094+
// __u64 r10; // 2
3095+
// __u64 r11; // 3
3096+
// __u64 r12; // 4
3097+
// __u64 r13; // 5
3098+
// __u64 r14; // 6
3099+
// __u64 r15; // 7
3100+
// __u64 di; // 8
3101+
// __u64 si; // 9
3102+
// __u64 bp; // 10
3103+
// __u64 bx; // 11
3104+
// __u64 dx; // 12
3105+
// __u64 ax; // 13
3106+
// __u64 cx; // 14
3107+
// __u64 sp; // 15
3108+
// __u64 ip; // 16
3109+
// ...
3110+
// };
3111+
const size_t idx_map[17] = {13, 12, 14, 11, 9, 8, 10, 15, 0,
3112+
1, 2, 3, 4, 5, 6, 7, 16};
3113+
3114+
for (int i = 0; i < 17; ++i) {
3115+
uint64_t value = _addressSpace.get64(sigctx + idx_map[i] * 8);
3116+
_registers.setRegister(i, value);
3117+
}
3118+
3119+
// The +1 story is the same as in DwarfInstructions::stepWithDwarf()
3120+
// (search for "returnAddress + cieInfo.isSignalFrame" or "Return address
3121+
// points to the next instruction"). This is probably not the right place for
3122+
// this because this function is not necessarily used with DWARF. Need to
3123+
// research whether the other unwind methods have the same +-1 situation or
3124+
// are off by one.
3125+
_registers.setIP(_registers.getIP() + 1);
3126+
3127+
_isSignalFrame = true;
3128+
return UNW_STEP_SUCCESS;
3129+
}
3130+
#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) &&
3131+
// defined(_LIBUNWIND_TARGET_X86_64)
3132+
30353133
template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) {
30363134
(void)stage2;
30373135
// Bottom of stack is defined is when unwind info cannot be found.

0 commit comments

Comments
 (0)