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
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index f576023..612f52f 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -176,11 +176,12 @@
}
// 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<char*>(linker_path);
+ map.l_name = const_cast<char*>(info.get_realpath());
phdr_table_get_dynamic_section(info.phdr, info.phnum, info.load_bias, &map.l_ld, nullptr);
}
@@ -232,9 +233,9 @@
}
#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 @@
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 @@
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 @@
parse_LD_LIBRARY_PATH(ldpath_env);
parse_LD_PRELOAD(ldpreload_env);
- std::vector<android_namespace_t*> namespaces = init_default_namespaces(exe_path.c_str());
+ std::vector<android_namespace_t*> namespaces = init_default_namespaces(exe_info.path.c_str());
if (!si->prelink_image()) __linker_cannot_link(g_argv[0]);
@@ -695,9 +710,8 @@
// 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);