Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libunwind/include/libunwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions libunwind/src/AddressSpace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,10 +416,10 @@ static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base,
if (EHHeaderParser<LocalAddressSpace>::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;
}
}
Expand Down
41 changes: 32 additions & 9 deletions libunwind/src/DwarfInstructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class DwarfInstructions {

static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
const R &registers) {
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)
Expand Down Expand Up @@ -293,17 +293,32 @@ int DwarfInstructions<A, R>::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
Expand Down Expand Up @@ -364,8 +379,16 @@ int DwarfInstructions<A, R>::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;
Expand Down
15 changes: 12 additions & 3 deletions libunwind/src/DwarfParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -450,7 +452,14 @@ bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
")\n",
static_cast<uint64_t>(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;
Expand Down Expand Up @@ -611,7 +620,7 @@ bool CFI_Parser<A>::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<pint_t>(~0) && "pointer overflow");
Expand Down
151 changes: 143 additions & 8 deletions libunwind/src/UnwindCursor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <errno.h>
#include <signal.h>
#include <sys/syscall.h>
Expand Down Expand Up @@ -152,6 +152,7 @@ bool DwarfFDECache<A>::_registeredForDyldUnloads = false;
template <typename A>
typename A::pint_t DwarfFDECache<A>::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)) {
Expand All @@ -162,6 +163,7 @@ typename A::pint_t DwarfFDECache<A>::findFDE(pint_t mh, pint_t pc) {
}
}
_LIBUNWIND_LOG_IF_FALSE(_lock.unlock_shared());
#endif
return result;
}

Expand Down Expand Up @@ -199,6 +201,7 @@ void DwarfFDECache<A>::add(pint_t mh, pint_t ip_start, pint_t ip_end,

template <typename A>
void DwarfFDECache<A>::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) {
Expand All @@ -210,6 +213,7 @@ void DwarfFDECache<A>::removeAllIn(pint_t mh) {
}
_bufferUsed = d;
_LIBUNWIND_LOG_IF_FALSE(_lock.unlock());
#endif
}

#ifdef __APPLE__
Expand All @@ -222,11 +226,13 @@ void DwarfFDECache<A>::dyldUnloadHook(const struct mach_header *mh, intptr_t ) {
template <typename A>
void DwarfFDECache<A>::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)

Expand Down Expand Up @@ -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 <typename Registers> bool setInfoForSigReturn(Registers &) {
return false;
Expand Down Expand Up @@ -1683,12 +1693,27 @@ bool UnwindCursor<A, R>::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<A>::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
Expand Down Expand Up @@ -2753,7 +2778,15 @@ int UnwindCursor<A, R>::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;
}
Expand Down Expand Up @@ -2917,12 +2950,105 @@ int UnwindCursor<A, R>::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 <typename A, typename R>
bool UnwindCursor<A, R>::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<pint_t>(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<const uint8_t *>(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 <typename A, typename R>
int UnwindCursor<A, R>::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 <typename A, typename R> int UnwindCursor<A, R>::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)
Expand Down Expand Up @@ -2951,6 +3077,14 @@ template <typename A, typename R> int UnwindCursor<A, R>::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;
Expand All @@ -2961,10 +3095,11 @@ template <typename A, typename R> int UnwindCursor<A, R>::step(bool stage2) {

template <typename A, typename R>
void UnwindCursor<A, R>::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 <typename A, typename R>
Expand Down
Loading