diff options
-rw-r--r-- | linker/Android.bp | 2 | ||||
-rw-r--r-- | linker/dlfcn.cpp | 3 | ||||
-rw-r--r-- | linker/linker.cpp | 1025 | ||||
-rw-r--r-- | linker/linker.h | 328 | ||||
-rw-r--r-- | linker/linker_common_types.h | 9 | ||||
-rw-r--r-- | linker/linker_globals.cpp | 50 | ||||
-rw-r--r-- | linker/linker_globals.h | 71 | ||||
-rw-r--r-- | linker/linker_namespaces.h | 1 | ||||
-rw-r--r-- | linker/linker_phdr.cpp | 1 | ||||
-rw-r--r-- | linker/linker_soinfo.cpp | 795 | ||||
-rw-r--r-- | linker/linker_soinfo.h | 345 | ||||
-rw-r--r-- | linker/linker_utils.cpp | 68 | ||||
-rw-r--r-- | linker/linker_utils.h | 13 | ||||
-rw-r--r-- | linker/tests/Android.mk | 2 |
14 files changed, 1460 insertions, 1253 deletions
diff --git a/linker/Android.bp b/linker/Android.bp index 73c0e903d..39f1da9c5 100644 --- a/linker/Android.bp +++ b/linker/Android.bp @@ -20,12 +20,14 @@ cc_binary { "linker_block_allocator.cpp", "linker_dlwarning.cpp", "linker_gdb_support.cpp", + "linker_globals.cpp", "linker_libc_support.c", "linker_namespaces.cpp", "linker_logger.cpp", "linker_mapped_file_fragment.cpp", "linker_phdr.cpp", "linker_sdk_versions.cpp", + "linker_soinfo.cpp", "linker_utils.cpp", "rt.cpp", ], diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index c03ffa812..4d9a2184d 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -15,6 +15,7 @@ */ #include "linker.h" +#include "linker_globals.h" #include "linker_dlwarning.h" #include <pthread.h> @@ -261,8 +262,6 @@ static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); static soinfo* __libdl_info = nullptr; -extern android_namespace_t g_default_namespace; - // This is used by the dynamic linker. Every process gets these symbols for free. soinfo* get_libdl_info() { if (__libdl_info == nullptr) { diff --git a/linker/linker.cpp b/linker/linker.cpp index b57ba2728..ca8ffbcbf 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -53,6 +53,7 @@ #include "linker.h" #include "linker_block_allocator.h" #include "linker_gdb_support.h" +#include "linker_globals.h" #include "linker_debug.h" #include "linker_dlwarning.h" #include "linker_namespaces.h" @@ -76,9 +77,6 @@ extern "C" void _start(); #undef ELF_ST_TYPE #define ELF_ST_TYPE(x) (static_cast<uint32_t>(x) & 0xf) -android_namespace_t g_default_namespace; - -static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; static android_namespace_t* g_anonymous_namespace = &g_default_namespace; static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); @@ -134,8 +132,6 @@ static const char* const kSystemLibDir = "/system/lib64"; static const char* const kSystemLibDir = "/system/lib"; #endif -static std::string dirname(const char *path); - // TODO(dimitry): The grey-list is a workaround for http://b/26394120 --- // gradually remove libraries from this list until it is gone. static bool is_greylisted(const char* name, const soinfo* needed_by) { @@ -186,9 +182,6 @@ static bool is_greylisted(const char* name, const soinfo* needed_by) { } // END OF WORKAROUND -static const ElfW(Versym) kVersymNotNeeded = 0; -static const ElfW(Versym) kVersymGlobal = 1; - static const char* const* g_default_ld_paths; static std::vector<std::string> g_ld_preload_names; @@ -200,19 +193,6 @@ static soinfo_list_t g_public_namespace; int g_ld_debug_verbosity; abort_msg_t* g_abort_message = nullptr; // For debuggerd. -// These values are used to call constructors for .init_array && .preinit_array -int g_argc = 0; -char** g_argv = nullptr; -char** g_envp = nullptr; - -static std::string dirname(const char *path) { - const char* last_slash = strrchr(path, '/'); - if (last_slash == path) return "/"; - else if (last_slash == nullptr) return "."; - else - return std::string(path, last_slash - path); -} - #if STATS struct linker_stats_t { int count[kRelocMax]; @@ -232,16 +212,6 @@ void count_relocation(RelocationKind) { uint32_t bitmask[4096]; #endif -static char __linker_dl_err_buf[768]; - -char* linker_get_error_buffer() { - return &__linker_dl_err_buf[0]; -} - -size_t linker_get_error_buffer_size() { - return sizeof(__linker_dl_err_buf); -} - static void notify_gdb_of_load(soinfo* info) { if (info->is_linker() || info->is_main_executable()) { // gdb already knows about the linker and the main executable. @@ -348,60 +318,6 @@ static void soinfo_free(soinfo* si) { g_soinfo_allocator.free(si); } -// For every path element this function checks of it exists, and is a directory, -// and normalizes it: -// 1. For regular path it converts it to realpath() -// 2. For path in a zip file it uses realpath on the zipfile -// normalizes entry name by calling normalize_path function. -static void resolve_paths(std::vector<std::string>& paths, - std::vector<std::string>* resolved_paths) { - resolved_paths->clear(); - for (const auto& path : paths) { - char resolved_path[PATH_MAX]; - const char* original_path = path.c_str(); - if (realpath(original_path, resolved_path) != nullptr) { - struct stat s; - if (stat(resolved_path, &s) == 0) { - if (S_ISDIR(s.st_mode)) { - resolved_paths->push_back(resolved_path); - } else { - DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path); - continue; - } - } else { - DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno)); - continue; - } - } else { - std::string zip_path; - std::string entry_path; - - std::string normalized_path; - - if (!normalize_path(original_path, &normalized_path)) { - DL_WARN("Warning: unable to normalize \"%s\"", original_path); - continue; - } - - if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) { - if (realpath(zip_path.c_str(), resolved_path) == nullptr) { - DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno)); - continue; - } - - resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path); - } - } - } -} - -static void split_path(const char* path, const char* delimiters, - std::vector<std::string>* paths) { - if (path != nullptr && path[0] != 0) { - *paths = android::base::Split(path, delimiters); - } -} - static void parse_path(const char* path, const char* delimiters, std::vector<std::string>* resolved_paths) { std::vector<std::string> paths; @@ -415,45 +331,6 @@ static void parse_LD_LIBRARY_PATH(const char* path) { g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths)); } -void soinfo::set_dt_runpath(const char* path) { - if (!has_min_version(3)) { - return; - } - - std::vector<std::string> runpaths; - - split_path(path, ":", &runpaths); - - std::string origin = dirname(get_realpath()); - // FIXME: add $LIB and $PLATFORM. - std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}}; - for (auto&& s : runpaths) { - size_t pos = 0; - while (pos < s.size()) { - pos = s.find("$", pos); - if (pos == std::string::npos) break; - for (const auto& subst : substs) { - const std::string& token = subst.first; - const std::string& replacement = subst.second; - if (s.substr(pos + 1, token.size()) == token) { - s.replace(pos, token.size() + 1, replacement); - // -1 to compensate for the ++pos below. - pos += replacement.size() - 1; - break; - } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") { - s.replace(pos, token.size() + 3, replacement); - pos += replacement.size() - 1; - break; - } - } - // Skip $ in case it did not match any of the known substitutions. - ++pos; - } - } - - resolve_paths(runpaths, &dt_runpath_); -} - static void parse_LD_PRELOAD(const char* path) { g_ld_preload_names.clear(); if (path != nullptr) { @@ -520,332 +397,6 @@ int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), v return rv; } -const ElfW(Versym)* soinfo::get_versym(size_t n) const { - if (has_min_version(2) && versym_ != nullptr) { - return versym_ + n; - } - - return nullptr; -} - -ElfW(Addr) soinfo::get_verneed_ptr() const { - if (has_min_version(2)) { - return verneed_ptr_; - } - - return 0; -} - -size_t soinfo::get_verneed_cnt() const { - if (has_min_version(2)) { - return verneed_cnt_; - } - - return 0; -} - -ElfW(Addr) soinfo::get_verdef_ptr() const { - if (has_min_version(2)) { - return verdef_ptr_; - } - - return 0; -} - -size_t soinfo::get_verdef_cnt() const { - if (has_min_version(2)) { - return verdef_cnt_; - } - - return 0; -} - -template<typename F> -static bool for_each_verdef(const soinfo* si, F functor) { - if (!si->has_min_version(2)) { - return true; - } - - uintptr_t verdef_ptr = si->get_verdef_ptr(); - if (verdef_ptr == 0) { - return true; - } - - size_t offset = 0; - - size_t verdef_cnt = si->get_verdef_cnt(); - for (size_t i = 0; i<verdef_cnt; ++i) { - const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset); - size_t verdaux_offset = offset + verdef->vd_aux; - offset += verdef->vd_next; - - if (verdef->vd_version != 1) { - DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s", - i, verdef->vd_version, si->get_realpath()); - return false; - } - - if ((verdef->vd_flags & VER_FLG_BASE) != 0) { - // "this is the version of the file itself. It must not be used for - // matching a symbol. It can be used to match references." - // - // http://www.akkadia.org/drepper/symbol-versioning - continue; - } - - if (verdef->vd_cnt == 0) { - DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i); - return false; - } - - const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset); - - if (functor(i, verdef, verdaux) == true) { - break; - } - } - - return true; -} - -bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const { - if (vi == nullptr) { - *versym = kVersymNotNeeded; - return true; - } - - *versym = kVersymGlobal; - - return for_each_verdef(this, - [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { - if (verdef->vd_hash == vi->elf_hash && - strcmp(vi->name, get_string(verdaux->vda_name)) == 0) { - *versym = verdef->vd_ndx; - return true; - } - - return false; - } - ); -} - -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) { - return s->st_shndx != SHN_UNDEF; - } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) { - DL_WARN("unexpected ST_BIND value: %d for \"%s\" in \"%s\"", - ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath()); - } - - return false; -} - -static const ElfW(Versym) kVersymHiddenBit = 0x8000; - -static inline bool is_versym_hidden(const ElfW(Versym)* versym) { - // the symbol is hidden if bit 15 of versym is set. - return versym != nullptr && (*versym & kVersymHiddenBit) != 0; -} - -static inline bool check_symbol_version(const ElfW(Versym) verneed, - const ElfW(Versym)* verdef) { - return verneed == kVersymNotNeeded || - verdef == nullptr || - verneed == (*verdef & ~kVersymHiddenBit); -} - -bool soinfo::gnu_lookup(SymbolName& symbol_name, - const version_info* vi, - uint32_t* symbol_index) const { - uint32_t hash = symbol_name.gnu_hash(); - uint32_t h2 = hash >> gnu_shift2_; - - uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8; - 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)); - - // test against bloom filter - if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { - TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", - symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); - - return true; - } - - // bloom test says "probably yes"... - uint32_t n = gnu_bucket_[hash % gnu_nbucket_]; - - if (n == 0) { - TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", - symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); - - return true; - } - - // lookup versym for the version definition in this library - // note the difference between "version is not requested" (vi == nullptr) - // and "version not found". In the first case verneed is kVersymNotNeeded - // 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(vi, &verneed)) { - return false; - } - - do { - ElfW(Sym)* s = symtab_ + n; - const ElfW(Versym)* verdef = get_versym(n); - // skip hidden versions when verneed == kVersymNotNeeded (0) - if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { - continue; - } - if (((gnu_chain_[n] ^ hash) >> 1) == 0 && - check_symbol_version(verneed, verdef) && - strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && - is_symbol_global_and_defined(this, s)) { - 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; - } - } 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; -} - -bool soinfo::elf_lookup(SymbolName& symbol_name, - const version_info* vi, - uint32_t* symbol_index) 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(vi, &verneed)) { - return false; - } - - for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { - ElfW(Sym)* s = symtab_ + n; - const ElfW(Versym)* verdef = get_versym(n); - - // skip hidden versions when verneed == 0 - if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { - continue; - } - - if (check_symbol_version(verneed, verdef) && - strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && - is_symbol_global_and_defined(this, s)) { - 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; - } - } - - TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", - symbol_name.get_name(), get_realpath(), - reinterpret_cast<void*>(base), hash, hash % nbucket_); - - *symbol_index = 0; - return true; -} - -soinfo::soinfo(android_namespace_t* ns, const char* realpath, - const struct stat* file_stat, off64_t file_offset, - int rtld_flags) { - memset(this, 0, sizeof(*this)); - - if (realpath != nullptr) { - realpath_ = realpath; - } - - flags_ = FLAG_NEW_SOINFO; - version_ = SOINFO_VERSION; - - if (file_stat != nullptr) { - this->st_dev_ = file_stat->st_dev; - this->st_ino_ = file_stat->st_ino; - this->file_offset_ = file_offset; - } - - this->rtld_flags_ = rtld_flags; - this->primary_namespace_ = ns; -} - -soinfo::~soinfo() { - g_soinfo_handles_map.erase(handle_); -} - -static uint32_t calculate_elf_hash(const char* name) { - const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name); - uint32_t h = 0, g; - - while (*name_bytes) { - h = (h << 4) + *name_bytes++; - g = h & 0xf0000000; - h ^= g; - h ^= g >> 24; - } - - return h; -} - -uint32_t SymbolName::elf_hash() { - if (!has_elf_hash_) { - elf_hash_ = calculate_elf_hash(name_); - has_elf_hash_ = true; - } - - return elf_hash_; -} - -uint32_t SymbolName::gnu_hash() { - if (!has_gnu_hash_) { - uint32_t h = 5381; - const uint8_t* name = reinterpret_cast<const uint8_t*>(name_); - while (*name != 0) { - h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c - } - - gnu_hash_ = h; - has_gnu_hash_ = true; - } - - return gnu_hash_; -} bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, soinfo** si_found_in, const soinfo_list_t& global_group, @@ -1304,52 +855,6 @@ soinfo* find_containing_library(const void* p) { return nullptr; } -ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) { - return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr); -} - -static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) { - return sym->st_shndx != SHN_UNDEF && - soaddr >= sym->st_value && - soaddr < sym->st_value + sym->st_size; -} - -ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { - ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; - - for (size_t i = 0; i < gnu_nbucket_; ++i) { - uint32_t n = gnu_bucket_[i]; - - if (n == 0) { - continue; - } - - do { - ElfW(Sym)* sym = symtab_ + n; - if (symbol_matches_soaddr(sym, soaddr)) { - return sym; - } - } while ((gnu_chain_[n++] & 1) == 0); - } - - return nullptr; -} - -ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) { - ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; - - // Search the library's symbol table for any defined symbol which - // contains this address. - for (size_t i = 0; i < nchain_; ++i) { - ElfW(Sym)* sym = symtab_ + i; - if (symbol_matches_soaddr(sym, soaddr)) { - return sym; - } - } - - return nullptr; -} - class ZipArchiveCache { public: ZipArchiveCache() {} @@ -2549,7 +2054,7 @@ android_namespace_t* create_namespace(const void* caller_addr, return ns; } -static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { +ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { typedef ElfW(Addr) (*ifunc_resolver_t)(void); ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr); ElfW(Addr) ifunc_addr = ifunc_resolver(); @@ -2628,6 +2133,75 @@ bool VersionTracker::init_verneed(const soinfo* si_from) { return true; } +template <typename F> +static bool for_each_verdef(const soinfo* si, F functor) { + if (!si->has_min_version(2)) { + return true; + } + + uintptr_t verdef_ptr = si->get_verdef_ptr(); + if (verdef_ptr == 0) { + return true; + } + + size_t offset = 0; + + size_t verdef_cnt = si->get_verdef_cnt(); + for (size_t i = 0; i<verdef_cnt; ++i) { + const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset); + size_t verdaux_offset = offset + verdef->vd_aux; + offset += verdef->vd_next; + + if (verdef->vd_version != 1) { + DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1) library: %s", + i, verdef->vd_version, si->get_realpath()); + return false; + } + + if ((verdef->vd_flags & VER_FLG_BASE) != 0) { + // "this is the version of the file itself. It must not be used for + // matching a symbol. It can be used to match references." + // + // http://www.akkadia.org/drepper/symbol-versioning + continue; + } + + if (verdef->vd_cnt == 0) { + DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i); + return false; + } + + const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset); + + if (functor(i, verdef, verdaux) == true) { + break; + } + } + + return true; +} + +bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym) { + if (vi == nullptr) { + *versym = kVersymNotNeeded; + return true; + } + + *versym = kVersymGlobal; + + return 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; + return true; + } + + return false; + } + ); +} + bool VersionTracker::init_verdef(const soinfo* si_from) { return for_each_verdef(si_from, [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) { @@ -2646,6 +2220,25 @@ bool VersionTracker::init(const soinfo* si_from) { return init_verneed(si_from) && init_verdef(si_from); } +#if !defined(__mips__) +#if defined(USE_RELA) +static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { + return rela->r_addend; +} +#else +static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { + if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || + ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) { + return *reinterpret_cast<ElfW(Addr)*>(reloc_addr); + } + return 0; +} +#endif + +// TODO (dimitry): Methods below need to be moved out of soinfo +// and in more isolated file in order minimize dependencies on +// unnecessary object in the linker binary. Consider making them +// independent from soinfo (?). bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, const char* sym_name, const version_info** vi) { const ElfW(Versym)* sym_ver_ptr = get_versym(sym); @@ -2667,21 +2260,6 @@ bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Wor return true; } -#if !defined(__mips__) -#if defined(USE_RELA) -static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { - return rela->r_addend; -} -#else -static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { - if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || - ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) { - return *reinterpret_cast<ElfW(Addr)*>(reloc_addr); - } - return 0; -} -#endif - template<typename ElfRelIteratorT> bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { @@ -3046,409 +2624,9 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r } #endif // !defined(__mips__) -static void call_function(const char* function_name __unused, - linker_ctor_function_t function, - const char* realpath __unused) { - if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { - return; - } - - TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); - function(g_argc, g_argv, g_envp); - TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); -} - -static void call_function(const char* function_name __unused, - linker_dtor_function_t function, - const char* realpath __unused) { - if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { - return; - } - - TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); - function(); - TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); -} - -template <typename F> -static void call_array(const char* array_name __unused, - F* functions, - size_t count, - bool reverse, - const char* realpath) { - if (functions == nullptr) { - return; - } - - TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath); - - int begin = reverse ? (count - 1) : 0; - int end = reverse ? -1 : count; - int step = reverse ? -1 : 1; - - for (int i = begin; i != end; i += step) { - TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]); - call_function("function", functions[i], realpath); - } - - TRACE("[ Done calling %s for '%s' ]", array_name, realpath); -} - -void soinfo::call_pre_init_constructors() { - // DT_PREINIT_ARRAY functions are called before any other constructors for executables, - // but ignored in a shared library. - call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath()); -} - -void soinfo::call_constructors() { - if (constructors_called) { - return; - } - - // We set constructors_called before actually calling the constructors, otherwise it doesn't - // protect against recursive constructor calls. One simple example of constructor recursion - // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so: - // 1. The program depends on libc, so libc's constructor is called here. - // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. - // 3. dlopen() calls the constructors on the newly created - // soinfo for libc_malloc_debug_leak.so. - // 4. The debug .so depends on libc, so CallConstructors is - // called again with the libc soinfo. If it doesn't trigger the early- - // out above, the libc constructor will be called again (recursively!). - constructors_called = true; - - if (!is_main_executable() && preinit_array_ != nullptr) { - // The GNU dynamic linker silently ignores these, but we warn the developer. - PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath()); - } - - get_children().for_each([] (soinfo* si) { - si->call_constructors(); - }); - - TRACE("\"%s\": calling constructors", get_realpath()); - - // DT_INIT should be called before DT_INIT_ARRAY if both are present. - call_function("DT_INIT", init_func_, get_realpath()); - call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath()); -} - -void soinfo::call_destructors() { - if (!constructors_called) { - return; - } - TRACE("\"%s\": calling destructors", get_realpath()); - - // DT_FINI_ARRAY must be parsed in reverse order. - call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath()); - - // DT_FINI should be called after DT_FINI_ARRAY if both are present. - call_function("DT_FINI", fini_func_, get_realpath()); -} - -void soinfo::add_child(soinfo* child) { - if (has_min_version(0)) { - child->parents_.push_back(this); - this->children_.push_back(child); - } -} - -void soinfo::remove_all_links() { - if (!has_min_version(0)) { - return; - } - - // 1. Untie connected soinfos from 'this'. - children_.for_each([&] (soinfo* child) { - child->parents_.remove_if([&] (const soinfo* parent) { - return parent == this; - }); - }); - - parents_.for_each([&] (soinfo* parent) { - parent->children_.remove_if([&] (const soinfo* child) { - return child == this; - }); - }); - - // 2. Remove from the primary namespace - primary_namespace_->remove_soinfo(this); - primary_namespace_ = nullptr; - - // 3. Remove from secondary namespaces - secondary_namespaces_.for_each([&](android_namespace_t* ns) { - ns->remove_soinfo(this); - }); - - - // 4. Once everything untied - clear local lists. - parents_.clear(); - children_.clear(); - secondary_namespaces_.clear(); -} - -dev_t soinfo::get_st_dev() const { - if (has_min_version(0)) { - return st_dev_; - } - - return 0; -}; - -ino_t soinfo::get_st_ino() const { - if (has_min_version(0)) { - return st_ino_; - } - - return 0; -} - -off64_t soinfo::get_file_offset() const { - if (has_min_version(1)) { - return file_offset_; - } - - return 0; -} - -uint32_t soinfo::get_rtld_flags() const { - if (has_min_version(1)) { - return rtld_flags_; - } - - return 0; -} - -uint32_t soinfo::get_dt_flags_1() const { - if (has_min_version(1)) { - return dt_flags_1_; - } - - return 0; -} - -void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { - if (has_min_version(1)) { - if ((dt_flags_1 & DF_1_GLOBAL) != 0) { - rtld_flags_ |= RTLD_GLOBAL; - } - - if ((dt_flags_1 & DF_1_NODELETE) != 0) { - rtld_flags_ |= RTLD_NODELETE; - } - - dt_flags_1_ = dt_flags_1; - } -} - -void soinfo::set_nodelete() { - rtld_flags_ |= RTLD_NODELETE; -} - -const char* soinfo::get_realpath() const { -#if defined(__work_around_b_24465209__) - if (has_min_version(2)) { - return realpath_.c_str(); - } else { - return old_name_; - } -#else - return realpath_.c_str(); -#endif -} - -void soinfo::set_soname(const char* soname) { -#if defined(__work_around_b_24465209__) - if (has_min_version(2)) { - soname_ = soname; - } - strlcpy(old_name_, soname_, sizeof(old_name_)); -#else - soname_ = soname; -#endif -} - -const char* soinfo::get_soname() const { -#if defined(__work_around_b_24465209__) - if (has_min_version(2)) { - return soname_; - } else { - return old_name_; - } -#else - return soname_; -#endif -} - -// This is a return on get_children()/get_parents() if -// 'this->flags' does not have FLAG_NEW_SOINFO set. +// An empty list of soinfos static soinfo_list_t g_empty_list; -soinfo_list_t& soinfo::get_children() { - if (has_min_version(0)) { - return children_; - } - - return g_empty_list; -} - -const soinfo_list_t& soinfo::get_children() const { - if (has_min_version(0)) { - return children_; - } - - return g_empty_list; -} - -soinfo_list_t& soinfo::get_parents() { - if (has_min_version(0)) { - return parents_; - } - - return g_empty_list; -} - -static std::vector<std::string> g_empty_runpath; - -const std::vector<std::string>& soinfo::get_dt_runpath() const { - if (has_min_version(3)) { - return dt_runpath_; - } - - return g_empty_runpath; -} - -android_namespace_t* soinfo::get_primary_namespace() { - if (has_min_version(3)) { - return primary_namespace_; - } - - return &g_default_namespace; -} - -void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) { - CHECK(has_min_version(3)); - secondary_namespaces_.push_back(secondary_ns); -} - -ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { - if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { - return call_ifunc_resolver(s->st_value + load_bias); - } - - return static_cast<ElfW(Addr)>(s->st_value + load_bias); -} - -const char* soinfo::get_string(ElfW(Word) index) const { - if (has_min_version(1) && (index >= strtab_size_)) { - __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", - get_realpath(), strtab_size_, index); - } - - return strtab_ + index; -} - -bool soinfo::is_gnu_hash() const { - return (flags_ & FLAG_GNU_HASH) != 0; -} - -bool soinfo::can_unload() const { - return !is_linked() || ((get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0); -} - -bool soinfo::is_linked() const { - return (flags_ & FLAG_LINKED) != 0; -} - -bool soinfo::is_main_executable() const { - return (flags_ & FLAG_EXE) != 0; -} - -bool soinfo::is_linker() const { - return (flags_ & FLAG_LINKER) != 0; -} - -void soinfo::set_linked() { - flags_ |= FLAG_LINKED; -} - -void soinfo::set_linker_flag() { - flags_ |= FLAG_LINKER; -} - -void soinfo::set_main_executable() { - flags_ |= FLAG_EXE; -} - -void soinfo::increment_ref_count() { - local_group_root_->ref_count_++; -} - -size_t soinfo::decrement_ref_count() { - return --local_group_root_->ref_count_; -} - -soinfo* soinfo::get_local_group_root() const { - return local_group_root_; -} - - -void soinfo::set_mapped_by_caller(bool mapped_by_caller) { - if (mapped_by_caller) { - flags_ |= FLAG_MAPPED_BY_CALLER; - } else { - flags_ &= ~FLAG_MAPPED_BY_CALLER; - } -} - -bool soinfo::is_mapped_by_caller() const { - return (flags_ & FLAG_MAPPED_BY_CALLER) != 0; -} - -// This function returns api-level at the time of -// dlopen/load. Note that libraries opened by system -// will always have 'current' api level. -uint32_t soinfo::get_target_sdk_version() const { - if (!has_min_version(2)) { - return __ANDROID_API__; - } - - return local_group_root_->target_sdk_version_; -} - -uintptr_t soinfo::get_handle() const { - CHECK(has_min_version(3)); - CHECK(handle_ != 0); - return handle_; -} - -void* soinfo::to_handle() { - if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) { - return this; - } - - return reinterpret_cast<void*>(get_handle()); -} - -void soinfo::generate_handle() { - CHECK(has_min_version(3)); - CHECK(handle_ == 0); // Make sure this is the first call - - // Make sure the handle is unique and does not collide - // with special values which are RTLD_DEFAULT and RTLD_NEXT. - do { - arc4random_buf(&handle_, sizeof(handle_)); - // the least significant bit for the handle is always 1 - // making it easy to test the type of handle passed to - // dl* functions. - handle_ = handle_ | 1; - } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) || - handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) || - g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end()); - - g_soinfo_handles_map[handle_] = this; -} - bool soinfo::prelink_image() { /* Extract dynamic section */ ElfW(Word) dynamic_flags = 0; @@ -4371,8 +3549,8 @@ static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) { return 0; } -static void __linker_cannot_link() { - __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); +static void __linker_cannot_link(const char* argv0) { + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", argv0, linker_get_error_buffer()); } /* @@ -4387,10 +3565,6 @@ static void __linker_cannot_link() { extern "C" ElfW(Addr) __linker_init(void* raw_args) { KernelArgumentBlock args(raw_args); - g_argc = args.argc; - g_argv = args.argv; - g_envp = args.envp; - ElfW(Addr) linker_addr = args.getauxval(AT_BASE); ElfW(Addr) entry_point = args.getauxval(AT_ENTRY); ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr); @@ -4407,7 +3581,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) { __libc_format_fd(STDOUT_FILENO, "This is %s, the helper program for shared library executables.\n", - g_argv[0]); + args.argv[0]); exit(0); } @@ -4420,7 +3594,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { linker_so.set_linker_flag(); // Prelink the linker so we can access linker globals. - if (!linker_so.prelink_image()) __linker_cannot_link(); + if (!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 @@ -4428,7 +3602,7 @@ 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(); + if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]); #if defined(__i386__) // On x86, we can't make system calls before this point. @@ -4444,11 +3618,16 @@ extern "C" ElfW(Addr) __linker_init(void* raw_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(); + if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]); // Initialize the linker's static libc's globals __libc_init_globals(args); + // store argc/argv/envp to use them for calling constructors + g_argc = args.argc; + g_argv = args.argv; + g_envp = args.envp; + // Initialize the linker's own global variables linker_so.call_constructors(); diff --git a/linker/linker.h b/linker/linker.h index 61ef259a0..aa6d00b9d 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -42,25 +42,11 @@ #include "linked_list.h" #include "linker_common_types.h" #include "linker_logger.h" +#include "linker_soinfo.h" #include <string> #include <vector> -#define DL_ERR(fmt, x...) \ - do { \ - __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \ - /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \ - LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \ - } while (false) - -#define DL_WARN(fmt, x...) \ - do { \ - __libc_format_log(ANDROID_LOG_WARN, "linker", fmt, ##x); \ - __libc_format_fd(2, "WARNING: linker: "); \ - __libc_format_fd(2, fmt, ##x); \ - __libc_format_fd(2, "\n"); \ - } while (false) - #if defined(__LP64__) #define ELFW(what) ELF64_ ## what #else @@ -81,61 +67,8 @@ #define ELF64_R_TYPE(info) (((info) >> 56) & 0xff) #endif -#define FLAG_LINKED 0x00000001 -#define FLAG_EXE 0x00000004 // The main executable -#define FLAG_LINKER 0x00000010 // The linker itself -#define FLAG_GNU_HASH 0x00000040 // uses gnu hash -#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller - // and should not be unmapped -#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format - #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) -#define SOINFO_VERSION 3 - -#if defined(__work_around_b_24465209__) -#define SOINFO_NAME_LEN 128 -#endif - -typedef void (*linker_dtor_function_t)(); -typedef void (*linker_ctor_function_t)(int, char**, char**); - -// Android uses RELA for aarch64 and x86_64. mips64 still uses REL. -#if defined(__aarch64__) || defined(__x86_64__) -#define USE_RELA 1 -#endif - -class SymbolName { - public: - explicit SymbolName(const char* name) - : name_(name), has_elf_hash_(false), has_gnu_hash_(false), - elf_hash_(0), gnu_hash_(0) { } - - const char* get_name() { - return name_; - } - - uint32_t elf_hash(); - uint32_t gnu_hash(); - - private: - const char* name_; - bool has_elf_hash_; - bool has_gnu_hash_; - uint32_t elf_hash_; - uint32_t gnu_hash_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName); -}; - -struct version_info { - constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {} - - uint32_t elf_hash; - const char* name; - const soinfo* target_si; -}; - // Class used construct version dependency graph. class VersionTracker { public: @@ -154,262 +87,6 @@ class VersionTracker { DISALLOW_COPY_AND_ASSIGN(VersionTracker); }; -struct soinfo { - public: -#if defined(__work_around_b_24465209__) - private: - char old_name_[SOINFO_NAME_LEN]; -#endif - public: - const ElfW(Phdr)* phdr; - size_t phnum; -#if defined(__work_around_b_24465209__) - ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility. -#endif - ElfW(Addr) base; - size_t size; - -#if defined(__work_around_b_24465209__) - uint32_t unused1; // DO NOT USE, maintained for compatibility. -#endif - - ElfW(Dyn)* dynamic; - -#if defined(__work_around_b_24465209__) - uint32_t unused2; // DO NOT USE, maintained for compatibility - uint32_t unused3; // DO NOT USE, maintained for compatibility -#endif - - soinfo* next; - private: - uint32_t flags_; - - const char* strtab_; - ElfW(Sym)* symtab_; - - size_t nbucket_; - size_t nchain_; - uint32_t* bucket_; - uint32_t* chain_; - -#if defined(__mips__) || !defined(__LP64__) - // This is only used by mips and mips64, but needs to be here for - // all 32-bit architectures to preserve binary compatibility. - ElfW(Addr)** plt_got_; -#endif - -#if defined(USE_RELA) - ElfW(Rela)* plt_rela_; - size_t plt_rela_count_; - - ElfW(Rela)* rela_; - size_t rela_count_; -#else - ElfW(Rel)* plt_rel_; - size_t plt_rel_count_; - - ElfW(Rel)* rel_; - size_t rel_count_; -#endif - - linker_ctor_function_t* preinit_array_; - size_t preinit_array_count_; - - linker_ctor_function_t* init_array_; - size_t init_array_count_; - linker_dtor_function_t* fini_array_; - size_t fini_array_count_; - - linker_ctor_function_t init_func_; - linker_dtor_function_t fini_func_; - -#if defined(__arm__) - public: - // ARM EABI section used for stack unwinding. - uint32_t* ARM_exidx; - size_t ARM_exidx_count; - private: -#elif defined(__mips__) - uint32_t mips_symtabno_; - uint32_t mips_local_gotno_; - uint32_t mips_gotsym_; - bool mips_relocate_got(const VersionTracker& version_tracker, - const soinfo_list_t& global_group, - const soinfo_list_t& local_group); -#if !defined(__LP64__) - bool mips_check_and_adjust_fp_modes(); -#endif -#endif - size_t ref_count_; - public: - link_map link_map_head; - - bool constructors_called; - - // When you read a virtual address from the ELF file, add this - // value to get the corresponding address in the process' address space. - ElfW(Addr) load_bias; - -#if !defined(__LP64__) - bool has_text_relocations; -#endif - bool has_DT_SYMBOLIC; - - public: - soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat, - off64_t file_offset, int rtld_flags); - ~soinfo(); - - void call_constructors(); - void call_destructors(); - void call_pre_init_constructors(); - bool prelink_image(); - bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, - const android_dlextinfo* extinfo); - bool protect_relro(); - - void add_child(soinfo* child); - void remove_all_links(); - - ino_t get_st_ino() const; - dev_t get_st_dev() const; - off64_t get_file_offset() const; - - uint32_t get_rtld_flags() const; - uint32_t get_dt_flags_1() const; - void set_dt_flags_1(uint32_t dt_flags_1); - - soinfo_list_t& get_children(); - const soinfo_list_t& get_children() const; - - soinfo_list_t& get_parents(); - - bool find_symbol_by_name(SymbolName& symbol_name, - const version_info* vi, - const ElfW(Sym)** symbol) const; - - ElfW(Sym)* find_symbol_by_address(const void* addr); - ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const; - - const char* get_string(ElfW(Word) index) const; - bool can_unload() const; - bool is_gnu_hash() const; - - bool inline has_min_version(uint32_t min_version __unused) const { -#if defined(__work_around_b_24465209__) - return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version; -#else - return true; -#endif - } - - bool is_linked() const; - bool is_linker() const; - bool is_main_executable() const; - - void set_linked(); - void set_linker_flag(); - void set_main_executable(); - void set_nodelete(); - - void increment_ref_count(); - size_t decrement_ref_count(); - - soinfo* get_local_group_root() const; - - void set_soname(const char* soname); - const char* get_soname() const; - const char* get_realpath() const; - const ElfW(Versym)* get_versym(size_t n) const; - ElfW(Addr) get_verneed_ptr() const; - size_t get_verneed_cnt() const; - ElfW(Addr) get_verdef_ptr() const; - size_t get_verdef_cnt() const; - - bool find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const; - - uint32_t get_target_sdk_version() const; - - void set_dt_runpath(const char *); - const std::vector<std::string>& get_dt_runpath() const; - android_namespace_t* get_primary_namespace(); - void add_secondary_namespace(android_namespace_t* secondary_ns); - - void set_mapped_by_caller(bool reserved_map); - bool is_mapped_by_caller() const; - - uintptr_t get_handle() const; - void generate_handle(); - void* to_handle(); - - private: - 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; - ElfW(Sym)* gnu_addr_lookup(const void* addr); - - bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, - const char* sym_name, const version_info** vi); - - template<typename ElfRelIteratorT> - bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, - const soinfo_list_t& global_group, const soinfo_list_t& local_group); - - private: - // This part of the structure is only available - // when FLAG_NEW_SOINFO is set in this->flags. - uint32_t version_; - - // version >= 0 - dev_t st_dev_; - ino_t st_ino_; - - // dependency graph - soinfo_list_t children_; - soinfo_list_t parents_; - - // version >= 1 - off64_t file_offset_; - uint32_t rtld_flags_; - uint32_t dt_flags_1_; - size_t strtab_size_; - - // version >= 2 - - size_t gnu_nbucket_; - uint32_t* gnu_bucket_; - uint32_t* gnu_chain_; - uint32_t gnu_maskwords_; - uint32_t gnu_shift2_; - ElfW(Addr)* gnu_bloom_filter_; - - soinfo* local_group_root_; - - uint8_t* android_relocs_; - size_t android_relocs_size_; - - const char* soname_; - std::string realpath_; - - const ElfW(Versym)* versym_; - - ElfW(Addr) verdef_ptr_; - size_t verdef_cnt_; - - ElfW(Addr) verneed_ptr_; - size_t verneed_cnt_; - - uint32_t target_sdk_version_; - - // version >= 3 - std::vector<std::string> dt_runpath_; - android_namespace_t* primary_namespace_; - android_namespace_list_t secondary_namespaces_; - uintptr_t handle_; - - friend soinfo* get_libdl_info(); -}; - bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi, soinfo** si_found_in, const soinfo_list_t& global_group, const soinfo_list_t& local_group, const ElfW(Sym)** symbol); @@ -438,9 +115,6 @@ bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, int do_dladdr(const void* addr, Dl_info* info); -char* linker_get_error_buffer(); -size_t linker_get_error_buffer_size(); - void set_application_target_sdk_version(uint32_t target); uint32_t get_application_target_sdk_version(); diff --git a/linker/linker_common_types.h b/linker/linker_common_types.h index 1888c0c54..6afd95087 100644 --- a/linker/linker_common_types.h +++ b/linker/linker_common_types.h @@ -32,6 +32,15 @@ #include <android/dlext.h> #include "linked_list.h" +// TODO(dimitry): move this to linker_defines.h? Unless it is removed by +// consequent refactoring steps. + +// Android uses RELA for aarch64 and x86_64. mips64 still uses REL. +#if defined(__aarch64__) || defined(__x86_64__) +#define USE_RELA 1 +#endif + + struct soinfo; class SoinfoListAllocator { diff --git a/linker/linker_globals.cpp b/linker/linker_globals.cpp new file mode 100644 index 000000000..155ebf45a --- /dev/null +++ b/linker/linker_globals.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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_globals.h" +#include "linker_namespaces.h" + +int g_argc = 0; +char** g_argv = nullptr; +char** g_envp = nullptr; + +android_namespace_t g_default_namespace; + +std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; + +static char __linker_dl_err_buf[768]; + +char* linker_get_error_buffer() { + return &__linker_dl_err_buf[0]; +} + +size_t linker_get_error_buffer_size() { + return sizeof(__linker_dl_err_buf); +} + diff --git a/linker/linker_globals.h b/linker/linker_globals.h new file mode 100644 index 000000000..265af2777 --- /dev/null +++ b/linker/linker_globals.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef __LINKER_GLOBALS_H +#define __LINKER_GLOBALS_H + +#include <link.h> +#include <stddef.h> + +#include <unordered_map> + +#define DL_ERR(fmt, x...) \ + do { \ + __libc_format_buffer(linker_get_error_buffer(), linker_get_error_buffer_size(), fmt, ##x); \ + /* If LD_DEBUG is set high enough, log every dlerror(3) message. */ \ + LD_LOG(kLogErrors, "%s\n", linker_get_error_buffer()); \ + } while (false) + +#define DL_WARN(fmt, x...) \ + do { \ + __libc_format_log(ANDROID_LOG_WARN, "linker", fmt, ##x); \ + __libc_format_fd(2, "WARNING: linker: "); \ + __libc_format_fd(2, fmt, ##x); \ + __libc_format_fd(2, "\n"); \ + } while (false) + +constexpr ElfW(Versym) kVersymNotNeeded = 0; +constexpr ElfW(Versym) kVersymGlobal = 1; + +// These values are used to call constructors for .init_array && .preinit_array +extern int g_argc; +extern char** g_argv; +extern char** g_envp; + +struct soinfo; +struct android_namespace_t; + +extern android_namespace_t g_default_namespace; + +extern std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; + +// Error buffer "variable" +char* linker_get_error_buffer(); +size_t linker_get_error_buffer_size(); + +#endif /* __LINKER_GLOBALS_H */ diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h index d4b5efd42..c1cee8ec6 100644 --- a/linker/linker_namespaces.h +++ b/linker/linker_namespaces.h @@ -31,6 +31,7 @@ #include "linker_common_types.h" +#include <string> #include <vector> struct android_namespace_t { diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index ebc31663e..5f1d280bb 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -36,6 +36,7 @@ #include <unistd.h> #include "linker.h" +#include "linker_globals.h" #include "linker_debug.h" #include "linker_utils.h" diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp new file mode 100644 index 000000000..59bdc4dfe --- /dev/null +++ b/linker/linker_soinfo.cpp @@ -0,0 +1,795 @@ +/* + * Copyright (C) 2016 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_soinfo.h" + +#include <dlfcn.h> +#include <elf.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "linker_debug.h" +#include "linker_globals.h" +#include "linker_logger.h" +#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); +uint32_t get_application_target_sdk_version(); + +soinfo::soinfo(android_namespace_t* ns, const char* realpath, + const struct stat* file_stat, off64_t file_offset, + int rtld_flags) { + memset(this, 0, sizeof(*this)); + + if (realpath != nullptr) { + realpath_ = realpath; + } + + flags_ = FLAG_NEW_SOINFO; + version_ = SOINFO_VERSION; + + if (file_stat != nullptr) { + this->st_dev_ = file_stat->st_dev; + this->st_ino_ = file_stat->st_ino; + this->file_offset_ = file_offset; + } + + this->rtld_flags_ = rtld_flags; + this->primary_namespace_ = ns; +} + +soinfo::~soinfo() { + g_soinfo_handles_map.erase(handle_); +} + +void soinfo::set_dt_runpath(const char* path) { + if (!has_min_version(3)) { + return; + } + + std::vector<std::string> runpaths; + + split_path(path, ":", &runpaths); + + std::string origin = dirname(get_realpath()); + // FIXME: add $LIB and $PLATFORM. + std::pair<std::string, std::string> substs[] = {{"ORIGIN", origin}}; + for (auto&& s : runpaths) { + size_t pos = 0; + while (pos < s.size()) { + pos = s.find("$", pos); + if (pos == std::string::npos) break; + for (const auto& subst : substs) { + const std::string& token = subst.first; + const std::string& replacement = subst.second; + if (s.substr(pos + 1, token.size()) == token) { + s.replace(pos, token.size() + 1, replacement); + // -1 to compensate for the ++pos below. + pos += replacement.size() - 1; + break; + } else if (s.substr(pos + 1, token.size() + 2) == "{" + token + "}") { + s.replace(pos, token.size() + 3, replacement); + pos += replacement.size() - 1; + break; + } + } + // Skip $ in case it did not match any of the known substitutions. + ++pos; + } + } + + resolve_paths(runpaths, &dt_runpath_); +} + +const ElfW(Versym)* soinfo::get_versym(size_t n) const { + if (has_min_version(2) && versym_ != nullptr) { + return versym_ + n; + } + + return nullptr; +} + +ElfW(Addr) soinfo::get_verneed_ptr() const { + if (has_min_version(2)) { + return verneed_ptr_; + } + + return 0; +} + +size_t soinfo::get_verneed_cnt() const { + if (has_min_version(2)) { + return verneed_cnt_; + } + + return 0; +} + +ElfW(Addr) soinfo::get_verdef_ptr() const { + if (has_min_version(2)) { + return verdef_ptr_; + } + + return 0; +} + +size_t soinfo::get_verdef_cnt() const { + if (has_min_version(2)) { + return verdef_cnt_; + } + + 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) { + return s->st_shndx != SHN_UNDEF; + } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) { + DL_WARN("unexpected ST_BIND value: %d for \"%s\" in \"%s\"", + ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->get_realpath()); + } + + return false; +} + +static const ElfW(Versym) kVersymHiddenBit = 0x8000; + +static inline bool is_versym_hidden(const ElfW(Versym)* versym) { + // the symbol is hidden if bit 15 of versym is set. + return versym != nullptr && (*versym & kVersymHiddenBit) != 0; +} + +static inline bool check_symbol_version(const ElfW(Versym) verneed, + const ElfW(Versym)* verdef) { + return verneed == kVersymNotNeeded || + verdef == nullptr || + verneed == (*verdef & ~kVersymHiddenBit); +} + +bool soinfo::gnu_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) const { + uint32_t hash = symbol_name.gnu_hash(); + uint32_t h2 = hash >> gnu_shift2_; + + uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8; + 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)); + + // test against bloom filter + if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + return true; + } + + // bloom test says "probably yes"... + uint32_t n = gnu_bucket_[hash % gnu_nbucket_]; + + if (n == 0) { + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base)); + + return true; + } + + // lookup versym for the version definition in this library + // note the difference between "version is not requested" (vi == nullptr) + // and "version not found". In the first case verneed is kVersymNotNeeded + // 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; + } + + do { + ElfW(Sym)* s = symtab_ + n; + const ElfW(Versym)* verdef = get_versym(n); + // skip hidden versions when verneed == kVersymNotNeeded (0) + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } + if (((gnu_chain_[n] ^ hash) >> 1) == 0 && + check_symbol_version(verneed, verdef) && + strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + is_symbol_global_and_defined(this, s)) { + 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; + } + } 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; +} + +bool soinfo::elf_lookup(SymbolName& symbol_name, + const version_info* vi, + uint32_t* symbol_index) 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; + } + + for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { + ElfW(Sym)* s = symtab_ + n; + const ElfW(Versym)* verdef = get_versym(n); + + // skip hidden versions when verneed == 0 + if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) { + continue; + } + + if (check_symbol_version(verneed, verdef) && + strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && + is_symbol_global_and_defined(this, s)) { + 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; + } + } + + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", + symbol_name.get_name(), get_realpath(), + reinterpret_cast<void*>(base), hash, hash % nbucket_); + + *symbol_index = 0; + return true; +} + +ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) { + return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr); +} + +static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) { + return sym->st_shndx != SHN_UNDEF && + soaddr >= sym->st_value && + soaddr < sym->st_value + sym->st_size; +} + +ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { + ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; + + for (size_t i = 0; i < gnu_nbucket_; ++i) { + uint32_t n = gnu_bucket_[i]; + + if (n == 0) { + continue; + } + + do { + ElfW(Sym)* sym = symtab_ + n; + if (symbol_matches_soaddr(sym, soaddr)) { + return sym; + } + } while ((gnu_chain_[n++] & 1) == 0); + } + + return nullptr; +} + +ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) { + ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; + + // Search the library's symbol table for any defined symbol which + // contains this address. + for (size_t i = 0; i < nchain_; ++i) { + ElfW(Sym)* sym = symtab_ + i; + if (symbol_matches_soaddr(sym, soaddr)) { + return sym; + } + } + + return nullptr; +} + +static void call_function(const char* function_name __unused, + linker_ctor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { + return; + } + + TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(g_argc, g_argv, g_envp); + TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +static void call_function(const char* function_name __unused, + linker_dtor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { + return; + } + + TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(); + TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +template <typename F> +static void call_array(const char* array_name __unused, + F* functions, + size_t count, + bool reverse, + const char* realpath) { + if (functions == nullptr) { + return; + } + + TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath); + + int begin = reverse ? (count - 1) : 0; + int end = reverse ? -1 : count; + int step = reverse ? -1 : 1; + + for (int i = begin; i != end; i += step) { + TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]); + call_function("function", functions[i], realpath); + } + + TRACE("[ Done calling %s for '%s' ]", array_name, realpath); +} + +void soinfo::call_pre_init_constructors() { + // DT_PREINIT_ARRAY functions are called before any other constructors for executables, + // but ignored in a shared library. + call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath()); +} + +void soinfo::call_constructors() { + if (constructors_called) { + return; + } + + // We set constructors_called before actually calling the constructors, otherwise it doesn't + // protect against recursive constructor calls. One simple example of constructor recursion + // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so: + // 1. The program depends on libc, so libc's constructor is called here. + // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so. + // 3. dlopen() calls the constructors on the newly created + // soinfo for libc_malloc_debug_leak.so. + // 4. The debug .so depends on libc, so CallConstructors is + // called again with the libc soinfo. If it doesn't trigger the early- + // out above, the libc constructor will be called again (recursively!). + constructors_called = true; + + if (!is_main_executable() && preinit_array_ != nullptr) { + // The GNU dynamic linker silently ignores these, but we warn the developer. + PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath()); + } + + get_children().for_each([] (soinfo* si) { + si->call_constructors(); + }); + + TRACE("\"%s\": calling constructors", get_realpath()); + + // DT_INIT should be called before DT_INIT_ARRAY if both are present. + call_function("DT_INIT", init_func_, get_realpath()); + call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath()); +} + +void soinfo::call_destructors() { + if (!constructors_called) { + return; + } + TRACE("\"%s\": calling destructors", get_realpath()); + + // DT_FINI_ARRAY must be parsed in reverse order. + call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath()); + + // DT_FINI should be called after DT_FINI_ARRAY if both are present. + call_function("DT_FINI", fini_func_, get_realpath()); +} + +void soinfo::add_child(soinfo* child) { + if (has_min_version(0)) { + child->parents_.push_back(this); + this->children_.push_back(child); + } +} + +void soinfo::remove_all_links() { + if (!has_min_version(0)) { + return; + } + + // 1. Untie connected soinfos from 'this'. + children_.for_each([&] (soinfo* child) { + child->parents_.remove_if([&] (const soinfo* parent) { + return parent == this; + }); + }); + + parents_.for_each([&] (soinfo* parent) { + parent->children_.remove_if([&] (const soinfo* child) { + return child == this; + }); + }); + + // 2. Remove from the primary namespace + primary_namespace_->remove_soinfo(this); + primary_namespace_ = nullptr; + + // 3. Remove from secondary namespaces + secondary_namespaces_.for_each([&](android_namespace_t* ns) { + ns->remove_soinfo(this); + }); + + + // 4. Once everything untied - clear local lists. + parents_.clear(); + children_.clear(); + secondary_namespaces_.clear(); +} + +dev_t soinfo::get_st_dev() const { + if (has_min_version(0)) { + return st_dev_; + } + + return 0; +}; + +ino_t soinfo::get_st_ino() const { + if (has_min_version(0)) { + return st_ino_; + } + + return 0; +} + +off64_t soinfo::get_file_offset() const { + if (has_min_version(1)) { + return file_offset_; + } + + return 0; +} + +uint32_t soinfo::get_rtld_flags() const { + if (has_min_version(1)) { + return rtld_flags_; + } + + return 0; +} + +uint32_t soinfo::get_dt_flags_1() const { + if (has_min_version(1)) { + return dt_flags_1_; + } + + return 0; +} + +void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { + if (has_min_version(1)) { + if ((dt_flags_1 & DF_1_GLOBAL) != 0) { + rtld_flags_ |= RTLD_GLOBAL; + } + + if ((dt_flags_1 & DF_1_NODELETE) != 0) { + rtld_flags_ |= RTLD_NODELETE; + } + + dt_flags_1_ = dt_flags_1; + } +} + +void soinfo::set_nodelete() { + rtld_flags_ |= RTLD_NODELETE; +} + +const char* soinfo::get_realpath() const { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + return realpath_.c_str(); + } else { + return old_name_; + } +#else + return realpath_.c_str(); +#endif +} + +void soinfo::set_soname(const char* soname) { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + soname_ = soname; + } + strlcpy(old_name_, soname_, sizeof(old_name_)); +#else + soname_ = soname; +#endif +} + +const char* soinfo::get_soname() const { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + return soname_; + } else { + return old_name_; + } +#else + return soname_; +#endif +} + +// This is a return on get_children()/get_parents() if +// 'this->flags' does not have FLAG_NEW_SOINFO set. +static soinfo_list_t g_empty_list; + +soinfo_list_t& soinfo::get_children() { + if (has_min_version(0)) { + return children_; + } + + return g_empty_list; +} + +const soinfo_list_t& soinfo::get_children() const { + if (has_min_version(0)) { + return children_; + } + + return g_empty_list; +} + +soinfo_list_t& soinfo::get_parents() { + if (has_min_version(0)) { + return parents_; + } + + return g_empty_list; +} + +static std::vector<std::string> g_empty_runpath; + +const std::vector<std::string>& soinfo::get_dt_runpath() const { + if (has_min_version(3)) { + return dt_runpath_; + } + + return g_empty_runpath; +} + +android_namespace_t* soinfo::get_primary_namespace() { + if (has_min_version(3)) { + return primary_namespace_; + } + + return &g_default_namespace; +} + +void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) { + CHECK(has_min_version(3)); + secondary_namespaces_.push_back(secondary_ns); +} + +ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { + if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { + return call_ifunc_resolver(s->st_value + load_bias); + } + + return static_cast<ElfW(Addr)>(s->st_value + load_bias); +} + +const char* soinfo::get_string(ElfW(Word) index) const { + if (has_min_version(1) && (index >= strtab_size_)) { + __libc_fatal("%s: strtab out of bounds error; STRSZ=%zd, name=%d", + get_realpath(), strtab_size_, index); + } + + return strtab_ + index; +} + +bool soinfo::is_gnu_hash() const { + return (flags_ & FLAG_GNU_HASH) != 0; +} + +bool soinfo::can_unload() const { + return !is_linked() || ((get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0); +} + +bool soinfo::is_linked() const { + return (flags_ & FLAG_LINKED) != 0; +} + +bool soinfo::is_main_executable() const { + return (flags_ & FLAG_EXE) != 0; +} + +bool soinfo::is_linker() const { + return (flags_ & FLAG_LINKER) != 0; +} + +void soinfo::set_linked() { + flags_ |= FLAG_LINKED; +} + +void soinfo::set_linker_flag() { + flags_ |= FLAG_LINKER; +} + +void soinfo::set_main_executable() { + flags_ |= FLAG_EXE; +} + +void soinfo::increment_ref_count() { + local_group_root_->ref_count_++; +} + +size_t soinfo::decrement_ref_count() { + return --local_group_root_->ref_count_; +} + +soinfo* soinfo::get_local_group_root() const { + return local_group_root_; +} + + +void soinfo::set_mapped_by_caller(bool mapped_by_caller) { + if (mapped_by_caller) { + flags_ |= FLAG_MAPPED_BY_CALLER; + } else { + flags_ &= ~FLAG_MAPPED_BY_CALLER; + } +} + +bool soinfo::is_mapped_by_caller() const { + return (flags_ & FLAG_MAPPED_BY_CALLER) != 0; +} + +// This function returns api-level at the time of +// dlopen/load. Note that libraries opened by system +// will always have 'current' api level. +uint32_t soinfo::get_target_sdk_version() const { + if (!has_min_version(2)) { + return __ANDROID_API__; + } + + return local_group_root_->target_sdk_version_; +} + +uintptr_t soinfo::get_handle() const { + CHECK(has_min_version(3)); + CHECK(handle_ != 0); + return handle_; +} + +void* soinfo::to_handle() { + if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) { + return this; + } + + return reinterpret_cast<void*>(get_handle()); +} + +void soinfo::generate_handle() { + CHECK(has_min_version(3)); + CHECK(handle_ == 0); // Make sure this is the first call + + // Make sure the handle is unique and does not collide + // with special values which are RTLD_DEFAULT and RTLD_NEXT. + do { + arc4random_buf(&handle_, sizeof(handle_)); + // the least significant bit for the handle is always 1 + // making it easy to test the type of handle passed to + // dl* functions. + handle_ = handle_ | 1; + } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) || + handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) || + g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end()); + + g_soinfo_handles_map[handle_] = this; +} + +// TODO(dimitry): Move SymbolName methods to a separate file. + +uint32_t calculate_elf_hash(const char* name) { + const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name); + uint32_t h = 0, g; + + while (*name_bytes) { + h = (h << 4) + *name_bytes++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + + return h; +} + +uint32_t SymbolName::elf_hash() { + if (!has_elf_hash_) { + elf_hash_ = calculate_elf_hash(name_); + has_elf_hash_ = true; + } + + return elf_hash_; +} + +uint32_t SymbolName::gnu_hash() { + if (!has_gnu_hash_) { + uint32_t h = 5381; + const uint8_t* name = reinterpret_cast<const uint8_t*>(name_); + while (*name != 0) { + h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c + } + + gnu_hash_ = h; + has_gnu_hash_ = true; + } + + return gnu_hash_; +} + + diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h new file mode 100644 index 000000000..d7b584e2a --- /dev/null +++ b/linker/linker_soinfo.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef __LINKER_SOINFO_H +#define __LINKER_SOINFO_H + +#include <link.h> + +#include <string> + +#include "linker_namespaces.h" + +#define FLAG_LINKED 0x00000001 +#define FLAG_EXE 0x00000004 // The main executable +#define FLAG_LINKER 0x00000010 // The linker itself +#define FLAG_GNU_HASH 0x00000040 // uses gnu hash +#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller + // and should not be unmapped +#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format + +#define SOINFO_VERSION 3 + +typedef void (*linker_dtor_function_t)(); +typedef void (*linker_ctor_function_t)(int, char**, char**); + +class SymbolName { + public: + explicit SymbolName(const char* name) + : name_(name), has_elf_hash_(false), has_gnu_hash_(false), + elf_hash_(0), gnu_hash_(0) { } + + const char* get_name() { + return name_; + } + + uint32_t elf_hash(); + uint32_t gnu_hash(); + + private: + const char* name_; + bool has_elf_hash_; + bool has_gnu_hash_; + uint32_t elf_hash_; + uint32_t gnu_hash_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName); +}; + +struct version_info { + constexpr version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {} + + uint32_t elf_hash; + const char* name; + const soinfo* target_si; +}; + +// TODO(dimitry): remove reference from soinfo member functions to this class. +class VersionTracker; + +#if defined(__work_around_b_24465209__) +#define SOINFO_NAME_LEN 128 +#endif + +struct soinfo { +#if defined(__work_around_b_24465209__) + private: + char old_name_[SOINFO_NAME_LEN]; +#endif + public: + const ElfW(Phdr)* phdr; + size_t phnum; +#if defined(__work_around_b_24465209__) + ElfW(Addr) unused0; // DO NOT USE, maintained for compatibility. +#endif + ElfW(Addr) base; + size_t size; + +#if defined(__work_around_b_24465209__) + uint32_t unused1; // DO NOT USE, maintained for compatibility. +#endif + + ElfW(Dyn)* dynamic; + +#if defined(__work_around_b_24465209__) + uint32_t unused2; // DO NOT USE, maintained for compatibility + uint32_t unused3; // DO NOT USE, maintained for compatibility +#endif + + soinfo* next; + private: + uint32_t flags_; + + const char* strtab_; + ElfW(Sym)* symtab_; + + size_t nbucket_; + size_t nchain_; + uint32_t* bucket_; + uint32_t* chain_; + +#if defined(__mips__) || !defined(__LP64__) + // This is only used by mips and mips64, but needs to be here for + // all 32-bit architectures to preserve binary compatibility. + ElfW(Addr)** plt_got_; +#endif + +#if defined(USE_RELA) + ElfW(Rela)* plt_rela_; + size_t plt_rela_count_; + + ElfW(Rela)* rela_; + size_t rela_count_; +#else + ElfW(Rel)* plt_rel_; + size_t plt_rel_count_; + + ElfW(Rel)* rel_; + size_t rel_count_; +#endif + + linker_ctor_function_t* preinit_array_; + size_t preinit_array_count_; + + linker_ctor_function_t* init_array_; + size_t init_array_count_; + linker_dtor_function_t* fini_array_; + size_t fini_array_count_; + + linker_ctor_function_t init_func_; + linker_dtor_function_t fini_func_; + +#if defined(__arm__) + public: + // ARM EABI section used for stack unwinding. + uint32_t* ARM_exidx; + size_t ARM_exidx_count; + private: +#elif defined(__mips__) + uint32_t mips_symtabno_; + uint32_t mips_local_gotno_; + uint32_t mips_gotsym_; + bool mips_relocate_got(const VersionTracker& version_tracker, + const soinfo_list_t& global_group, + const soinfo_list_t& local_group); +#if !defined(__LP64__) + bool mips_check_and_adjust_fp_modes(); +#endif +#endif + size_t ref_count_; + public: + link_map link_map_head; + + bool constructors_called; + + // When you read a virtual address from the ELF file, add this + // value to get the corresponding address in the process' address space. + ElfW(Addr) load_bias; + +#if !defined(__LP64__) + bool has_text_relocations; +#endif + bool has_DT_SYMBOLIC; + + public: + soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat, + off64_t file_offset, int rtld_flags); + ~soinfo(); + + void call_constructors(); + void call_destructors(); + void call_pre_init_constructors(); + bool prelink_image(); + bool link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group, + const android_dlextinfo* extinfo); + bool protect_relro(); + + void add_child(soinfo* child); + void remove_all_links(); + + ino_t get_st_ino() const; + dev_t get_st_dev() const; + off64_t get_file_offset() const; + + uint32_t get_rtld_flags() const; + uint32_t get_dt_flags_1() const; + void set_dt_flags_1(uint32_t dt_flags_1); + + soinfo_list_t& get_children(); + const soinfo_list_t& get_children() const; + + soinfo_list_t& get_parents(); + + bool find_symbol_by_name(SymbolName& symbol_name, + const version_info* vi, + const ElfW(Sym)** symbol) const; + + ElfW(Sym)* find_symbol_by_address(const void* addr); + ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const; + + const char* get_string(ElfW(Word) index) const; + bool can_unload() const; + bool is_gnu_hash() const; + + bool inline has_min_version(uint32_t min_version __unused) const { +#if defined(__work_around_b_24465209__) + return (flags_ & FLAG_NEW_SOINFO) != 0 && version_ >= min_version; +#else + return true; +#endif + } + + bool is_linked() const; + bool is_linker() const; + bool is_main_executable() const; + + void set_linked(); + void set_linker_flag(); + void set_main_executable(); + void set_nodelete(); + + void increment_ref_count(); + size_t decrement_ref_count(); + + soinfo* get_local_group_root() const; + + void set_soname(const char* soname); + const char* get_soname() const; + const char* get_realpath() const; + const ElfW(Versym)* get_versym(size_t n) const; + ElfW(Addr) get_verneed_ptr() const; + size_t get_verneed_cnt() const; + ElfW(Addr) get_verdef_ptr() const; + size_t get_verdef_cnt() const; + + uint32_t get_target_sdk_version() const; + + void set_dt_runpath(const char *); + const std::vector<std::string>& get_dt_runpath() const; + android_namespace_t* get_primary_namespace(); + void add_secondary_namespace(android_namespace_t* secondary_ns); + + void set_mapped_by_caller(bool reserved_map); + bool is_mapped_by_caller() const; + + uintptr_t get_handle() const; + void generate_handle(); + void* to_handle(); + + private: + 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; + ElfW(Sym)* gnu_addr_lookup(const void* addr); + + bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, + const char* sym_name, const version_info** vi); + + template<typename ElfRelIteratorT> + bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, + const soinfo_list_t& global_group, const soinfo_list_t& local_group); + + private: + // This part of the structure is only available + // when FLAG_NEW_SOINFO is set in this->flags. + uint32_t version_; + + // version >= 0 + dev_t st_dev_; + ino_t st_ino_; + + // dependency graph + soinfo_list_t children_; + soinfo_list_t parents_; + + // version >= 1 + off64_t file_offset_; + uint32_t rtld_flags_; + uint32_t dt_flags_1_; + size_t strtab_size_; + + // version >= 2 + + size_t gnu_nbucket_; + uint32_t* gnu_bucket_; + uint32_t* gnu_chain_; + uint32_t gnu_maskwords_; + uint32_t gnu_shift2_; + ElfW(Addr)* gnu_bloom_filter_; + + soinfo* local_group_root_; + + uint8_t* android_relocs_; + size_t android_relocs_size_; + + const char* soname_; + std::string realpath_; + + const ElfW(Versym)* versym_; + + ElfW(Addr) verdef_ptr_; + size_t verdef_cnt_; + + ElfW(Addr) verneed_ptr_; + size_t verneed_cnt_; + + uint32_t target_sdk_version_; + + // version >= 3 + std::vector<std::string> dt_runpath_; + android_namespace_t* primary_namespace_; + android_namespace_list_t secondary_namespaces_; + uintptr_t handle_; + + friend soinfo* get_libdl_info(); +}; + +// This function is used by dlvsym() to calculate hash of sym_ver +uint32_t calculate_elf_hash(const char* name); + +#endif /* __LINKER_SOINFO_H */ diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp index fb070eed0..e7447e402 100644 --- a/linker/linker_utils.cpp +++ b/linker/linker_utils.cpp @@ -15,7 +15,26 @@ */ #include "linker_utils.h" + #include "linker_debug.h" +#include "linker_globals.h" + +#include "android-base/strings.h" + +#include <sys/stat.h> +#include <unistd.h> + +std::string dirname(const char* path) { + const char* last_slash = strrchr(path, '/'); + + if (last_slash == path) { + return "/"; + } else if (last_slash == nullptr) { + return "."; + } else { + return std::string(path, last_slash - path); + } +} bool normalize_path(const char* path, std::string* normalized_path) { // Input should be an absolute path @@ -134,3 +153,52 @@ size_t page_offset(off64_t offset) { return static_cast<size_t>(offset & (PAGE_SIZE-1)); } +void split_path(const char* path, const char* delimiters, + std::vector<std::string>* paths) { + if (path != nullptr && path[0] != 0) { + *paths = android::base::Split(path, delimiters); + } +} + +void resolve_paths(std::vector<std::string>& paths, + std::vector<std::string>* resolved_paths) { + resolved_paths->clear(); + for (const auto& path : paths) { + char resolved_path[PATH_MAX]; + const char* original_path = path.c_str(); + if (realpath(original_path, resolved_path) != nullptr) { + struct stat s; + if (stat(resolved_path, &s) == 0) { + if (S_ISDIR(s.st_mode)) { + resolved_paths->push_back(resolved_path); + } else { + DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path); + continue; + } + } else { + DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno)); + continue; + } + } else { + std::string zip_path; + std::string entry_path; + + std::string normalized_path; + + if (!normalize_path(original_path, &normalized_path)) { + DL_WARN("Warning: unable to normalize \"%s\"", original_path); + continue; + } + + if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) { + if (realpath(zip_path.c_str(), resolved_path) == nullptr) { + DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno)); + continue; + } + + resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path); + } + } + } +} + diff --git a/linker/linker_utils.h b/linker/linker_utils.h index 987eabd9c..2e015a578 100644 --- a/linker/linker_utils.h +++ b/linker/linker_utils.h @@ -17,14 +17,25 @@ #define __LINKER_UTILS_H #include <string> +#include <vector> extern const char* const kZipFileSeparator; -bool normalize_path(const char* path, std::string* normalized_path); bool file_is_in_dir(const std::string& file, const std::string& dir); bool file_is_under_dir(const std::string& file, const std::string& dir); +bool normalize_path(const char* path, std::string* normalized_path); bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path); +// For every path element this function checks of it exists, and is a directory, +// and normalizes it: +// 1. For regular path it converts it to realpath() +// 2. For path in a zip file it uses realpath on the zipfile +// normalizes entry name by calling normalize_path function. +void resolve_paths(std::vector<std::string>& paths, std::vector<std::string>* resolved_paths); +void split_path(const char* path, const char* delimiters, std::vector<std::string>* paths); + +std::string dirname(const char* path); + off64_t page_start(off64_t offset); size_t page_offset(off64_t offset); bool safe_add(off64_t* out, off64_t a, size_t b); diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk index 48c637478..d5b57f152 100644 --- a/linker/tests/Android.mk +++ b/linker/tests/Android.mk @@ -40,4 +40,6 @@ LOCAL_SRC_FILES := \ # for __libc_fatal LOCAL_SRC_FILES += ../../libc/bionic/libc_logging.cpp +LOCAL_STATIC_LIBRARIES += libbase + include $(BUILD_NATIVE_TEST) |