From b16d6653c01cee37f51a98dd4092c4ef302c47a5 Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Wed, 23 Sep 2020 14:25:13 -0700 Subject: [PATCH] [libunwind] Combine dl_iterate_phdr codepaths for DWARF and EHABI dl_iterate_phdr is used to search for unwind info provided by either PT_GNU_EH_FRAME or PT_ARM_EXIDX. Most of the code between the two is the same, so combine them, and factor out what's different into checkForUnwindInfoSegment. Details: - The FrameHeaderCache can now be enabled for ARM EHABI. - findUnwindSectionsByPhdr now finds the last PT_ARM_EXIDX rather than the first. There should only be one segment. - The dso_base and text_segment_length fields of UnwindInfoSections are now needed for dl_iterate_phdr when using EHABI, to hold the low and high PC values for a cache entry. Reviewed By: compnerd, danielkiss, #libunwind, saugustine Differential Revision: https://reviews.llvm.org/D87880 --- libunwind/src/AddressSpace.hpp | 114 ++++++++---------- libunwind/test/frameheadercache_test.pass.cpp | 1 - 2 files changed, 47 insertions(+), 68 deletions(-) diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 26397c28798e..7a926ec0be55 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -114,13 +114,13 @@ namespace libunwind { /// Used by findUnwindSections() to return info about needed sections. struct UnwindInfoSections { -#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) || \ - defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - // No dso_base for SEH or ARM EHABI. +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || \ + defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \ + defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + // No dso_base for SEH. uintptr_t dso_base; #endif -#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) && \ - defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) +#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) uintptr_t text_segment_length; #endif #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) @@ -394,11 +394,6 @@ struct _LIBUNWIND_HIDDEN dl_iterate_cb_data { 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." - #endif - #if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) #include "FrameHeaderCache.hpp" @@ -421,6 +416,38 @@ static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base, return false; } +static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base, + dl_iterate_cb_data *cbdata) { +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (phdr->p_type == PT_GNU_EH_FRAME) { + EHHeaderParser::EHHeaderInfo hdrInfo; + 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; + if (EHHeaderParser::decodeEHHdr( + *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz, + hdrInfo)) { + // .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; + return true; + } + } + return false; +#elif defined(_LIBUNWIND_ARM_EHABI) + if (phdr->p_type == PT_ARM_EXIDX) { + uintptr_t exidx_start = image_base + phdr->p_vaddr; + cbdata->sects->arm_section = exidx_start; + cbdata->sects->arm_section_length = phdr->p_memsz; + return true; + } + return false; +#else +#error Need one of _LIBUNWIND_SUPPORT_DWARF_INDEX or _LIBUNWIND_ARM_EHABI +#endif +} + static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, size_t pinfo_size, void *data) { auto cbdata = static_cast(data); @@ -435,35 +462,22 @@ static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, #endif Elf_Addr image_base = calculateImageBase(pinfo); - bool found_obj = false; - bool found_hdr = false; + bool found_text = false; + bool found_unwind = false; // Third phdr is usually the executable phdr. if (pinfo->dlpi_phnum > 2) - found_obj = checkAddrInSegment(&pinfo->dlpi_phdr[2], image_base, cbdata); + found_text = checkAddrInSegment(&pinfo->dlpi_phdr[2], image_base, cbdata); - // PT_GNU_EH_FRAME is usually near the end. Iterate backward. We already know - // that there is one or more phdrs. + // PT_GNU_EH_FRAME and PT_ARM_EXIDX are usually near the end. Iterate + // backward. We already know that there is one or more phdrs. 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; - 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) { - // .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); - } - if (found_obj && found_hdr) { + if (!found_unwind && checkForUnwindInfoSegment(phdr, image_base, cbdata)) + found_unwind = true; + else if (!found_text && checkAddrInSegment(phdr, image_base, cbdata)) + found_text = true; + if (found_text && found_unwind) { #if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) TheFrameHeaderCache.add(cbdata->sects); #endif @@ -473,40 +487,6 @@ static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, return 0; } -#elif defined(_LIBUNWIND_ARM_EHABI) - -static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, size_t, - void *data) { - auto *cbdata = static_cast(data); - bool found_obj = false; - bool found_hdr = false; - - assert(cbdata); - assert(cbdata->sects); - - if (cbdata->targetAddr < pinfo->dlpi_addr) - return 0; - - Elf_Addr image_base = calculateImageBase(pinfo); - - for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) { - const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i]; - if (phdr->p_type == PT_LOAD) { - uintptr_t begin = image_base + phdr->p_vaddr; - uintptr_t end = begin + phdr->p_memsz; - if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) - found_obj = true; - } else if (phdr->p_type == PT_ARM_EXIDX) { - uintptr_t exidx_start = image_base + phdr->p_vaddr; - cbdata->sects->arm_section = exidx_start; - cbdata->sects->arm_section_length = phdr->p_memsz; - found_hdr = true; - } - } - return found_obj && found_hdr; -} - -#endif #endif // defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) diff --git a/libunwind/test/frameheadercache_test.pass.cpp b/libunwind/test/frameheadercache_test.pass.cpp index 15c7c67c58ea..7f14830fa30d 100644 --- a/libunwind/test/frameheadercache_test.pass.cpp +++ b/libunwind/test/frameheadercache_test.pass.cpp @@ -5,7 +5,6 @@ // Only run this test under supported configurations. #if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) && \ - defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) && \ defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) #include