Skip to content

Commit

Permalink
Make weak undefined symbols to mark shared libraries as "needed"
Browse files Browse the repository at this point in the history
It is not clearly defined when undefined weak symbols are resolved.
It looks like there are two possible approaches:

 1. Promote all weak undefined symbols to dynamic ones so that they'll
    have another chance to be resolved at load-time, or

 2. Promote weak undefined symbols to dynamic ones only when there are
    definitions in other DSOs at link-time.

(1) provides the maximum flexibility. For example, consider a main program
that has a weak undefined symbol `foo` and there's no DSO that defines it
at link-time. In (1), `foo` gets promoted to a dynamic symbol, so that one
of its depending DSO is upgraded to define `foo`, the main executable's
`foo` is resolved to that symbol at load-time. On the other hand, in (2),
`foo` would have already been converted to an absolute symbol at address
zero at link-time, so you need to rebuild the main executable to use the
new definition of `foo` in the shared library.

However, (1) is not compatible with copy relocations. This is because we
need to know the size of the symbol when creating a copy relocation, but
that information is not available unless we have a definition. It's also
not compatible with canonical PLTs because canonical PLTs have non-zero
addresses and therefore weak undefined symbols would always be resolved to
non-zero addresses.

As a workaround, GNU ld promotes weak undefs to dynamic symbols only when
they don't need copy relocations or canonical PLTs. In other words, weak
undef's behavior is different between -fPIC and -fno-PIC. In the former
case, they become dynamic symbols, and vice versa.

I don't think that workaround is a good one. So, mold took the second
approach.

There is, however, another thing to consider. What if we can find a
defined symbol in a DSO that is specified as `-as-needed`? Previously,
mold did not mark the library as "needed" and converted the weak undef
into an absolute symbol.

However, libstdc++ assumes that if weak undef symbol
`__pthread_key_create` is not resolved, it assumes that multi-threading is
not used in the executable, which resulted in a mis-detection with mold.

Therefore, this patch changes the mold's behavior so that it makes weak
undefs to keep DSOs "needed".

Fixes #1286
  • Loading branch information
rui314 committed Jun 22, 2024
1 parent 0b5e5dc commit 06b5926
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 9 deletions.
18 changes: 10 additions & 8 deletions elf/input-files.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1072,14 +1072,16 @@ ObjectFile<E>::mark_live_objects(Context<E> &ctx,
if (sym.is_traced)
print_trace_symbol(ctx, *this, esym, sym);

if (sym.file && !esym.is_weak() &&
(esym.is_undef() || (esym.is_common() && !sym.esym().is_common())) &&
!sym.file->is_alive.test_and_set()) {
feeder(sym.file);

if (sym.is_traced)
Out(ctx) << "trace-symbol: " << *this << " keeps " << *sym.file
<< " for " << sym;
if (sym.file) {
bool undef_ref = esym.is_undef() && (!esym.is_weak() || sym.file->is_dso);
bool common_ref = esym.is_common() && !sym.esym().is_common();

if ((undef_ref || common_ref) && !sym.file->is_alive.test_and_set()) {
feeder(sym.file);
if (sym.is_traced)
Out(ctx) << "trace-symbol: " << *this << " keeps " << *sym.file
<< " for " << sym;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/elf/weak-export-dso2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ int main() {
EOF

$CC -B. -o $t/d.so $t/c.o $t/b.so -shared
readelf -W --dyn-syms $t/d.so | grep -q 'WEAK DEFAULT UND foo'
readelf -W --dyn-syms $t/d.so | grep -q 'WEAK DEFAULT .* UND foo'
21 changes: 21 additions & 0 deletions test/elf/weak-undef5.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
. $(dirname $0)/common.inc

cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
#include <stdio.h>
__attribute__((weak)) int foo();
int main() {
printf("%d\n", foo ? foo() : -1);
}
EOF

cat <<EOF | $CC -c -o $t/b.o -fPIC -xc -
#include <stdio.h>
int foo() { return 2; }
EOF

$CC -B. -o $t/libfoobar.so $t/b.o -shared
$CC -B. -o $t/exe $t/a.o -Wl,--as-needed -L$t -lfoobar -Wl,-rpath,$t

readelf --dynamic $t/exe | grep -q 'NEEDED.*libfoobar'
$QEMU $t/exe | grep -q '^2$'

0 comments on commit 06b5926

Please sign in to comment.