Merge "Reland "Use the dynamic table instead of __rela?_iplt_* to find the linker's IRELATIVE relocs." with a fix."
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 2a690e9..9e5be34 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -39,6 +39,7 @@
#include "linker_globals.h"
#include "linker_phdr.h"
#include "linker_relocate.h"
+#include "linker_relocs.h"
#include "linker_tls.h"
#include "linker_utils.h"
@@ -596,31 +597,66 @@
}
}
-// 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)
-extern __LIBC_HIDDEN__ ElfW(Rela) __rela_iplt_start[], __rela_iplt_end[];
-
-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);
- }
-}
+using RelType = ElfW(Rela);
+const unsigned kRelTag = DT_RELA;
+const unsigned kRelSzTag = DT_RELASZ;
#else
-extern __LIBC_HIDDEN__ ElfW(Rel) __rel_iplt_start[], __rel_iplt_end[];
+using RelType = ElfW(Rel);
+const unsigned kRelTag = DT_REL;
+const unsigned kRelSzTag = DT_RELSZ;
+#endif
-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;
+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
*offset = __bionic_call_ifunc_resolver(resolver);
}
}
-#endif
+
+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));
+ }
+ }
+}
// Usable before ifunc resolvers have been called. This function is compiled with -ffreestanding.
static void linker_memclr(void* dst, size_t cnt) {
@@ -682,14 +718,13 @@
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.
- const ElfW(Addr) load_bias = get_elf_exec_load_bias(elf_hdr);
- call_ifunc_resolvers(load_bias);
+ call_ifunc_resolvers();
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 = load_bias;
+ tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
tmp_linker_so.dynamic = nullptr;
tmp_linker_so.phdr = phdr;
tmp_linker_so.phnum = elf_hdr->e_phnum;