From cf9ed12d107c05acfec5a4fbf70c416bfd34a2b3 Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Tue, 4 Jun 2019 20:56:56 -0700 Subject: [PATCH] Use PT_INTERP as the linker's l_name path Ordinary executables have a PT_INTERP path of /system/bin/linker[64], but: - executables using bootstrap Bionic use /system/bin/bootstrap/linker[64] - ASAN executables use /system/bin/linker_asan[64] gdb appears to use the PT_INTERP path for debugging the dynamic linker before the linker has initialized the r_debug module list. If the linker's l_name differs from PT_INTERP, then gdb assumes that the linker has been unloaded and searches for a new solib using the linker's l_name path. gdb may print a warning like: warning: Temporarily disabling breakpoints for unloaded shared library "$OUT/symbols/system/bin/linker64" If I'm currently debugging the linker when this happens, gdb apparently doesn't load debug symbols for the linker. This can be worked around with gdb's "sharedlibrary" command, but it's better to avoid it. Previously, when PT_INTERP was the bootstrap linker, but l_name was "/system/bin/linker[64]", gdb would find the default non-bootstrap linker binary and (presumably) get confused about symbol addresses. (Also, remove the "static std::string exe_path" variable because the soinfo::realpath_ field is a std::string that already lasts until exit. We already use it for link_map_head.l_name in notify_gdb_of_load.) Bug: http://b/134183407 Test: manual Change-Id: I9a95425a3a5e9fd01e9dd272273c6ed3667dbb9a --- linker/dlfcn.cpp | 4 ++-- linker/linker.h | 2 +- linker/linker_main.cpp | 44 ++++++++++++++++++++++++++-------------- linker/linker_soinfo.cpp | 10 +++++++++ linker/linker_soinfo.h | 3 ++- 5 files changed, 44 insertions(+), 19 deletions(-) diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 5a47272c1..18301e0e2 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -308,11 +308,11 @@ static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); static soinfo* __libdl_info = nullptr; // This is used by the dynamic linker. Every process gets these symbols for free. -soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si) { +soinfo* get_libdl_info(const soinfo& linker_si) { CHECK((linker_si.flags_ & FLAG_GNU_HASH) != 0); if (__libdl_info == nullptr) { - __libdl_info = new (__libdl_info_buf) soinfo(&g_default_namespace, linker_path, nullptr, 0, 0); + __libdl_info = new (__libdl_info_buf) soinfo(&g_default_namespace, nullptr, nullptr, 0, 0); __libdl_info->flags_ |= (FLAG_LINKED | FLAG_GNU_HASH); __libdl_info->strtab_ = linker_si.strtab_; __libdl_info->symtab_ = linker_si.symtab_; diff --git a/linker/linker.h b/linker/linker.h index 4c89cebcd..0f3094616 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -99,7 +99,7 @@ enum RelocationKind { void count_relocation(RelocationKind kind); -soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si); +soinfo* get_libdl_info(const soinfo& linker_si); soinfo* find_containing_library(const void* p); diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index f5760231a..612f52f88 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -176,11 +176,12 @@ static void add_vdso() { } // Initializes an soinfo's link_map_head field using other fields from the -// soinfo (phdr, phnum, load_bias). -static void init_link_map_head(soinfo& info, const char* linker_path) { +// soinfo (phdr, phnum, load_bias). The soinfo's realpath must not change after +// this function is called. +static void init_link_map_head(soinfo& info) { auto& map = info.link_map_head; map.l_addr = info.load_bias; - map.l_name = const_cast(linker_path); + map.l_name = const_cast(info.get_realpath()); phdr_table_get_dynamic_section(info.phdr, info.phnum, info.load_bias, &map.l_ld, nullptr); } @@ -232,9 +233,9 @@ static ExecutableInfo get_executable_info() { } #if defined(__LP64__) -static char kLinkerPath[] = "/system/bin/linker64"; +static char kFallbackLinkerPath[] = "/system/bin/linker64"; #else -static char kLinkerPath[] = "/system/bin/linker"; +static char kFallbackLinkerPath[] = "/system/bin/linker"; #endif __printflike(1, 2) @@ -350,15 +351,11 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load const ExecutableInfo exe_info = exe_to_load ? load_executable(exe_to_load) : get_executable_info(); - // Assign to a static variable for the sake of the debug map, which needs - // a C-style string to last until the program exits. - static std::string exe_path = exe_info.path; - - INFO("[ Linking executable \"%s\" ]", exe_path.c_str()); + INFO("[ Linking executable \"%s\" ]", exe_info.path.c_str()); // Initialize the main exe's soinfo. soinfo* si = soinfo_alloc(&g_default_namespace, - exe_path.c_str(), &exe_info.file_stat, + exe_info.path.c_str(), &exe_info.file_stat, 0, RTLD_GLOBAL); somain = si; si->phdr = exe_info.phdr; @@ -367,7 +364,25 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load si->size = phdr_table_get_load_size(si->phdr, si->phnum); si->dynamic = nullptr; si->set_main_executable(); - init_link_map_head(*si, exe_path.c_str()); + init_link_map_head(*si); + + // Use the executable's PT_INTERP string as the solinker filename in the + // dynamic linker's module list. gdb reads both PT_INTERP and the module list, + // and if the paths for the linker are different, gdb will report that the + // PT_INTERP linker path was unloaded once the module list is initialized. + // There are three situations to handle: + // - the APEX linker (/system/bin/linker[64] -> /apex/.../linker[64]) + // - the ASAN linker (/system/bin/linker_asan[64] -> /apex/.../linker[64]) + // - the bootstrap linker (/system/bin/bootstrap/linker[64]) + const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum, + somain->load_bias); + if (interp == nullptr) { + // This case can happen if the linker attempts to execute itself + // (e.g. "linker64 /system/bin/linker64"). + interp = kFallbackLinkerPath; + } + solinker->set_realpath(interp); + init_link_map_head(*solinker); // Register the main executable and the linker upfront to have // gdb aware of them before loading the rest of the dependency @@ -405,7 +420,7 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load parse_LD_LIBRARY_PATH(ldpath_env); parse_LD_PRELOAD(ldpreload_env); - std::vector namespaces = init_default_namespaces(exe_path.c_str()); + std::vector namespaces = init_default_namespaces(exe_info.path.c_str()); if (!si->prelink_image()) __linker_cannot_link(g_argv[0]); @@ -695,9 +710,8 @@ __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) // Initialize static variables. Note that in order to // get correct libdl_info we need to call constructors // before get_libdl_info(). - sonext = solist = solinker = get_libdl_info(kLinkerPath, tmp_linker_so); + sonext = solist = solinker = get_libdl_info(tmp_linker_so); g_default_namespace.add_soinfo(solinker); - init_link_map_head(*solinker, kLinkerPath); ElfW(Addr) start_address = linker_main(args, exe_to_load); diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp index 5f40528e9..d4b25414e 100644 --- a/linker/linker_soinfo.cpp +++ b/linker/linker_soinfo.cpp @@ -550,6 +550,16 @@ void soinfo::set_nodelete() { rtld_flags_ |= RTLD_NODELETE; } +void soinfo::set_realpath(const char* path) { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + realpath_ = path; + } +#else + realpath_ = path; +#endif +} + const char* soinfo::get_realpath() const { #if defined(__work_around_b_24465209__) if (has_min_version(2)) { diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h index 80c51af9a..27032c2da 100644 --- a/linker/linker_soinfo.h +++ b/linker/linker_soinfo.h @@ -278,6 +278,7 @@ struct soinfo { void set_soname(const char* soname); const char* get_soname() const; + void set_realpath(const char* path); const char* get_realpath() const; const ElfW(Versym)* get_versym(size_t n) const; ElfW(Addr) get_verneed_ptr() const; @@ -372,7 +373,7 @@ struct soinfo { android_namespace_list_t secondary_namespaces_; uintptr_t handle_; - friend soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si); + friend soinfo* get_libdl_info(const soinfo& linker_si); // version >= 4 ElfW(Relr)* relr_;