mirror of
https://github.com/rui314/mold.git
synced 2026-11-08 17:48:41 -10:00
Do not demote non-exported globals to local symbols in .symtab
We computed the binding of a symbol in the static symbol table from its dynamic import/export status, so in an ordinary executable, every global symbol was emitted as STB_LOCAL. That misleads all symbol table consumers; nm prints "t main" instead of "T main", and gdb had to work around it for a feature that depends on symbol bindings. Only symbols hidden by visibility or localized by a version script should be demoted. GNU ld keeps hidden symbols STB_GLOBAL with hidden visibility while lld converts them to STB_LOCAL; we follow lld, which also matches our previous output for such symbols. One wrinkle: claim_unresolved_symbols() assigned version index 0 (VER_NDX_LOCAL) to imported symbols so that their .gnu.version entries would be 0, which would make the new is_local() misclassify them as version-script-localized. Leave ver_idx unassigned for imported symbols and have the .gnu.version writer emit 0 for unversioned undefined symbols directly, so that VER_NDX_LOCAL on a symbol means only "localized by a version script". Fixes https://github.com/rui314/mold/issues/906 Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
fba0a2185d
commit
35f4a334a0
4 files changed
+45
-5
No files matched your search
+6
-1
@@ -3618,11 +3618,16 @@ inline bool Symbol<E>::is_absolute() const {
|
||||
!get_output_section();
|
||||
}
|
||||
|
||||
// Returns true if the symbol should be emitted as a local symbol in the
|
||||
// output symbol table. Note that a symbol that is merely not exported to
|
||||
// the dynamic symbol table is still a global symbol; only ones hidden by
|
||||
// symbol visibility or localized by a version script are demoted.
|
||||
template <typename E>
|
||||
inline bool Symbol<E>::is_local(Context<E> &ctx) const {
|
||||
if (ctx.arg.relocatable)
|
||||
return esym().st_bind == STB_LOCAL;
|
||||
return !is_imported && !is_exported;
|
||||
return visibility == STV_HIDDEN || visibility == STV_INTERNAL ||
|
||||
ver_idx == VER_NDX_LOCAL;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
|
||||
+10
-3
@@ -2953,7 +2953,7 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
|
||||
// Handle --default-symver
|
||||
if (ctx.arg.default_symver)
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym && !sym->file->is_dso)
|
||||
if (sym && !sym->file->is_dso && !sym->esym().is_undef())
|
||||
if (u16 ver = sym->ver_idx;
|
||||
ver == VER_NDX_GLOBAL || ver == VER_NDX_UNSPECIFIED)
|
||||
sym->ver_idx = VER_NDX_LAST_RESERVED + 1;
|
||||
@@ -2962,9 +2962,16 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
|
||||
ctx.versym->contents.resize(ctx.dynsym->symbols.size(), VER_NDX_GLOBAL);
|
||||
ctx.versym->contents[0] = VER_NDX_LOCAL;
|
||||
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym && !sym->file->is_dso && sym->ver_idx != VER_NDX_UNSPECIFIED)
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols) {
|
||||
if (!sym || sym->file->is_dso)
|
||||
continue;
|
||||
|
||||
// An unversioned undefined symbol takes version index 0.
|
||||
if (sym->ver_idx != VER_NDX_UNSPECIFIED)
|
||||
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = sym->ver_idx;
|
||||
else if (sym->esym().is_undef())
|
||||
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = VER_NDX_LOCAL;
|
||||
}
|
||||
|
||||
// Allocate a buffer for .gnu.version_d and write to it
|
||||
contents.resize((sizeof(ElfVerdef<E>) + sizeof(ElfVerdaux<E>)) *
|
||||
|
||||
+1
-1
@@ -1809,7 +1809,7 @@ void claim_unresolved_symbols(Context<E> &ctx) {
|
||||
sym.is_weak = false;
|
||||
sym.is_imported = is_imported;
|
||||
sym.is_exported = false;
|
||||
sym.ver_idx = is_imported ? 0 : ctx.default_version;
|
||||
sym.ver_idx = is_imported ? VER_NDX_UNSPECIFIED : ctx.default_version;
|
||||
};
|
||||
|
||||
if (esym.is_undef_weak()) {
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# A symbol that is merely not exported to the dynamic symbol table must
|
||||
# remain global in .symtab. https://github.com/rui314/mold/issues/906
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
__attribute__((visibility("hidden"))) void hidden_fn() {}
|
||||
__attribute__((weak)) void weak_fn() {}
|
||||
void global_fn() {}
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o
|
||||
readelf -sW $t/exe1 > $t/log1
|
||||
|
||||
grep -E 'FUNC GLOBAL DEFAULT .* main$' $t/log1
|
||||
grep -E 'FUNC GLOBAL DEFAULT .* global_fn$' $t/log1
|
||||
grep -E 'FUNC WEAK DEFAULT .* weak_fn$' $t/log1
|
||||
grep -E 'FUNC LOCAL HIDDEN .* hidden_fn$' $t/log1
|
||||
|
||||
# A symbol localized by a version script becomes local.
|
||||
echo '{ global: global_fn; local: *; };' > $t/b.ver
|
||||
$CC -B. -shared -o $t/c.so $t/a.o -Wl,--version-script=$t/b.ver
|
||||
readelf -sW $t/c.so > $t/log2
|
||||
|
||||
grep -E 'FUNC GLOBAL DEFAULT .* global_fn$' $t/log2
|
||||
grep -E 'FUNC LOCAL DEFAULT .* weak_fn$' $t/log2
|
||||
Reference in new issue
Block a user