diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -601,6 +601,62 @@ if (info.arm_section && info.arm_section_length) return true; #elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + // Use DLFO_STRUCT_HAS_EH_DBASE to determine the existence of + // `_dl_find_object`. Use _LIBUNWIND_SUPPORT_DWARF_INDEX, because libunwind + // support for _dl_find_object on other unwind formats is not implemented, + // yet. +#if defined(DLFO_STRUCT_HAS_EH_DBASE) & defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + // We expect to run on a platform which does not use a base address for + // exception information. +#if DLFO_STRUCT_HAS_EH_DBASE +#error dlfo_eh_dbase is not supported for DWARF-based unwinding +#endif + // We expect `_dl_find_object` to return the the PT_GNU_EH_FRAME section. +#if DLFO_EH_SEGMENT_TYPE != PT_GNU_EH_FRAME +#error _dl_find_object` retrieves an unexpected section type +#endif + // We look-up `dl_find_object` dynamically at runtime to ensure backwards + // compatibility with earlier version of glibc not yet providing it. On older + // systems, we gracefully fallback to `dl_iterate_phdr`. Cache the pointer + // so we only look it up once. Do manual lock to avoid _cxa_guard_acquire. + static decltype(_dl_find_object) *dlFindObject; + static bool dlFindObjectChecked = false; + if (!dlFindObjectChecked) { + dlFindObject = reinterpret_cast( + dlsym(RTLD_DEFAULT, "_dl_find_object")); + dlFindObjectChecked = true; + } + // Try to find the unwind info using `dl_find_object` + dl_find_object findResult; + if (dlFindObject && dlFindObject((void *)targetAddr, &findResult) == 0) { + if (findResult.dlfo_eh_frame == nullptr) { + // Found an entry for `targetAddr`, but there is no unwind info. + return false; + } + info.dso_base = reinterpret_cast(findResult.dlfo_map_start); + info.text_segment_length = static_cast( + (char *)findResult.dlfo_map_end - (char *)findResult.dlfo_map_start); + + // `_dl_find_object` gives us the start of the PT_GNU_EH_FRAME section. + info.dwarf_index_section = + reinterpret_cast(findResult.dlfo_eh_frame); + // `_dl_find_object` does not give us the size of the PT_GNU_EH_FRAME + // section. Setting length to `SIZE_MAX` effectively disables all range + // checks. + info.dwarf_index_section_length = SIZE_MAX; + EHHeaderParser::EHHeaderInfo hdrInfo; + if (!EHHeaderParser::decodeEHHdr( + *this, info.dwarf_index_section, info.dwarf_index_section_length, + hdrInfo)) { + return false; + } + // .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. + info.dwarf_section = hdrInfo.eh_frame_ptr; + info.dwarf_section_length = SIZE_MAX; + return true; + } +#endif dl_iterate_cb_data cb_data = {this, &info, targetAddr}; int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data); return static_cast(found);