diff options
author | Ryan Prichard <rprichard@google.com> | 2019-10-16 02:38:47 +0000 |
---|---|---|
committer | Ryan Prichard <rprichard@google.com> | 2019-10-16 02:38:47 +0000 |
commit | 4454bbf4cac4083a180edc71f15aeba9697265f3 (patch) | |
tree | f2af48328a8eb06d3ce6fba0a8a04c905efb632e | |
parent | b956c52c878776391430443706d884c124610c41 (diff) |
[libunwind][Android] Improve workaround for PIE zero-dlpi_addr bug
Summary:
The workaround added in https://reviews.llvm.org/rL299575 appears to be
working around a bug in Android JB 4.1.x and 4.2.x (API 16 and 17).
Starting in API 16, Android added support for PIE binaries, but the
dynamic linker failed to initialize dlpi_addr to the address that the
executable was loaded at. The bug was fixed in Android JB 4.3.x (API 18).
Improve the true load bias calculation:
* The code was assuming that the first segment would be the PT_PHDR
segment. I think it's better to be explicit and search for PT_PHDR. (It
will be almost as fast in practice.)
* It's more correct to use p_vaddr rather than p_offset. If a PIE
executable is linked with a non-zero image base (e.g. lld's
-Wl,--image-base=xxxx), then we must use p_vaddr here.
The "phdr->p_vaddr < image_base" condition seems unnecessary and maybe
slightly wrong. If the OS were to load a binary at an address smaller than
a vaddr in the binary, we would still want to do this workaround.
The workaround is safe when the linker bug isn't present, because it
should calculate an image_base equal to dlpi_addr. Note that with API 21
and up, this workaround should never activate for dynamically-linked
objects, because non-PIE executables aren't allowed.
Consolidate the fix into a single block of code that calculates the true
image base, and make it clear that the fix no longer applies after API 18.
See https://github.com/android/ndk/issues/505 for details.
Reviewers: mclow.lists, srhines, danalbert, compnerd
Reviewed By: compnerd
Subscribers: srhines, krytarowski, christof, libcxx-commits
Tags: #libc
Differential Revision: https://reviews.llvm.org/D68971
git-svn-id: https://llvm.org/svn/llvm-project/libunwind/trunk@374969 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | src/AddressSpace.hpp | 50 |
1 files changed, 27 insertions, 23 deletions
diff --git a/src/AddressSpace.hpp b/src/AddressSpace.hpp index bf57fda..6c13408 100644 --- a/src/AddressSpace.hpp +++ b/src/AddressSpace.hpp @@ -497,32 +497,40 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, #if !defined(Elf_Phdr) typedef ElfW(Phdr) Elf_Phdr; #endif +#if !defined(Elf_Addr) + typedef ElfW(Addr) Elf_Addr; +#endif + + Elf_Addr image_base = pinfo->dlpi_addr; + +#if defined(__ANDROID__) && __ANDROID_API__ < 18 + if (image_base == 0) { + // Normally, an image base of 0 indicates a non-PIE executable. On + // versions of Android prior to API 18, the dynamic linker reported a + // dlpi_addr of 0 for PIE executables. Compute the true image base + // using the PT_PHDR segment. + // See https://github.com/android/ndk/issues/505. + for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i]; + if (phdr->p_type == PT_PHDR) { + image_base = reinterpret_cast<Elf_Addr>(pinfo->dlpi_phdr) - + phdr->p_vaddr; + break; + } + } + } +#endif #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 size_t object_length; -#if defined(__ANDROID__) -#if !defined(Elf_Addr) - typedef ElfW(Addr) Elf_Addr; -#endif - Elf_Addr image_base = - pinfo->dlpi_phnum - ? reinterpret_cast<Elf_Addr>(pinfo->dlpi_phdr) - - reinterpret_cast<const Elf_Phdr *>(pinfo->dlpi_phdr) - ->p_offset - : 0; -#endif 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 = pinfo->dlpi_addr + phdr->p_vaddr; -#if defined(__ANDROID__) - if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base) - begin = begin + image_base; -#endif + uintptr_t begin = image_base + phdr->p_vaddr; uintptr_t end = begin + phdr->p_memsz; if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) { cbdata->sects->dso_base = begin; @@ -531,11 +539,7 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, } } else if (phdr->p_type == PT_GNU_EH_FRAME) { EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo; - uintptr_t eh_frame_hdr_start = pinfo->dlpi_addr + phdr->p_vaddr; -#if defined(__ANDROID__) - if (pinfo->dlpi_addr == 0 && phdr->p_vaddr < image_base) - eh_frame_hdr_start = eh_frame_hdr_start + image_base; -#endif + 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<LocalAddressSpace>::decodeEHHdr( @@ -556,12 +560,12 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, 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 = pinfo->dlpi_addr + phdr->p_vaddr; + 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 = pinfo->dlpi_addr + phdr->p_vaddr; + 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; |