Skip to content

Commit

Permalink
Support --no-allow-shlib-undefined
Browse files Browse the repository at this point in the history
Fixes #1313
  • Loading branch information
rui314 committed Jul 21, 2024
1 parent 39442db commit 3001f02
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 8 deletions.
6 changes: 4 additions & 2 deletions elf/cmdline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.auxiliary.push_back(arg);
} else if (read_arg("filter") || read_arg("F")) {
ctx.arg.filter.push_back(arg);
} 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_arg("O")) {
} else if (read_flag("EB")) {
} else if (read_flag("EL")) {
Expand All @@ -1260,8 +1264,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("enable-new-dtags")) {
} else if (read_flag("disable-new-dtags")) {
} else if (read_flag("nostdlib")) {
} else if (read_flag("allow-shlib-undefined")) {
} else if (read_flag("no-allow-shlib-undefined")) {
} 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")) {
Expand Down
32 changes: 31 additions & 1 deletion elf/input-files.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {

this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
soname = get_soname(ctx);
dt_needed = read_dt_needed(ctx);
version_strings = read_verdef(ctx);

// Read a symbol table.
Expand Down Expand Up @@ -1265,6 +1266,35 @@ void SharedFile<E>::parse(Context<E> &ctx) {
counter += this->elf_syms.size();
}

template <typename E>
std::vector<std::string_view> SharedFile<E>::read_dt_needed(Context<E> &ctx) {
// Get the contents of a .dynamic seciton
std::span<Word<E>> dynamic;
for (ElfPhdr<E> &phdr : this->get_phdrs()) {
if (phdr.p_type == PT_DYNAMIC) {
dynamic = {(Word<E> *)(this->mf->data + phdr.p_offset),
phdr.p_memsz / sizeof(Word<E>)};
break;
}
}

// Find a string table
char *strtab = nullptr;
for (i64 i = 0; i < dynamic.size(); i += 2)
if (dynamic[i] == DT_STRTAB)
strtab = (char *)this->mf->data + dynamic[i + 1];

if (!strtab)
return {};

// Find all DT_NEEDED entries
std::vector<std::string_view> vec;
for (i64 i = 0; i < dynamic.size(); i += 2)
if (dynamic[i] == DT_NEEDED)
vec.push_back(strtab + dynamic[i + 1]);
return vec;
}

// Symbol versioning is a GNU extension to the ELF file format. I don't
// particularly like the feature as it complicates the semantics of
// dynamic linking, but we need to support it anyway because it is
Expand Down Expand Up @@ -1356,7 +1386,7 @@ SharedFile<E>::mark_live_objects(Context<E> &ctx,
if (sym.is_traced)
print_trace_symbol(ctx, *this, esym, sym);

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

Expand Down
3 changes: 3 additions & 0 deletions elf/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ int elf_main(int argc, char **argv) {
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);

if (!ctx.arg.allow_shlib_undefined)
check_shlib_undefined(ctx);

// Warn if symbols with different types are defined under the same name.
check_symbol_types(ctx);

Expand Down
12 changes: 8 additions & 4 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -1311,13 +1311,15 @@ class SharedFile : public InputFile<E> {

std::string soname;
std::vector<std::string_view> version_strings;
std::vector<std::string_view> dt_needed;
std::vector<ElfSym<E>> elf_syms2;

private:
SharedFile(Context<E> &ctx, MappedFile *mf) : InputFile<E>(ctx, mf) {}

std::string get_soname(Context<E> &ctx);
void maybe_override_symbol(Symbol<E> &sym, const ElfSym<E> &esym);
std::vector<std::string_view> read_dt_needed(Context<E> &ctx);
std::vector<std::string_view> read_verdef(Context<E> &ctx);

std::vector<u16> versyms;
Expand Down Expand Up @@ -1474,6 +1476,7 @@ template <typename E> void check_cet_errors(Context<E> &);
template <typename E> void print_dependencies(Context<E> &);
template <typename E> void write_repro_file(Context<E> &);
template <typename E> void check_duplicate_symbols(Context<E> &);
template <typename E> void check_shlib_undefined(Context<E> &);
template <typename E> void check_symbol_types(Context<E> &);
template <typename E> void sort_init_fini(Context<E> &);
template <typename E> void sort_ctor_dtor(Context<E> &);
Expand Down Expand Up @@ -1764,6 +1767,7 @@ struct Context {

// Command-line arguments
struct {
BsymbolicKind Bsymbolic = BSYMBOLIC_NONE;
BuildId build_id;
CetReportKind z_cet_report = CET_REPORT_NONE;
CompressKind compress_debug_sections = COMPRESS_NONE;
Expand All @@ -1774,8 +1778,8 @@ struct Context {
Symbol<E> *fini = nullptr;
Symbol<E> *init = nullptr;
UnresolvedKind unresolved_symbols = UNRESOLVED_IGNORE;
BsymbolicKind Bsymbolic = BSYMBOLIC_NONE;
bool allow_multiple_definition = false;
bool allow_shlib_undefined = true;
bool apply_dynamic_relocs = true;
bool color_diagnostics = false;
bool default_symver = false;
Expand All @@ -1797,7 +1801,6 @@ struct Context {
bool icf = false;
bool icf_all = false;
bool ignore_data_address_equality = false;
bool static_ = false;
bool lto_pass2 = false;
bool nmagic = false;
bool noinhibit_exec = false;
Expand All @@ -1819,6 +1822,7 @@ struct Context {
bool rosegment = true;
bool shared = false;
bool start_stop = false;
bool static_ = false;
bool stats = false;
bool strip_all = false;
bool strip_debug = false;
Expand Down Expand Up @@ -1854,8 +1858,6 @@ struct Context {
i64 spare_program_headers = 0;
i64 thread_count = 0;
i64 z_stack_size = 0;
u64 shuffle_sections_seed;
std::string_view emulation;
std::optional<Glob> unique;
std::optional<u64> physical_image_base;
std::string Map;
Expand All @@ -1870,6 +1872,7 @@ struct Context {
std::string separate_debug_file;
std::string soname;
std::string sysroot;
std::string_view emulation;
std::unique_ptr<std::unordered_set<std::string_view>> retain_symbols_file;
std::unordered_map<std::string_view, u64> section_align;
std::unordered_map<std::string_view, u64> section_start;
Expand All @@ -1887,6 +1890,7 @@ struct Context {
std::vector<std::string_view> filter;
std::vector<std::string_view> trace_symbol;
u64 image_base = 0x200000;
u64 shuffle_sections_seed = 0;
} arg;

std::vector<VersionPattern> version_patterns;
Expand Down
50 changes: 50 additions & 0 deletions elf/passes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,55 @@ void check_duplicate_symbols(Context<E> &ctx) {
ctx.checkpoint();
}

// If --no-allow-shlib-undefined is specified, we report errors on
// unresolved symbols in shared libraries. This is useful when you are
// creating a final executable and want to make sure that all symbols
// including ones in shared libraries have been resolved.
//
// If you do not pass --no-allow-shlib-undefined, undefined symbols in
// shared libraries will be reported as run-time error by the dynamic
// linker.
template <typename E>
void check_shlib_undefined(Context<E> &ctx) {
Timer t(ctx, "check_shlib_undefined");

// Obtain a list of DSOs whose all DT_NEEDED files are known to us.
// If a DSO depends on an unknown library, that library may provide
// definitions for missing symbols, so we ignore such libraries.
std::vector<SharedFile<E> *> dsos = ctx.dsos;
std::unordered_set<std::string_view> sonames;

for (SharedFile<E> *file : dsos)
sonames.insert(file->soname);

std::erase_if(dsos, [&](SharedFile<E> *file) {
for (std::string_view needed : file->dt_needed)
if (sonames.count(needed) == 0)
return true;
return false;
});

auto is_sparc_register = [](const ElfSym<E> &esym) {
// Dynamic symbol table for SPARC contains bogus entries which
// we need to ignore
if constexpr (is_sparc<E>)
return esym.st_type == STT_SPARC_REGISTER;
return false;
};

// Check if all undefined symbols have been resolved.
for (SharedFile<E> *file : dsos) {
for (i64 i = 0; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && !esym.is_weak() && !is_sparc_register(esym) &&
!sym.file)
Error(ctx) << *file << ": --no-allow-shlib-undefined: undefined symbol: "
<< sym;
}
}
}

template <typename E>
void check_symbol_types(Context<E> &ctx) {
Timer t(ctx, "check_symbol_types");
Expand Down Expand Up @@ -3270,6 +3319,7 @@ template void apply_section_align(Context<E> &);
template void print_dependencies(Context<E> &);
template void write_repro_file(Context<E> &);
template void check_duplicate_symbols(Context<E> &);
template void check_shlib_undefined(Context<E> &);
template void check_symbol_types(Context<E> &);
template void sort_init_fini(Context<E> &);
template void sort_ctor_dtor(Context<E> &);
Expand Down
2 changes: 1 addition & 1 deletion test/elf/as-needed-dso.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ EOF
$CC -B. -o $t/exe $t/a.o -L$t -Wl,--as-needed -lbar -lfoo
readelf -W --dynamic $t/exe > $t/log2
grep -q libbar $t/log2
! grep -q libfoo $t/log2 || false
grep -q libfoo $t/log2
21 changes: 21 additions & 0 deletions test/elf/no-allow-shlib-undefined.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash
. $(dirname $0)/common.inc

cat <<EOF | $CC -B. -shared -o $t/libfoo.so -xc -
void foo() {}
EOF

cat <<EOF | $CC -B. -shared -o $t/libbar.so -xc -
void foo();
void bar() { foo(); }
EOF

cat <<EOF | $CC -c -o $t/a.o -c -xc -
int bar();
int main() { bar(); }
EOF

$CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lfoo -lbar

! $CC -B. -o $t/exe2 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lbar >& $t/log || false
grep -Fq 'undefined symbol: foo' $t/log

0 comments on commit 3001f02

Please sign in to comment.