diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-04-22 10:33:33 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-04-22 10:33:34 +0000 |
commit | c363e5dd0a15aac9036b453f655a3a0ae5cd32cd (patch) | |
tree | 385627eccdd2c853f1f571902d27a55ebfb23675 | |
parent | 16f6429c38c24236418cf127a4c11c5d8a2fd47b (diff) | |
parent | 12bbb9164578b6512b8b07a3fb093858244b7c7b (diff) | |
download | bionic-c363e5dd0a15aac9036b453f655a3a0ae5cd32cd.zip bionic-c363e5dd0a15aac9036b453f655a3a0ae5cd32cd.tar.gz bionic-c363e5dd0a15aac9036b453f655a3a0ae5cd32cd.tar.bz2 |
Merge "Support loading libraries to a reserved address."
-rw-r--r-- | libc/include/android/dlext.h | 17 | ||||
-rwxr-xr-x | linker/linker.cpp | 18 | ||||
-rw-r--r-- | linker/linker_phdr.cpp | 38 | ||||
-rw-r--r-- | linker/linker_phdr.h | 4 | ||||
-rw-r--r-- | tests/Android.mk | 13 | ||||
-rw-r--r-- | tests/dlext_test.cpp | 136 | ||||
-rw-r--r-- | tests/dlext_test_library.cpp | 43 |
7 files changed, 249 insertions, 20 deletions
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 5de39c6..3ae0965 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -24,12 +24,27 @@ __BEGIN_DECLS /* bitfield definitions for android_dlextinfo.flags */ enum { + /* When set, the reserved_addr and reserved_size fields must point to an + * already-reserved region of address space which will be used to load the + * library if it fits. If the reserved region is not large enough, the load + * will fail. + */ + ANDROID_DLEXT_RESERVED_ADDRESS = 0x1, + + /* As DLEXT_RESERVED_ADDRESS, but if the reserved region is not large enough, + * the linker will choose an available address instead. + */ + ANDROID_DLEXT_RESERVED_ADDRESS_HINT = 0x2, + /* Mask of valid bits */ - ANDROID_DLEXT_VALID_FLAG_BITS = 0, + ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS | + ANDROID_DLEXT_RESERVED_ADDRESS_HINT, }; typedef struct { int flags; + void* reserved_addr; + size_t reserved_size; } android_dlextinfo; extern void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo); diff --git a/linker/linker.cpp b/linker/linker.cpp index 2e145b5..bfb0c1d 100755 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -690,7 +690,7 @@ static int open_library(const char* name) { return fd; } -static soinfo* load_library(const char* name) { +static soinfo* load_library(const char* name, const android_dlextinfo* extinfo) { // Open the file. int fd = open_library(name); if (fd == -1) { @@ -700,7 +700,7 @@ static soinfo* load_library(const char* name) { // Read the ELF header and load the segments. ElfReader elf_reader(name, fd); - if (!elf_reader.Load()) { + if (!elf_reader.Load(extinfo)) { return NULL; } @@ -735,7 +735,7 @@ static soinfo *find_loaded_library(const char* name) { return NULL; } -static soinfo* find_library_internal(const char* name) { +static soinfo* find_library_internal(const char* name, const android_dlextinfo* extinfo) { if (name == NULL) { return somain; } @@ -750,7 +750,7 @@ static soinfo* find_library_internal(const char* name) { } TRACE("[ '%s' has not been loaded yet. Locating...]", name); - si = load_library(name); + si = load_library(name, extinfo); if (si == NULL) { return NULL; } @@ -769,8 +769,8 @@ static soinfo* find_library_internal(const char* name) { return si; } -static soinfo* find_library(const char* name) { - soinfo* si = find_library_internal(name); +static soinfo* find_library(const char* name, const android_dlextinfo* extinfo) { + soinfo* si = find_library_internal(name, extinfo); if (si != NULL) { si->ref_count++; } @@ -821,7 +821,7 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) return NULL; } set_soinfo_pool_protection(PROT_READ | PROT_WRITE); - soinfo* si = find_library(name); + soinfo* si = find_library(name, extinfo); if (si != NULL) { si->CallConstructors(); } @@ -1803,7 +1803,7 @@ static bool soinfo_link_image(soinfo* si) { memset(gLdPreloads, 0, sizeof(gLdPreloads)); size_t preload_count = 0; for (size_t i = 0; gLdPreloadNames[i] != NULL; i++) { - soinfo* lsi = find_library(gLdPreloadNames[i]); + soinfo* lsi = find_library(gLdPreloadNames[i], NULL); if (lsi != NULL) { gLdPreloads[preload_count++] = lsi; } else { @@ -1821,7 +1821,7 @@ static bool soinfo_link_image(soinfo* si) { if (d->d_tag == DT_NEEDED) { const char* library_name = si->strtab + d->d_un.d_val; DEBUG("%s needs %s", si->name, library_name); - soinfo* lsi = find_library(library_name); + soinfo* lsi = find_library(library_name, NULL); if (lsi == NULL) { strlcpy(tmp_err_buf, linker_get_error_buffer(), sizeof(tmp_err_buf)); DL_ERR("could not load library \"%s\" needed by \"%s\"; caused by %s", diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 7c5d3f6..7e97aa9 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -132,11 +132,11 @@ ElfReader::~ElfReader() { } } -bool ElfReader::Load() { +bool ElfReader::Load(const android_dlextinfo* extinfo) { return ReadElfHeader() && VerifyElfHeader() && ReadProgramHeader() && - ReserveAddressSpace() && + ReserveAddressSpace(extinfo) && LoadSegments() && FindPhdr(); } @@ -291,7 +291,7 @@ size_t phdr_table_get_load_size(const ElfW(Phdr)* phdr_table, size_t phdr_count, // Reserve a virtual address range big enough to hold all loadable // segments of a program header table. This is done by creating a // private anonymous mmap() with PROT_NONE. -bool ElfReader::ReserveAddressSpace() { +bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { ElfW(Addr) min_vaddr; load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); if (load_size_ == 0) { @@ -300,11 +300,33 @@ bool ElfReader::ReserveAddressSpace() { } uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr); - int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; - void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0); - if (start == MAP_FAILED) { - DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); - return false; + void* start; + size_t reserved_size = 0; + bool reserved_hint = true; + + if (extinfo != NULL) { + if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) { + reserved_size = extinfo->reserved_size; + reserved_hint = false; + } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) { + reserved_size = extinfo->reserved_size; + } + } + + if (load_size_ > reserved_size) { + if (!reserved_hint) { + DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"", + reserved_size - load_size_, load_size_, name_); + return false; + } + int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; + start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0); + if (start == MAP_FAILED) { + DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); + return false; + } + } else { + start = extinfo->reserved_addr; } load_start_ = start; diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 6b72caf..430c6ec 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -42,7 +42,7 @@ class ElfReader { ElfReader(const char* name, int fd); ~ElfReader(); - bool Load(); + bool Load(const android_dlextinfo* extinfo); size_t phdr_count() { return phdr_num_; } ElfW(Addr) load_start() { return reinterpret_cast<ElfW(Addr)>(load_start_); } @@ -54,7 +54,7 @@ class ElfReader { bool ReadElfHeader(); bool VerifyElfHeader(); bool ReadProgramHeader(); - bool ReserveAddressSpace(); + bool ReserveAddressSpace(const android_dlextinfo* extinfo); bool LoadSegments(); bool FindPhdr(); bool CheckPhdr(ElfW(Addr)); diff --git a/tests/Android.mk b/tests/Android.mk index 723d7cf..d2bcce8 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -190,6 +190,18 @@ include $(LOCAL_PATH)/Android.build.mk endif # ----------------------------------------------------------------------------- +# Library used by dlext tests. +# ----------------------------------------------------------------------------- +libdlext_test_src_files := \ + dlext_test_library.cpp \ + +module := libdlext_test +module_tag := optional +build_type := target +build_target := SHARED_LIBRARY +include $(LOCAL_PATH)/Android.build.mk + +# ----------------------------------------------------------------------------- # Tests for the device using bionic's .so. Run with: # adb shell /data/nativetest/bionic-unit-tests/bionic-unit-tests # ----------------------------------------------------------------------------- @@ -197,6 +209,7 @@ bionic-unit-tests_whole_static_libraries := \ libBionicTests \ bionic-unit-tests_src_files := \ + dlext_test.cpp \ dlfcn_test.cpp \ bionic-unit-tests_ldflags := \ diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp new file mode 100644 index 0000000..299b408 --- /dev/null +++ b/tests/dlext_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <dlfcn.h> +#include <android/dlext.h> +#include <sys/mman.h> + + +#define ASSERT_DL_NOTNULL(ptr) \ + ASSERT_TRUE(ptr != NULL) << "dlerror: " << dlerror() + +#define ASSERT_DL_ZERO(i) \ + ASSERT_EQ(0, i) << "dlerror: " << dlerror() + + +typedef int (*fn)(void); +#define LIBNAME "libdlext_test.so" +#define LIBSIZE 1024*1024 // how much address space to reserve for it + + +class DlExtTest : public ::testing::Test { +protected: + virtual void SetUp() { + handle_ = NULL; + // verify that we don't have the library loaded already + ASSERT_EQ(NULL, dlsym(RTLD_DEFAULT, "getRandomNumber")); + // call dlerror() to swallow the error, and check it was the one we wanted + ASSERT_STREQ("undefined symbol: getRandomNumber", dlerror()); + } + + virtual void TearDown() { + if (handle_ != NULL) { + ASSERT_DL_ZERO(dlclose(handle_)); + } + } + + void* handle_; +}; + +TEST_F(DlExtTest, ExtInfoNull) { + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, NULL); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_EQ(4, f()); +} + +TEST_F(DlExtTest, ExtInfoNoFlags) { + android_dlextinfo extinfo; + extinfo.flags = 0; + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_EQ(4, f()); +} + +TEST_F(DlExtTest, Reserved) { + void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; + extinfo.reserved_addr = start; + extinfo.reserved_size = LIBSIZE; + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_GE(f, start); + EXPECT_LT(reinterpret_cast<void*>(f), + reinterpret_cast<char*>(start) + LIBSIZE); + EXPECT_EQ(4, f()); +} + +TEST_F(DlExtTest, ReservedTooSmall) { + void* start = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS; + extinfo.reserved_addr = start; + extinfo.reserved_size = PAGE_SIZE; + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + EXPECT_EQ(NULL, handle_); +} + +TEST_F(DlExtTest, ReservedHint) { + void* start = mmap(NULL, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; + extinfo.reserved_addr = start; + extinfo.reserved_size = LIBSIZE; + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_GE(f, start); + EXPECT_LT(reinterpret_cast<void*>(f), + reinterpret_cast<char*>(start) + LIBSIZE); + EXPECT_EQ(4, f()); +} + +TEST_F(DlExtTest, ReservedHintTooSmall) { + void* start = mmap(NULL, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + ASSERT_TRUE(start != MAP_FAILED); + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS_HINT; + extinfo.reserved_addr = start; + extinfo.reserved_size = PAGE_SIZE; + handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo); + ASSERT_DL_NOTNULL(handle_); + fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber")); + ASSERT_DL_NOTNULL(f); + EXPECT_TRUE(f < start || (reinterpret_cast<void*>(f) >= + reinterpret_cast<char*>(start) + PAGE_SIZE)); + EXPECT_EQ(4, f()); +} diff --git a/tests/dlext_test_library.cpp b/tests/dlext_test_library.cpp new file mode 100644 index 0000000..5c04329 --- /dev/null +++ b/tests/dlext_test_library.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class A { +public: + virtual int getRandomNumber() { + return 4; // chosen by fair dice roll. + // guaranteed to be random. + } + + virtual ~A() {} +}; + +A a; + +// nested macros to make it easy to define a large amount of read-only data +// which will require relocation. +#define A_16 &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, &a, +#define A_128 A_16 A_16 A_16 A_16 A_16 A_16 A_16 A_16 +#define A_1024 A_128 A_128 A_128 A_128 A_128 A_128 A_128 A_128 + +extern "C" A* const lots_of_relro[] = { + A_1024 A_1024 A_1024 A_1024 A_1024 A_1024 A_1024 A_1024 +}; + +extern "C" int getRandomNumber() { + // access the relro section (twice, in fact, once for the pointer, and once + // for the vtable of A) to check it's actually there. + return lots_of_relro[0]->getRandomNumber(); +} |