Support symbol versioning
Bug: http://b/20139821 Change-Id: I64122a0fb0960c20b2ce614161b7ab048456b681
This commit is contained in:
parent
c6ccdfaf1f
commit
2a81536144
19 changed files with 895 additions and 43 deletions
|
@ -194,4 +194,10 @@ typedef struct {
|
|||
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
|
||||
#define VER_FLG_BASE 0x1
|
||||
#define VER_FLG_WEAK 0x2
|
||||
|
||||
#define VER_NDX_LOCAL 0
|
||||
#define VER_NDX_GLOBAL 1
|
||||
|
||||
#endif /* _ELF_H */
|
||||
|
|
|
@ -100,7 +100,7 @@ void* dlsym(void* handle, const char* symbol) {
|
|||
}
|
||||
|
||||
soinfo* found = nullptr;
|
||||
ElfW(Sym)* sym = nullptr;
|
||||
const ElfW(Sym)* sym = nullptr;
|
||||
void* caller_addr = __builtin_return_address(0);
|
||||
soinfo* caller = find_containing_library(caller_addr);
|
||||
|
||||
|
|
|
@ -136,6 +136,17 @@ class LinkedList {
|
|||
}
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T* find_if(F predicate) const {
|
||||
for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
|
||||
if (predicate(e->element)) {
|
||||
return e->element;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t copy_to_array(T* array[], size_t array_length) const {
|
||||
size_t sz = 0;
|
||||
for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2008, 2009 The Android Open Source Project
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -100,6 +100,9 @@ static const char* const kDefaultLdPaths[] = {
|
|||
nullptr
|
||||
};
|
||||
|
||||
static const ElfW(Versym) kVersymNotNeeded = 0;
|
||||
static const ElfW(Versym) kVersymGlobal = 1;
|
||||
|
||||
static std::vector<std::string> g_ld_library_paths;
|
||||
static std::vector<std::string> g_ld_preload_names;
|
||||
|
||||
|
@ -379,8 +382,128 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void
|
|||
return rv;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
|
||||
return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
|
||||
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)", i, verdef->vd_version);
|
||||
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) {
|
||||
|
@ -395,7 +518,23 @@ static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
|
|||
return false;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
||||
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_;
|
||||
|
||||
|
@ -403,6 +542,8 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
|||
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
|
||||
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
|
||||
|
||||
*symbol_index = 0;
|
||||
|
||||
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
|
@ -411,7 +552,7 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
|||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// bloom test says "probably yes"...
|
||||
|
@ -421,43 +562,77 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
|
|||
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
|
||||
symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
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_soname(), reinterpret_cast<void*>(s->st_value),
|
||||
static_cast<size_t>(s->st_size));
|
||||
return s;
|
||||
*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_soname(), reinterpret_cast<void*>(base));
|
||||
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
|
||||
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_soname(),
|
||||
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;
|
||||
if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
|
||||
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_soname(),
|
||||
reinterpret_cast<void*>(s->st_value),
|
||||
static_cast<size_t>(s->st_size));
|
||||
return s;
|
||||
*symbol_index = n;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,7 +640,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
|
|||
symbol_name.get_name(), get_soname(),
|
||||
reinterpret_cast<void*>(base), hash, hash % nbucket_);
|
||||
|
||||
return nullptr;
|
||||
*symbol_index = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
|
||||
|
@ -523,10 +699,11 @@ uint32_t SymbolName::gnu_hash() {
|
|||
return gnu_hash_;
|
||||
}
|
||||
|
||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
|
||||
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
|
||||
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
|
||||
SymbolName symbol_name(name);
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
|
||||
/* "This element's presence in a shared object library alters the dynamic linker's
|
||||
* symbol resolution algorithm for references within the library. Instead of starting
|
||||
|
@ -541,7 +718,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
*/
|
||||
if (si_from->has_DT_SYMBOLIC) {
|
||||
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
|
||||
s = si_from->find_symbol_by_name(symbol_name);
|
||||
if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = si_from;
|
||||
}
|
||||
|
@ -549,10 +729,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
|
||||
// 1. Look for it in global_group
|
||||
if (s == nullptr) {
|
||||
bool error = false;
|
||||
global_group.visit([&](soinfo* global_si) {
|
||||
DEBUG("%s: looking up %s in %s (from global group)",
|
||||
si_from->get_soname(), name, global_si->get_soname());
|
||||
s = global_si->find_symbol_by_name(symbol_name);
|
||||
if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = global_si;
|
||||
return false;
|
||||
|
@ -560,10 +745,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Look for it in the local group
|
||||
if (s == nullptr) {
|
||||
bool error = false;
|
||||
local_group.visit([&](soinfo* local_si) {
|
||||
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
|
||||
// we already did this - skip
|
||||
|
@ -572,7 +762,11 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
|
||||
DEBUG("%s: looking up %s in %s (from local group)",
|
||||
si_from->get_soname(), name, local_si->get_soname());
|
||||
s = local_si->find_symbol_by_name(symbol_name);
|
||||
if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
|
||||
error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*si_found_in = local_si;
|
||||
return false;
|
||||
|
@ -580,6 +774,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
|
@ -590,7 +788,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
|
|||
reinterpret_cast<void*>((*si_found_in)->load_bias));
|
||||
}
|
||||
|
||||
return s;
|
||||
*symbol = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
class ProtectedDataGuard {
|
||||
|
@ -735,13 +934,16 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s
|
|||
|
||||
// This is used by dlsym(3). It performs symbol lookup only within the
|
||||
// specified soinfo object and its dependencies in breadth first order.
|
||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
||||
ElfW(Sym)* result = nullptr;
|
||||
const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
||||
const ElfW(Sym)* result = nullptr;
|
||||
SymbolName symbol_name(name);
|
||||
|
||||
|
||||
walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
|
||||
result = current_soinfo->find_symbol_by_name(symbol_name);
|
||||
if (!current_soinfo->find_symbol_by_name(symbol_name, nullptr, &result)) {
|
||||
result = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != nullptr) {
|
||||
*found = current_soinfo;
|
||||
return false;
|
||||
|
@ -758,7 +960,10 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
|
|||
beginning of the global solist. Otherwise the search starts at the
|
||||
specified soinfo (for RTLD_NEXT).
|
||||
*/
|
||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle) {
|
||||
const ElfW(Sym)* dlsym_linear_lookup(const char* name,
|
||||
soinfo** found,
|
||||
soinfo* caller,
|
||||
void* handle) {
|
||||
SymbolName symbol_name(name);
|
||||
|
||||
soinfo* start = solist;
|
||||
|
@ -771,13 +976,16 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
|
|||
}
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
for (soinfo* si = start; si != nullptr; si = si->next) {
|
||||
if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
s = si->find_symbol_by_name(symbol_name);
|
||||
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*found = si;
|
||||
break;
|
||||
|
@ -800,7 +1008,10 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
|
|||
break;
|
||||
}
|
||||
|
||||
s = si->find_symbol_by_name(symbol_name);
|
||||
if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (s != nullptr) {
|
||||
*found = si;
|
||||
break;
|
||||
|
@ -1444,6 +1655,93 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
|
|||
return ifunc_addr;
|
||||
}
|
||||
|
||||
const version_info* VersionTracker::get_version_info(ElfW(Versym) source_symver) const {
|
||||
if (source_symver < 2 ||
|
||||
source_symver >= version_infos.size() ||
|
||||
version_infos[source_symver].name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &version_infos[source_symver];
|
||||
}
|
||||
|
||||
void VersionTracker::add_version_info(size_t source_index,
|
||||
ElfW(Word) elf_hash,
|
||||
const char* ver_name,
|
||||
const soinfo* target_si) {
|
||||
if (source_index >= version_infos.size()) {
|
||||
version_infos.resize(source_index+1);
|
||||
}
|
||||
|
||||
version_infos[source_index].elf_hash = elf_hash;
|
||||
version_infos[source_index].name = ver_name;
|
||||
version_infos[source_index].target_si = target_si;
|
||||
}
|
||||
|
||||
bool VersionTracker::init_verneed(const soinfo* si_from) {
|
||||
uintptr_t verneed_ptr = si_from->get_verneed_ptr();
|
||||
|
||||
if (verneed_ptr == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t verneed_cnt = si_from->get_verneed_cnt();
|
||||
|
||||
for (size_t i = 0, offset = 0; i<verneed_cnt; ++i) {
|
||||
const ElfW(Verneed)* verneed = reinterpret_cast<ElfW(Verneed)*>(verneed_ptr + offset);
|
||||
size_t vernaux_offset = offset + verneed->vn_aux;
|
||||
offset += verneed->vn_next;
|
||||
|
||||
if (verneed->vn_version != 1) {
|
||||
DL_ERR("unsupported verneed[%zd] vn_version: %d (expected 1)", i, verneed->vn_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* target_soname = si_from->get_string(verneed->vn_file);
|
||||
// find it in dependencies
|
||||
soinfo* target_si = si_from->get_children().find_if([&](const soinfo* si) {
|
||||
return strcmp(si->get_soname(), target_soname) == 0;
|
||||
});
|
||||
|
||||
if (target_si == nullptr) {
|
||||
DL_ERR("cannot find \"%s\" from verneed[%zd] in DT_NEEDED list for \"%s\"",
|
||||
target_soname, i, si_from->get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j<verneed->vn_cnt; ++j) {
|
||||
const ElfW(Vernaux)* vernaux = reinterpret_cast<ElfW(Vernaux)*>(verneed_ptr + vernaux_offset);
|
||||
vernaux_offset += vernaux->vna_next;
|
||||
|
||||
const ElfW(Word) elf_hash = vernaux->vna_hash;
|
||||
const char* ver_name = si_from->get_string(vernaux->vna_name);
|
||||
ElfW(Half) source_index = vernaux->vna_other;
|
||||
|
||||
add_version_info(source_index, elf_hash, ver_name, target_si);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VersionTracker::init_verdef(const soinfo* si_from) {
|
||||
return for_each_verdef(si_from,
|
||||
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
|
||||
add_version_info(verdef->vd_ndx, verdef->vd_hash,
|
||||
si_from->get_string(verdaux->vda_name), si_from);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool VersionTracker::init(const soinfo* si_from) {
|
||||
if (!si_from->has_min_version(2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -1462,6 +1760,12 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
|
|||
template<typename ElfRelIteratorT>
|
||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group) {
|
||||
VersionTracker version_tracker;
|
||||
|
||||
if (!version_tracker.init(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||
const auto rel = rel_iterator.next();
|
||||
if (rel == nullptr) {
|
||||
|
@ -1481,12 +1785,32 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa
|
|||
continue;
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
soinfo* lsi = nullptr;
|
||||
|
||||
if (sym != 0) {
|
||||
sym_name = get_string(symtab_[sym].st_name);
|
||||
s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
|
||||
const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
|
||||
ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
|
||||
|
||||
if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
|
||||
// there is no version info for this one
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const version_info* vi = version_tracker.get_version_info(sym_ver);
|
||||
|
||||
if (vi == nullptr) {
|
||||
DL_ERR("cannot find verneed/verdef for version index=%d "
|
||||
"referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (s == nullptr) {
|
||||
// We only allow an undefined symbol if this is a weak reference...
|
||||
s = &symtab_[sym];
|
||||
|
@ -1977,6 +2301,14 @@ soinfo::soinfo_list_t& soinfo::get_children() {
|
|||
return g_empty_list;
|
||||
}
|
||||
|
||||
const soinfo::soinfo_list_t& soinfo::get_children() const {
|
||||
if (has_min_version(0)) {
|
||||
return children_;
|
||||
}
|
||||
|
||||
return g_empty_list;
|
||||
}
|
||||
|
||||
soinfo::soinfo_list_t& soinfo::get_parents() {
|
||||
if (has_min_version(0)) {
|
||||
return parents_;
|
||||
|
@ -1985,7 +2317,7 @@ soinfo::soinfo_list_t& soinfo::get_parents() {
|
|||
return g_empty_list;
|
||||
}
|
||||
|
||||
ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) {
|
||||
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);
|
||||
}
|
||||
|
@ -2452,12 +2784,23 @@ bool soinfo::prelink_image() {
|
|||
case DT_BIND_NOW:
|
||||
break;
|
||||
|
||||
// Ignore: bionic does not support symbol versioning...
|
||||
case DT_VERSYM:
|
||||
versym_ = reinterpret_cast<ElfW(Versym)*>(load_bias + d->d_un.d_ptr);
|
||||
break;
|
||||
|
||||
case DT_VERDEF:
|
||||
verdef_ptr_ = load_bias + d->d_un.d_ptr;
|
||||
break;
|
||||
case DT_VERDEFNUM:
|
||||
verdef_cnt_ = d->d_un.d_val;
|
||||
break;
|
||||
|
||||
case DT_VERNEED:
|
||||
verneed_ptr_ = load_bias + d->d_un.d_ptr;
|
||||
break;
|
||||
|
||||
case DT_VERNEEDNUM:
|
||||
verneed_cnt_ = d->d_un.d_val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "linked_list.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define DL_ERR(fmt, x...) \
|
||||
do { \
|
||||
|
@ -142,6 +143,32 @@ class SymbolName {
|
|||
DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
|
||||
};
|
||||
|
||||
struct version_info {
|
||||
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:
|
||||
VersionTracker() = default;
|
||||
bool init(const soinfo* si_from);
|
||||
|
||||
const version_info* get_version_info(ElfW(Versym) source_symver) const;
|
||||
private:
|
||||
bool init_verneed(const soinfo* si_from);
|
||||
bool init_verdef(const soinfo* si_from);
|
||||
void add_version_info(size_t source_index, ElfW(Word) elf_hash,
|
||||
const char* ver_name, const soinfo* target_si);
|
||||
|
||||
std::vector<version_info> version_infos;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(VersionTracker);
|
||||
};
|
||||
|
||||
struct soinfo {
|
||||
public:
|
||||
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
|
||||
|
@ -260,11 +287,16 @@ struct soinfo {
|
|||
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();
|
||||
|
||||
ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name);
|
||||
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(ElfW(Sym)* s);
|
||||
ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
|
||||
|
||||
const char* get_string(ElfW(Word) index) const;
|
||||
bool can_unload() const;
|
||||
|
@ -292,11 +324,18 @@ struct soinfo {
|
|||
|
||||
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;
|
||||
|
||||
private:
|
||||
ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
|
||||
bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
|
||||
ElfW(Sym)* elf_addr_lookup(const void* addr);
|
||||
ElfW(Sym)* gnu_lookup(SymbolName& symbol_name);
|
||||
bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
|
||||
ElfW(Sym)* gnu_addr_lookup(const void* addr);
|
||||
|
||||
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
|
||||
|
@ -341,11 +380,20 @@ struct soinfo {
|
|||
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_;
|
||||
|
||||
friend soinfo* get_libdl_info();
|
||||
};
|
||||
|
||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
|
||||
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
|
||||
bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
|
||||
soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
|
||||
const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol);
|
||||
|
||||
enum RelocationKind {
|
||||
kRelocAbsolute = 0,
|
||||
|
@ -364,10 +412,10 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
|
|||
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo);
|
||||
void do_dlclose(soinfo* si);
|
||||
|
||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
|
||||
const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
|
||||
soinfo* find_containing_library(const void* addr);
|
||||
|
||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
|
||||
const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
|
||||
|
||||
void debuggerd_init();
|
||||
extern "C" abort_msg_t* g_abort_message;
|
||||
|
|
|
@ -50,6 +50,12 @@ template <typename ElfRelIteratorT>
|
|||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
||||
const soinfo_list_t& global_group,
|
||||
const soinfo_list_t& local_group) {
|
||||
VersionTracker version_tracker;
|
||||
|
||||
if (!version_tracker.init(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
|
||||
const auto rel = rel_iterator.next();
|
||||
|
||||
|
@ -69,12 +75,33 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
|
|||
continue;
|
||||
}
|
||||
|
||||
ElfW(Sym)* s = nullptr;
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
soinfo* lsi = nullptr;
|
||||
|
||||
if (sym != 0) {
|
||||
sym_name = get_string(symtab_[sym].st_name);
|
||||
s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
|
||||
const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
|
||||
ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
|
||||
|
||||
if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
|
||||
// there is no version info for this one
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const version_info* vi = version_tracker.get_version_info(sym_ver);
|
||||
|
||||
if (vi == nullptr) {
|
||||
DL_ERR("cannot find verneed/verdef for version index=%d "
|
||||
"referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
// mips does not support relocation with weak-undefined symbols
|
||||
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname());
|
||||
|
@ -147,7 +174,11 @@ bool soinfo::mips_relocate_got(const soinfo_list_t& global_group,
|
|||
// This is an undefined reference... try to locate it.
|
||||
const char* sym_name = get_string(sym->st_name);
|
||||
soinfo* lsi = nullptr;
|
||||
ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
|
||||
const ElfW(Sym)* s = nullptr;
|
||||
if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s == nullptr) {
|
||||
// We only allow an undefined symbol if this is a weak reference.
|
||||
s = &symtab_[g];
|
||||
|
|
|
@ -925,3 +925,63 @@ TEST(dlfcn, dlopen_dlopen_from_ctor) {
|
|||
GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_v1) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(1, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_v2) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv2.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(2, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_other_v2) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv2_other.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(20, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_use_other_v3) {
|
||||
void* handle = dlopen("libtest_versioned_uselibv3_other.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(3, fn());
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
TEST(dlfcn, symbol_versioning_default_via_dlsym) {
|
||||
void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW);
|
||||
ASSERT_TRUE(handle != nullptr) << dlerror();
|
||||
typedef int (*fn_t)();
|
||||
fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "versioned_function"));
|
||||
ASSERT_TRUE(fn != nullptr) << dlerror();
|
||||
ASSERT_EQ(3, fn()); // the default version is 3
|
||||
dlclose(handle);
|
||||
}
|
||||
|
||||
// This preempts the implementation from libtest_versioned_lib.so
|
||||
extern "C" int version_zero_function() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This preempts the implementation from libtest_versioned_uselibv*.so
|
||||
extern "C" int version_zero_function2() {
|
||||
return 0;
|
||||
}
|
||||
|
|
120
tests/libs/Android.build.versioned_lib.mk
Normal file
120
tests/libs/Android.build.versioned_lib.mk
Normal file
|
@ -0,0 +1,120 @@
|
|||
#
|
||||
# Copyright (C) 2015 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Libraries used to test versioned symbols
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv1_src_files := versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv1_shared_libraries := \
|
||||
libtest_versioned_libv1
|
||||
|
||||
module := libtest_versioned_uselibv1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv2_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv2_shared_libraries := \
|
||||
libtest_versioned_libv2
|
||||
|
||||
libtest_versioned_uselibv2_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_uselib.map
|
||||
|
||||
module := libtest_versioned_uselibv2
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv2_other_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv2_other_shared_libraries := \
|
||||
libtest_versioned_otherlib_empty libtest_versioned_libv2
|
||||
|
||||
module := libtest_versioned_uselibv2_other
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_uselibv3_other_src_files := \
|
||||
versioned_uselib.cpp
|
||||
|
||||
libtest_versioned_uselibv3_other_shared_libraries := \
|
||||
libtest_versioned_otherlib_empty libtest_versioned_lib
|
||||
|
||||
module := libtest_versioned_uselibv3_other
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# lib v1 - this one used during static linking but never used at runtime
|
||||
# which forces libtest_versioned_uselibv1 to use function v1 from
|
||||
# libtest_versioned_lib.so
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_libv1_src_files := \
|
||||
versioned_lib_v1.cpp
|
||||
|
||||
libtest_versioned_libv1_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v1.map \
|
||||
-Wl,-soname,libtest_versioned_lib.so
|
||||
|
||||
module := libtest_versioned_libv1
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# lib v2 - to make libtest_versioned_uselibv2.so use version 2 of versioned_function()
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_libv2_src_files := \
|
||||
versioned_lib_v2.cpp
|
||||
|
||||
libtest_versioned_libv2_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v2.map \
|
||||
-Wl,-soname,libtest_versioned_lib.so
|
||||
|
||||
module := libtest_versioned_libv2
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# last version - this one is used at the runtime and exports 3 versions
|
||||
# of versioned_symbol().
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_lib_src_files := \
|
||||
versioned_lib_v3.cpp
|
||||
|
||||
libtest_versioned_lib_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v3.map
|
||||
|
||||
module := libtest_versioned_lib
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# This library is empty, the actual implementation will provide an unversioned
|
||||
# symbol for versioned_function().
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_otherlib_empty_src_files := empty.cpp
|
||||
|
||||
libtest_versioned_otherlib_empty_ldflags := -Wl,-soname,libtest_versioned_otherlib.so
|
||||
module := libtest_versioned_otherlib_empty
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
libtest_versioned_otherlib_src_files := versioned_lib_other.cpp
|
||||
|
||||
libtest_versioned_otherlib_ldflags := \
|
||||
-Wl,--version-script,$(LOCAL_PATH)/versioned_lib_other.map
|
||||
|
||||
module := libtest_versioned_otherlib
|
||||
include $(LOCAL_PATH)/Android.build.testlib.mk
|
|
@ -26,6 +26,7 @@ common_additional_dependencies := \
|
|||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
|
||||
$(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
|
||||
$(LOCAL_PATH)/Android.build.testlib.mk \
|
||||
$(LOCAL_PATH)/Android.build.versioned_lib.mk \
|
||||
$(TEST_PATH)/Android.build.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
@ -197,6 +198,11 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk
|
|||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build libtest_versioned_lib.so with its dependencies.
|
||||
# -----------------------------------------------------------------------------
|
||||
include $(LOCAL_PATH)/Android.build.versioned_lib.mk
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Library with dependency loop used by dlfcn tests
|
||||
#
|
||||
|
|
21
tests/libs/versioned_lib_other.cpp
Normal file
21
tests/libs/versioned_lib_other.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" int versioned_function_v2() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
|
9
tests/libs/versioned_lib_other.map
Normal file
9
tests/libs/versioned_lib_other.map
Normal file
|
@ -0,0 +1,9 @@
|
|||
TESTLIB_V0 {
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
30
tests/libs/versioned_lib_v1.cpp
Normal file
30
tests/libs/versioned_lib_v1.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v1,versioned_function@@TESTLIB_V1");
|
12
tests/libs/versioned_lib_v1.map
Normal file
12
tests/libs/versioned_lib_v1.map
Normal file
|
@ -0,0 +1,12 @@
|
|||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
35
tests/libs/versioned_lib_v2.cpp
Normal file
35
tests/libs/versioned_lib_v2.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v2(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int versioned_function_v2() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 200;
|
||||
}
|
||||
__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
|
||||
__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
|
16
tests/libs/versioned_lib_v2.map
Normal file
16
tests/libs/versioned_lib_v2.map
Normal file
|
@ -0,0 +1,16 @@
|
|||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V1;
|
42
tests/libs/versioned_lib_v3.cpp
Normal file
42
tests/libs/versioned_lib_v3.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function_v1(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v2(); // __attribute__((visibility("hidden")));
|
||||
int versioned_function_v3(); // __attribute__((visibility("hidden")));
|
||||
int version_zero_function();
|
||||
}
|
||||
|
||||
int versioned_function_v1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int versioned_function_v2() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int versioned_function_v3() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
int version_zero_function() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
|
||||
__asm__(".symver versioned_function_v2,versioned_function@TESTLIB_V2");
|
||||
__asm__(".symver versioned_function_v3,versioned_function@@TESTLIB_V3");
|
21
tests/libs/versioned_lib_v3.map
Normal file
21
tests/libs/versioned_lib_v3.map
Normal file
|
@ -0,0 +1,21 @@
|
|||
TESTLIB_V0 {
|
||||
global:
|
||||
version_zero_function;
|
||||
local:
|
||||
versioned_function_v*;
|
||||
};
|
||||
|
||||
TESTLIB_V1 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V0;
|
||||
|
||||
TESTLIB_V2 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V1;
|
||||
|
||||
TESTLIB_V3 {
|
||||
global:
|
||||
versioned_function;
|
||||
} TESTLIB_V2;
|
32
tests/libs/versioned_uselib.cpp
Normal file
32
tests/libs/versioned_uselib.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
int versioned_function();
|
||||
|
||||
int get_function_version();
|
||||
int version_zero_function();
|
||||
int version_zero_function2() __attribute__((weak));
|
||||
}
|
||||
|
||||
int get_function_version() {
|
||||
return version_zero_function2() + version_zero_function() + versioned_function();
|
||||
}
|
||||
|
||||
// we expect this function to be preempted by main executable.
|
||||
int version_zero_function2() {
|
||||
return 40000;
|
||||
}
|
9
tests/libs/versioned_uselib.map
Normal file
9
tests/libs/versioned_uselib.map
Normal file
|
@ -0,0 +1,9 @@
|
|||
TESTLIB_NONE {
|
||||
global:
|
||||
get_function_version;
|
||||
};
|
||||
|
||||
TESTLIB_ZERO {
|
||||
global:
|
||||
version_zero_function2;
|
||||
} TESTLIB_NONE;
|
Loading…
Reference in a new issue