diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index b2dae8feed9a3..344c2629dd68c 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -115,6 +115,9 @@ extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; +// (ClickHouse addition) +extern int unw_backtrace(void **, int) LIBUNWIND_AVAIL; + #ifdef __arm__ /* Save VFP registers in FSTMX format (instead of FSTMD). */ extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 5551c7d4bef1c..b49b2c2ea3b9a 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -416,10 +416,10 @@ static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base, if (EHHeaderParser::decodeEHHdr( *cbdata->addressSpace, eh_frame_hdr_start, eh_frame_hdr_start + phdr->p_memsz, hdrInfo)) { - // .eh_frame_hdr records the start of .eh_frame, but not its size. - // Rely on a zero terminator to find the end of the section. cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; - cbdata->sects->dwarf_section_length = SIZE_MAX; + // .eh_frame_hdr records the start of .eh_frame, but not its size. + // See https://bugs.llvm.org/show_bug.cgi?id=36005 + cbdata->sects->dwarf_section_length = cbdata->sects->text_segment_length - (cbdata->sects->dwarf_section - cbdata->sects->dso_base); return true; } } diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index bd9ece60ee588..96744e5275417 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -64,7 +64,7 @@ class DwarfInstructions { static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, const R ®isters) { - if (prolog.cfaRegister != 0) + if (prolog.cfaRegister != (uint32_t)(-1)) return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + prolog.cfaRegisterOffset); if (prolog.cfaExpression != 0) @@ -293,17 +293,32 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, #if !defined(_LIBUNWIND_IS_NATIVE_ONLY) return UNW_ECROSSRASIGNING; #else - register unsigned long long x17 __asm("x17") = returnAddress; - register unsigned long long x16 __asm("x16") = cfa; - // These are the autia1716/autib1716 instructions. The hint instructions // are used here as gcc does not assemble autia1716/autib1716 for pre // armv8.3a targets. + if (cieInfo.addressesSignedWithBKey) - asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 + { + asm volatile( + "mov x17, %x0;" + "mov x16, %x1;" + "hint 0xe;" // autib1716 + "mov %0, x17" + : "+r"(returnAddress) + : "r"(cfa) + : "x16", "x17"); + } else - asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 - returnAddress = x17; + { + asm volatile( + "mov x17, %x0;" + "mov x16, %x1;" + "hint 0xc;" // autia1716 + "mov %0, x17" + : "+r"(returnAddress) + : "r"(cfa) + : "x16", "x17"); + } #endif } #endif @@ -364,8 +379,16 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, #endif // Return address is address after call site instruction, so setting IP to - // that does simulates a return. - newRegisters.setIP(returnAddress); + // that simulates a return. + // + // The +-1 situation is subtle. + // Return address points to the next instruction after the `call` + // instruction, but logically we're "inside" the call instruction, and + // FDEs are constructed accordingly. + // So our FDE parsing implicitly subtracts 1 from the address. + // But for signal return, there's no `call` instruction, and + // subtracting 1 would be incorrect. So we add 1 here to compensate. + newRegisters.setIP(returnAddress + cieInfo.isSignalFrame); // Simulate the step by replacing the register set with the new ones. registers = newRegisters; diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index 0682942ce1379..488043dfd6b53 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -95,8 +95,10 @@ class CFI_Parser { // When saving registers, this data structure is lazily initialized. PrologInfo(InitializeTime IT = InitializeTime::kNormal) { - if (IT == InitializeTime::kNormal) + if (IT == InitializeTime::kNormal) { memset(this, 0, sizeof(*this)); + cfaRegister = (uint32_t)(-1); + } } void checkSaveRegister(uint64_t reg, PrologInfo &initialState) { if (!savedRegisters[reg].initialStateSaved) { @@ -450,7 +452,14 @@ bool CFI_Parser::parseFDEInstructions(A &addressSpace, ")\n", static_cast(instructionsEnd)); - // see DWARF Spec, section 6.4.2 for details on unwind opcodes + // see DWARF Spec, section 6.4.2 for details on unwind opcodes; + // + // Note that we're looking for the PrologInfo for address `codeOffset - 1`, + // hence '<' instead of '<=" in `codeOffset < pcoffset` + // (compare to DWARF Spec section 6.4.3 "Call Frame Instruction Usage"). + // The -1 accounts for the fact that function return address points to the + // next instruction *after* the `call` instruction, while control is + // logically "inside" the `call` instruction. while ((p < instructionsEnd) && (codeOffset < pcoffset)) { uint64_t reg; uint64_t reg2; @@ -611,7 +620,7 @@ bool CFI_Parser::parseFDEInstructions(A &addressSpace, results->cfaRegisterOffset); break; case DW_CFA_def_cfa_expression: - results->cfaRegister = 0; + results->cfaRegister = (uint32_t)(-1); results->cfaExpression = (int64_t)p; length = addressSpace.getULEB128(p, instructionsEnd); assert(length < static_cast(~0) && "pointer overflow"); diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 06e654197351d..243100d701caf 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -32,7 +32,7 @@ #if defined(_LIBUNWIND_TARGET_LINUX) && \ (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ - defined(_LIBUNWIND_TARGET_S390X)) + defined(_LIBUNWIND_TARGET_S390X) || defined(_LIBUNWIND_TARGET_X86_64)) #include #include #include @@ -152,6 +152,7 @@ bool DwarfFDECache::_registeredForDyldUnloads = false; template typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { pint_t result = 0; +#if !defined(_LIBUNWIND_NO_HEAP) _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); for (entry *p = _buffer; p < _bufferUsed; ++p) { if ((mh == p->mh) || (mh == kSearchAll)) { @@ -162,6 +163,7 @@ typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { } } _LIBUNWIND_LOG_IF_FALSE(_lock.unlock_shared()); +#endif return result; } @@ -199,6 +201,7 @@ void DwarfFDECache::add(pint_t mh, pint_t ip_start, pint_t ip_end, template void DwarfFDECache::removeAllIn(pint_t mh) { +#if !defined(_LIBUNWIND_NO_HEAP) _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); entry *d = _buffer; for (const entry *s = _buffer; s < _bufferUsed; ++s) { @@ -210,6 +213,7 @@ void DwarfFDECache::removeAllIn(pint_t mh) { } _bufferUsed = d; _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif } #ifdef __APPLE__ @@ -222,11 +226,13 @@ void DwarfFDECache::dyldUnloadHook(const struct mach_header *mh, intptr_t ) { template void DwarfFDECache::iterateCacheEntries(void (*func)( unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { +#if !defined(_LIBUNWIND_NO_HEAP) _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); for (entry *p = _buffer; p < _bufferUsed; ++p) { (*func)(p->ip_start, p->ip_end, p->fde, p->mh); } _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif } #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) @@ -1003,6 +1009,10 @@ class UnwindCursor : public AbstractUnwindCursor{ #if defined(_LIBUNWIND_TARGET_S390X) bool setInfoForSigReturn(Registers_s390x &); int stepThroughSigReturn(Registers_s390x &); +#endif +#if defined(_LIBUNWIND_TARGET_X86_64) + bool setInfoForSigReturn(Registers_x86_64 &); + int stepThroughSigReturn(Registers_x86_64 &); #endif template bool setInfoForSigReturn(Registers &) { return false; @@ -1683,12 +1693,27 @@ bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, foundInCache = foundFDE; } } - if (!foundFDE) { + + /** This code path is disabled, because it is prohibitively slow + * when we cannot use the cache (_LIBUNWIND_NO_HEAP). + * The "DWARF index" is typically present, so we should not get here. + * + * But it is possible that the FDE is not found, + * when some assembly functions are not properly annotated + * (with .cfi_startproc, .cfi_endproc, etc). + * This is the case when building with Musl instead of GLibC, + * for example see the function src/thread/x86_64/clone.s in Musl + * and sysdeps/unix/sysv/linux/x86_64/clone.S in GLibC. + * + * But in this case, the FDE will not be found anyway. + */ +/* if (!foundFDE) { // Still not found, do full scan of __eh_frame section. foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, sects.dwarf_section_length, 0, &fdeInfo, &cieInfo); - } + }*/ + if (foundFDE) { if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) { // Add to cache (to make next lookup faster) if we had no hint @@ -2753,7 +2778,15 @@ int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { _registers.setRegister(UNW_AARCH64_X0 + i, value); } _registers.setSP(_addressSpace.get64(sigctx + kOffsetSp)); - _registers.setIP(_addressSpace.get64(sigctx + kOffsetPc)); + + // The +1 story is the same as in DwarfInstructions::stepWithDwarf() + // (search for "returnAddress + cieInfo.isSignalFrame" or "Return address points to the next instruction"). + // This is probably not the right place for this because this function is not necessarily used + // with DWARF. Need to research whether the other unwind methods have the same +-1 situation or + // are off by one. + pint_t returnAddress = _addressSpace.get64(sigctx + kOffsetPc); + _registers.setIP(returnAddress + 1); + _isSignalFrame = true; return UNW_STEP_SUCCESS; } @@ -2917,12 +2950,105 @@ int UnwindCursor::stepThroughSigReturn(Registers_s390x &) { #endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && // defined(_LIBUNWIND_TARGET_S390X) +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_X86_64) +template +bool UnwindCursor::setInfoForSigReturn(Registers_x86_64 &) { + // Look for the sigreturn trampoline. The trampoline's body is two + // specific instructions (see below). Typically the trampoline comes from the + // vDSO or from libc. + // + // This special code path is a fallback that is only used if the trampoline + // lacks proper (e.g. DWARF) unwind info. + const uint8_t amd64_linux_sigtramp_code[9] = { + 0x48, 0xc7, 0xc0, 0x0f, 0x00, 0x00, 0x00, // mov rax, 15 + 0x0f, 0x05 // syscall + }; + const size_t code_size = sizeof(amd64_linux_sigtramp_code); + + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a SIGSEGV. + unw_word_t pc = static_cast(this->getReg(UNW_REG_IP)); + if (!isReadableAddr(pc)) + return false; + // If near page boundary, check the next page too. + if (((pc + code_size - 1) & 4095) != (pc & 4095) && !isReadableAddr(pc + code_size - 1)) + return false; + + const uint8_t *pc_ptr = reinterpret_cast(pc); + if (memcmp(pc_ptr, amd64_linux_sigtramp_code, code_size)) + return false; + + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + code_size; + _isSigReturn = true; + return true; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_x86_64 &) { + // In the signal trampoline frame, sp points to ucontext: + // struct ucontext { + // unsigned long uc_flags; + // struct ucontext *uc_link; + // stack_t uc_stack; // 24 bytes + // struct sigcontext uc_mcontext; + // ... + // }; + const pint_t kOffsetSpToSigcontext = (8 + 8 + 24); + pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + + // UNW_X86_64_* -> field in struct sigcontext_64. + // struct sigcontext_64 { + // __u64 r8; // 0 + // __u64 r9; // 1 + // __u64 r10; // 2 + // __u64 r11; // 3 + // __u64 r12; // 4 + // __u64 r13; // 5 + // __u64 r14; // 6 + // __u64 r15; // 7 + // __u64 di; // 8 + // __u64 si; // 9 + // __u64 bp; // 10 + // __u64 bx; // 11 + // __u64 dx; // 12 + // __u64 ax; // 13 + // __u64 cx; // 14 + // __u64 sp; // 15 + // __u64 ip; // 16 + // ... + // }; + const size_t idx_map[17] = {13, 12, 14, 11, 9, 8, 10, 15, 0, 1, 2, 3, 4, 5, 6, 7, 16}; + + for (int i = 0; i < 17; ++i) { + uint64_t value = _addressSpace.get64(sigctx + idx_map[i] * 8); + _registers.setRegister(i, value); + } + + // The +1 story is the same as in DwarfInstructions::stepWithDwarf() + // (search for "returnAddress + cieInfo.isSignalFrame" or "Return address points to the next instruction"). + // This is probably not the right place for this because this function is not necessarily used + // with DWARF. Need to research whether the other unwind methods have the same +-1 situation or + // are off by one. + _registers.setIP(_registers.getIP() + 1); + + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_X86_64) + template int UnwindCursor::step(bool stage2) { (void)stage2; // Bottom of stack is defined is when unwind info cannot be found. if (_unwindInfoMissing) return UNW_STEP_END; + unw_word_t previousIP = getReg(UNW_REG_IP); + unw_word_t previousSP = getReg(UNW_REG_SP); + // Use unwinding info to modify register set as if function returned. int result; #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) @@ -2951,6 +3077,14 @@ template int UnwindCursor::step(bool stage2) { // update info based on new PC if (result == UNW_STEP_SUCCESS) { + // Detect cycles of length 1. In particular this happens in musl's + // __clone(), which has incorrect DWARF unwind information. + // We don't check all registers, so it's not strictly guaranteed that + // unwinding would be stuck in a cycle, but seems like a reasonable + // heuristic. + if (getReg(UNW_REG_SP) == previousSP && getReg(UNW_REG_IP) == previousIP) + return UNW_EBADFRAME; + this->setInfoBasedOnIPRegister(true); if (_unwindInfoMissing) return UNW_STEP_END; @@ -2961,10 +3095,11 @@ template int UnwindCursor::step(bool stage2) { template void UnwindCursor::getInfo(unw_proc_info_t *info) { - if (_unwindInfoMissing) - memset(info, 0, sizeof(*info)); - else - *info = _info; + /// TODO: Revert after disable backtrace inteception in asan + // if (_unwindInfoMissing) + // memset(info, 0, sizeof(*info)); + // else + *info = _info; } template diff --git a/libunwind/src/config.h b/libunwind/src/config.h index deb5a4d4d73d4..1bde4ba93f221 100644 --- a/libunwind/src/config.h +++ b/libunwind/src/config.h @@ -17,6 +17,7 @@ #include #include #include +#include #include <__libunwind_config.h> @@ -146,6 +147,25 @@ #define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED #endif +#ifndef fileno +int fileno(FILE *stream); +#endif + +/// Wrapper for fprintf, to avoid using if from signal handler. +/// Function snprintf is not signal-safe as well, but hopefully it will be fine. +/// Don't care about checking result of write() here. +#define _FPRINTF_WRAPPER(file, msg, ...) \ + do { \ + char buf[128]; \ + int num_bytes = snprintf(buf, 128, msg __VA_OPT__(,) __VA_ARGS__); \ + int fd = fileno(file); \ + if (num_bytes > 0 && fd >= 0) \ + { \ + num_bytes = num_bytes < 128 ? num_bytes : 127; \ + write(fd, buf, num_bytes); \ + } \ + } while(0); + #if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL) #define _LIBUNWIND_ABORT(msg) \ do { \ @@ -154,7 +174,7 @@ #else #define _LIBUNWIND_ABORT(msg) \ do { \ - fprintf(stderr, "libunwind: %s - %s\n", __func__, msg); \ + _FPRINTF_WRAPPER(stderr, "libunwind: %s - %s\n", __func__, msg); \ fflush(stderr); \ abort(); \ } while (0) @@ -165,11 +185,11 @@ #define _LIBUNWIND_LOG(msg, ...) #else #define _LIBUNWIND_LOG0(msg) do { \ - fprintf(stderr, "libunwind: " msg "\n"); \ + _FPRINTF_WRAPPER(stderr, "libunwind: " msg "\n"); \ fflush(stderr); \ } while (0) #define _LIBUNWIND_LOG(msg, ...) do { \ - fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__); \ + _FPRINTF_WRAPPER(stderr, "libunwind: " msg "\n", __VA_ARGS__); \ fflush(stderr); \ } while (0) #endif @@ -219,7 +239,7 @@ #define _LIBUNWIND_TRACE_DWARF(...) \ do { \ if (logDWARF()) \ - fprintf(stderr, __VA_ARGS__); \ + _FPRINTF_WRAPPER(stderr, __VA_ARGS__); \ } while (0) #endif diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index cf39ec5f7dbdf..7c78debe8e531 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -218,6 +218,27 @@ _LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { } _LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume) +/// ClickHouse: Convenient helper (added for jemalloc) +int unw_backtrace(void **buffer, int size) { + unw_context_t context; + unw_cursor_t cursor; + if (unw_getcontext(&context) || unw_init_local(&cursor, &context)) { + return 0; + } + + unw_word_t ip; + int current = 0; + while (unw_step(&cursor) > 0) { + if (current >= size || unw_get_reg(&cursor, UNW_REG_IP, &ip)) { + break; + } + + buffer[current++] = reinterpret_cast(static_cast(ip)); + } + + return current; +} + /// Get name of function at cursor position in stack frame. _LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, size_t bufLen, unw_word_t *offset) {