diff options
Diffstat (limited to 'linker/linker_main.cpp')
-rw-r--r-- | linker/linker_main.cpp | 75 |
1 files changed, 20 insertions, 55 deletions
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index 9e5be345d..2a690e9a0 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -39,7 +39,6 @@ #include "linker_globals.h" #include "linker_phdr.h" #include "linker_relocate.h" -#include "linker_relocs.h" #include "linker_tls.h" #include "linker_utils.h" @@ -597,66 +596,31 @@ static void set_bss_vma_name(soinfo* si) { } } +// TODO: There is a similar ifunc resolver calling loop in libc_init_static.cpp, but that version +// uses weak symbols, which don't work in the linker prior to its relocation. This version also +// supports a load bias. When we stop supporting the gold linker in the NDK, then maybe we can use +// non-weak definitions and merge the two loops. #if defined(USE_RELA) -using RelType = ElfW(Rela); -const unsigned kRelTag = DT_RELA; -const unsigned kRelSzTag = DT_RELASZ; -#else -using RelType = ElfW(Rel); -const unsigned kRelTag = DT_REL; -const unsigned kRelSzTag = DT_RELSZ; -#endif +extern __LIBC_HIDDEN__ ElfW(Rela) __rela_iplt_start[], __rela_iplt_end[]; -extern __LIBC_HIDDEN__ ElfW(Ehdr) __ehdr_start; - -static void call_ifunc_resolvers_for_section(RelType* begin, RelType* end) { - auto ehdr = reinterpret_cast<ElfW(Addr)>(&__ehdr_start); - for (RelType *r = begin; r != end; ++r) { - if (ELFW(R_TYPE)(r->r_info) != R_GENERIC_IRELATIVE) { - continue; - } - ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(ehdr + r->r_offset); -#if defined(USE_RELA) - ElfW(Addr) resolver = ehdr + r->r_addend; -#else - ElfW(Addr) resolver = ehdr + *offset; -#endif +static void call_ifunc_resolvers(ElfW(Addr) load_bias) { + for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) { + ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias); + ElfW(Addr) resolver = r->r_addend + load_bias; *offset = __bionic_call_ifunc_resolver(resolver); } } +#else +extern __LIBC_HIDDEN__ ElfW(Rel) __rel_iplt_start[], __rel_iplt_end[]; -static void call_ifunc_resolvers() { - // Find the IRELATIVE relocations using the DT_JMPREL and DT_PLTRELSZ, or DT_RELA? and DT_RELA?SZ - // dynamic tags. - auto ehdr = reinterpret_cast<ElfW(Addr)>(&__ehdr_start); - auto* phdr = reinterpret_cast<ElfW(Phdr)*>(ehdr + __ehdr_start.e_phoff); - for (size_t i = 0; i != __ehdr_start.e_phnum; ++i) { - if (phdr[i].p_type != PT_DYNAMIC) { - continue; - } - auto *dyn = reinterpret_cast<ElfW(Dyn)*>(ehdr + phdr[i].p_vaddr); - ElfW(Addr) pltrel = 0, pltrelsz = 0, rel = 0, relsz = 0; - for (size_t j = 0, size = phdr[i].p_filesz / sizeof(ElfW(Dyn)); j != size; ++j) { - if (dyn[j].d_tag == DT_JMPREL) { - pltrel = dyn[j].d_un.d_ptr; - } else if (dyn[j].d_tag == DT_PLTRELSZ) { - pltrelsz = dyn[j].d_un.d_ptr; - } else if (dyn[j].d_tag == kRelTag) { - rel = dyn[j].d_un.d_ptr; - } else if (dyn[j].d_tag == kRelSzTag) { - relsz = dyn[j].d_un.d_ptr; - } - } - if (pltrel && pltrelsz) { - call_ifunc_resolvers_for_section(reinterpret_cast<RelType*>(ehdr + pltrel), - reinterpret_cast<RelType*>(ehdr + pltrel + pltrelsz)); - } - if (rel && relsz) { - call_ifunc_resolvers_for_section(reinterpret_cast<RelType*>(ehdr + rel), - reinterpret_cast<RelType*>(ehdr + rel + relsz)); - } +static void call_ifunc_resolvers(ElfW(Addr) load_bias) { + for (ElfW(Rel) *r = __rel_iplt_start; r != __rel_iplt_end; ++r) { + ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset + load_bias); + ElfW(Addr) resolver = *offset + load_bias; + *offset = __bionic_call_ifunc_resolver(resolver); } } +#endif // Usable before ifunc resolvers have been called. This function is compiled with -ffreestanding. static void linker_memclr(void* dst, size_t cnt) { @@ -718,13 +682,14 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff); // string.h functions must not be used prior to calling the linker's ifunc resolvers. - call_ifunc_resolvers(); + const ElfW(Addr) load_bias = get_elf_exec_load_bias(elf_hdr); + call_ifunc_resolvers(load_bias); soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0); tmp_linker_so.base = linker_addr; tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum); - tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr); + tmp_linker_so.load_bias = load_bias; tmp_linker_so.dynamic = nullptr; tmp_linker_so.phdr = phdr; tmp_linker_so.phnum = elf_hdr->e_phnum; |