summaryrefslogtreecommitdiff
path: root/linker/linker_main.cpp
diff options
context:
space:
mode:
authorRyan Prichard <rprichard@google.com>2019-06-04 20:56:56 -0700
committerRyan Prichard <rprichard@google.com>2019-06-05 18:02:40 -0700
commitcf9ed12d107c05acfec5a4fbf70c416bfd34a2b3 (patch)
tree11ab821a3d69569a9b24707e63aafed48a67a187 /linker/linker_main.cpp
parentc3c492965995a2bd5cfe12846f5038c4d7d2c91e (diff)
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
Diffstat (limited to 'linker/linker_main.cpp')
-rw-r--r--linker/linker_main.cpp44
1 files changed, 29 insertions, 15 deletions
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<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 @@ 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<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 @@ __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);