Fix the base address for the unwind table.
Apparently this hadn't actually been working before. The header search was
failing, and then the seach was falling back to scanning the whole table.
Thanks to Saleem for noticing my mistake.
Unfortunately this means that we can't actually test this code directly. If it
fails to find the entry in the search table, it will still find it by scanning
the whole section. We should probably add some real unit tests to this project
at some point to deal with this.
http://reviews.llvm.org/D6848
Files:
src/Unwind/AddressSpace.hpp
src/Unwind/EHHeaderParser.hpp
src/Unwind/UnwindCursor.hpp
src/Unwind/config.h
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
Index: src/Unwind/AddressSpace.hpp
===================================================================
--- src/Unwind/AddressSpace.hpp
+++ src/Unwind/AddressSpace.hpp
@@ -56,6 +56,9 @@
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 @@
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 @@
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 @@
_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 @@
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.ehFramePtr;
+ 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 @@
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);
Index: src/Unwind/EHHeaderParser.hpp
===================================================================
--- /dev/null
+++ src/Unwind/EHHeaderParser.hpp
@@ -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 ehFramePtr;
+ size_t fdeCount;
+ pint_t searchTable;
+ uint8_t searchTableEnc;
+ };
+
+ 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 ehFramePtrEnc = addressSpace.get8(p++);
+ uint8_t fdeCountEnc = addressSpace.get8(p++);
+ ehHdrInfo.searchTableEnc = addressSpace.get8(p++);
+
+ ehHdrInfo.ehFramePtr =
+ addressSpace.getEncodedP(p, ehHdrEnd, ehFramePtrEnc, ehHdrStart);
+ ehHdrInfo.fdeCount =
+ addressSpace.getEncodedP(p, ehHdrEnd, fdeCountEnc, ehHdrStart);
+ ehHdrInfo.searchTable = 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.searchTableEnc);
+ pint_t tableEntry;
+
+ size_t low = 0;
+ for (size_t len = hdrInfo.fdeCount; len > 1;) {
+ size_t mid = low + (len / 2);
+ tableEntry = hdrInfo.searchTable + mid * tableEntrySize;
+ pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd,
+ hdrInfo.searchTableEnc, ehHdrStart);
+
+ if (start == pc) {
+ low = mid;
+ break;
+ } else if (start < pc) {
+ low = mid;
+ len -= (len / 2);
+ } else {
+ len /= 2;
+ }
+ }
+
+ tableEntry = hdrInfo.searchTable + low * tableEntrySize;
+ if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd,
+ hdrInfo.searchTableEnc, 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
Index: src/Unwind/UnwindCursor.hpp
===================================================================
--- src/Unwind/UnwindCursor.hpp
+++ src/Unwind/UnwindCursor.hpp
@@ -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 @@
}
#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) {
Index: src/Unwind/config.h
===================================================================
--- src/Unwind/config.h
+++ src/Unwind/config.h
@@ -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
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits