From 3bbd218ef1a70e59662e704c59af6dff1f9d1253 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Fri, 22 Aug 2014 12:25:04 -0700 Subject: [PATCH] Bump soinfo version This includes: 1. Placing has_ifunc after fields with version = 0 2. Switch to has_min_version(v) function. 3. Minor soinfo initialization refactoring (placement new + ctor) (cherry picked from commit 0d15094287fe0f288d9c258953143fc1998b6b5a) Change-Id: Idf135fdd3d4826b5653f32add2adc6db5d4a4f95 --- linker/dlfcn.cpp | 11 ++---- linker/linker.cpp | 110 ++++++++++++++++++++++++++---------------------------- linker/linker.h | 18 ++++++--- 3 files changed, 69 insertions(+), 70 deletions(-) diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 5d6db8e29..e15f54d56 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -232,17 +232,12 @@ static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 }; static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; #endif -// Defined as global because we do not yet have access -// to synchronization functions __cxa_guard_* needed -// to define statics inside functions. -static soinfo __libdl_info; +static soinfo __libdl_info("libdl.so", nullptr); // This is used by the dynamic linker. Every process gets these symbols for free. soinfo* get_libdl_info() { - if (__libdl_info.name[0] == '\0') { - // initialize - strncpy(__libdl_info.name, "libdl.so", sizeof(__libdl_info.name)); - __libdl_info.flags = FLAG_LINKED | FLAG_NEW_SOINFO; + if ((__libdl_info.flags & FLAG_LINKED) == 0) { + __libdl_info.flags |= FLAG_LINKED; __libdl_info.strtab = ANDROID_LIBDL_STRTAB; __libdl_info.symtab = g_libdl_symtab; __libdl_info.nbucket = sizeof(g_libdl_buckets)/sizeof(unsigned); diff --git a/linker/linker.cpp b/linker/linker.cpp index ec0681fe6..e4a6523b7 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -35,9 +35,10 @@ #include #include #include -#include #include +#include + // Private C library headers. #include "private/bionic_tls.h" #include "private/KernelArgumentBlock.h" @@ -290,17 +291,7 @@ static soinfo* soinfo_alloc(const char* name, struct stat* file_stat) { return NULL; } - soinfo* si = g_soinfo_allocator.alloc(); - - // Initialize the new element. - memset(si, 0, sizeof(soinfo)); - strlcpy(si->name, name, sizeof(si->name)); - si->flags = FLAG_NEW_SOINFO; - - if (file_stat != NULL) { - si->set_st_dev(file_stat->st_dev); - si->set_st_ino(file_stat->st_ino); - } + soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat); sonext->next = si; sonext = si; @@ -466,6 +457,19 @@ static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name) return NULL; } +soinfo::soinfo(const char* name, const struct stat* file_stat) { + memset(this, 0, sizeof(*this)); + + strlcpy(this->name, name, sizeof(this->name)); + flags = FLAG_NEW_SOINFO; + version = SOINFO_VERSION; + + if (file_stat != NULL) { + set_st_dev(file_stat->st_dev); + set_st_ino(file_stat->st_ino); + } +} + void soinfo::resolve_ifunc_symbols() { if (!get_has_ifuncs()) { return; @@ -878,7 +882,7 @@ static void soinfo_unload(soinfo* si) { TRACE("unloading '%s'", si->name); si->CallDestructors(); - if ((si->flags | FLAG_NEW_SOINFO) != 0) { + if (si->has_min_version(0)) { si->get_children().for_each([&] (soinfo* child) { TRACE("%s needs to unload %s", si->name, child->name); soinfo_unload(child); @@ -1617,16 +1621,14 @@ void soinfo::CallDestructors() { } void soinfo::add_child(soinfo* child) { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return; + if (has_min_version(0)) { + this->children.push_front(child); + child->parents.push_front(this); } - - this->children.push_front(child); - child->parents.push_front(this); } void soinfo::remove_all_links() { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { + if (!has_min_version(0)) { return; } @@ -1649,51 +1651,45 @@ void soinfo::remove_all_links() { } void soinfo::set_st_dev(dev_t dev) { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return; + if (has_min_version(0)) { + st_dev = dev; } - - st_dev = dev; } void soinfo::set_st_ino(ino_t ino) { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return; + if (has_min_version(0)) { + st_ino = ino; } - - st_ino = ino; } void soinfo::set_has_ifuncs(bool ifuncs) { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return; + if (has_min_version(1)) { + has_ifuncs = ifuncs; } - - has_ifuncs = ifuncs; } dev_t soinfo::get_st_dev() { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return 0; + if (has_min_version(0)) { + return st_dev; } - return st_dev; + return 0; }; ino_t soinfo::get_st_ino() { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return 0; + if (has_min_version(0)) { + return st_ino; } - return st_ino; + return 0; } bool soinfo::get_has_ifuncs() { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return false; + if (has_min_version(1)) { + return has_ifuncs; } - return has_ifuncs; + return false; } // This is a return on get_children() in case @@ -1701,11 +1697,11 @@ bool soinfo::get_has_ifuncs() { static soinfo::soinfo_list_t g_empty_list; soinfo::soinfo_list_t& soinfo::get_children() { - if ((this->flags & FLAG_NEW_SOINFO) == 0) { - return g_empty_list; + if (has_min_version(0)) { + return this->children; } - return this->children; + return g_empty_list; } /* Force any of the closed stdin, stdout and stderr to be associated with @@ -2167,7 +2163,12 @@ static void add_vdso(KernelArgumentBlock& args __unused) { /* * This is linker soinfo for GDB. See details below. */ -static soinfo linker_soinfo_for_gdb; +#if defined(__LP64__) +#define LINKER_PATH "/system/bin/linker64" +#else +#define LINKER_PATH "/system/bin/linker" +#endif +static soinfo linker_soinfo_for_gdb(LINKER_PATH, nullptr); /* gdb expects the linker to be in the debug shared object list. * Without this, gdb has trouble locating the linker's ".text" @@ -2177,12 +2178,6 @@ static soinfo linker_soinfo_for_gdb; * be on the soinfo list. */ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { -#if defined(__LP64__) - strlcpy(linker_soinfo_for_gdb.name, "/system/bin/linker64", sizeof(linker_soinfo_for_gdb.name)); -#else - strlcpy(linker_soinfo_for_gdb.name, "/system/bin/linker", sizeof(linker_soinfo_for_gdb.name)); -#endif - linker_soinfo_for_gdb.flags = FLAG_NEW_SOINFO; linker_soinfo_for_gdb.base = linker_base; /* @@ -2401,10 +2396,6 @@ extern "C" void _start(); * function, or other GOT reference will generate a segfault. */ extern "C" ElfW(Addr) __linker_init(void* raw_args) { - // Initialize static variables. - solist = get_libdl_info(); - sonext = get_libdl_info(); - KernelArgumentBlock args(raw_args); ElfW(Addr) linker_addr = args.getauxval(AT_BASE); @@ -2412,8 +2403,7 @@ 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; - memset(&linker_so, 0, sizeof(soinfo)); + soinfo linker_so("[dynamic linker]", nullptr); // 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 @@ -2425,7 +2415,6 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { __libc_fatal("This is %s, the helper program for shared library executables.\n", args.argv[0]); } - strcpy(linker_so.name, "[dynamic linker]"); 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); @@ -2449,6 +2438,13 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // Initialize the linker's own global variables linker_so.CallConstructors(); + // Initialize static variables. Note that in order to + // get correct libdl_info we need to call constructors + // before get_libdl_info(). + solist = get_libdl_info(); + sonext = get_libdl_info(); + + // We have successfully fixed our own relocations. It's safe to run // the main part of the linker now. args.abort_message_ptr = &g_abort_message; diff --git a/linker/linker.h b/linker/linker.h index 684561a88..5e21f7015 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -30,6 +30,7 @@ #define _LINKER_H_ #include +#include #include #include #include @@ -88,6 +89,8 @@ #define FLAG_LINKER 0x00000010 // The linker itself #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format +#define SOINFO_VERSION 1 + #define SOINFO_NAME_LEN 128 typedef void (*linker_function_t)(); @@ -195,6 +198,9 @@ struct soinfo { bool has_text_relocations; #endif bool has_DT_SYMBOLIC; + + soinfo(const char* name, const struct stat* file_stat); + void CallConstructors(); void CallDestructors(); void CallPreInitConstructors(); @@ -209,10 +215,11 @@ struct soinfo { dev_t get_st_dev(); bool get_has_ifuncs(); - - soinfo_list_t& get_children(); + bool inline has_min_version(uint32_t min_version) { + return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version; + } private: void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void CallFunction(const char* function_name, linker_function_t function); @@ -221,10 +228,9 @@ struct soinfo { private: // This part of the structure is only available // when FLAG_NEW_SOINFO is set in this->flags. - unsigned int version; - - bool has_ifuncs; + uint32_t version; + // version >= 0 dev_t st_dev; ino_t st_ino; @@ -232,6 +238,8 @@ struct soinfo { soinfo_list_t children; soinfo_list_t parents; + // version >= 1 + bool has_ifuncs; }; extern soinfo* get_libdl_info(); -- 2.11.0