aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linker/Android.bp2
-rw-r--r--linker/dlfcn.cpp3
-rw-r--r--linker/linker.cpp1025
-rw-r--r--linker/linker.h328
-rw-r--r--linker/linker_common_types.h9
-rw-r--r--linker/linker_globals.cpp50
-rw-r--r--linker/linker_globals.h71
-rw-r--r--linker/linker_namespaces.h1
-rw-r--r--linker/linker_phdr.cpp1
-rw-r--r--linker/linker_soinfo.cpp795
-rw-r--r--linker/linker_soinfo.h345
-rw-r--r--linker/linker_utils.cpp68
-rw-r--r--linker/linker_utils.h13
-rw-r--r--linker/tests/Android.mk2
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)