diff options
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 1025 |
1 files changed, 102 insertions, 923 deletions
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(); |