summaryrefslogtreecommitdiff
path: root/linker/linker.cpp
diff options
context:
space:
mode:
authorPeter Collingbourne <pcc@google.com>2019-03-01 13:12:49 -0800
committerPeter Collingbourne <pcc@google.com>2019-03-08 18:34:34 -0800
commitb39cb3c31db1ca67faadc60ba9c8338168da0b36 (patch)
tree36d5198635cfbb8248b6bd775e6748f0d1a41f7b /linker/linker.cpp
parent0771b752f1b955e7ded6ccbbf825b0ae93439eb5 (diff)
linker: Handle libraries with disjoint mappings correctly.
It's possible and sometimes beneficial for a library to have disjoint mappings and for other libraries to be mapped into the gap between the mappings using ANDROID_DLEXT_RESERVED_ADDRESS. See for example the proposal for partitioning in lld [1]. Because the find_containing_library and do_dl_unwind_find_exidx functions use a simple bounds check to figure out whether a pointer belongs to a library they will, given a pointer into a library mapped into the gap of a library with disjoint mappings, return a pointer to the soinfo for the outer library instead of the inner one, because the outer library will appear before the inner one in the solist. From a user perspective this means that we won't be able to unwind the inner library's frames on 32-bit ARM with libgcc, dladdr() will return information for the outer library given a pointer to the inner one and dlopen() et al will use the linker namespace of the outer library when called from the inner one (although they will usually be the same). To make this work correctly, make it so that once find_containing_library sees a match for the bounds check, it examines the library's PT_LOADs to make sure that there is a mapping for the given address. This is similar to how libgcc and libunwind_llvm already handle finding the PT_GNU_EH_FRAME on non-ARM32 platforms [2,3]. do_dl_unwind_find_exidx is reimplemented in terms of find_containing_library. [1] http://lists.llvm.org/pipermail/llvm-dev/2019-February/130583.html [2] https://github.com/llvm/llvm-project/blob/e739ac0e255597d818c907223034ddf3bc18a593/libunwind/src/AddressSpace.hpp#L523 [3] https://android.googlesource.com/toolchain/gcc/+/master/gcc-4.9/libgcc/unwind-dw2-fde-dip.c#294 Test: /data/nativetest{,64}/bionic-unit-tests/bionic-unit-tests on walleye-userdebug Change-Id: I368fe6ad3c470b3dff80f7d9b04253566d63a7d2
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r--linker/linker.cpp22
1 files changed, 15 insertions, 7 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 61cd818dd..56e85e4ed 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -415,11 +415,9 @@ static bool realpath_fd(int fd, std::string* realpath) {
//
// Intended to be called by libc's __gnu_Unwind_Find_exidx().
_Unwind_Ptr do_dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
- for (soinfo* si = solist_get_head(); si != 0; si = si->next) {
- if ((pc >= si->base) && (pc < (si->base + si->size))) {
- *pcount = si->ARM_exidx_count;
- return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx);
- }
+ if (soinfo* si = find_containing_library(reinterpret_cast<void*>(pc))) {
+ *pcount = si->ARM_exidx_count;
+ return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx);
}
*pcount = 0;
return 0;
@@ -938,8 +936,18 @@ static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns,
soinfo* find_containing_library(const void* p) {
ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p);
for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) {
- if (address >= si->base && address - si->base < si->size) {
- return si;
+ if (address < si->base || address - si->base >= si->size) {
+ continue;
+ }
+ ElfW(Addr) vaddr = address - si->load_bias;
+ for (size_t i = 0; i != si->phnum; ++i) {
+ const ElfW(Phdr)* phdr = &si->phdr[i];
+ if (phdr->p_type != PT_LOAD) {
+ continue;
+ }
+ if (vaddr >= phdr->p_vaddr && vaddr < phdr->p_vaddr + phdr->p_memsz) {
+ return si;
+ }
}
}
return nullptr;