|
33 | 33 | #if defined(_LIBUNWIND_TARGET_LINUX) && \ |
34 | 34 | (defined(_LIBUNWIND_TARGET_AARCH64) || \ |
35 | 35 | 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)) |
37 | 38 | #include <errno.h> |
38 | 39 | #include <signal.h> |
39 | 40 | #include <sys/syscall.h> |
@@ -1008,6 +1009,10 @@ class UnwindCursor : public AbstractUnwindCursor{ |
1008 | 1009 | #if defined(_LIBUNWIND_TARGET_S390X) |
1009 | 1010 | bool setInfoForSigReturn(Registers_s390x &); |
1010 | 1011 | 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 &); |
1011 | 1016 | #endif |
1012 | 1017 | template <typename Registers> bool setInfoForSigReturn(Registers &) { |
1013 | 1018 | return false; |
@@ -3032,6 +3037,99 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) { |
3032 | 3037 | #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && |
3033 | 3038 | // defined(_LIBUNWIND_TARGET_S390X) |
3034 | 3039 |
|
| 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 | + |
3035 | 3133 | template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) { |
3036 | 3134 | (void)stage2; |
3037 | 3135 | // Bottom of stack is defined is when unwind info cannot be found. |
|
0 commit comments