diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -83,8 +83,10 @@ // __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; // __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; +#if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) extern char __eh_frame_start; extern char __eh_frame_end; +#endif #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) extern char __eh_frame_hdr_start; @@ -114,12 +116,16 @@ /// Used by findUnwindSections() to return info about needed sections. struct UnwindInfoSections { -#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) || \ +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || \ defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) // No dso_base for SEH or ARM EHABI. uintptr_t dso_base; #endif -#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) && \ + defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + uintptr_t dso_length; +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) uintptr_t dwarf_section; uintptr_t dwarf_section_length; #endif @@ -390,9 +396,14 @@ uintptr_t targetAddr; }; -#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - #if !defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) - #error "_LIBUNWIND_SUPPORT_DWARF_UNWIND requires _LIBUNWIND_SUPPORT_DWARF_INDEX on this platform." +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + #if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) + // dl_iterate_phdr finds PT_GNU_EH_FRAME (.eh_frame_hdr section), which + // provides the start of the .eh_frame section, but not its length. The + // .eh_frame length isn't strictly necessary if we assume the section ends + // with a terminator, but sometimes it doesn't, and if we have the index, + // then scanning .eh_frame shouldn't be necessary anyway. + #error _LIBUNWIND_SUPPORT_DWARF_SECTION is unsupported with dl_iterate_phdr. #endif #if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) @@ -410,7 +421,7 @@ 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->dso_length = phdr->p_memsz; return true; } } @@ -443,15 +454,10 @@ for (Elf_Half i = pinfo->dlpi_phnum; i > 0; i--) { const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i - 1]; if (!found_hdr && phdr->p_type == PT_GNU_EH_FRAME) { - EHHeaderParser::EHHeaderInfo hdrInfo; + found_hdr = true; uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr; cbdata->sects->dwarf_index_section = eh_frame_hdr_start; cbdata->sects->dwarf_index_section_length = phdr->p_memsz; - found_hdr = EHHeaderParser::decodeEHHdr( - *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz, - hdrInfo); - if (found_hdr) - cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; } else if (!found_obj) { found_obj = checkAddrInSegment(phdr, image_base, cbdata); } @@ -462,7 +468,6 @@ return 1; } } - cbdata->sects->dwarf_section_length = 0; return 0; } @@ -509,7 +514,7 @@ dyld_unwind_sections dyldInfo; if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) { info.dso_base = (uintptr_t)dyldInfo.mh; - #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + #if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; info.dwarf_section_length = dyldInfo.dwarf_section_length; #endif @@ -518,19 +523,23 @@ return true; } #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) + bool result = false; + #if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) // Bare metal is statically linked, so no need to ask the dynamic loader info.dwarf_section_length = (uintptr_t)(&__eh_frame_end - &__eh_frame_start); info.dwarf_section = (uintptr_t)(&__eh_frame_start); _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p", (void *)info.dwarf_section, (void *)info.dwarf_section_length); -#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + result = result || info.dwarf_section_length; + #endif + #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) info.dwarf_index_section = (uintptr_t)(&__eh_frame_hdr_start); info.dwarf_index_section_length = (uintptr_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start); _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p", (void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length); -#endif - if (info.dwarf_section_length) - return true; + result = result || info.dwarf_index_section_length; + #endif + return result; #elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL) // Bare metal is statically linked, so no need to ask the dynamic loader info.arm_section = (uintptr_t)(&__exidx_start); @@ -539,7 +548,7 @@ (void *)info.arm_section, (void *)info.arm_section_length); if (info.arm_section && info.arm_section_length) return true; -#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32) +#elif defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) && defined(_WIN32) HMODULE mods[1024]; HANDLE process = GetCurrentProcess(); DWORD needed; diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -135,8 +135,8 @@ }; static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, - uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, - CIE_Info *cieInfo); + uint32_t sectionLength, pint_t scanStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo); static const char *decodeFDE(A &addressSpace, pint_t fdeStart, FDE_Info *fdeInfo, CIE_Info *cieInfo); static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, @@ -211,11 +211,11 @@ /// Scan an eh_frame section to find an FDE for a pc template bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, - uint32_t sectionLength, pint_t fdeHint, + uint32_t sectionLength, pint_t scanStart, 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; + pint_t p = scanStart; + const pint_t ehSectionEnd = ehSectionStart + sectionLength; while (p < ehSectionEnd) { pint_t currentCFI = p; //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); diff --git a/libunwind/src/FrameHeaderCache.hpp b/libunwind/src/FrameHeaderCache.hpp --- a/libunwind/src/FrameHeaderCache.hpp +++ b/libunwind/src/FrameHeaderCache.hpp @@ -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.dso_length; }; UnwindInfoSections Info; CacheEntry *Next; }; diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -927,8 +927,6 @@ bool getInfoFromFdeCie(const typename CFI_Parser::FDE_Info &fdeInfo, const typename CFI_Parser::CIE_Info &cieInfo, pint_t pc, uintptr_t dso_base); - bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, - uint32_t fdeSectionOffsetHint=0); int stepWithDwarfFDE() { return DwarfInstructions::stepWithDwarf(_addressSpace, (pint_t)this->getReg(UNW_REG_IP), @@ -937,6 +935,11 @@ } #endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) + bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint = 0); +#endif + #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) bool getInfoFromCompactEncodingSection(pint_t pc, const UnwindInfoSections §s); @@ -1503,54 +1506,36 @@ } return false; } +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) template -bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, - const UnwindInfoSections §s, - uint32_t fdeSectionOffsetHint) { +bool UnwindCursor::getInfoFromDwarfSection( + pint_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint) { typename CFI_Parser::FDE_Info fdeInfo; typename CFI_Parser::CIE_Info cieInfo; - bool foundFDE = false; - bool foundInCache = false; - // If compact encoding table gave offset into dwarf section, go directly there - if (fdeSectionOffsetHint != 0) { - foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - (uint32_t)sects.dwarf_section_length, - sects.dwarf_section + fdeSectionOffsetHint, - &fdeInfo, &cieInfo); - } -#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) - if (!foundFDE && (sects.dwarf_index_section != 0)) { - foundFDE = EHHeaderParser::findFDE( - _addressSpace, pc, sects.dwarf_index_section, - (uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo); - } -#endif - if (!foundFDE) { + // If there is a compact encoding hint, then start the scan at the hint, and + // neither search nor add to the cache. + const bool useCache = (fdeSectionOffsetHint == 0); + if (useCache) { // otherwise, search cache of previously found FDEs. pint_t cachedFDE = DwarfFDECache::findFDE(sects.dso_base, pc); if (cachedFDE != 0) { - foundFDE = - CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - (uint32_t)sects.dwarf_section_length, - cachedFDE, &fdeInfo, &cieInfo); - foundInCache = foundFDE; + if (CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + static_cast(sects.dwarf_section_length), + cachedFDE, &fdeInfo, &cieInfo)) { + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) + return true; + } } } - if (!foundFDE) { - // Still not found, do full scan of __eh_frame section. - foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - (uint32_t)sects.dwarf_section_length, 0, - &fdeInfo, &cieInfo); - } - if (foundFDE) { + if (CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + static_cast(sects.dwarf_section_length), + sects.dwarf_section + fdeSectionOffsetHint, + &fdeInfo, &cieInfo)) { if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) { - // Add to cache (to make next lookup faster) if we had no hint - // and there was no index. - if (!foundInCache && (fdeSectionOffsetHint == 0)) { - #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) - if (sects.dwarf_index_section == 0) - #endif + if (useCache) { + // Add to cache (to make next lookup faster). DwarfFDECache::add(sects.dso_base, fdeInfo.pcStart, fdeInfo.pcEnd, fdeInfo.fdeStart); } @@ -1560,7 +1545,7 @@ //_LIBUNWIND_DEBUG_LOG("can't find/use FDE for pc=0x%llX", (uint64_t)pc); return false; } -#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) @@ -1899,14 +1884,15 @@ // If there is a compact unwind encoding table, look there first. if (sects.compact_unwind_section != 0) { if (this->getInfoFromCompactEncodingSection(pc, sects)) { - #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - // Found info in table, done unless encoding says to use dwarf. + #if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) + // Found info in table, done unless encoding says to use dwarf. Scan + // __eh_frame if we have a non-zero hint. Otherwise, scan the entire + // __eh_frame below. uint32_t dwarfOffset; - if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset)) { - if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) { - // found info in dwarf, done + if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset) && + (dwarfOffset != 0)) { + if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) return; - } } #endif // If unwind table has entry, but entry says there is no unwind info, @@ -1924,15 +1910,31 @@ return; #endif -#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - // If there is dwarf unwind info, look there next. - if (sects.dwarf_section != 0) { - if (this->getInfoFromDwarfSection(pc, sects)) { - // found info in dwarf, done - return; + bool hasDwarfIndex = false; +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (sects.dwarf_index_section != 0) { + hasDwarfIndex = true; + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (EHHeaderParser::findFDE( + _addressSpace, pc, sects.dwarf_index_section, + static_cast(sects.dwarf_index_section_length), &fdeInfo, + &cieInfo)) { + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) + return; } } #endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_SECTION) + // If we have both eh_frame and its index (e.g. for a bare-metal target), + // and the index doesn't have the FDE, there is no need to scan the + // eh_frame. + if ((sects.dwarf_section != 0) && !hasDwarfIndex) { + if (this->getInfoFromDwarfSection(pc, sects)) + return; + } +#endif + (void)hasDwarfIndex; #if defined(_LIBUNWIND_ARM_EHABI) // If there is ARM EHABI unwind info, look there next. diff --git a/libunwind/src/config.h b/libunwind/src/config.h --- a/libunwind/src/config.h +++ b/libunwind/src/config.h @@ -29,12 +29,14 @@ #else #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1 #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_SECTION 1 #endif #elif defined(_WIN32) #ifdef __SEH__ #define _LIBUNWIND_SUPPORT_SEH_UNWIND 1 #else #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_SECTION 1 #endif #elif defined(__BIONIC__) && defined(_LIBUNWIND_ARM_EHABI) // For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After @@ -42,7 +44,9 @@ #define _LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX 1 #elif defined(_LIBUNWIND_IS_BAREMETAL) #if !defined(_LIBUNWIND_ARM_EHABI) + // Fall back to scanning .eh_frame if the index is unavailable. #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_SECTION 1 #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 #endif #else diff --git a/libunwind/test/frameheadercache_test.pass.cpp b/libunwind/test/frameheadercache_test.pass.cpp --- a/libunwind/test/frameheadercache_test.pass.cpp +++ b/libunwind/test/frameheadercache_test.pass.cpp @@ -16,7 +16,7 @@ #include "../src/AddressSpace.hpp" #define kBaseAddr 0xFFF000 -#define kDwarfSectionLength 0xFF +#define kDsoLength 0xFF using namespace libunwind; @@ -32,7 +32,7 @@ UnwindInfoSections UIS; UIS.dso_base = kBaseAddr; - UIS.dwarf_section_length = kDwarfSectionLength; + UIS.dso_length = kDsoLength; dl_iterate_cb_data CBData; // Unused by the cache. CBData.addressSpace = nullptr; @@ -58,7 +58,7 @@ 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 + (kDsoLength * i); FHC.add(&UIS); } CBData.targetAddr = kBaseAddr;