From 3001f02b55aa80123e77a4551df519ef5e031cdb Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 21 Jul 2024 11:38:16 +0900 Subject: [PATCH] Support --no-allow-shlib-undefined Fixes https://github.com/rui314/mold/issues/1313 --- elf/cmdline.cc | 6 ++-- elf/input-files.cc | 32 +++++++++++++++++- elf/main.cc | 3 ++ elf/mold.h | 12 ++++--- elf/passes.cc | 50 ++++++++++++++++++++++++++++ test/elf/as-needed-dso.sh | 2 +- test/elf/no-allow-shlib-undefined.sh | 21 ++++++++++++ 7 files changed, 118 insertions(+), 8 deletions(-) create mode 100755 test/elf/no-allow-shlib-undefined.sh diff --git a/elf/cmdline.cc b/elf/cmdline.cc index 9147f4a7b2..c299b27126 100644 --- a/elf/cmdline.cc +++ b/elf/cmdline.cc @@ -1243,6 +1243,10 @@ std::vector parse_nonpositional_args(Context &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")) { @@ -1260,8 +1264,6 @@ std::vector parse_nonpositional_args(Context &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")) { diff --git a/elf/input-files.cc b/elf/input-files.cc index e8eb025d45..4fd9fdb5c3 100644 --- a/elf/input-files.cc +++ b/elf/input-files.cc @@ -1224,6 +1224,7 @@ void SharedFile::parse(Context &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. @@ -1265,6 +1266,35 @@ void SharedFile::parse(Context &ctx) { counter += this->elf_syms.size(); } +template +std::vector SharedFile::read_dt_needed(Context &ctx) { + // Get the contents of a .dynamic seciton + std::span> dynamic; + for (ElfPhdr &phdr : this->get_phdrs()) { + if (phdr.p_type == PT_DYNAMIC) { + dynamic = {(Word *)(this->mf->data + phdr.p_offset), + phdr.p_memsz / sizeof(Word)}; + 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 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 @@ -1356,7 +1386,7 @@ SharedFile::mark_live_objects(Context &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); diff --git a/elf/main.cc b/elf/main.cc index 2054d551ef..ebf9b1c1b2 100644 --- a/elf/main.cc +++ b/elf/main.cc @@ -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); diff --git a/elf/mold.h b/elf/mold.h index 59eada23fe..36a40b7483 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -1311,6 +1311,7 @@ class SharedFile : public InputFile { std::string soname; std::vector version_strings; + std::vector dt_needed; std::vector> elf_syms2; private: @@ -1318,6 +1319,7 @@ class SharedFile : public InputFile { std::string get_soname(Context &ctx); void maybe_override_symbol(Symbol &sym, const ElfSym &esym); + std::vector read_dt_needed(Context &ctx); std::vector read_verdef(Context &ctx); std::vector versyms; @@ -1474,6 +1476,7 @@ template void check_cet_errors(Context &); template void print_dependencies(Context &); template void write_repro_file(Context &); template void check_duplicate_symbols(Context &); +template void check_shlib_undefined(Context &); template void check_symbol_types(Context &); template void sort_init_fini(Context &); template void sort_ctor_dtor(Context &); @@ -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; @@ -1774,8 +1778,8 @@ struct Context { Symbol *fini = nullptr; Symbol *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; @@ -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; @@ -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; @@ -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 unique; std::optional physical_image_base; std::string Map; @@ -1870,6 +1872,7 @@ struct Context { std::string separate_debug_file; std::string soname; std::string sysroot; + std::string_view emulation; std::unique_ptr> retain_symbols_file; std::unordered_map section_align; std::unordered_map section_start; @@ -1887,6 +1890,7 @@ struct Context { std::vector filter; std::vector trace_symbol; u64 image_base = 0x200000; + u64 shuffle_sections_seed = 0; } arg; std::vector version_patterns; diff --git a/elf/passes.cc b/elf/passes.cc index af83ed0e34..2527631b26 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -1082,6 +1082,55 @@ void check_duplicate_symbols(Context &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 +void check_shlib_undefined(Context &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 *> dsos = ctx.dsos; + std::unordered_set sonames; + + for (SharedFile *file : dsos) + sonames.insert(file->soname); + + std::erase_if(dsos, [&](SharedFile *file) { + for (std::string_view needed : file->dt_needed) + if (sonames.count(needed) == 0) + return true; + return false; + }); + + auto is_sparc_register = [](const ElfSym &esym) { + // Dynamic symbol table for SPARC contains bogus entries which + // we need to ignore + if constexpr (is_sparc) + return esym.st_type == STT_SPARC_REGISTER; + return false; + }; + + // Check if all undefined symbols have been resolved. + for (SharedFile *file : dsos) { + for (i64 i = 0; i < file->elf_syms.size(); i++) { + const ElfSym &esym = file->elf_syms[i]; + Symbol &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 void check_symbol_types(Context &ctx) { Timer t(ctx, "check_symbol_types"); @@ -3270,6 +3319,7 @@ template void apply_section_align(Context &); template void print_dependencies(Context &); template void write_repro_file(Context &); template void check_duplicate_symbols(Context &); +template void check_shlib_undefined(Context &); template void check_symbol_types(Context &); template void sort_init_fini(Context &); template void sort_ctor_dtor(Context &); diff --git a/test/elf/as-needed-dso.sh b/test/elf/as-needed-dso.sh index 40f0a46b3a..60fd6bd691 100755 --- a/test/elf/as-needed-dso.sh +++ b/test/elf/as-needed-dso.sh @@ -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 diff --git a/test/elf/no-allow-shlib-undefined.sh b/test/elf/no-allow-shlib-undefined.sh new file mode 100755 index 0000000000..53daabaf3f --- /dev/null +++ b/test/elf/no-allow-shlib-undefined.sh @@ -0,0 +1,21 @@ +#!/bin/bash +. $(dirname $0)/common.inc + +cat <& $t/log || false +grep -Fq 'undefined symbol: foo' $t/log