aboutsummaryrefslogtreecommitdiffstats
path: root/linker
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2018-10-17 15:59:38 -0400
committerTorne (Richard Coles) <torne@google.com>2019-02-20 17:29:36 -0500
commitefbe9a5eefce28db6d1dbba18c212eb45915cf78 (patch)
tree0e35795b7b52bc65af89143af0662b5b0f4ce3e1 /linker
parent73ee7a6320024c8f390d954a01b7a067c919bfc4 (diff)
downloadandroid_bionic-efbe9a5eefce28db6d1dbba18c212eb45915cf78.tar.gz
android_bionic-efbe9a5eefce28db6d1dbba18c212eb45915cf78.tar.bz2
android_bionic-efbe9a5eefce28db6d1dbba18c212eb45915cf78.zip
linker: allow using reserved space for multiple libraries.
Introduce a new flag ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE which instructs the linker to use the reserved address space to load all of the newly-loaded libraries required by a dlopen() call instead of only the main library. They will be loaded consecutively into that region if they fit. The RELRO sections of all the loaded libraries will also be considered for reading/writing shared RELRO data. This will allow the WebView implementation to potentially consist of more than one .so file while still benefiting from the RELRO sharing optimisation, which would otherwise only apply to the "root" .so file. Test: bionic-unit-tests (existing and newly added) Bug: 110790153 Change-Id: I61da775c29fd5017d9a1e2b6b3757c3d20a355b3
Diffstat (limited to 'linker')
-rw-r--r--linker/linker.cpp38
-rw-r--r--linker/linker.h6
-rw-r--r--linker/linker_main.cpp9
-rw-r--r--linker/linker_phdr.cpp44
-rw-r--r--linker/linker_phdr.h6
-rw-r--r--linker/linker_soinfo.h2
6 files changed, 64 insertions, 41 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 428dd25de..415b36373 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -658,9 +658,9 @@ class LoadTask {
return elf_reader.Read(realpath, fd_, file_offset_, file_size);
}
- bool load() {
+ bool load(address_space_params* address_space) {
ElfReader& elf_reader = get_elf_reader();
- if (!elf_reader.Load(extinfo_)) {
+ if (!elf_reader.Load(address_space)) {
return false;
}
@@ -1651,10 +1651,34 @@ bool find_libraries(android_namespace_t* ns,
load_list.push_back(task);
}
}
- shuffle(&load_list);
+ bool reserved_address_recursive = false;
+ if (extinfo) {
+ reserved_address_recursive = extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_RECURSIVE;
+ }
+ if (!reserved_address_recursive) {
+ // Shuffle the load order in the normal case, but not if we are loading all
+ // the libraries to a reserved address range.
+ shuffle(&load_list);
+ }
+
+ // Set up address space parameters.
+ address_space_params extinfo_params, default_params;
+ size_t relro_fd_offset = 0;
+ if (extinfo) {
+ if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
+ extinfo_params.start_addr = extinfo->reserved_addr;
+ extinfo_params.reserved_size = extinfo->reserved_size;
+ extinfo_params.must_use_address = true;
+ } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) {
+ extinfo_params.start_addr = extinfo->reserved_addr;
+ extinfo_params.reserved_size = extinfo->reserved_size;
+ }
+ }
for (auto&& task : load_list) {
- if (!task->load()) {
+ address_space_params* address_space =
+ (reserved_address_recursive || !task->is_dt_needed()) ? &extinfo_params : &default_params;
+ if (!task->load(address_space)) {
return false;
}
}
@@ -1764,7 +1788,7 @@ bool find_libraries(android_namespace_t* ns,
// we should avoid linking them (because if they are not linked -> they
// are in the local_group_roots and will be linked later).
if (!si->is_linked() && si->get_primary_namespace() == local_group_ns) {
- if (!si->link_image(global_group, local_group, extinfo) ||
+ if (!si->link_image(global_group, local_group, extinfo, &relro_fd_offset) ||
!get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
return false;
}
@@ -3712,7 +3736,7 @@ bool soinfo::prelink_image() {
}
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
- const android_dlextinfo* extinfo) {
+ const android_dlextinfo* extinfo, size_t* relro_fd_offset) {
if (is_image_linked()) {
// already linked.
return true;
@@ -3859,7 +3883,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
}
} else if (extinfo && (extinfo->flags & ANDROID_DLEXT_USE_RELRO)) {
if (phdr_table_map_gnu_relro(phdr, phnum, load_bias,
- extinfo->relro_fd) < 0) {
+ extinfo->relro_fd, relro_fd_offset) < 0) {
DL_ERR("failed mapping GNU RELRO section for \"%s\": %s",
get_realpath(), strerror(errno));
return false;
diff --git a/linker/linker.h b/linker/linker.h
index 964c26638..4c89cebcd 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -188,3 +188,9 @@ void increment_dso_handle_reference_counter(void* dso_handle);
void decrement_dso_handle_reference_counter(void* dso_handle);
void purge_unused_memory();
+
+struct address_space_params {
+ void* start_addr = nullptr;
+ size_t reserved_size = 0;
+ bool must_use_address = false;
+};
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 7486cd79b..763b15c4b 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -165,7 +165,7 @@ static void add_vdso() {
si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
si->prelink_image();
- si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr);
+ si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr, nullptr);
// prevents accidental unloads...
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_NODELETE);
si->set_linked();
@@ -281,7 +281,8 @@ static ExecutableInfo load_executable(const char* orig_path) {
if (!elf_reader.Read(result.path.c_str(), fd.get(), file_offset, result.file_stat.st_size)) {
__linker_error("error: %s\n", linker_get_error_buffer());
}
- if (!elf_reader.Load(nullptr)) {
+ address_space_params address_space;
+ if (!elf_reader.Load(&address_space)) {
__linker_error("error: %s\n", linker_get_error_buffer());
}
@@ -448,7 +449,7 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
&namespaces)) {
__linker_cannot_link(g_argv[0]);
} else if (needed_libraries_count == 0) {
- if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
+ if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr, nullptr)) {
__linker_cannot_link(g_argv[0]);
}
si->increment_ref_count();
@@ -615,7 +616,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) {
// itself without having to look into local_group and (2) allocators
// are not yet initialized, and therefore we cannot use linked_list.push_*
// functions at this point.
- if (!tmp_linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
+ if (!tmp_linker_so.link_image(g_empty_list, g_empty_list, nullptr, nullptr)) __linker_cannot_link(args.argv[0]);
return __linker_init_post_relocation(args, tmp_linker_so);
}
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 34ac60668..1aaa17f82 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -166,14 +166,12 @@ bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file
return did_read_;
}
-bool ElfReader::Load(const android_dlextinfo* extinfo) {
+bool ElfReader::Load(address_space_params* address_space) {
CHECK(did_read_);
if (did_load_) {
return true;
}
- if (ReserveAddressSpace(extinfo) &&
- LoadSegments() &&
- FindPhdr()) {
+ if (ReserveAddressSpace(address_space) && LoadSegments() && FindPhdr()) {
did_load_ = true;
}
@@ -559,7 +557,7 @@ static void* ReserveAligned(size_t size, size_t align) {
// 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(const android_dlextinfo* extinfo) {
+bool ElfReader::ReserveAddressSpace(address_space_params* address_space) {
ElfW(Addr) min_vaddr;
load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
if (load_size_ == 0) {
@@ -569,22 +567,11 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) {
uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
void* start;
- size_t reserved_size = 0;
- bool reserved_hint = true;
-
- if (extinfo != nullptr) {
- 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) {
+ if (load_size_ > address_space->reserved_size) {
+ if (address_space->must_use_address) {
DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"",
- reserved_size - load_size_, load_size_, name_.c_str());
+ load_size_ - address_space->reserved_size, load_size_, name_.c_str());
return false;
}
start = ReserveAligned(load_size_, kLibraryAlignment);
@@ -593,8 +580,12 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) {
return false;
}
} else {
- start = extinfo->reserved_addr;
+ start = address_space->start_addr;
mapped_by_caller_ = true;
+
+ // Update the reserved address space to subtract the space used by this library.
+ address_space->start_addr = reinterpret_cast<uint8_t*>(address_space->start_addr) + load_size_;
+ address_space->reserved_size -= load_size_;
}
load_start_ = start;
@@ -887,13 +878,15 @@ int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table,
* phdr_count -> number of entries in tables
* load_bias -> load bias
* fd -> readable file descriptor to use
+ * file_offset -> pointer to offset into file descriptor to use/update
* Return:
* 0 on error, -1 on failure (error code in errno).
*/
int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
size_t phdr_count,
ElfW(Addr) load_bias,
- int fd) {
+ int fd,
+ size_t* file_offset) {
// Map the file at a temporary location so we can compare its contents.
struct stat file_stat;
if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) {
@@ -907,7 +900,6 @@ int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
return -1;
}
}
- size_t file_offset = 0;
// Iterate over the relro segments and compare/remap the pages.
const ElfW(Phdr)* phdr = phdr_table;
@@ -921,12 +913,12 @@ int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
ElfW(Addr) seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
ElfW(Addr) seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
- char* file_base = static_cast<char*>(temp_mapping) + file_offset;
+ char* file_base = static_cast<char*>(temp_mapping) + *file_offset;
char* mem_base = reinterpret_cast<char*>(seg_page_start);
size_t match_offset = 0;
size_t size = seg_page_end - seg_page_start;
- if (file_size - file_offset < size) {
+ if (file_size - *file_offset < size) {
// File is too short to compare to this segment. The contents are likely
// different as well (it's probably for a different library version) so
// just don't bother checking.
@@ -950,7 +942,7 @@ int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
// Map over similar pages.
if (mismatch_offset > match_offset) {
void* map = mmap(mem_base + match_offset, mismatch_offset - match_offset,
- PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, match_offset);
+ PROT_READ, MAP_PRIVATE|MAP_FIXED, fd, *file_offset + match_offset);
if (map == MAP_FAILED) {
munmap(temp_mapping, file_size);
return -1;
@@ -961,7 +953,7 @@ int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table,
}
// Add to the base file offset in case there are multiple relro segments.
- file_offset += size;
+ *file_offset += size;
}
munmap(temp_mapping, file_size);
return 0;
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 0eee08933..9761bc8ca 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -43,7 +43,7 @@ class ElfReader {
ElfReader();
bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size);
- bool Load(const android_dlextinfo* extinfo);
+ bool Load(address_space_params* address_space);
const char* name() const { return name_.c_str(); }
size_t phdr_count() const { return phdr_num_; }
@@ -62,7 +62,7 @@ class ElfReader {
bool ReadProgramHeaders();
bool ReadSectionHeaders();
bool ReadDynamicSection();
- bool ReserveAddressSpace(const android_dlextinfo* extinfo);
+ bool ReserveAddressSpace(address_space_params* address_space);
bool LoadSegments();
bool FindPhdr();
bool CheckPhdr(ElfW(Addr));
@@ -122,7 +122,7 @@ int phdr_table_serialize_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_cou
ElfW(Addr) load_bias, int fd);
int phdr_table_map_gnu_relro(const ElfW(Phdr)* phdr_table, size_t phdr_count,
- ElfW(Addr) load_bias, int fd);
+ ElfW(Addr) load_bias, int fd, size_t* file_offset);
#if defined(__arm__)
int phdr_table_get_arm_exidx(const ElfW(Phdr)* phdr_table, size_t phdr_count, ElfW(Addr) load_bias,
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index dd9c6aa53..80c51af9a 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -223,7 +223,7 @@ struct soinfo {
void call_pre_init_constructors();
bool prelink_image();
bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
- const android_dlextinfo* extinfo);
+ const android_dlextinfo* extinfo, size_t* relro_fd_offset);
bool protect_relro();
void add_child(soinfo* child);