Delay setting linker soname until post-reloc and post-ctor

Setting the linker's soname ("ld-android.so") can allocate heap memory
now that the name uses an std::string, and it's probably a good idea to
defer doing this until after the linker has relocated itself (and after
it has called C++ constructors for global variables.)

Bug: none
Test: bionic unit tests
Test: verify that dlopen("ld-android.so", RTLD_NOLOAD) works
Change-Id: I6b9bd7552c3ae9b77e3ee9e2a98b069b8eef25ca
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 772e7b8..af05027 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -328,7 +328,7 @@
     __libdl_info->ref_count_ = 1;
     __libdl_info->strtab_size_ = linker_si.strtab_size_;
     __libdl_info->local_group_root_ = __libdl_info;
-    __libdl_info->soname_ = linker_si.soname_.c_str();
+    __libdl_info->soname_ = linker_si.soname_;
     __libdl_info->target_sdk_version_ = __ANDROID_API__;
     __libdl_info->generate_handle();
 #if defined(__work_around_b_24465209__)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 77824f6..c240c56 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -3193,26 +3193,27 @@
     return false;
   }
 
-  // second pass - parse entries relying on strtab
-  for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
-    switch (d->d_tag) {
-      case DT_SONAME:
-        set_soname(get_string(d->d_un.d_val));
-        break;
-      case DT_RUNPATH:
-        set_dt_runpath(get_string(d->d_un.d_val));
-        break;
+  // Second pass - parse entries relying on strtab. Skip this while relocating the linker so as to
+  // avoid doing heap allocations until later in the linker's initialization.
+  if (!relocating_linker) {
+    for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
+      switch (d->d_tag) {
+        case DT_SONAME:
+          set_soname(get_string(d->d_un.d_val));
+          break;
+        case DT_RUNPATH:
+          set_dt_runpath(get_string(d->d_un.d_val));
+          break;
+      }
     }
   }
 
-  // Before M release linker was using basename in place of soname.
-  // In the case when dt_soname is absent some apps stop working
-  // because they can't find dt_needed library by soname.
-  // This workaround should keep them working. (Applies only
-  // for apps targeting sdk version < M.) Make an exception for
-  // the main executable and linker; they do not need to have dt_soname.
-  // TODO: >= O the linker doesn't need this workaround.
-  if (soname_.empty() && this != solist_get_somain() && (flags_ & FLAG_LINKER) == 0 &&
+  // Before M release, linker was using basename in place of soname. In the case when DT_SONAME is
+  // absent some apps stop working because they can't find DT_NEEDED library by soname. This
+  // workaround should keep them working. (Applies only for apps targeting sdk version < M.) Make
+  // an exception for the main executable, which does not need to have DT_SONAME. The linker has an
+  // DT_SONAME but the soname_ field is initialized later on.
+  if (soname_.empty() && this != solist_get_somain() && !relocating_linker &&
       get_application_target_sdk_version() < 23) {
     soname_ = basename(realpath_.c_str());
     DL_WARN_documented_change(23, "missing-soname-enforced-for-api-level-23",
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 59e8036..0b501a7 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -725,6 +725,13 @@
   // Initialize the linker's own global variables
   tmp_linker_so.call_constructors();
 
+  // Setting the linker soinfo's soname can allocate heap memory, so delay it until here.
+  for (const ElfW(Dyn)* d = tmp_linker_so.dynamic; d->d_tag != DT_NULL; ++d) {
+    if (d->d_tag == DT_SONAME) {
+      tmp_linker_so.set_soname(tmp_linker_so.get_string(d->d_un.d_val));
+    }
+  }
+
   // When the linker is run directly rather than acting as PT_INTERP, parse
   // arguments and determine the executable to load. When it's instead acting
   // as PT_INTERP, AT_ENTRY will refer to the loaded executable rather than the