Index: src/DwarfParser.hpp =================================================================== --- src/DwarfParser.hpp +++ src/DwarfParser.hpp @@ -96,6 +96,7 @@ PrologInfo info; }; + static bool isCIE(A &addressSpace, pint_t start); static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, uint32_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, CIE_Info *cieInfo); @@ -115,6 +116,23 @@ PrologInfo *results); }; +/// Check whether this is an FDE or a CIE +template +bool CFI_Parser::isCIE(A &addressSpace, pint_t start) { + pint_t p = start; + pint_t cfiLength = (pint_t)addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return false; // end marker + uint32_t id = addressSpace.get32(p); + return id == 0; // This is zero for CIEs and nonzero for FDEs. +} + /// Parse a FDE into a CIE_Info and an FDE_Info template const char *CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, Index: src/UnwindCursor.hpp =================================================================== --- src/UnwindCursor.hpp +++ src/UnwindCursor.hpp @@ -44,10 +44,12 @@ public: static pint_t findFDE(pint_t mh, pint_t pc); static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void addSection(pint_t section_start); static void removeAllIn(pint_t mh); static void iterateCacheEntries(void (*func)(unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); + template static void iterateSections(Func func); private: @@ -70,6 +72,11 @@ static entry *_bufferUsed; static entry *_bufferEnd; static entry _initialBuffer[64]; + + static pint_t *_sections; + static pint_t *_sectionsUsed; + static pint_t *_sectionsEnd; + static pint_t _initialSectionsBuffer[64]; }; template @@ -88,6 +95,21 @@ typename DwarfFDECache::entry DwarfFDECache::_initialBuffer[64]; template +typename DwarfFDECache::pint_t * +DwarfFDECache::_sections = _initialSectionsBuffer; + +template +typename DwarfFDECache::pint_t * +DwarfFDECache::_sectionsUsed = _initialSectionsBuffer; + +template +typename DwarfFDECache::pint_t * +DwarfFDECache::_sectionsEnd = &_initialSectionsBuffer[64]; + +template +typename DwarfFDECache::pint_t DwarfFDECache::_initialSectionsBuffer[64]; + +template RWMutex DwarfFDECache::_lock; #ifdef __APPLE__ @@ -144,6 +166,28 @@ } template +void DwarfFDECache::addSection(pint_t section_start) { +#if !defined(_LIBUNWIND_NO_HEAP) + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + if (_sectionsUsed >= _sectionsEnd) { + size_t oldSize = (size_t)(_sectionsEnd - _sections); + size_t newSize = oldSize * 4; + // Can't use operator new (we are below it). + pint_t *newSections = (pint_t *)malloc(newSize * sizeof(pint_t)); + memcpy(newSections, _sections, oldSize * sizeof(pint_t)); + if (_sections != _initialSectionsBuffer) + free(_sections); + _sections = newSections; + _sectionsUsed = &newSections[oldSize]; + _sectionsEnd = &newSections[newSize]; + } + *_sectionsUsed = section_start; + ++_sectionsUsed; + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif +} + +template void DwarfFDECache::removeAllIn(pint_t mh) { _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); entry *d = _buffer; @@ -155,6 +199,15 @@ } } _bufferUsed = d; + pint_t *sectionsDest = _sections; + for (const pint_t *s = _sections; s < _sectionsUsed; ++s) { + if (*s != mh) { + if (sectionsDest != s) + *sectionsDest = *s; + ++sectionsDest; + } + } + _sectionsUsed = sectionsDest; _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); } @@ -174,6 +227,16 @@ } _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); } + +template +template +void DwarfFDECache::iterateSections(Func func) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + for (pint_t *p = _sections; p < _sectionsUsed; ++p) { + func(*p); + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) @@ -1352,6 +1415,28 @@ } } + bool foundSection = false; + DwarfFDECache::iterateSections([&](pint_t section) { + if (foundSection) + return; + sects.dso_base = section; + sects.dwarf_section = section; + sects.dwarf_section_length = ~(pint_t)0 - section; + if (sects.dwarf_section_length > ~(uint32_t)0) + sects.dwarf_section_length = ~(uint32_t)0; + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + // Only parse and see if it is the right frame here, + // to avoid recursive self-locking. + foundSection = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + (uint32_t)sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); + }); + // This call will lock the cache when adding a new match; only do this + // outside of the iteration (which also keeps a lock). + if (foundSection && this->getInfoFromDwarfSection(pc, sects)) + return; + // Lastly, ask AddressSpace object about platform specific ways to locate // other FDEs. pint_t fde; Index: src/libunwind.cpp =================================================================== --- src/libunwind.cpp +++ src/libunwind.cpp @@ -326,20 +326,27 @@ /// IPI: for __register_frame() void _unw_add_dynamic_fde(unw_word_t fde) { - CFI_Parser::FDE_Info fdeInfo; - CFI_Parser::CIE_Info cieInfo; - const char *message = CFI_Parser::decodeFDE( - LocalAddressSpace::sThisAddressSpace, - (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo); - if (message == NULL) { - // dynamically registered FDEs don't have a mach_header group they are in. - // Use fde as mh_group - unw_word_t mh_group = fdeInfo.fdeStart; - DwarfFDECache::add((LocalAddressSpace::pint_t)mh_group, - fdeInfo.pcStart, fdeInfo.pcEnd, - fdeInfo.fdeStart); + if (CFI_Parser::isCIE(LocalAddressSpace::sThisAddressSpace, + (LocalAddressSpace::pint_t) fde)) { + // This wasn't a plain FDE, but it started with a CIE - register a full + // .eh_frame section. + DwarfFDECache::addSection((LocalAddressSpace::pint_t)fde); } else { - _LIBUNWIND_DEBUG_LOG("_unw_add_dynamic_fde: bad fde: %s", message); + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *message = CFI_Parser::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo); + if (message == NULL) { + // dynamically registered FDEs don't have a mach_header group they are in. + // Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache::add((LocalAddressSpace::pint_t)mh_group, + fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } else { + _LIBUNWIND_DEBUG_LOG("_unw_add_dynamic_fde: bad fde: %s", message); + } } }