1
1
mirror of https://github.com/rui314/mold.git synced 2024-12-26 01:44:29 +03:00

Promote weak undefined symbols to weak dynamic symbols

Previously, if a weak undefined symbol cannot be resolved within
an output of the linker, mold turned it into an absolute symbol
with value 0. However, other linkers export such symbols as a
weak dynamic symbol, so that the symbol gets another chance to be
resolved at runtime.

This patch implements the behavior.

This is needed by Gentoo's dev-libs/nsync-1.20.1 package.
This commit is contained in:
Rui Ueyama 2021-06-25 00:36:47 +09:00
parent a8ebb63b00
commit d9ecc2e3e6
5 changed files with 85 additions and 103 deletions

21
main.cc
View File

@ -447,26 +447,13 @@ int do_main(int argc, char **argv) {
// Beyond this point, no new files will be added to ctx.objs
// or ctx.dsos.
// Convert weak symbols to absolute symbols with value 0.
convert_undefined_weak_symbols(ctx);
// If we do not handle undefined symbols as errors, such symbols
// are converted to absolute symbols with value 0.
if (ctx.arg.unresolved_symbols != UnresolvedKind::ERROR) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->ignore_unresolved_symbols(ctx);
});
}
// If we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
if (ctx.arg.shared && !ctx.arg.z_defs) {
Timer t(ctx, "claim_unresolved_symbols");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->claim_unresolved_symbols(ctx);
});
}
//
// If we are linking an executable, weak undefs are converted to
// weakly imported symbol so that they'll have another chance to be
claim_unresolved_symbols(ctx);
// Beyond this point, no new symbols will be added to the result.

3
mold.h
View File

@ -930,7 +930,6 @@ public:
void convert_undefined_weak_symbols(Context<E> &ctx);
void resolve_comdat_groups();
void eliminate_duplicate_comdat_groups();
void ignore_unresolved_symbols(Context<E> &ctx);
void claim_unresolved_symbols(Context<E> &ctx);
void scan_relocations(Context<E> &ctx);
void convert_common_symbols(Context<E> &ctx);
@ -1331,7 +1330,7 @@ template <typename E> void sort_init_fini(Context<E> &);
template <typename E> std::vector<OutputChunk<E> *>
collect_output_sections(Context<E> &);
template <typename E> void compute_section_sizes(Context<E> &);
template <typename E> void convert_undefined_weak_symbols(Context<E> &);
template <typename E> void claim_unresolved_symbols(Context<E> &);
template <typename E> void scan_rels(Context<E> &);
template <typename E> void apply_version_script(Context<E> &);
template <typename E> void parse_symbol_version(Context<E> &);

View File

@ -883,36 +883,6 @@ void ObjectFile<E>::resolve_common_symbols(Context<E> &ctx) {
}
}
template <typename E>
void ObjectFile<E>::convert_undefined_weak_symbols(Context<E> &ctx) {
for (i64 i = first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = elf_syms[i];
if (!esym.is_undef() || !esym.is_weak())
continue;
Symbol<E> &sym = *this->symbols[i];
std::lock_guard lock(sym.mu);
if (!sym.file ||
(sym.esym().is_undef_weak() && this->priority < sym.file->priority)) {
sym.file = this;
sym.input_section = nullptr;
sym.value = 0;
sym.sym_idx = i;
sym.ver_idx = ctx.arg.default_version;
sym.is_lazy = false;
sym.is_weak = true;
if (ctx.arg.shared)
sym.is_imported = true;
if (sym.traced)
SyncOut(ctx) << "trace-symbol: " << *this
<< ": unresolved weak symbol " << sym;
}
}
}
template <typename E>
void ObjectFile<E>::resolve_comdat_groups() {
for (auto &pair : comdat_groups) {
@ -938,50 +908,25 @@ void ObjectFile<E>::eliminate_duplicate_comdat_groups() {
}
}
template <typename E>
void ObjectFile<E>::ignore_unresolved_symbols(Context<E> &ctx) {
if (!this->is_alive)
return;
for (i64 i = first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = elf_syms[i];
Symbol<E> &sym = *this->symbols[i];
if (esym.is_defined())
continue;
std::lock_guard lock(sym.mu);
if (!sym.file ||
(sym.esym().is_undef_strong() && sym.file->priority < this->priority)) {
if (ctx.arg.unresolved_symbols == UnresolvedKind::WARN)
Warn(ctx) << "undefined symbol: " << *this << ": " << sym;
sym.file = this;
sym.input_section = nullptr;
sym.value = esym.st_value;
sym.sym_idx = i;
sym.ver_idx = ctx.arg.default_version;
sym.is_lazy = false;
sym.is_weak = false;
sym.is_imported = false;
sym.is_exported = false;
}
}
}
template <typename E>
void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
if (!this->is_alive)
return;
bool claim_all = ctx.arg.shared && !ctx.arg.z_defs;
for (i64 i = first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = elf_syms[i];
Symbol<E> &sym = *this->symbols[i];
if (esym.is_defined())
if (!esym.is_undef())
continue;
std::lock_guard lock(sym.mu);
if (!sym.file ||
(sym.esym().is_undef_strong() && sym.file->priority < this->priority)) {
(sym.esym().is_undef() && sym.file->priority < this->priority)) {
if (claim_all || esym.is_weak()) {
// Convert remaining undefined symbols to dynamic symbols.
sym.file = this;
sym.input_section = nullptr;
sym.value = 0;
@ -989,8 +934,29 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
sym.ver_idx = ctx.arg.default_version;
sym.is_lazy = false;
sym.is_weak = false;
sym.is_imported = true;
sym.is_imported = !ctx.arg.is_static;
sym.is_exported = false;
if (sym.traced)
SyncOut(ctx) << "trace-symbol: " << *this << ": unresolved"
<< (esym.is_weak() ? " weak" : "")
<< " symbol " << sym;
} else if (ctx.arg.unresolved_symbols != UnresolvedKind::ERROR) {
// Convert remaining undefined symbols to absolute symbols with
// value 0.
sym.file = this;
sym.input_section = nullptr;
sym.value = 0;
sym.sym_idx = i;
sym.ver_idx = ctx.arg.default_version;
sym.is_lazy = false;
sym.is_weak = false;
sym.is_imported = false;
sym.is_exported = false;
if (ctx.arg.unresolved_symbols == UnresolvedKind::WARN)
Warn(ctx) << "undefined symbol: " << *this << ": " << sym;
}
}
}
}
@ -1034,7 +1000,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
Symbol<E> *sym = this->symbols[i];
if (sym->file != this) {
if (ctx.arg.warn_common)
Warn(ctx) << *this << ": " << "multiple common symbols: " << *sym;
Warn(ctx) << *this << ": multiple common symbols: " << *sym;
continue;
}

