Add .eh_frame_hdr search to Linux unwinder.
This improves the performance of unwinding on DWARF based targets. The 32-bit x86 support for scanning the full eh_frame (CFI_Parser::findFDE) apparently does not work (at least not on Linux). Since the eh_frame_hdr code delegates to that, this still doesn't work for x86 Linux, but it has been tested on x86_64 Linux and aarch64 Android. llvm-svn: 230802
This commit is contained in:
parent
e3959eb54e
commit
198d366fad
|
@ -56,6 +56,9 @@ extern EHTEntry __exidx_start;
|
|||
extern EHTEntry __exidx_end;
|
||||
#endif // !defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
|
||||
#elif _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
#include <link.h>
|
||||
#include "EHHeaderParser.hpp"
|
||||
#endif // LIBCXXABI_ARM_EHABI
|
||||
|
||||
namespace libunwind {
|
||||
|
@ -132,7 +135,8 @@ public:
|
|||
static uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||
static int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding);
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase = 0);
|
||||
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||
unw_word_t *offset);
|
||||
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||
|
@ -195,9 +199,9 @@ inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
|
|||
return result;
|
||||
}
|
||||
|
||||
inline LocalAddressSpace::pint_t LocalAddressSpace::getEncodedP(pint_t &addr,
|
||||
pint_t end,
|
||||
uint8_t encoding) {
|
||||
inline LocalAddressSpace::pint_t
|
||||
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase) {
|
||||
pint_t startAddr = addr;
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
pint_t result;
|
||||
|
@ -263,7 +267,12 @@ inline LocalAddressSpace::pint_t LocalAddressSpace::getEncodedP(pint_t &addr,
|
|||
_LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_datarel:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_datarel pointer encoding not supported");
|
||||
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
|
||||
// default value of 0, and we abort in the event that someone calls this
|
||||
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
|
||||
if (datarelBase == 0)
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
|
||||
result += datarelBase;
|
||||
break;
|
||||
case DW_EH_PE_funcrel:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
|
||||
|
@ -353,6 +362,64 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
|
|||
info.arm_section, info.arm_section_length);
|
||||
if (info.arm_section && info.arm_section_length)
|
||||
return true;
|
||||
#elif _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_INDEX
|
||||
struct dl_iterate_cb_data {
|
||||
LocalAddressSpace *addressSpace;
|
||||
UnwindInfoSections *sects;
|
||||
uintptr_t targetAddr;
|
||||
};
|
||||
|
||||
dl_iterate_cb_data cb_data = {this, &info, targetAddr};
|
||||
int found = dl_iterate_phdr(
|
||||
[](struct dl_phdr_info *pinfo, size_t, void *data) -> int {
|
||||
auto cbdata = static_cast<dl_iterate_cb_data *>(data);
|
||||
size_t object_length;
|
||||
bool found_obj = false;
|
||||
bool found_hdr = false;
|
||||
|
||||
assert(cbdata);
|
||||
assert(cbdata->sects);
|
||||
|
||||
if (cbdata->targetAddr < pinfo->dlpi_addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ElfW(Half) i = 0; i < pinfo->dlpi_phnum; i++) {
|
||||
const ElfW(Phdr) *phdr = &pinfo->dlpi_phdr[i];
|
||||
if (phdr->p_type == PT_LOAD) {
|
||||
uintptr_t begin = pinfo->dlpi_addr + phdr->p_vaddr;
|
||||
uintptr_t end = begin + phdr->p_memsz;
|
||||
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
|
||||
cbdata->sects->dso_base = begin;
|
||||
object_length = phdr->p_memsz;
|
||||
found_obj = true;
|
||||
}
|
||||
} else if (phdr->p_type == PT_GNU_EH_FRAME) {
|
||||
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
|
||||
uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr;
|
||||
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
|
||||
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
|
||||
EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
|
||||
*cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
|
||||
hdrInfo);
|
||||
cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
|
||||
found_hdr = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_obj && found_hdr) {
|
||||
cbdata->sects->dwarf_section_length = object_length;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
&cb_data);
|
||||
return static_cast<bool>(found);
|
||||
#else
|
||||
#error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform."
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return false;
|
||||
|
@ -408,7 +475,8 @@ public:
|
|||
pint_t getP(pint_t addr);
|
||||
uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||
int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding);
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase = 0);
|
||||
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||
unw_word_t *offset);
|
||||
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
//===------------------------- EHHeaderParser.hpp -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//
|
||||
// Parses ELF .eh_frame_hdr sections.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __EHHEADERPARSER_HPP__
|
||||
#define __EHHEADERPARSER_HPP__
|
||||
|
||||
#include "libunwind.h"
|
||||
|
||||
#include "AddressSpace.hpp"
|
||||
#include "DwarfParser.hpp"
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section.
|
||||
///
|
||||
/// See DWARF spec for details:
|
||||
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||
///
|
||||
template <typename A> class EHHeaderParser {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
|
||||
/// Information encoded in the EH frame header.
|
||||
struct EHHeaderInfo {
|
||||
pint_t eh_frame_ptr;
|
||||
size_t fde_count;
|
||||
pint_t table;
|
||||
uint8_t table_enc;
|
||||
};
|
||||
|
||||
static void decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
EHHeaderInfo &ehHdrInfo);
|
||||
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
|
||||
private:
|
||||
static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry,
|
||||
pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
static size_t getTableEntrySize(uint8_t tableEnc);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
void EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
|
||||
pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
|
||||
pint_t p = ehHdrStart;
|
||||
uint8_t version = addressSpace.get8(p++);
|
||||
if (version != 1)
|
||||
_LIBUNWIND_ABORT("Unsupported .eh_frame_hdr version");
|
||||
|
||||
uint8_t eh_frame_ptr_enc = addressSpace.get8(p++);
|
||||
uint8_t fde_count_enc = addressSpace.get8(p++);
|
||||
ehHdrInfo.table_enc = addressSpace.get8(p++);
|
||||
|
||||
ehHdrInfo.eh_frame_ptr =
|
||||
addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
|
||||
ehHdrInfo.fde_count =
|
||||
addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
|
||||
ehHdrInfo.table = p;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::decodeTableEntry(
|
||||
A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc, typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
// Have to decode the whole FDE for the PC range anyway, so just throw away
|
||||
// the PC start.
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
pint_t fde =
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
const char *message =
|
||||
CFI_Parser<A>::decodeFDE(addressSpace, fde, fdeInfo, cieInfo);
|
||||
if (message != NULL) {
|
||||
_LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s\n",
|
||||
message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
pint_t ehHdrEnd = ehHdrStart + sectionLength;
|
||||
|
||||
EHHeaderParser<A>::EHHeaderInfo hdrInfo;
|
||||
EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd, hdrInfo);
|
||||
|
||||
size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc);
|
||||
pint_t tableEntry;
|
||||
|
||||
size_t low = 0;
|
||||
for (size_t len = hdrInfo.fde_count; len > 1;) {
|
||||
size_t mid = low + (len / 2);
|
||||
tableEntry = hdrInfo.table + mid * tableEntrySize;
|
||||
pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd,
|
||||
hdrInfo.table_enc, ehHdrStart);
|
||||
|
||||
if (start == pc) {
|
||||
low = mid;
|
||||
break;
|
||||
} else if (start < pc) {
|
||||
low = mid;
|
||||
len -= (len / 2);
|
||||
} else {
|
||||
len /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
tableEntry = hdrInfo.table + low * tableEntrySize;
|
||||
if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd,
|
||||
hdrInfo.table_enc, fdeInfo, cieInfo)) {
|
||||
if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
size_t EHHeaderParser<A>::getTableEntrySize(uint8_t tableEnc) {
|
||||
switch (tableEnc & 0x0f) {
|
||||
case DW_EH_PE_sdata2:
|
||||
case DW_EH_PE_udata2:
|
||||
return 4;
|
||||
case DW_EH_PE_sdata4:
|
||||
case DW_EH_PE_udata4:
|
||||
return 8;
|
||||
case DW_EH_PE_sdata8:
|
||||
case DW_EH_PE_udata8:
|
||||
return 16;
|
||||
case DW_EH_PE_sleb128:
|
||||
case DW_EH_PE_uleb128:
|
||||
_LIBUNWIND_ABORT("Can't binary search on variable length encoded data.");
|
||||
case DW_EH_PE_omit:
|
||||
return 0;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("Unknown DWARF encoding for search table.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -29,6 +29,7 @@
|
|||
#include "CompactUnwinder.hpp"
|
||||
#include "config.h"
|
||||
#include "DwarfInstructions.hpp"
|
||||
#include "EHHeaderParser.hpp"
|
||||
#include "libunwind.h"
|
||||
#include "Registers.hpp"
|
||||
#include "Unwind-EHABI.h"
|
||||
|
@ -829,8 +830,9 @@ bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
|
|||
}
|
||||
#if _LIBUNWIND_SUPPORT_DWARF_INDEX
|
||||
if (!foundFDE && (sects.dwarf_index_section != 0)) {
|
||||
// Have eh_frame_hdr section which is index into dwarf section.
|
||||
// TO DO: implement index search
|
||||
foundFDE = EHHeaderParser<A>::findFDE(
|
||||
_addressSpace, pc, sects.dwarf_index_section,
|
||||
(uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo);
|
||||
}
|
||||
#endif
|
||||
if (!foundFDE) {
|
||||
|
|
|
@ -83,8 +83,9 @@
|
|||
#define _LIBUNWIND_ABORT(msg) assert_rtn(__func__, __FILE__, __LINE__, msg)
|
||||
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 0
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 0
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 0
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND !defined(__arm__) || \
|
||||
defined(__ARM_DWARF_EH__)
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX _LIBUNWIND_SUPPORT_DWARF_UNWIND
|
||||
#endif
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue