Merge "Rework the linker_wrapper to work with lld"
diff --git a/linker/linker_wrapper.cpp b/linker/linker_wrapper.cpp
index 571d3ab..fc673aa 100644
--- a/linker/linker_wrapper.cpp
+++ b/linker/linker_wrapper.cpp
@@ -28,9 +28,27 @@
#include "private/KernelArgumentBlock.h"
-extern const char linker_code_start;
-extern const char original_start;
-extern const char linker_entry;
+extern const char linker_offset;
+
+// This will be replaced by host_bionic_inject, but must be non-zero
+// here so that it's placed in the data section.
+uintptr_t original_start = 42;
+
+/* Find the load bias and base address of an executable or shared object loaded
+ * by the kernel. The ELF file's PHDR table must have a PT_PHDR entry.
+ *
+ * A VDSO doesn't have a PT_PHDR entry in its PHDR table.
+ */
+static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_count,
+ ElfW(Addr)* base, ElfW(Addr)* load_bias) {
+ for (size_t i = 0; i < phdr_count; ++i) {
+ if (phdr_table[i].p_type == PT_PHDR) {
+ *load_bias = reinterpret_cast<ElfW(Addr)>(phdr_table) - phdr_table[i].p_vaddr;
+ *base = reinterpret_cast<ElfW(Addr)>(phdr_table) - phdr_table[i].p_offset;
+ return;
+ }
+ }
+}
/*
* This is the entry point for the linker wrapper, which finds
@@ -39,21 +57,26 @@
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
KernelArgumentBlock args(raw_args);
- static uintptr_t linker_offset = reinterpret_cast<uintptr_t>(&linker_code_start);
- static uintptr_t linktime_addr = reinterpret_cast<uintptr_t>(&linktime_addr);
- ElfW(Addr) my_addr = reinterpret_cast<uintptr_t>(&linktime_addr) - linktime_addr;
+ ElfW(Addr) base_addr = 0;
+ ElfW(Addr) load_bias = 0;
+ get_elf_base_from_phdr(
+ reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR)), args.getauxval(AT_PHNUM),
+ &base_addr, &load_bias);
- // Set AT_ENTRY to the proper entry point
+ ElfW(Addr) linker_addr = base_addr + reinterpret_cast<uintptr_t>(&linker_offset);
+ ElfW(Addr) linker_entry_offset = reinterpret_cast<ElfW(Ehdr)*>(linker_addr)->e_entry;
+
for (ElfW(auxv_t)* v = args.auxv; v->a_type != AT_NULL; ++v) {
if (v->a_type == AT_BASE) {
- v->a_un.a_val = my_addr + linker_offset;
+ // Set AT_BASE to the embedded linker
+ v->a_un.a_val = linker_addr;
}
if (v->a_type == AT_ENTRY) {
- v->a_un.a_val = my_addr + reinterpret_cast<uintptr_t>(&original_start);
+ // Set AT_ENTRY to the proper entry point
+ v->a_un.a_val = base_addr + original_start;
}
}
- // Return address of linker entry point -- may need to ensure that raw_args
- // was saved.
- return my_addr + linker_offset + reinterpret_cast<uintptr_t>(&linker_entry);
+ // Return address of linker entry point
+ return linker_addr + linker_entry_offset;
}