View File

@ -392,11 +392,10 @@ void compute_section_sizes(Context<E> &ctx) {
}
template <typename E>
void convert_undefined_weak_symbols(Context<E> &ctx) {
Timer t(ctx, "undef_weak");
void claim_unresolved_symbols(Context<E> &ctx) {
Timer t(ctx, "claim_unresolved_symbols");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->convert_undefined_weak_symbols(ctx);
file->claim_unresolved_symbols(ctx);
});
}
@ -444,6 +443,20 @@ void scan_rels(Context<E> &ctx) {
// Assign offsets in additional tables for each dynamic symbol.
for (Symbol<E> *sym : syms) {
if ((sym->flags & NEEDS_COPYREL) && !sym->file->is_dso) {
// Weak undefined symbols can be converted to imported symbols, but
// we can't create copy relocations against them because we don't
// know their symbol sizes. Therefore, if we have to create copyrels
// against such symbols, we instead turns the symbols into
// non-imported absoute ones with value 0. That means weak symbols
// are exported only when compiled with -fPIC, and the same program
// can have different meanings depending on the presence or absense
// that flag. That's bad, but this is the best we can do.
sym->is_imported = false;
sym->flags = 0;
continue;
}
if (sym->flags & NEEDS_DYNSYM)
ctx.dynsym->add_symbol(ctx, sym);
@ -873,7 +886,7 @@ void compress_debug_sections(Context<E> &ctx) {
template std::vector<OutputChunk<E> *> \
collect_output_sections(Context<E> &ctx); \
template void compute_section_sizes(Context<E> &ctx); \
template void convert_undefined_weak_symbols(Context<E> &ctx); \
template void claim_unresolved_symbols(Context<E> &ctx); \
template void scan_rels(Context<E> &ctx); \
template void apply_version_script(Context<E> &ctx); \
template void parse_symbol_version(Context<E> &ctx); \

View File

@ -5,15 +5,32 @@ echo -n "Testing $(basename -s .sh $0) ... "
t=$(pwd)/tmp/$(basename -s .sh $0)
mkdir -p $t
cat <<EOF | cc -o $t/a.o -c -x assembler -
.weak weak_fn
.global _start
_start:
nop
cat <<EOF > $t/a.c
#include <stdio.h>
__attribute__((weak)) int foo();
__attribute__((weak)) extern int bar;
int main() {
printf("%d %d\n", foo ? foo() : 3, &bar ? bar : 5);
}
EOF
cc -fno-PIC -c -o $t/c.o $t/a.c
clang -fuse-ld=`pwd`/../mold -o $t/exe $t/c.o
! readelf --dyn-syms $t/exe | grep -q 'NOTYPE WEAK DEFAULT UND foo' || false
$t/exe | grep -q '3 5'
../mold -o $t/exe $t/a.o
readelf -a $t/exe > /dev/null
cc -fPIC -c -o $t/b.o $t/a.c
clang -fuse-ld=`pwd`/../mold -o $t/exe $t/b.o
readelf --dyn-syms $t/exe | grep -q 'NOTYPE WEAK DEFAULT UND foo'
$t/exe | grep -q '3 5'
cat <<EOF | cc -shared -o $t/d.so -xc -
int foo() { return 42; }
int bar = 7;
EOF
LD_PRELOAD=$t/d.so $t/exe | grep -q '42 7'
echo OK