[libunwind][DWARF] Fix end of .eh_frame calculation

* When .eh_frame is located using .eh_frame_hdr (PT_GNU_EH_FRAME), the
   start of .eh_frame is known, but not the size. In this case, the
   unwinder must rely on a terminator present at the end of .eh_frame.
   Set dwarf_section_length to UINTPTR_MAX to indicate this.

 * Add a new field, text_segment_length, that the FrameHeaderCache uses
   to track the size of the PT_LOAD segment indicated by dso_base.

 * Compute ehSectionEnd by adding sectionLength to ehSectionStart,
   never to fdeHint.

Fixes PR46829.

Differential Revision: https://reviews.llvm.org/D87750
This commit is contained in:
Ryan Prichard 2020-09-16 01:22:55 -07:00
parent 436a43afb2
commit fb1abe0063
5 changed files with 24 additions and 15 deletions

View File

@ -119,6 +119,10 @@ struct UnwindInfoSections {
// No dso_base for SEH or ARM EHABI.
uintptr_t dso_base;
#endif
#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) && \
defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
uintptr_t text_segment_length;
#endif
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
uintptr_t dwarf_section;
uintptr_t dwarf_section_length;
@ -410,7 +414,7 @@ static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base,
uintptr_t end = begin + phdr->p_memsz;
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
cbdata->sects->dso_base = begin;
cbdata->sects->dwarf_section_length = phdr->p_memsz;
cbdata->sects->text_segment_length = phdr->p_memsz;
return true;
}
}
@ -450,8 +454,12 @@ static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo,
found_hdr = EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
*cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
hdrInfo);
if (found_hdr)
if (found_hdr) {
// .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 = UINTPTR_MAX;
}
} else if (!found_obj) {
found_obj = checkAddrInSegment(phdr, image_base, cbdata);
}
@ -462,7 +470,6 @@ static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo,
return 1;
}
}
cbdata->sects->dwarf_section_length = 0;
return 0;
}

View File

@ -136,7 +136,7 @@ public:
};
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
uintptr_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
CIE_Info *cieInfo);
static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
FDE_Info *fdeInfo, CIE_Info *cieInfo);
@ -167,7 +167,7 @@ const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
p += 8;
}
if (cfiLength == 0)
return "FDE has zero length"; // end marker
return "FDE has zero length"; // zero terminator
uint32_t ciePointer = addressSpace.get32(p);
if (ciePointer == 0)
return "FDE is really a CIE"; // this is a CIE not an FDE
@ -212,11 +212,13 @@ const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
/// Scan an eh_frame section to find an FDE for a pc
template <typename A>
bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
uint32_t sectionLength, pint_t fdeHint,
uintptr_t sectionLength, pint_t fdeHint,
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
//fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
const pint_t ehSectionEnd = p + sectionLength;
const pint_t ehSectionEnd = (sectionLength == UINTPTR_MAX)
? static_cast<pint_t>(-1)
: (ehSectionStart + sectionLength);
while (p < ehSectionEnd) {
pint_t currentCFI = p;
//fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
@ -228,7 +230,7 @@ bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
p += 8;
}
if (cfiLength == 0)
return false; // end marker
return false; // zero terminator
uint32_t id = addressSpace.get32(p);
if (id == 0) {
// Skip over CIEs.

View File

@ -32,7 +32,7 @@
class _LIBUNWIND_HIDDEN FrameHeaderCache {
struct CacheEntry {
uintptr_t LowPC() { return Info.dso_base; };
uintptr_t HighPC() { return Info.dso_base + Info.dwarf_section_length; };
uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; };
UnwindInfoSections Info;
CacheEntry *Next;
};

View File

@ -1517,7 +1517,7 @@ bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
// If compact encoding table gave offset into dwarf section, go directly there
if (fdeSectionOffsetHint != 0) {
foundFDE = CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section,
(uint32_t)sects.dwarf_section_length,
sects.dwarf_section_length,
sects.dwarf_section + fdeSectionOffsetHint,
&fdeInfo, &cieInfo);
}
@ -1534,7 +1534,7 @@ bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
if (cachedFDE != 0) {
foundFDE =
CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section,
(uint32_t)sects.dwarf_section_length,
sects.dwarf_section_length,
cachedFDE, &fdeInfo, &cieInfo);
foundInCache = foundFDE;
}
@ -1542,7 +1542,7 @@ bool UnwindCursor<A, R>::getInfoFromDwarfSection(pint_t pc,
if (!foundFDE) {
// Still not found, do full scan of __eh_frame section.
foundFDE = CFI_Parser<A>::findFDE(_addressSpace, pc, sects.dwarf_section,
(uint32_t)sects.dwarf_section_length, 0,
sects.dwarf_section_length, 0,
&fdeInfo, &cieInfo);
}
if (foundFDE) {

View File

@ -16,7 +16,7 @@
#include "../src/AddressSpace.hpp"
#define kBaseAddr 0xFFF000
#define kDwarfSectionLength 0xFF
#define kTextSegmentLength 0xFF
using namespace libunwind;
@ -32,7 +32,7 @@ int main() {
UnwindInfoSections UIS;
UIS.dso_base = kBaseAddr;
UIS.dwarf_section_length = kDwarfSectionLength;
UIS.text_segment_length = kTextSegmentLength;
dl_iterate_cb_data CBData;
// Unused by the cache.
CBData.addressSpace = nullptr;
@ -58,7 +58,7 @@ int main() {
abort();
// Add enough things to the cache that the entry is evicted.
for (int i = 0; i < 9; i++) {
UIS.dso_base = kBaseAddr + (kDwarfSectionLength * i);
UIS.dso_base = kBaseAddr + (kTextSegmentLength * i);
FHC.add(&UIS);
}
CBData.targetAddr = kBaseAddr;