diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 772e7b81c..af050274d 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -328,7 +328,7 @@ soinfo* get_libdl_info(const soinfo& linker_si) { __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 77824f615..c240c5639 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -3193,26 +3193,27 @@ bool soinfo::prelink_image() { 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 59e803665..0b501a7b1 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -725,6 +725,13 @@ __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) // 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