linker_main: acquire loader lock earlier and release it later
__linker_init calls soinfo::~soinfo after __linker_init_post_relocation
has returned, and ~soinfo modifies global loader state
(g_soinfo_handles_map), so the loader lock must be held.
Use a variable with a destructor (~DlMutexUnlocker) to release the
loader lock after ~soinfo returns.
It's not clear to me when the mutex should be acquired.
pthread_mutex_lock in theory can use __bionic_tls (ScopedTrace), but
that should only happen if the mutex is contended, and it won't be.
The loader constructors shouldn't be spawning a thread, and the vdso
shouldn't really have a constructor. ifunc relocations presumably don't
spawn a thread either. It probably doesn't matter much as long as it's
held before calling constructors in the executable or shared objects.
Bug: http://b/290318196
Test: treehugger
Test: bionic-unit-tests
Change-Id: I544ad65db07e5fe83315ad4489d77536d463a0a4
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index e92aada..8a20670 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -44,7 +44,6 @@
#include "linker_utils.h"
#include "private/KernelArgumentBlock.h"
-#include "private/ScopedPthreadMutexLocker.h"
#include "private/bionic_call_ifunc_resolver.h"
#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
@@ -499,11 +498,6 @@
if (!get_cfi_shadow()->InitialLinkDone(solist)) __linker_cannot_link(g_argv[0]);
- // A constructor could spawn a thread that calls into the loader, so as soon
- // as we've called a constructor, we need to hold the lock while accessing
- // global loader state.
- ScopedPthreadMutexLocker locker(&g_dl_mutex);
-
si->call_pre_init_constructors();
si->call_constructors();
@@ -696,6 +690,13 @@
* function, or other GOT reference will generate a segfault.
*/
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
+ // Unlock the loader mutex immediately before transferring to the executable's
+ // entry point. This must happen after destructors are called in this function
+ // (e.g. ~soinfo), so declare this variable very early.
+ struct DlMutexUnlocker {
+ ~DlMutexUnlocker() { pthread_mutex_unlock(&g_dl_mutex); }
+ } unlocker;
+
// Initialize TLS early so system calls and errno work.
KernelArgumentBlock args(raw_args);
bionic_tcb temp_tcb __attribute__((uninitialized));
@@ -758,6 +759,11 @@
// Initialize the linker's static libc's globals
__libc_init_globals();
+ // A constructor could spawn a thread that calls into the loader, so as soon
+ // as we've called a constructor, we need to hold the lock until transferring
+ // to the entry point.
+ pthread_mutex_lock(&g_dl_mutex);
+
// Initialize the linker's own global variables
tmp_linker_so.call_constructors();