Avoid post-reloc GOT usage in __linker_init
A GOT lookup happening prior to soinfo::link_image causes a segfault. With
-O0, the compiler moves GOT lookups from after __linker_init's link_image
call to the start of __linker_init.
Rename the existing __linker_init_post_relocation to linker_main, then
extract the existing post-link_image code to a new
__linker_init_post_relocation function.
Bug: http://b/80503879
Test: /data/nativetest64/bionic-unit-tests/bionic-unit-tests
Test: manual
Change-Id: If8a470f8360acbe35e2a308b0fbff570de6131cf
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 331f831..1a0d164 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -228,12 +228,7 @@
_exit(EXIT_FAILURE);
}
-/*
- * This code is called after the linker has linked itself and
- * fixed it's own GOT. It is safe to make references to externs
- * and other non-local data at this point.
- */
-static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) {
+static ElfW(Addr) linker_main(KernelArgumentBlock& args) {
ProtectedDataGuard guard;
#if TIMING
@@ -498,6 +493,11 @@
return 0;
}
+static ElfW(Addr) __attribute__((noinline))
+__linker_init_post_relocation(KernelArgumentBlock& args,
+ ElfW(Addr) linker_addr,
+ soinfo& linker_so);
+
/*
* This is the entry point for the linker, called from begin.S. This
* method is responsible for fixing the linker's own relocations, and
@@ -531,7 +531,6 @@
linker_addr += reinterpret_cast<uintptr_t>(raw_args);
#endif
- ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
@@ -556,6 +555,19 @@
// functions at this point.
if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
+ return __linker_init_post_relocation(args, linker_addr, linker_so);
+}
+
+/*
+ * This code is called after the linker has linked itself and fixed its own
+ * GOT. It is safe to make references to externs and other non-local data at
+ * this point. The compiler sometimes moves GOT references earlier in a
+ * function, so avoid inlining this function (http://b/80503879).
+ */
+static ElfW(Addr) __attribute__((noinline))
+__linker_init_post_relocation(KernelArgumentBlock& args,
+ ElfW(Addr) linker_addr,
+ soinfo& linker_so) {
// Initialize the main thread (including TLS, so system calls really work).
__libc_init_main_thread(args);
@@ -580,6 +592,7 @@
//
// This happens when user tries to run 'adb shell /system/bin/linker'
// see also https://code.google.com/p/android/issues/detail?id=63174
+ ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
async_safe_format_fd(STDOUT_FILENO,
"This is %s, the helper program for dynamic executables.\n",
@@ -595,10 +608,8 @@
sonext = solist = get_libdl_info(kLinkerPath, linker_so, linker_link_map);
g_default_namespace.add_soinfo(solist);
- // We have successfully fixed our own relocations. It's safe to run
- // the main part of the linker now.
args.abort_message_ptr = &g_abort_message;
- ElfW(Addr) start_address = __linker_init_post_relocation(args);
+ ElfW(Addr) start_address = linker_main(args);
INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));