From 0489645e000428eb08bfc201dcf6d0dfa919d4a8 Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Mon, 20 Aug 2018 17:44:42 -0700 Subject: [PATCH] Fix linker's _r_debug (gdb) info * Initialize the exe's l_ld correctly, and initialize its l_addr field earlier. * Copy the phdr/phnum fields from the linker's temporary soinfo to its final soinfo. This change ensures that dl_iterate_phdr shows the phdr table for the linker. * Change init_linker_info_for_gdb a little: use an soinfo's fields to init the soinfo::link_map_head field, then reuse the new init_link_map_head function to handle the linker and the executable. Test: manual Test: bionic-unit-tests Bug: https://issuetracker.google.com/112627083 Bug: http://b/110967431 Change-Id: I40fad2c4d48f409347aaa1ccb98d96db89da1dfe --- linker/dlfcn.cpp | 9 ++-- linker/linker.h | 4 +- linker/linker_main.cpp | 113 +++++++++++++++-------------------------- linker/linker_soinfo.h | 4 +- 4 files changed, 47 insertions(+), 83 deletions(-) diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 4b84537da..dfe8e8c81 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -303,9 +303,7 @@ 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, - const link_map& linker_map) { +soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si) { CHECK((linker_si.flags_ & FLAG_GNU_HASH) != 0); if (__libdl_info == nullptr) { @@ -314,6 +312,8 @@ soinfo* get_libdl_info(const char* linker_path, __libdl_info->strtab_ = linker_si.strtab_; __libdl_info->symtab_ = linker_si.symtab_; __libdl_info->load_bias = linker_si.load_bias; + __libdl_info->phdr = linker_si.phdr; + __libdl_info->phnum = linker_si.phnum; __libdl_info->gnu_nbucket_ = linker_si.gnu_nbucket_; __libdl_info->gnu_maskwords_ = linker_si.gnu_maskwords_; @@ -328,9 +328,6 @@ soinfo* get_libdl_info(const char* linker_path, __libdl_info->soname_ = linker_si.soname_; __libdl_info->target_sdk_version_ = __ANDROID_API__; __libdl_info->generate_handle(); - __libdl_info->link_map_head.l_addr = linker_map.l_addr; - __libdl_info->link_map_head.l_name = linker_map.l_name; - __libdl_info->link_map_head.l_ld = linker_map.l_ld; #if defined(__work_around_b_24465209__) strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_)); #endif diff --git a/linker/linker.h b/linker/linker.h index 260c89d71..7aa7e5fc8 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -99,9 +99,7 @@ enum RelocationKind { void count_relocation(RelocationKind kind); -soinfo* get_libdl_info(const char* linker_path, - const soinfo& linker_si, - const link_map& linker_map); +soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si); soinfo* find_containing_library(const void* p); diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index af4761990..c5e0b9635 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -67,6 +67,7 @@ static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_cou static soinfo* solist; static soinfo* sonext; static soinfo* somain; // main process, always the one after libdl_info +static soinfo* solinker; static soinfo* vdso; // vdso if present void solist_add_soinfo(soinfo* si) { @@ -170,29 +171,13 @@ static void add_vdso(KernelArgumentBlock& args) { vdso = si; } -/* gdb expects the linker to be in the debug shared object list. - * Without this, gdb has trouble locating the linker's ".text" - * and ".plt" sections. Gdb could also potentially use this to - * relocate the offset of our exported 'rtld_db_dlactivity' symbol. - * Note that the linker shouldn't be on the soinfo list. - */ -static link_map linker_link_map; - -static void init_linker_info_for_gdb(ElfW(Addr) linker_base, char* linker_path) { - linker_link_map.l_addr = linker_base; - linker_link_map.l_name = linker_path; - - /* - * Set the dynamic field in the link map otherwise gdb will complain with - * the following: - * warning: .dynamic section for "/system/bin/linker" is not at the - * expected address (wrong library or version mismatch?) - */ - ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_base); - ElfW(Phdr)* phdr = reinterpret_cast(linker_base + elf_hdr->e_phoff); - phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, - &linker_link_map.l_ld, nullptr); - +// 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) { + auto& map = info.link_map_head; + map.l_addr = info.load_bias; + map.l_name = const_cast(linker_path); + phdr_table_get_dynamic_section(info.phdr, info.phnum, info.load_bias, &map.l_ld, nullptr); } extern "C" int __system_properties_init(void); @@ -303,32 +288,32 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args) { stat("/init", &file_stat); } + // Initialize the main exe's soinfo. const char* executable_path = get_executable_path(); soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL); - - // Bootstrap the link map, the main exe always needs to be first. + somain = si; + si->phdr = reinterpret_cast(args.getauxval(AT_PHDR)); + si->phnum = args.getauxval(AT_PHNUM); + get_elf_base_from_phdr(si->phdr, si->phnum, &si->base, &si->load_bias); + si->size = phdr_table_get_load_size(si->phdr, si->phnum); + si->dynamic = nullptr; si->set_main_executable(); - link_map* map = &(si->link_map_head); + init_link_map_head(*si, executable_path); // Register the main executable and the linker upfront to have // gdb aware of them before loading the rest of the dependency // tree. - map->l_addr = 0; - map->l_name = const_cast(executable_path); - insert_link_map_into_debug_map(map); - insert_link_map_into_debug_map(&linker_link_map); + // + // gdb expects the linker to be in the debug shared object list. + // Without this, gdb has trouble locating the linker's ".text" + // and ".plt" sections. Gdb could also potentially use this to + // relocate the offset of our exported 'rtld_db_dlactivity' symbol. + // + insert_link_map_into_debug_map(&si->link_map_head); + insert_link_map_into_debug_map(&solinker->link_map_head); add_vdso(args); - // Extract information passed from the kernel. - si->phdr = reinterpret_cast(args.getauxval(AT_PHDR)); - si->phnum = args.getauxval(AT_PHNUM); - - get_elf_base_from_phdr(si->phdr, si->phnum, &si->base, &si->load_bias); - si->size = phdr_table_get_load_size(si->phdr, si->phnum); - - si->dynamic = nullptr; - ElfW(Ehdr)* elf_hdr = reinterpret_cast(si->base); // We haven't supported non-PIE since Lollipop for security reasons. @@ -351,8 +336,6 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args) { parse_LD_LIBRARY_PATH(ldpath_env); parse_LD_PRELOAD(ldpreload_env); - somain = si; - std::vector namespaces = init_default_namespaces(executable_path); if (!si->prelink_image()) __linker_cannot_link(g_argv[0]); @@ -411,13 +394,6 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args) { __get_tls()[TLS_SLOT_BIONIC_PREINIT] = &args; si->call_pre_init_constructors(); - - /* After the prelink_image, the si->load_bias is initialized. - * For so lib, the map->l_addr will be updated in notify_gdb_of_load. - * We need to update this value for so exe here. So Unwind_Backtrace - * for some arch like x86 could work correctly within so exe. - */ - map->l_addr = si->load_bias; si->call_constructors(); #if TIMING @@ -508,9 +484,7 @@ static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_cou } static ElfW(Addr) __attribute__((noinline)) -__linker_init_post_relocation(KernelArgumentBlock& args, - ElfW(Addr) linker_addr, - soinfo& linker_so); +__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& linker_so); /* * This is the entry point for the linker, called from begin.S. This @@ -543,18 +517,18 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_addr); ElfW(Phdr)* phdr = reinterpret_cast(linker_addr + elf_hdr->e_phoff); - soinfo linker_so(nullptr, nullptr, nullptr, 0, 0); + soinfo tmp_linker_so(nullptr, nullptr, nullptr, 0, 0); - linker_so.base = linker_addr; - linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum); - linker_so.load_bias = get_elf_exec_load_bias(elf_hdr); - linker_so.dynamic = nullptr; - linker_so.phdr = phdr; - linker_so.phnum = elf_hdr->e_phnum; - linker_so.set_linker_flag(); + tmp_linker_so.base = linker_addr; + tmp_linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum); + tmp_linker_so.load_bias = get_elf_exec_load_bias(elf_hdr); + tmp_linker_so.dynamic = nullptr; + tmp_linker_so.phdr = phdr; + tmp_linker_so.phnum = elf_hdr->e_phnum; + tmp_linker_so.set_linker_flag(); // Prelink the linker so we can access linker globals. - if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]); + if (!tmp_linker_so.prelink_image()) __linker_cannot_link(args.argv[0]); // This might not be obvious... The reasons why we pass g_empty_list // in place of local_group here are (1) we do not really need it, because @@ -562,9 +536,9 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // itself without having to look into local_group and (2) allocators // are not yet initialized, and therefore we cannot use linked_list.push_* // functions at this point. - if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]); + if (!tmp_linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]); - return __linker_init_post_relocation(args, linker_addr, linker_so); + return __linker_init_post_relocation(args, tmp_linker_so); } /* @@ -574,15 +548,13 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { * function, so avoid inlining this function (http://b/80503879). */ static ElfW(Addr) __attribute__((noinline)) -__linker_init_post_relocation(KernelArgumentBlock& args, - ElfW(Addr) linker_addr, - soinfo& linker_so) { +__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) { // Initialize the main thread (including TLS, so system calls really work). __libc_init_main_thread(args); // We didn't protect the linker's RELRO pages in link_image because we // couldn't make system calls on x86 at that point, but we can now... - if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]); + if (!tmp_linker_so.protect_relro()) __linker_cannot_link(args.argv[0]); // Initialize the linker/libc.so shared global inside the linker. static libc_shared_globals shared_globals; @@ -599,7 +571,7 @@ __linker_init_post_relocation(KernelArgumentBlock& args, g_envp = args.envp; // Initialize the linker's own global variables - linker_so.call_constructors(); + tmp_linker_so.call_constructors(); // If the linker is not acting as PT_INTERP entry_point is equal to // _start. Which means that the linker is running as an executable and @@ -615,13 +587,12 @@ __linker_init_post_relocation(KernelArgumentBlock& args, exit(0); } - init_linker_info_for_gdb(linker_addr, kLinkerPath); - // Initialize static variables. Note that in order to // get correct libdl_info we need to call constructors // before get_libdl_info(). - sonext = solist = get_libdl_info(kLinkerPath, linker_so, linker_link_map); - g_default_namespace.add_soinfo(solist); + sonext = solist = solinker = get_libdl_info(kLinkerPath, tmp_linker_so); + g_default_namespace.add_soinfo(solinker); + init_link_map_head(*solinker, kLinkerPath); args.abort_message_ptr = &g_abort_message; ElfW(Addr) start_address = linker_main(args); diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h index 09e506597..7331b2ffa 100644 --- a/linker/linker_soinfo.h +++ b/linker/linker_soinfo.h @@ -361,9 +361,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, - const link_map& linker_map); + friend soinfo* get_libdl_info(const char* linker_path, const soinfo& linker_si); // version >= 4 ElfW(Relr)* relr_;