From 126af757c6d6a6447c19236df8d98ba07f21996f Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 7 Oct 2015 16:34:20 -0700 Subject: [PATCH] Implement load at fixed address feature Bug: http://b/24683631 Change-Id: I3a39ab526c8f9e213339b60e135e5459d0f41381 --- libc/include/android/dlext.h | 21 +++++++++++++++++++- linker/linker.cpp | 8 ++++++++ linker/linker_phdr.cpp | 14 ++++++++++++-- tests/dlext_test.cpp | 37 ++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 40f610f29..4af5de311 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -80,6 +80,24 @@ enum { */ ANDROID_DLEXT_FORCE_FIXED_VADDR = 0x80, + /* Instructs dlopen to load the library at the address specified by reserved_addr. + * + * The difference between ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS and ANDROID_DLEXT_RESERVED_ADDRESS + * is that for ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS the linker reserves memory at reserved_addr + * whereas for ANDROID_DLEXT_RESERVED_ADDRESS the linker relies on the caller to reserve the memory. + * + * This flag can be used with ANDROID_DLEXT_FORCE_FIXED_VADDR; when ANDROID_DLEXT_FORCE_FIXED_VADDR + * is set and load_bias is not 0 (load_bias is min(p_vaddr) of PT_LOAD segments) this flag is ignored. + * This is implemented this way because the linker has to pick one address over the other and this + * way is more convenient for art. Note that ANDROID_DLEXT_FORCE_FIXED_VADDR does not generate + * an error when min(p_vaddr) is 0. + * + * Cannot be used with ANDROID_DLEXT_RESERVED_ADDRESS or ANDROID_DLEXT_RESERVED_ADDRESS_HINT. + * + * This flag is for ART internal use only. + */ + ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS = 0x100, + /* Mask of valid bits */ ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT | @@ -88,7 +106,8 @@ enum { ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET | ANDROID_DLEXT_FORCE_LOAD | - ANDROID_DLEXT_FORCE_FIXED_VADDR, + ANDROID_DLEXT_FORCE_FIXED_VADDR | + ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS, }; typedef struct { diff --git a/linker/linker.cpp b/linker/linker.cpp index 926fc9b91..9f0b559b9 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1995,12 +1995,20 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags); return nullptr; } + if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) { DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without " "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags); return nullptr; } + + if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 && + (extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) { + DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not " + "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT"); + return nullptr; + } } ProtectedDataGuard guard; diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index a26187a4d..347983eb9 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -386,8 +386,9 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { void* start; size_t reserved_size = 0; bool reserved_hint = true; + bool strict_hint = false; // Assume position independent executable by default. - uint8_t* mmap_hint = nullptr; + void* mmap_hint = nullptr; if (extinfo != nullptr) { if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) { @@ -397,8 +398,11 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { reserved_size = extinfo->reserved_size; } - if ((extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) { + if (addr != nullptr && (extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) { mmap_hint = addr; + } else if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0) { + mmap_hint = extinfo->reserved_addr; + strict_hint = true; } } @@ -414,6 +418,12 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str()); return false; } + if (strict_hint && (start != mmap_hint)) { + munmap(start, load_size_); + DL_ERR("couldn't reserve %zd bytes of address space at %p for \"%s\"", + load_size_, mmap_hint, name_.c_str()); + return false; + } } else { start = extinfo->reserved_addr; } diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index d5a5e562b..baf7eb595 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -343,6 +343,43 @@ TEST_F(DlExtTest, ReservedHintTooSmall) { EXPECT_EQ(4, f()); } +TEST_F(DlExtTest, LoadAtFixedAddress) { + void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + munmap(start, LIBSIZE); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS; + extinfo.reserved_addr = start; + + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_GE(reinterpret_cast(f), start); + EXPECT_LT(reinterpret_cast(f), reinterpret_cast(start) + LIBSIZE); + + EXPECT_EQ(4, f()); +} + +TEST_F(DlExtTest, LoadAtFixedAddressTooSmall) { + void* start = mmap(nullptr, LIBSIZE + PAGE_SIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + munmap(start, LIBSIZE + PAGE_SIZE); + void* new_addr = mmap(reinterpret_cast(start) + PAGE_SIZE, LIBSIZE, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_TRUE(new_addr != MAP_FAILED); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS; + extinfo.reserved_addr = start; + + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle_ == nullptr); +} + class DlExtRelroSharingTest : public DlExtTest { protected: virtual void SetUp() {