mirror of
https://github.com/rui314/mold.git
synced 2026-11-08 17:48:41 -10:00
Use .gnu.version_r when registering undefined DSO symbols
For an undefined symbol in a shared library, mold treated the versym as VER_NDX_GLOBAL and registered the symbol under its bare name. That meant an undef foo@VER_1.0 in libbar.so was looked up as just foo, so a non-default foo@VER_1.0 in another DSO did not satisfy it and --no-allow-shlib-undefined wrongly reported "undefined symbol: foo". Parse .gnu.version_r and, when an undef has a versym indexing into it, register the symbol as name@VERSION so the existing versioned-symbol resolution path picks up matching definitions. Fixes https://github.com/rui314/mold/issues/1571 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
82b5ed48bf
commit
6b8293bf8c
3 files changed
+84
-5
No files matched your search
+49
-5
@@ -1261,6 +1261,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
|
||||
soname = get_soname(ctx);
|
||||
version_strings = read_verdef(ctx);
|
||||
append(version_strings, read_verneed(ctx));
|
||||
|
||||
// Read a symbol table.
|
||||
std::span<ElfSym<E>> esyms = this->template get_data<ElfSym<E>>(ctx, *symtab_sec);
|
||||
@@ -1271,7 +1272,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
|
||||
for (i64 i = symtab_sec->sh_info; i < esyms.size(); i++) {
|
||||
u16 ver;
|
||||
if (vers.empty() || esyms[i].is_undef())
|
||||
if (vers.empty())
|
||||
ver = VER_NDX_GLOBAL;
|
||||
else
|
||||
ver = vers[i] & ~VERSYM_HIDDEN;
|
||||
@@ -1280,10 +1281,17 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
continue;
|
||||
|
||||
this->elf_syms2.push_back(esyms[i]);
|
||||
this->versyms.push_back(ver);
|
||||
// Defined symbols index into verdef; undefined symbols index into
|
||||
// verneed. We record VER_NDX_GLOBAL for undefined symbols here
|
||||
// because resolve_symbols only consults versyms[] for defined
|
||||
// symbols (see SharedFile::resolve_symbols).
|
||||
this->versyms.push_back(esyms[i].is_undef() ? (u16)VER_NDX_GLOBAL : ver);
|
||||
|
||||
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
|
||||
|
||||
bool has_version = ver != VER_NDX_GLOBAL && ver < version_strings.size() &&
|
||||
!version_strings[ver].empty();
|
||||
|
||||
auto get_versioned_sym = [&] {
|
||||
std::string_view key = save_string(
|
||||
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
|
||||
@@ -1304,12 +1312,13 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
// and `foo@VERSION` as usual, but with information to forward
|
||||
// references to `foo@VERSION` to `foo`. After name resolution, we
|
||||
// visit all symbol references to redirect `foo@VERSION` to `foo`.
|
||||
if (vers.empty() || ver == VER_NDX_GLOBAL) {
|
||||
if (!has_version) {
|
||||
// Unversioned symbol
|
||||
this->symbols.push_back(get_symbol(ctx, name));
|
||||
this->symbols2.push_back(nullptr);
|
||||
} else if (vers[i] & VERSYM_HIDDEN) {
|
||||
// Versioned non-default symbol
|
||||
} else if (esyms[i].is_undef() || (vers[i] & VERSYM_HIDDEN)) {
|
||||
// Versioned non-default symbol, or undefined reference whose
|
||||
// version comes from .gnu.version_r.
|
||||
this->symbols.push_back(get_versioned_sym());
|
||||
this->symbols2.push_back(nullptr);
|
||||
} else {
|
||||
@@ -1404,6 +1413,41 @@ std::vector<std::string_view> SharedFile<E>::read_verdef(Context<E> &ctx) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Reads .gnu.version_r and returns a vector of version names indexed
|
||||
// by vna_other. .gnu.version_r records the versions a shared object
|
||||
// needs from the libraries it depends on; the versym of an undefined
|
||||
// dynamic symbol indexes into these entries.
|
||||
template <typename E>
|
||||
std::vector<std::string_view> SharedFile<E>::read_verneed(Context<E> &ctx) {
|
||||
ElfShdr<E> *verneed_sec = this->find_section(SHT_GNU_VERNEED);
|
||||
if (!verneed_sec)
|
||||
return {};
|
||||
|
||||
std::vector<std::string_view> vec;
|
||||
std::string_view verneed = this->get_string(ctx, *verneed_sec);
|
||||
std::string_view strtab = this->get_string(ctx, verneed_sec->sh_link);
|
||||
|
||||
u8 *ptr = (u8 *)verneed.data();
|
||||
for (;;) {
|
||||
ElfVerneed<E> *vn = (ElfVerneed<E> *)ptr;
|
||||
u8 *aux_ptr = ptr + vn->vn_aux;
|
||||
for (i64 i = 0; i < vn->vn_cnt; i++) {
|
||||
ElfVernaux<E> *aux = (ElfVernaux<E> *)aux_ptr;
|
||||
u16 idx = aux->vna_other & ~VERSYM_HIDDEN;
|
||||
if (vec.size() <= idx)
|
||||
vec.resize(idx + 1);
|
||||
vec[idx] = strtab.data() + aux->vna_name;
|
||||
if (!aux->vna_next)
|
||||
break;
|
||||
aux_ptr += aux->vna_next;
|
||||
}
|
||||
if (!vn->vn_next)
|
||||
break;
|
||||
ptr += vn->vn_next;
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
for (i64 i = 0; i < this->symbols.size(); i++) {
|
||||
|
||||
@@ -1851,6 +1851,7 @@ private:
|
||||
void maybe_override_symbol(Symbol<E> &sym, const ElfSym<E> &esym);
|
||||
std::vector<std::string_view> read_dt_needed(Context<E> &ctx);
|
||||
std::vector<std::string_view> read_verdef(Context<E> &ctx);
|
||||
std::vector<std::string_view> read_verneed(Context<E> &ctx);
|
||||
|
||||
std::vector<u16> versyms;
|
||||
const ElfShdr<E> *symtab_sec;
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
# libfoo.so defines foo@VER_1.0 as a non-default version. Referencing
|
||||
# the bare name `foo` does NOT bind to this symbol.
|
||||
cat <<EOF > $t/libfoo.ver
|
||||
VER_1.0 {};
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc - \
|
||||
-Wl,--version-script=$t/libfoo.ver
|
||||
__asm__(".symver foo_impl,foo@VER_1.0");
|
||||
int foo_impl(void) { return 42; }
|
||||
EOF
|
||||
|
||||
# libbar.so references foo@VER_1.0 via .symver. Its dynsym holds an
|
||||
# undefined symbol with name "foo" whose versym indexes into the
|
||||
# verneed (.gnu.version_r) entry naming "VER_1.0".
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
|
||||
__asm__(".symver foo_v1,foo@VER_1.0");
|
||||
int foo_v1(void);
|
||||
int call_foo_v1(void) { return foo_v1(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc -
|
||||
int call_foo_v1(void);
|
||||
int main(void) { return call_foo_v1(); }
|
||||
EOF
|
||||
|
||||
# Both libs are present; libbar.so's undef foo@VER_1.0 is satisfied by
|
||||
# libfoo.so. --no-allow-shlib-undefined must accept the link.
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lbar -lfoo
|
||||
Reference in new issue
Block a user