From d6bf019204e258d89da56330fbcf9a36c39d23f5 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Mon, 15 Oct 2018 15:40:49 -0700 Subject: [PATCH] Rework the linker_wrapper to work with lld This is use by Host Bionic to bootstrap into an embedded copy of the linker by tweaking the AT_* values before calling in to the linker entry. Similarly to 9729f35922aee4d1662b97d62d82385f6b8124ef, get the base address from AT_PHDR, so that we're not relying on the relative offset before relocation, which doesn't work with lld (at least with the standard flags). To find the offset to the linker code, we can still use an absolute symbol created by extract_linker (which is currently hardcoded to 0x1000). Instead of relying on something similar for the linker entry point, we're now just reading the entry point from the linker's ELF header. Then we get the address to the real _start function using host_bionic_inject, which injects the value into a global variable after the link step is finished. It also uses that opportunity to verify that the linker is embedded as we expect it to be. Bug: 31559095 Test: build with host bionic Change-Id: I9d81ea77c51c079de06905da1ebe421fead1dc3b --- linker/linker_wrapper.cpp | 47 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/linker/linker_wrapper.cpp b/linker/linker_wrapper.cpp index 571d3ab23..fc673aa83 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(phdr_table) - phdr_table[i].p_vaddr; + *base = reinterpret_cast(phdr_table) - phdr_table[i].p_offset; + return; + } + } +} /* * This is the entry point for the linker wrapper, which finds @@ -39,21 +57,26 @@ extern const char linker_entry; extern "C" ElfW(Addr) __linker_init(void* raw_args) { KernelArgumentBlock args(raw_args); - static uintptr_t linker_offset = reinterpret_cast(&linker_code_start); - static uintptr_t linktime_addr = reinterpret_cast(&linktime_addr); - ElfW(Addr) my_addr = reinterpret_cast(&linktime_addr) - linktime_addr; + ElfW(Addr) base_addr = 0; + ElfW(Addr) load_bias = 0; + get_elf_base_from_phdr( + reinterpret_cast(args.getauxval(AT_PHDR)), args.getauxval(AT_PHNUM), + &base_addr, &load_bias); + + ElfW(Addr) linker_addr = base_addr + reinterpret_cast(&linker_offset); + ElfW(Addr) linker_entry_offset = reinterpret_cast(linker_addr)->e_entry; - // Set AT_ENTRY to the proper entry point 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(&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(&linker_entry); + // Return address of linker entry point + return linker_addr + linker_entry_offset; }