Validate defined versions in prelink_image
Validate the list of defined versions explicitly, during library
prelinking, rather than implicitly as part of constructing the
VersionTracker in soinfo::link_image.
Doing the validation upfront allows removing the symbol lookup failure
code paths, which only happen on a library with invalid version
information.
Helps on the walleye 64-bit linker relocation benchmark (146.2ms ->
131.6ms)
Bug: none
Test: bionic unit tests
Change-Id: Id17508aba3af2863909f0526897c4277419322b7
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 1069234..87335e4 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -508,10 +508,7 @@
*/
if (si_from->has_DT_SYMBOLIC) {
DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_realpath(), name);
- if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
- return false;
- }
-
+ s = si_from->find_symbol_by_name(symbol_name, vi);
if (s != nullptr) {
*si_found_in = si_from;
}
@@ -519,15 +516,10 @@
// 1. Look for it in global_group
if (s == nullptr) {
- bool error = false;
global_group.visit([&](soinfo* global_si) {
DEBUG("%s: looking up %s in %s (from global group)",
si_from->get_realpath(), name, global_si->get_realpath());
- if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
- error = true;
- return false;
- }
-
+ s = global_si->find_symbol_by_name(symbol_name, vi);
if (s != nullptr) {
*si_found_in = global_si;
return false;
@@ -535,15 +527,10 @@
return true;
});
-
- if (error) {
- return false;
- }
}
// 2. Look for it in the local group
if (s == nullptr) {
- bool error = false;
local_group.visit([&](soinfo* local_si) {
if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
// we already did this - skip
@@ -552,11 +539,7 @@
DEBUG("%s: looking up %s in %s (from local group)",
si_from->get_realpath(), name, local_si->get_realpath());
- if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
- error = true;
- return false;
- }
-
+ s = local_si->find_symbol_by_name(symbol_name, vi);
if (s != nullptr) {
*si_found_in = local_si;
return false;
@@ -564,10 +547,6 @@
return true;
});
-
- if (error) {
- return false;
- }
}
if (s != nullptr) {
@@ -863,11 +842,7 @@
return kWalkSkip;
}
- if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) {
- result = nullptr;
- return kWalkStop;
- }
-
+ result = current_soinfo->find_symbol_by_name(symbol_name, vi);
if (result != nullptr) {
*found = current_soinfo;
return kWalkStop;
@@ -947,10 +922,7 @@
continue;
}
- if (!si->find_symbol_by_name(symbol_name, vi, &s)) {
- return nullptr;
- }
-
+ s = si->find_symbol_by_name(symbol_name, vi);
if (s != nullptr) {
*found = si;
break;
@@ -2819,25 +2791,38 @@
return true;
}
-bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym) {
+ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi) {
if (vi == nullptr) {
- *versym = kVersymNotNeeded;
- return true;
+ return kVersymNotNeeded;
}
- *versym = kVersymGlobal;
+ ElfW(Versym) result = kVersymGlobal;
- return for_each_verdef(si,
+ if (!for_each_verdef(si,
[&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
if (verdef->vd_hash == vi->elf_hash &&
strcmp(vi->name, si->get_string(verdaux->vda_name)) == 0) {
- *versym = verdef->vd_ndx;
+ result = verdef->vd_ndx;
return true;
}
return false;
}
- );
+ )) {
+ // verdef should have already been validated in prelink_image.
+ async_safe_fatal("invalid verdef after prelinking: %s, %s",
+ si->get_realpath(), linker_get_error_buffer());
+ }
+
+ return result;
+}
+
+// Validate the library's verdef section. On error, returns false and invokes DL_ERR.
+bool validate_verdef_section(const soinfo* si) {
+ return for_each_verdef(si,
+ [&](size_t, const ElfW(Verdef)*, const ElfW(Verdaux)*) {
+ return false;
+ });
}
bool VersionTracker::init_verdef(const soinfo* si_from) {
@@ -3842,6 +3827,10 @@
// Don't call add_dlwarning because a missing DT_SONAME isn't important enough to show in the UI
}
+ // Validate each library's verdef section once, so we don't have to validate
+ // it each time we look up a symbol with a version.
+ if (!validate_verdef_section(this)) return false;
+
flags_ |= FLAG_PRELINKED;
return true;
}
diff --git a/linker/linker.h b/linker/linker.h
index 789640c..16c65a1 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -204,3 +204,5 @@
size_t reserved_size = 0;
bool must_use_address = false;
};
+
+ElfW(Versym) find_verdef_version_index(const soinfo* si, const version_info* vi);
diff --git a/linker/linker_cfi.cpp b/linker/linker_cfi.cpp
index 435bb1a..5995013 100644
--- a/linker/linker_cfi.cpp
+++ b/linker/linker_cfi.cpp
@@ -142,8 +142,7 @@
static uintptr_t soinfo_find_symbol(soinfo* si, const char* s) {
SymbolName name(s);
- const ElfW(Sym) * sym;
- if (si->find_symbol_by_name(name, nullptr, &sym) && sym) {
+ if (const ElfW(Sym)* sym = si->find_symbol_by_name(name, nullptr)) {
return si->resolve_symbol_address(sym);
}
return 0;
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 04aa27b..8efc9fe 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -36,6 +36,7 @@
#include <async_safe/log.h>
+#include "linker.h"
#include "linker_config.h"
#include "linker_debug.h"
#include "linker_globals.h"
@@ -43,7 +44,6 @@
#include "linker_utils.h"
// TODO(dimitry): These functions are currently located in linker.cpp - find a better place for it
-bool find_verdef_version_index(const soinfo* si, const version_info* vi, ElfW(Versym)* versym);
ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr);
int get_application_target_sdk_version();
@@ -135,22 +135,6 @@
return 0;
}
-bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
- const version_info* vi,
- const ElfW(Sym)** symbol) const {
- uint32_t symbol_index;
- bool success =
- is_gnu_hash() ?
- gnu_lookup(symbol_name, vi, &symbol_index) :
- elf_lookup(symbol_name, vi, &symbol_index);
-
- if (success) {
- *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
- }
-
- return success;
-}
-
static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
ELF_ST_BIND(s->st_info) == STB_WEAK) {
@@ -177,9 +161,12 @@
verneed == (*verdef & ~kVersymHiddenBit);
}
-bool soinfo::gnu_lookup(SymbolName& symbol_name,
- const version_info* vi,
- uint32_t* symbol_index) const {
+const ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name,
+ const version_info* vi) const {
+ return is_gnu_hash() ? gnu_lookup(symbol_name, vi) : elf_lookup(symbol_name, vi);
+}
+
+const ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name, const version_info* vi) const {
uint32_t hash = symbol_name.gnu_hash();
uint32_t h2 = hash >> gnu_shift2_;
@@ -187,8 +174,6 @@
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
- *symbol_index = 0;
-
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
@@ -197,7 +182,7 @@
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
- return true;
+ return nullptr;
}
// bloom test says "probably yes"...
@@ -207,7 +192,7 @@
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
- return true;
+ return nullptr;
}
// lookup versym for the version definition in this library
@@ -216,10 +201,7 @@
// which implies that the default version can be accepted; the second case results in
// verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
// for this library and consider only *global* ones.
- ElfW(Versym) verneed = 0;
- if (!find_verdef_version_index(this, vi, &verneed)) {
- return false;
- }
+ const ElfW(Versym) verneed = find_verdef_version_index(this, vi);
do {
ElfW(Sym)* s = symtab_ + n;
@@ -235,30 +217,24 @@
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
- *symbol_index = n;
- return true;
+ return symtab_ + n;
}
} while ((gnu_chain_[n++] & 1) == 0);
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), get_realpath(), reinterpret_cast<void*>(base));
- return true;
+ return nullptr;
}
-bool soinfo::elf_lookup(SymbolName& symbol_name,
- const version_info* vi,
- uint32_t* symbol_index) const {
+const ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name, const version_info* vi) const {
uint32_t hash = symbol_name.elf_hash();
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(base), hash, hash % nbucket_);
- ElfW(Versym) verneed = 0;
- if (!find_verdef_version_index(this, vi, &verneed)) {
- return false;
- }
+ const ElfW(Versym) verneed = find_verdef_version_index(this, vi);
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym)* s = symtab_ + n;
@@ -276,8 +252,7 @@
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
- *symbol_index = n;
- return true;
+ return symtab_ + n;
}
}
@@ -285,8 +260,7 @@
symbol_name.get_name(), get_realpath(),
reinterpret_cast<void*>(base), hash, hash % nbucket_);
- *symbol_index = 0;
- return true;
+ return nullptr;
}
ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 1cb727c..9ae17ea 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -243,9 +243,7 @@
soinfo_list_t& get_parents();
- bool find_symbol_by_name(SymbolName& symbol_name,
- const version_info* vi,
- const ElfW(Sym)** symbol) const;
+ const ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name, const version_info* vi) const;
ElfW(Sym)* find_symbol_by_address(const void* addr);
ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
@@ -310,10 +308,10 @@
bool is_image_linked() const;
void set_image_linked();
- bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
- ElfW(Sym)* elf_addr_lookup(const void* addr);
- bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
+ const ElfW(Sym)* gnu_lookup(SymbolName& symbol_name, const version_info* vi) const;
+ const ElfW(Sym)* elf_lookup(SymbolName& symbol_name, const version_info* vi) const;
ElfW(Sym)* gnu_addr_lookup(const void* addr);
+ ElfW(Sym)* elf_addr_lookup(const void* addr);
bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym,
const char* sym_name, const version_info** vi);