mirror of
https://github.com/rui314/mold.git
synced 2024-10-04 08:37:28 +03:00
[ELF] Fix regression in symbol name resolution
This fix is needed to build Gentoo's net-fs/fuse package.
This commit is contained in:
parent
3015f5e418
commit
d391fd9a59
@ -447,27 +447,53 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
// Initialize global symbols
|
||||
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = this->elf_syms[i];
|
||||
|
||||
// Get a symbol name
|
||||
std::string_view key = symbol_strtab.data() + esym.st_name;
|
||||
std::string_view name = key;
|
||||
|
||||
// Parse symbol version after atsign
|
||||
if (i64 pos = name.find('@'); pos != name.npos) {
|
||||
std::string_view ver = name.substr(pos + 1);
|
||||
name = name.substr(0, pos);
|
||||
|
||||
if (!ver.empty() && ver != "@") {
|
||||
if (ver.starts_with('@'))
|
||||
key = name;
|
||||
if (esym.is_defined())
|
||||
symvers[i - this->first_global] = ver.data();
|
||||
}
|
||||
}
|
||||
|
||||
this->symbols[i] = insert_symbol(ctx, esym, key, name);
|
||||
if (esym.is_common())
|
||||
has_common_symbol = true;
|
||||
|
||||
// Get a symbol name
|
||||
std::string_view name = symbol_strtab.data() + esym.st_name;
|
||||
|
||||
// Parse symbol version. There are two types of versioned symbols.
|
||||
//
|
||||
// 1. The default version is in the form of `foo@@version`, and
|
||||
// such symbol is used to resolve both `foo` and `foo@version`.
|
||||
//
|
||||
// 2. The non-default version is in the form of `foo@version`,
|
||||
// and such symbol is used to resolve only `foo@version`.
|
||||
i64 pos = name.find('@');
|
||||
|
||||
// Usual non-versioned symbol
|
||||
if (pos == name.npos) {
|
||||
this->symbols[i] = insert_symbol(ctx, esym, name, name);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string_view stem = name.substr(0, pos);
|
||||
std::string_view verstr = name.substr(pos + 1);
|
||||
|
||||
// If version part is empty (i.e. `foo@` or `foo@@`), that
|
||||
// version part is ignored for compatibility with GNU linkers.
|
||||
if (verstr.empty() || verstr == "@") {
|
||||
this->symbols[i] = insert_symbol(ctx, esym, stem, stem);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (esym.is_defined())
|
||||
symvers[i - this->first_global] = verstr.data();
|
||||
|
||||
// Default versioned symbol
|
||||
if (verstr.starts_with('@')) {
|
||||
std::string_view decorated =
|
||||
save_string(ctx, std::string(stem) + std::string(verstr));
|
||||
this->symbols[i] = insert_symbol(ctx, esym, decorated, stem);
|
||||
|
||||
typename decltype(ctx.symbol_map2)::accessor acc;
|
||||
ctx.symbol_map2.insert(acc, {stem, this->symbols[i]});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-default versioned symbol
|
||||
this->symbols[i] = insert_symbol(ctx, esym, name, stem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,7 +513,7 @@ void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
|
||||
sorted_rels.resize(sections.size());
|
||||
|
||||
for (i64 i = 1; i < sections.size(); i++) {
|
||||
std::unique_ptr<InputSection<E>> &isec = sections[i];;
|
||||
std::unique_ptr<InputSection<E>> &isec = sections[i];
|
||||
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
|
||||
@ -965,6 +991,17 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
|
||||
(!sym.esym().is_undef() || sym.file->priority <= this->priority))
|
||||
continue;
|
||||
|
||||
// `foo` can be resolved to `foo@@symbol_version` (with two at-signs).
|
||||
// So, check if such a versioned symbol exists.
|
||||
if (typename decltype(ctx.symbol_map2)::accessor acc;
|
||||
ctx.symbol_map2.find(acc, sym.name())) {
|
||||
Symbol<E> *sym2 = acc->second;
|
||||
if (sym2->file) {
|
||||
this->symbols[i] = sym2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If a symbol name is in the form of "foo@version", search for
|
||||
// symbol "foo" and check if the symbol has version "version".
|
||||
std::string_view key = symbol_strtab.data() + esym.st_name;
|
||||
|
@ -1698,6 +1698,7 @@ struct Context {
|
||||
|
||||
// Symbol table
|
||||
tbb::concurrent_hash_map<std::string_view, Symbol<E>, HashCmp> symbol_map;
|
||||
tbb::concurrent_hash_map<std::string_view, Symbol<E> *> symbol_map2;
|
||||
tbb::concurrent_hash_map<std::string_view, ComdatGroup, HashCmp> comdat_groups;
|
||||
tbb::concurrent_vector<std::unique_ptr<MergedSection<E>>> merged_sections;
|
||||
tbb::concurrent_vector<std::unique_ptr<Chunk<E>>> output_chunks;
|
||||
|
@ -1121,10 +1121,13 @@ void parse_symbol_version(Context<E> &ctx) {
|
||||
|
||||
// If both symbol `foo` and `foo@VERSION` are defined, `foo@VERSION`
|
||||
// hides `foo` so that all references to `foo` are resolved to a
|
||||
// versioned symbol.
|
||||
// versioned symbol. Likewise, if `foo@VERSION` and `foo@@VERSION` are
|
||||
// defined, the default one takes precedence.
|
||||
Symbol<E> *sym2 = get_symbol(ctx, sym->name());
|
||||
if (sym2->file == file && !file->symvers[sym2->sym_idx - file->first_global])
|
||||
sym2->ver_idx = VER_NDX_LOCAL;
|
||||
if (sym2->ver_idx == ctx.default_version ||
|
||||
(sym2->ver_idx & ~VERSYM_HIDDEN) == (sym->ver_idx & ~VERSYM_HIDDEN))
|
||||
sym2->ver_idx = VER_NDX_LOCAL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ EOF
|
||||
|
||||
echo 'VER1 { local: *; }; VER2 { local: *; }; VER3 { local: *; };' > $t/b.ver
|
||||
$CC -B. -shared -o $t/c.so $t/a.o -Wl,--version-script=$t/b.ver
|
||||
readelf --symbols $t/c.so > $t/log
|
||||
readelf --dyn-syms $t/c.so > $t/log
|
||||
|
||||
fgrep -q 'foo@VER1' $t/log
|
||||
fgrep -q 'foo@VER2' $t/log
|
||||
|
40
test/elf/symbol-version3.sh
Executable file
40
test/elf/symbol-version3.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
void foo() {}
|
||||
void foo2() {}
|
||||
void foo3() {}
|
||||
|
||||
__asm__(".symver foo2, foo@TEST2");
|
||||
__asm__(".symver foo3, foo@TEST3");
|
||||
EOF
|
||||
|
||||
cat <<EOF > $t/b.version
|
||||
TEST1 { global: foo; };
|
||||
TEST2 {};
|
||||
TEST3 {};
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--version-script=$t/b.version
|
||||
readelf -W --dyn-syms $t/c.so > $t/log
|
||||
|
||||
grep -q ' foo@@TEST1$' $t/log
|
||||
grep -q ' foo@TEST2$' $t/log
|
||||
grep -q ' foo@TEST3$' $t/log
|
||||
! grep -q ' foo$' $t/log || false
|
||||
|
||||
echo OK
|
Loading…
Reference in New Issue
Block a user