1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-04 08:37:28 +03:00

Implement `--(no-)allow-shlib-undefined.

This also fixes the `note-property` test which was hanging on my
machine. Turns out `$CC` was stuck waiting to read from `stdin`,
and as such I decided to write something into it.

Signed-off-by: Robert Bartlensky <bartlensky.robert@gmail.com>
This commit is contained in:
Robert Bartlensky 2022-06-12 02:18:10 +01:00
parent 9a3188bcf5
commit 21a2d6bb6c
No known key found for this signature in database
GPG Key ID: 6C53EF29189641EF
7 changed files with 114 additions and 15 deletions

View File

@ -54,6 +54,8 @@ Options:
--Tdata Set address to .data
--Ttext Set address to .text
--allow-multiple-definition Allow multiple definitions
--allow-shlib-undefined Allow undefined symbols in shared libraries (default for --shared)
--no-allow-shlib-undefined
--as-needed Only set DT_NEEDED if used
--no-as-needed
--build-id [none,md5,sha1,sha256,uuid,HEXSTRING]
@ -932,7 +934,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("disable-new-dtags")) {
} else if (read_flag("nostdlib")) {
} else if (read_flag("allow-shlib-undefined")) {
ctx.arg.allow_shlib_undefined = true;
} else if (read_flag("no-allow-shlib-undefined")) {
ctx.arg.allow_shlib_undefined = false;
} else if (read_flag("no-add-needed")) {
} else if (read_flag("no-call-graph-profile-sort")) {
} else if (read_flag("no-copy-dt-needed-entries")) {
@ -1069,6 +1073,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.shared)
ctx.overwrite_output_file = false;
// By default, this is set to true when building shared objects
if (!ctx.arg.allow_shlib_undefined)
ctx.arg.allow_shlib_undefined = ctx.arg.shared;
if (version_shown && remaining.empty())
exit(0);
return remaining;

View File

@ -63,6 +63,19 @@ void InputFile<E>::clear_symbols() {
}
}
template <typename E>
void InputFile<E>::report_undef(Context<E> &ctx, const Symbol<E> &sym) const {
std::stringstream ss;
if (std::string_view source = this->get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
}
// Find the source filename. It should be listed in symtab as STT_FILE.
template <typename E>
std::string_view InputFile<E>::get_source_name() const {
@ -993,18 +1006,6 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
if (!this->is_alive)
return;
auto report_undef = [&](Symbol<E> &sym) {
std::stringstream ss;
if (std::string_view source = this->get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
};
for (i64 i = this->first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = this->elf_syms[i];
Symbol<E> &sym = *this->symbols[i];
@ -1017,7 +1018,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// imported symbol, it's handled as if no symbols were found.
if (sym.file && sym.file->is_dso &&
(sym.visibility == STV_PROTECTED || sym.visibility == STV_HIDDEN)) {
report_undef(sym);
this->report_undef(ctx, sym);
continue;
}
@ -1047,7 +1048,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
};
if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
report_undef(sym);
this->report_undef(ctx, sym);
// Convert remaining undefined symbols to dynamic symbols.
if (ctx.arg.shared) {
@ -1531,6 +1532,24 @@ void SharedFile<E>::write_symtab(Context<E> &ctx) {
}
}
template <typename E>
void SharedFile<E>::report_undefs(Context<E> &ctx) {
if (!this->is_alive)
return;
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
const ElfSym<E> &esym = this->elf_syms[i];
const Symbol<E> &sym = *this->symbols[i];
if (esym.is_undef_strong()) {
const Symbol<E> *ctx_sym = get_symbol(ctx, sym.name());
if (!ctx_sym || !ctx_sym->file || ctx_sym->esym().is_undef())
// we found an undef symbol in this dso, which isn't defined by any
// other object file
this->report_undef(ctx, sym);
}
}
}
#define INSTANTIATE(E) \
template class InputFile<E>; \
template class ObjectFile<E>; \

View File

@ -520,6 +520,9 @@ static int elf_main(int argc, char **argv) {
// Beyond this point, no new symbols will be added to the result.
if (!*ctx.arg.allow_shlib_undefined)
report_shlib_undefined(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);

View File

@ -1066,6 +1066,8 @@ public:
std::span<Symbol<E> *> get_global_syms();
std::string_view get_source_name() const;
void report_undef(Context<E> &ctx, const Symbol<E> &sym) const;
MappedFile<Context<E>> *mf = nullptr;
std::span<ElfShdr<E>> elf_sections;
std::span<ElfSym<E>> elf_syms;
@ -1197,6 +1199,7 @@ public:
void compute_symtab(Context<E> &ctx);
void write_symtab(Context<E> &ctx);
void report_undefs(Context<E> &ctx);
bool is_needed = false;
std::string soname;
@ -1314,6 +1317,7 @@ template <typename E> void shuffle_sections(Context<E> &);
template <typename E> std::vector<Chunk<E> *>
collect_output_sections(Context<E> &);
template <typename E> void compute_section_sizes(Context<E> &);
template <typename E> void report_shlib_undefined(Context<E> &);
template <typename E> void claim_unresolved_symbols(Context<E> &);
template <typename E> void scan_rels(Context<E> &);
template <typename E> void construct_relr(Context<E> &);
@ -1543,6 +1547,7 @@ struct Context {
i64 thread_count = 0;
std::optional<Glob> unique;
std::optional<u64> shuffle_sections_seed;
std::optional<bool> allow_shlib_undefined;
std::string Map;
std::string chroot;
std::string dependency_file;

View File

@ -863,6 +863,14 @@ void compute_section_sizes(Context<E> &ctx) {
create_range_extension_thunks(ctx, *osec);
}
template <typename E>
void report_shlib_undefined(Context<E> &ctx) {
Timer t(ctx, "report_shlib_undefined");
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
file->report_undefs(ctx);
});
}
template <typename E>
void claim_unresolved_symbols(Context<E> &ctx) {
Timer t(ctx, "claim_unresolved_symbols");
@ -1698,6 +1706,7 @@ void write_dependency_file(Context<E> &ctx) {
template void shuffle_sections(Context<E> &); \
template std::vector<Chunk<E> *> collect_output_sections(Context<E> &); \
template void compute_section_sizes(Context<E> &); \
template void report_shlib_undefined(Context<E> &); \
template void claim_unresolved_symbols(Context<E> &); \
template void scan_rels(Context<E> &); \
template void create_reloc_sections(Context<E> &); \

View File

@ -16,7 +16,7 @@ mkdir -p $t
# Skip if target is not x86-64
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
$CC -fcf-protection=branch /dev/null -o /dev/null -xc - 2> /dev/null || \
echo "" | $CC -fcf-protection=branch /dev/null -o /dev/null -xc - 2> /dev/null || \
{ echo skipped; exit; }
cat <<EOF | $CC -fcf-protection=branch -c -o $t/a.o -xc -

55
test/elf/shlib-undefined.sh Executable file
View File

@ -0,0 +1,55 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
t=out/test/elf/$testname
mkdir -p $t
# DSO with an undefined symbol that is referenced
cat <<'EOF' | $CC -shared -fPIC -o $t/liba.so -xc -
void fn2();
void fn1() { fn2(); }
void fn3() {}
EOF
# call a function from `liba.so` which uses an undefined symbol
cat <<'EOF' > $t/b.c
extern void fn1();
int main() {
fn1();
return 0;
}
EOF
# default is `--allow-shlib-undefined` for `-shared`
$CC -B. -shared -fPIC -o $t/libb.so $t/b.c -Wl,-allow-shlib-undefined $t/liba.so
# default is `--no-allow-shlib-undefined` for executables
output=$($CC -B. -o $t/b $t/b.c -Wl,--no-allow-shlib-undefined $t/liba.so 2>&1 || :)
echo $output | grep -Eq 'undefined symbol: fn2'
# fails because `fn2` is undefined in `liba.so`
output=$($CC -B. -shared -fPIC -o $t/libb.so $t/b.c -Wl,--no-allow-shlib-undefined $t/liba.so 2>&1 || :)
echo $output | grep -Eq 'undefined symbol: fn2'
$CC -B. -o $t/b $t/b.c -Wl,--allow-shlib-undefined $t/liba.so
# DSO with an undefined symbol that is not referenced
cat <<'EOF' | $CC -shared -fPIC -o $t/liba2.so -xc -
void fn2();
void fn1() {}
void fn3() {}
EOF
# works because even though `fn2` is undefined, `b.o` doesn't reference it
$CC -B. -o $t/b $t/b.c $t/liba2.so
echo OK