|
32 | 32 |
|
33 | 33 | #if defined(_LIBUNWIND_TARGET_LINUX) && \ |
34 | 34 | (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ |
35 | | - defined(_LIBUNWIND_TARGET_S390X)) |
| 35 | + defined(_LIBUNWIND_TARGET_S390X) || defined(_LIBUNWIND_TARGET_X86_64)) |
36 | 36 | #include <errno.h> |
37 | 37 | #include <signal.h> |
38 | 38 | #include <sys/syscall.h> |
@@ -1003,6 +1003,10 @@ class UnwindCursor : public AbstractUnwindCursor{ |
1003 | 1003 | #if defined(_LIBUNWIND_TARGET_S390X) |
1004 | 1004 | bool setInfoForSigReturn(Registers_s390x &); |
1005 | 1005 | 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 &); |
1006 | 1010 | #endif |
1007 | 1011 | template <typename Registers> bool setInfoForSigReturn(Registers &) { |
1008 | 1012 | return false; |
@@ -2917,6 +2921,96 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_s390x &) { |
2917 | 2921 | #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && |
2918 | 2922 | // defined(_LIBUNWIND_TARGET_S390X) |
2919 | 2923 |
|
| 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 | + |
2920 | 3014 | template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) { |
2921 | 3015 | (void)stage2; |
2922 | 3016 | // Bottom of stack is defined is when unwind info cannot be found. |
|
0 commit comments