diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-03-20 20:57:11 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-03-20 20:57:11 +0000 |
commit | c3e6b9d390489e2b8ecba74f4732438c31806d22 (patch) | |
tree | 89b02879f8d7ce6afd3fc73bfe3617102f0283b5 /contrib/llvm/tools/lld | |
parent | 7114b1763ca4da5a97b13b1244860b2efbb08735 (diff) |
Pull in r352826 from upstream lld trunk (by Fangrui Song):
[ELF] Support --{,no-}allow-shlib-undefined
Summary:
In ld.bfd/gold, --no-allow-shlib-undefined is the default when
linking an executable. This patch implements a check to error on
undefined symbols in a shared object, if all of its DT_NEEDED entries
are seen.
Our approach resembles the one used in gold, achieves a good balance
to be useful but not too smart (ld.bfd traces all DSOs and emulates
the behavior of a dynamic linker to catch more cases).
The error is issued based on the symbol table, different from
undefined reference errors issued for relocations. It is most
effective when there are DSOs that were not linked with -z defs (e.g.
when static sanitizers runtime is used).
gold has a comment that some system libraries on GNU/Linux may have
spurious undefined references and thus system libraries should be
excluded (https://sourceware.org/bugzilla/show_bug.cgi?id=6811). The
story may have changed now but we make --allow-shlib-undefined the
default for now. Its interaction with -shared can be discussed in the
future.
Reviewers: ruiu, grimar, pcc, espindola
Reviewed By: ruiu
Subscribers: joerg, emaste, arichardson, llvm-commits
Differential Revision: https://reviews.llvm.org/D57385
Pull in r352943 from upstream lld trunk (by Fangrui Song):
[ELF] Default to --no-allow-shlib-undefined for executables
Summary:
This follows the ld.bfd/gold behavior.
The error check is useful as it captures a common type of ld.so
undefined symbol errors as link-time errors:
// a.cc => a.so (not linked with -z defs)
void f(); // f is undefined
void g() { f(); }
// b.cc => executable with a DT_NEEDED entry on a.so
void g();
int main() { g(); }
// ld.so errors when g() is executed (lazy binding) or when the program is started (-z now)
// symbol lookup error: ... undefined symbol: f
Reviewers: ruiu, grimar, pcc, espindola
Reviewed By: ruiu
Subscribers: llvm-commits, emaste, arichardson
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D57569
Together, these add support for --no-allow-shlib-undefined, and make it
the default for executables, so they will fail to link if any symbols
from needed shared libraries are undefined.
Reported by: jbeich
PR: 236062, 236141
MFC after: 1 month
X-MFC-With: r344779
Notes
Notes:
svn path=/head/; revision=345349
Diffstat (limited to 'contrib/llvm/tools/lld')
-rw-r--r-- | contrib/llvm/tools/lld/ELF/Config.h | 1 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/Driver.cpp | 3 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/InputFiles.cpp | 12 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/InputFiles.h | 6 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/Options.td | 6 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/SymbolTable.cpp | 2 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/SymbolTable.h | 6 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/ELF/Writer.cpp | 21 | ||||
-rw-r--r-- | contrib/llvm/tools/lld/docs/ld.lld.1 | 6 |
9 files changed, 52 insertions, 11 deletions
diff --git a/contrib/llvm/tools/lld/ELF/Config.h b/contrib/llvm/tools/lld/ELF/Config.h index fb29396926bf..e8bb6bf70ee0 100644 --- a/contrib/llvm/tools/lld/ELF/Config.h +++ b/contrib/llvm/tools/lld/ELF/Config.h @@ -122,6 +122,7 @@ struct Configuration { uint64_t> CallGraphProfile; bool AllowMultipleDefinition; + bool AllowShlibUndefined; bool AndroidPackDynRelocs; bool ARMHasBlx = false; bool ARMHasMovtMovw = false; diff --git a/contrib/llvm/tools/lld/ELF/Driver.cpp b/contrib/llvm/tools/lld/ELF/Driver.cpp index 407f1734f143..3e565bcb8732 100644 --- a/contrib/llvm/tools/lld/ELF/Driver.cpp +++ b/contrib/llvm/tools/lld/ELF/Driver.cpp @@ -758,6 +758,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || hasZOption(Args, "muldefs"); + Config->AllowShlibUndefined = + Args.hasFlag(OPT_allow_shlib_undefined, OPT_no_allow_shlib_undefined, + Args.hasArg(OPT_shared)); Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); diff --git a/contrib/llvm/tools/lld/ELF/InputFiles.cpp b/contrib/llvm/tools/lld/ELF/InputFiles.cpp index bc7e61072e64..c5922d3155d9 100644 --- a/contrib/llvm/tools/lld/ELF/InputFiles.cpp +++ b/contrib/llvm/tools/lld/ELF/InputFiles.cpp @@ -865,7 +865,7 @@ SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName) // Partially parse the shared object file so that we can call // getSoName on this object. -template <class ELFT> void SharedFile<ELFT>::parseSoName() { +template <class ELFT> void SharedFile<ELFT>::parseDynamic() { const Elf_Shdr *DynamicSec = nullptr; const ELFFile<ELFT> Obj = this->getObj(); ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this); @@ -902,12 +902,16 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() { ArrayRef<Elf_Dyn> Arr = CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this); for (const Elf_Dyn &Dyn : Arr) { - if (Dyn.d_tag == DT_SONAME) { + if (Dyn.d_tag == DT_NEEDED) { + uint64_t Val = Dyn.getVal(); + if (Val >= this->StringTable.size()) + fatal(toString(this) + ": invalid DT_NEEDED entry"); + DtNeeded.push_back(this->StringTable.data() + Val); + } else if (Dyn.d_tag == DT_SONAME) { uint64_t Val = Dyn.getVal(); if (Val >= this->StringTable.size()) fatal(toString(this) + ": invalid DT_SONAME entry"); SoName = this->StringTable.data() + Val; - return; } } } @@ -975,7 +979,7 @@ uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections, return (Ret > UINT32_MAX) ? 0 : Ret; } -// Fully parse the shared object file. This must be called after parseSoName(). +// Fully parse the shared object file. This must be called after parseDynamic(). // // This function parses symbol versions. If a DSO has version information, // the file has a ".gnu.version_d" section which contains symbol version diff --git a/contrib/llvm/tools/lld/ELF/InputFiles.h b/contrib/llvm/tools/lld/ELF/InputFiles.h index d7cbbc67a365..db9097fb868e 100644 --- a/contrib/llvm/tools/lld/ELF/InputFiles.h +++ b/contrib/llvm/tools/lld/ELF/InputFiles.h @@ -323,6 +323,7 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> { public: std::vector<const Elf_Verdef *> Verdefs; + std::vector<StringRef> DtNeeded; std::string SoName; static bool classof(const InputFile *F) { @@ -331,7 +332,7 @@ public: SharedFile(MemoryBufferRef M, StringRef DefaultSoName); - void parseSoName(); + void parseDynamic(); void parseRest(); uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym); std::vector<const Elf_Verdef *> parseVerdefs(); @@ -349,6 +350,9 @@ public: // data structures in the output file. std::map<const Elf_Verdef *, NeededVer> VerdefMap; + // Used for --no-allow-shlib-undefined. + bool AllNeededIsKnown; + // Used for --as-needed bool IsNeeded; }; diff --git a/contrib/llvm/tools/lld/ELF/Options.td b/contrib/llvm/tools/lld/ELF/Options.td index 439fe341644c..3a19b230780a 100644 --- a/contrib/llvm/tools/lld/ELF/Options.td +++ b/contrib/llvm/tools/lld/ELF/Options.td @@ -63,6 +63,10 @@ defm allow_multiple_definition: B<"allow-multiple-definition", "Allow multiple definitions", "Do not allow multiple definitions (default)">; +defm allow_shlib_undefined: B<"allow-shlib-undefined", + "Allow unresolved references in shared libraries (default when linking a shared library)", + "Do not allow unresolved references in shared libraries (default when linking an executable)">; + defm apply_dynamic_relocs: B<"apply-dynamic-relocs", "Apply link-time values for dynamic relocations", "Do not apply link-time values for dynamic relocations (default)">; @@ -492,12 +496,10 @@ def plugin_opt_thinlto: J<"plugin-opt=thinlto">; def plugin_opt_slash: J<"plugin-opt=/">; // Options listed below are silently ignored for now for compatibility. -def: F<"allow-shlib-undefined">; def: F<"detect-odr-violations">; def: Flag<["-"], "g">; def: F<"long-plt">; def: F<"no-add-needed">; -def: F<"no-allow-shlib-undefined">; def: F<"no-copy-dt-needed-entries">; def: F<"no-ctors-in-init-array">; def: F<"no-keep-memory">; diff --git a/contrib/llvm/tools/lld/ELF/SymbolTable.cpp b/contrib/llvm/tools/lld/ELF/SymbolTable.cpp index 7615e12199fa..bf1bf4511e96 100644 --- a/contrib/llvm/tools/lld/ELF/SymbolTable.cpp +++ b/contrib/llvm/tools/lld/ELF/SymbolTable.cpp @@ -93,7 +93,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) { // .so file if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) { // DSOs are uniquified not by filename but by soname. - F->parseSoName(); + F->parseDynamic(); if (errorCount()) return; diff --git a/contrib/llvm/tools/lld/ELF/SymbolTable.h b/contrib/llvm/tools/lld/ELF/SymbolTable.h index 898185fc9612..675a4915598f 100644 --- a/contrib/llvm/tools/lld/ELF/SymbolTable.h +++ b/contrib/llvm/tools/lld/ELF/SymbolTable.h @@ -80,6 +80,9 @@ public: void handleDynamicList(); + // Set of .so files to not link the same shared object file more than once. + llvm::DenseMap<StringRef, InputFile *> SoNames; + private: std::pair<Symbol *, bool> insertName(StringRef Name); @@ -107,9 +110,6 @@ private: // is used to uniquify them. llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups; - // Set of .so files to not link the same shared object file more than once. - llvm::DenseMap<StringRef, InputFile *> SoNames; - // A map from demangled symbol names to their symbol objects. // This mapping is 1:N because two symbols with different versions // can have the same name. We use this map to handle "extern C++ {}" diff --git a/contrib/llvm/tools/lld/ELF/Writer.cpp b/contrib/llvm/tools/lld/ELF/Writer.cpp index 5c987ca5a813..dcabf52e64f3 100644 --- a/contrib/llvm/tools/lld/ELF/Writer.cpp +++ b/contrib/llvm/tools/lld/ELF/Writer.cpp @@ -1668,6 +1668,27 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() { if (In.Iplt && !In.Iplt->empty()) In.Iplt->addSymbols(); + if (!Config->AllowShlibUndefined) { + // Error on undefined symbols in a shared object, if all of its DT_NEEDED + // entires are seen. These cases would otherwise lead to runtime errors + // reported by the dynamic linker. + // + // ld.bfd traces all DT_NEEDED to emulate the logic of the dynamic linker to + // catch more cases. That is too much for us. Our approach resembles the one + // used in ld.gold, achieves a good balance to be useful but not too smart. + for (InputFile *File : SharedFiles) { + SharedFile<ELFT> *F = cast<SharedFile<ELFT>>(File); + F->AllNeededIsKnown = llvm::all_of(F->DtNeeded, [&](StringRef Needed) { + return Symtab->SoNames.count(Needed); + }); + } + for (Symbol *Sym : Symtab->getSymbols()) + if (Sym->isUndefined() && !Sym->isWeak()) + if (auto *F = dyn_cast_or_null<SharedFile<ELFT>>(Sym->File)) + if (F->AllNeededIsKnown) + error(toString(F) + ": undefined reference to " + toString(*Sym)); + } + // Now that we have defined all possible global symbols including linker- // synthesized ones. Visit all symbols to give the finishing touches. for (Symbol *Sym : Symtab->getSymbols()) { diff --git a/contrib/llvm/tools/lld/docs/ld.lld.1 b/contrib/llvm/tools/lld/docs/ld.lld.1 index 04bf19d6b23c..6fd746f030dd 100644 --- a/contrib/llvm/tools/lld/docs/ld.lld.1 +++ b/contrib/llvm/tools/lld/docs/ld.lld.1 @@ -56,6 +56,9 @@ option. .It Fl -allow-multiple-definition Do not error if a symbol is defined multiple times. The first definition will be used. +.It Fl -allow-shlib-undefined +Allow unresolved references in shared libraries. +This option is enabled by default when linking a shared library. .It Fl -apply-dynamic-relocs Apply link-time values for dynamic relocations. .It Fl -as-needed @@ -252,6 +255,9 @@ Set target emulation. .It Fl -Map Ns = Ns Ar file , Fl M Ar file Print a link map to .Ar file . +.It Fl -no-allow-shlib-undefined +Do not allow unresolved references in shared libraries. +This option is enabled by default when linking an executable. .It Fl -no-as-needed Always set .Dv DT_NEEDED |