diff options
-rw-r--r-- | linker/Android.bp | 2 | ||||
-rw-r--r-- | linker/linker.cpp | 72 | ||||
-rw-r--r-- | linker/linker.h | 2 | ||||
-rw-r--r-- | linker/linker_cfi.cpp | 3 | ||||
-rw-r--r-- | linker/linker_debug.cpp | 47 | ||||
-rw-r--r-- | linker/linker_debug.h | 26 | ||||
-rw-r--r-- | linker/linker_soinfo.cpp | 58 | ||||
-rw-r--r-- | linker/linker_soinfo.h | 11 |
8 files changed, 119 insertions, 102 deletions
diff --git a/linker/Android.bp b/linker/Android.bp index 3b25d123e..8e810d2fe 100644 --- a/linker/Android.bp +++ b/linker/Android.bp @@ -156,6 +156,7 @@ filegroup { "linker_dlwarning.cpp", "linker_cfi.cpp", "linker_config.cpp", + "linker_debug.cpp", "linker_gdb_support.cpp", "linker_globals.cpp", "linker_libc_support.c", @@ -460,6 +461,7 @@ cc_test { // Parts of the linker that we're testing. "linker_block_allocator.cpp", "linker_config.cpp", + "linker_debug.cpp", "linker_test_globals.cpp", "linker_utils.cpp", ], diff --git a/linker/linker.cpp b/linker/linker.cpp index 8b0520ae8..10833be9d 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -508,10 +508,7 @@ bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, */ if (si_from->has_DT_SYMBOLIC) { DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_realpath(), name); - if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) { - return false; - } - + s = si_from->find_symbol_by_name(symbol_name, vi); if (s != nullptr) { *si_found_in = si_from; } @@ -519,15 +516,10 @@ bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, // 1. Look for it in global_group if (s == nullptr) { - bool error = false; global_group.visit([&](soinfo* global_si) { DEBUG("%s: looking up %s in %s (from global group)", si_from->get_realpath(), name, global_si->get_realpath()); - if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) { - error = true; - return false; - } - + s = global_si->find_symbol_by_name(symbol_name, vi); if (s != nullptr) { *si_found_in = global_si; return false; @@ -535,15 +527,10 @@ bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, return true; }); - - if (error) { - return false; - } } // 2. Look for it in the local group if (s == nullptr) { - bool error = false; local_group.visit([&](soinfo* local_si) { if (local_si == si_from && si_from->has_DT_SYMBOLIC) { // we already did this - skip @@ -552,11 +539,7 @@ bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, DEBUG("%s: looking up %s in %s (from local group)", si_from->get_realpath(), name, local_si->get_realpath()); - if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) { - error = true; - return false; - } - + s = local_si->find_symbol_by_name(symbol_name, vi); if (s != nullptr) { *si_found_in = local_si; return false; @@ -564,10 +547,6 @@ bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, return true; }); - - if (error) { - return false; - } } if (s != nullptr) { @@ -863,11 +842,7 @@ static const ElfW(Sym)* dlsym_handle_lookup(android_namespace_t* ns, return kWalkSkip; } - if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) { - result = nullptr; - return kWalkStop; - } - + result = current_soinfo->find_symbol_by_name(symbol_name, vi); if (result != nullptr) { *found = current_soinfo; return kWalkStop; @@ -947,10 +922,7 @@ static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, continue; } - if (!si->find_symbol_by_name(symbol_name, vi, &s)) { - return nullptr; - } - + s = si->find_symbol_by_name(symbol_name, vi); if (s != nullptr) { *found = si; break; @@ -2819,25 +2791,38 @@ static bool for_each_verdef(const soinfo* si, F functor) { return true; } -bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym) { +ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi) { if (vi == nullptr) { - *versym = kVersymNotNeeded; - return true; + return kVersymNotNeeded; } - *versym = kVersymGlobal; + ElfW(Versym) result = kVersymGlobal; - return for_each_verdef(si, + if (!for_each_verdef(si, [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { if (verdef->vd_hash == vi->elf_hash && strcmp(vi->name, si->get_string(verdaux->vda_name)) == 0) { - *versym = verdef->vd_ndx; + result = verdef->vd_ndx; return true; } return false; } - ); + )) { + // verdef should have already been validated in prelink_image. + async_safe_fatal("invalid verdef after prelinking: %s, %s", + si->get_realpath(), linker_get_error_buffer()); + } + + return result; +} + +// Validate the library's verdef section. On error, returns false and invokes DL_ERR. +bool validate_verdef_section(const soinfo* si) { + return for_each_verdef(si, + [&](size_t, const ElfW(Verdef)*, const ElfW(Verdaux)*) { + return false; + }); } bool VersionTracker::init_verdef(const soinfo* si_from) { @@ -3346,6 +3331,7 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r static soinfo_list_t g_empty_list; bool soinfo::prelink_image() { + if (flags_ & FLAG_PRELINKED) return true; /* Extract dynamic section */ ElfW(Word) dynamic_flags = 0; phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags); @@ -3840,6 +3826,12 @@ bool soinfo::prelink_image() { // Don't call add_dlwarning because a missing DT_SONAME isn't important enough to show in the UI } + + // Validate each library's verdef section once, so we don't have to validate + // it each time we look up a symbol with a version. + if (!validate_verdef_section(this)) return false; + + flags_ |= FLAG_PRELINKED; return true; } diff --git a/linker/linker.h b/linker/linker.h index 789640cc5..16c65a1e0 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -204,3 +204,5 @@ struct address_space_params { size_t reserved_size = 0; bool must_use_address = false; }; + +ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi); diff --git a/linker/linker_cfi.cpp b/linker/linker_cfi.cpp index 435bb1ac3..5995013b4 100644 --- a/linker/linker_cfi.cpp +++ b/linker/linker_cfi.cpp @@ -142,8 +142,7 @@ static soinfo* find_libdl(soinfo* solist) { static uintptr_t soinfo_find_symbol(soinfo* si, const char* s) { SymbolName name(s); - const ElfW(Sym) * sym; - if (si->find_symbol_by_name(name, nullptr, &sym) && sym) { + if (const ElfW(Sym)* sym = si->find_symbol_by_name(name, nullptr)) { return si->resolve_symbol_address(sym); } return 0; diff --git a/linker/linker_debug.cpp b/linker/linker_debug.cpp new file mode 100644 index 000000000..b0aae79b9 --- /dev/null +++ b/linker/linker_debug.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "linker_debug.h" + +#include <unistd.h> + +void linker_log_va_list(int prio __unused, const char* fmt, va_list ap) { +#if LINKER_DEBUG_TO_LOG + async_safe_format_log_va_list(5 - prio, "linker", fmt, ap); +#else + async_safe_format_fd_va_list(STDOUT_FILENO, fmt, ap); + write(STDOUT_FILENO, "\n", 1); +#endif +} + +void linker_log(int prio, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + linker_log_va_list(prio, fmt, ap); + va_end(ap); +} diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 6031850a4..738ff6f58 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -52,31 +52,33 @@ * To enable/disable specific debug options, change the defines above *********************************************************************/ +#include <stdarg.h> #include <unistd.h> #include <async_safe/log.h> #include <async_safe/CHECK.h> +#define LINKER_VERBOSITY_PRINT -1 +#define LINKER_VERBOSITY_INFO 0 +#define LINKER_VERBOSITY_TRACE 1 +#define LINKER_VERBOSITY_DEBUG 2 + __LIBC_HIDDEN__ extern int g_ld_debug_verbosity; -#if LINKER_DEBUG_TO_LOG -#define _PRINTVF(v, x...) \ - do { \ - if (g_ld_debug_verbosity > (v)) async_safe_format_log(5-(v), "linker", x); \ - } while (0) -#else /* !LINKER_DEBUG_TO_LOG */ +__LIBC_HIDDEN__ void linker_log_va_list(int prio, const char* fmt, va_list ap); +__LIBC_HIDDEN__ void linker_log(int prio, const char* fmt, ...) __printflike(2, 3); + #define _PRINTVF(v, x...) \ do { \ - if (g_ld_debug_verbosity > (v)) { async_safe_format_fd(1, x); write(1, "\n", 1); } \ + if (g_ld_debug_verbosity > (v)) linker_log((v), x); \ } while (0) -#endif /* !LINKER_DEBUG_TO_LOG */ -#define PRINT(x...) _PRINTVF(-1, x) -#define INFO(x...) _PRINTVF(0, x) -#define TRACE(x...) _PRINTVF(1, x) +#define PRINT(x...) _PRINTVF(LINKER_VERBOSITY_PRINT, x) +#define INFO(x...) _PRINTVF(LINKER_VERBOSITY_INFO, x) +#define TRACE(x...) _PRINTVF(LINKER_VERBOSITY_TRACE, x) #if TRACE_DEBUG -#define DEBUG(x...) _PRINTVF(2, "DEBUG: " x) +#define DEBUG(x...) _PRINTVF(LINKER_VERBOSITY_DEBUG, "DEBUG: " x) #else /* !TRACE_DEBUG */ #define DEBUG(x...) do {} while (0) #endif /* TRACE_DEBUG */ diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp index 04aa27bd2..8efc9fee7 100644 --- a/linker/linker_soinfo.cpp +++ b/linker/linker_soinfo.cpp @@ -36,6 +36,7 @@ #include <async_safe/log.h> +#include "linker.h" #include "linker_config.h" #include "linker_debug.h" #include "linker_globals.h" @@ -43,7 +44,6 @@ #include "linker_utils.h" // TODO(dimitry): These functions are currently located in linker.cpp - find a better place for it -bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym); ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr); int get_application_target_sdk_version(); @@ -135,22 +135,6 @@ size_t soinfo::get_verdef_cnt() const { return 0; } -bool soinfo::find_symbol_by_name(SymbolName& symbol_name, - const version_info* vi, - const ElfW(Sym)** symbol) const { - uint32_t symbol_index; - bool success = - is_gnu_hash() ? - gnu_lookup(symbol_name, vi, &symbol_index) : - elf_lookup(symbol_name, vi, &symbol_index); - - if (success) { - *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index; - } - - return success; -} - static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { if (ELF_ST_BIND(s->st_info) == STB_GLOBAL || ELF_ST_BIND(s->st_info) == STB_WEAK) { @@ -177,9 +161,12 @@ static inline bool check_symbol_version(const ElfW(Versym) verneed, verneed == (*verdef & ~kVersymHiddenBit); } -bool soinfo::gnu_lookup(SymbolName& symbol_name, - const version_info* vi, - uint32_t* symbol_index) const { +const ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi) const { + return is_gnu_hash() ? gnu_lookup(symbol_name, vi) : elf_lookup(symbol_name, vi); +} + +const ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name, const version_info* vi) const { uint32_t hash = symbol_name.gnu_hash(); uint32_t h2 = hash >> gnu_shift2_; @@ -187,8 +174,6 @@ bool soinfo::gnu_lookup(SymbolName& symbol_name, uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_; ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num]; - *symbol_index = 0; - TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); @@ -197,7 +182,7 @@ bool soinfo::gnu_lookup(SymbolName& symbol_name, TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); - return true; + return nullptr; } // bloom test says "probably yes"... @@ -207,7 +192,7 @@ bool soinfo::gnu_lookup(SymbolName& symbol_name, TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); - return true; + return nullptr; } // lookup versym for the version definition in this library @@ -216,10 +201,7 @@ bool soinfo::gnu_lookup(SymbolName& symbol_name, // which implies that the default version can be accepted; the second case results in // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols // for this library and consider only *global* ones. - ElfW(Versym) verneed = 0; - if (!find_verdef_version_index(this, vi, &verneed)) { - return false; - } + const ElfW(Versym) verneed = find_verdef_version_index(this, vi); do { ElfW(Sym)* s = symtab_ + n; @@ -235,30 +217,24 @@ bool soinfo::gnu_lookup(SymbolName& symbol_name, TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value), static_cast<size_t>(s->st_size)); - *symbol_index = n; - return true; + return symtab_ + n; } } while ((gnu_chain_[n++] & 1) == 0); TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); - return true; + return nullptr; } -bool soinfo::elf_lookup(SymbolName& symbol_name, - const version_info* vi, - uint32_t* symbol_index) const { +const ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name, const version_info* vi) const { uint32_t hash = symbol_name.elf_hash(); TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd", symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base), hash, hash % nbucket_); - ElfW(Versym) verneed = 0; - if (!find_verdef_version_index(this, vi, &verneed)) { - return false; - } + const ElfW(Versym) verneed = find_verdef_version_index(this, vi); for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { ElfW(Sym)* s = symtab_ + n; @@ -276,8 +252,7 @@ bool soinfo::elf_lookup(SymbolName& symbol_name, symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value), static_cast<size_t>(s->st_size)); - *symbol_index = n; - return true; + return symtab_ + n; } } @@ -285,8 +260,7 @@ bool soinfo::elf_lookup(SymbolName& symbol_name, symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base), hash, hash % nbucket_); - *symbol_index = 0; - return true; + return nullptr; } ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) { diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h index dd3817cf7..9ae17eab8 100644 --- a/linker/linker_soinfo.h +++ b/linker/linker_soinfo.h @@ -63,6 +63,7 @@ // destructor associated with this // soinfo is executed and this flag is // unset. +#define FLAG_PRELINKED 0x00000400 // prelink_image has successfully processed this soinfo #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format #define SOINFO_VERSION 5 @@ -242,9 +243,7 @@ struct soinfo { soinfo_list_t& get_parents(); - bool find_symbol_by_name(SymbolName& symbol_name, - const version_info* vi, - const ElfW(Sym)** symbol) const; + const ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name, const version_info* vi) const; ElfW(Sym)* find_symbol_by_address(const void* addr); ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const; @@ -309,10 +308,10 @@ struct soinfo { bool is_image_linked() const; void set_image_linked(); - bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; - ElfW(Sym)* elf_addr_lookup(const void* addr); - bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; + const ElfW(Sym)* gnu_lookup(SymbolName& symbol_name, const version_info* vi) const; + const ElfW(Sym)* elf_lookup(SymbolName& symbol_name, const version_info* vi) const; ElfW(Sym)* gnu_addr_lookup(const void* addr); + ElfW(Sym)* elf_addr_lookup(const void* addr); bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, const char* sym_name, const version_info** vi); |