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:
parent
9a3188bcf5
commit
21a2d6bb6c
@ -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;
|
||||
|
@ -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>; \
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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> &); \
|
||||
|
@ -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
55
test/elf/shlib-undefined.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user