[libcxxabi] r230802 - Add .eh_frame_hdr search to Linux unwinder.
Dan Albert
danalbert at google.com
Fri Feb 27 14:21:08 PST 2015
Author: danalbert
Date: Fri Feb 27 16:21:07 2015
New Revision: 230802
URL: http://llvm.org/viewvc/llvm-project?rev=230802&view=rev
Log:
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.
Added:
libcxxabi/trunk/src/Unwind/EHHeaderParser.hpp
Modified:
libcxxabi/trunk/src/Unwind/AddressSpace.hpp
libcxxabi/trunk/src/Unwind/UnwindCursor.hpp
libcxxabi/trunk/src/Unwind/config.h
Modified: libcxxabi/trunk/src/Unwind/AddressSpace.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/Unwind/AddressSpace.hpp?rev=230802&r1=230801&r2=230802&view=diff
==============================================================================
--- libcxxabi/trunk/src/Unwind/AddressSpace.hpp (original)
+++ libcxxabi/trunk/src/Unwind/AddressSpace.hpp Fri Feb 27 16:21:07 2015
@@ -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::getSLE
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 LocalAd
_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::findUnwin
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);
Added: libcxxabi/trunk/src/Unwind/EHHeaderParser.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/Unwind/EHHeaderParser.hpp?rev=230802&view=auto
==============================================================================
--- libcxxabi/trunk/src/Unwind/EHHeaderParser.hpp (added)
+++ libcxxabi/trunk/src/Unwind/EHHeaderParser.hpp Fri Feb 27 16:21:07 2015
@@ -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
Modified: libcxxabi/trunk/src/Unwind/UnwindCursor.hpp
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/Unwind/UnwindCursor.hpp?rev=230802&r1=230801&r2=230802&view=diff
==============================================================================
--- libcxxabi/trunk/src/Unwind/UnwindCursor.hpp (original)
+++ libcxxabi/trunk/src/Unwind/UnwindCursor.hpp Fri Feb 27 16:21:07 2015
@@ -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>::getInfoFromDwar
}
#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) {
Modified: libcxxabi/trunk/src/Unwind/config.h
URL: http://llvm.org/viewvc/llvm-project/libcxxabi/trunk/src/Unwind/config.h?rev=230802&r1=230801&r2=230802&view=diff
==============================================================================
--- libcxxabi/trunk/src/Unwind/config.h (original)
+++ libcxxabi/trunk/src/Unwind/config.h Fri Feb 27 16:21:07 2015
@@ -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
More information about the cfe-commits
mailing list