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();