Reland "Use the dynamic table instead of __rela?_iplt_* to find the linker's IRELATIVE relocs." with a fix.
A recent change to lld [1] made it so that the __rela?_iplt_*
symbols are no longer defined for PIEs and shared libraries. Since
the linker is a PIE, this prevents it from being able to look up
its own relocations via these symbols. We don't need these symbols
to find the relocations however, as their location is available via
the dynamic table. Therefore, start using the dynamic table to find
the relocations instead of using the symbols.
Previously landed in r.android.com/1801427 and reverted in
r.android.com/1804876 due to linux-bionic breakage. This time,
search .rela.dyn as well as .rela.plt, since the linker may put the
relocations in either location (see [2]).
[1] https://github.com/llvm/llvm-project/commit/f8cb78e99aae9aa3f89f7bfe667db2c5b767f21f
[2] https://reviews.llvm.org/D65651
Bug: 197420743
Change-Id: I5bef157472e9893822e3ca507ef41a15beefc6f1
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;