// 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 we are linking an executable, weak undefs are converted to // weakly imported symbols so that they'll have another chance to be // resolved. claim_unresolved_symbols(ctx);
template <typename E> void ObjectFile<E>::claim_unresolved_symbols(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]; Symbol<E> &sym = *this->symbols[i]; if (!esym.is_undef()) continue;
std::scoped_lock lock(sym.mu);
// If a protected/hidden undefined symbol is resolved to an // 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); continue; }
if (sym.file && (!sym.esym().is_undef() || sym.file->priority <= this->priority)) continue;
// If a symbol name is in the form of "foo@version", search for // symbol "foo" and check if the symbol has version "version". std::string_view key = this->symbol_strtab.data() + esym.st_name; if (i64 pos = key.find('@'); pos != key.npos) { Symbol<E> *sym2 = get_symbol(ctx, key.substr(0, pos)); if (sym2->file && sym2->file->is_dso && sym2->get_version() == key.substr(pos + 1)) { this->symbols[i] = sym2; continue; } } ... if (esym.is_undef_weak()) { if (ctx.arg.shared && sym.visibility != STV_HIDDEN && ctx.arg.z_dynamic_undefined_weak) { // Global weak undefined symbols are promoted to dynamic symbols // when when linking a DSO, unless `-z nodynamic_undefined_weak` // was given. claim(true); } else { // Otherwise, weak undefs are converted to absolute symbols with value 0. claim(false); } continue; }
if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN) report_undef(sym);
// Traditionally, remaining undefined symbols cause a link failure // only when we are creating an executable. Undefined symbols in // shared objects are promoted to dynamic symbols, so that they'll // get another chance to be resolved at run-time. You can change the // behavior by passing `-z defs` to the linker. // // Even if `-z defs` is given, weak undefined symbols are still // promoted to dynamic symbols for compatibility with other linkers. // Some major programs, notably Firefox, depend on the behavior // (they use this loophole to export symbols from libxul.so). if (ctx.arg.shared && sym.visibility != STV_HIDDEN && (!ctx.arg.z_defs || ctx.arg.unresolved_symbols != UNRESOLVED_ERROR)) { claim(true); continue; }
// Convert remaining undefined symbols to absolute symbols with value 0. if (ctx.arg.unresolved_symbols != UNRESOLVED_ERROR || ctx.arg.noinhibit_exec) claim(false); } }
template <typename E> voidprint_dependencies(Context<E> &ctx){ SyncOut(ctx) << R"(# This is an output of the mold linker's --print-dependencies option. # # Each line consists of three fields, <file1>, <file2> and <symbol> # separated by tab characters. It indicates that <file1> depends on # <file2> to use <symbol>.)";
auto print = [&](InputFile<E> *file) { for (i64 i = file->first_global; i < file->elf_syms.size(); i++) { ElfSym<E> &esym = file->elf_syms[i]; Symbol<E> &sym = *file->symbols[i]; if (esym.is_undef() && sym.file && sym.file != file) SyncOut(ctx) << *file << "\t" << *sym.file << "\t" << sym; } };
for (InputFile<E> *file : ctx.objs) print(file); for (InputFile<E> *file : ctx.dsos) print(file); }
template <typename E> voidprint_dependencies_full(Context<E> &ctx){ SyncOut(ctx) << R"(# This is an output of the mold linker's --print-dependencies=full option. # # Each line consists of 4 fields, <section1>, <section2>, <symbol-type> and # <symbol>, separated by tab characters. It indicates that <section1> depends # on <section2> to use <symbol>. <symbol-type> is either "u" or "w" for # regular undefined or weak undefined, respectively. # # If you want to obtain dependency information per function granularity, # compile source files with the -ffunction-sections compiler flag.)";
std::unordered_set<std::string> seen; for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool) { if (!mf->parent) { std::string path = to_abs_path(mf->name).string(); if (seen.insert(path).second) { // We reopen a file because we may have modified the contents of mf // in memory, which is mapped with PROT_WRITE and MAP_PRIVATE. MappedFile<Context<E>> *mf2 = MappedFile<Context<E>>::must_open(ctx, path); tar->append(path, mf2->get_contents()); mf2->unmap(); } } } }
if (cwd != "/") { out << "--chroot .."; i64 depth = std::count(cwd.begin(), cwd.end(), '/'); for (i64 i = 1; i < depth; i++) out << "/.."; out << "\n"; }
for (i64 i = 1; i < ctx.cmdline_args.size(); i++) { std::string_view arg = ctx.cmdline_args[i]; if (arg != "-repro" && arg != "--repro") out << arg << "\n"; } return out.str(); }