summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Config.h1
-rw-r--r--lld/ELF/Driver.cpp2
-rw-r--r--lld/ELF/InputFiles.cpp12
-rw-r--r--lld/ELF/InputFiles.h6
-rw-r--r--lld/ELF/Options.td6
-rw-r--r--lld/ELF/SymbolTable.cpp2
-rw-r--r--lld/ELF/SymbolTable.h6
-rw-r--r--lld/ELF/Writer.cpp21
-rw-r--r--lld/test/ELF/allow-shlib-undefined.s32
9 files changed, 62 insertions, 26 deletions
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index aea42778cf0..8943ced3cb2 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -119,6 +119,7 @@ struct Configuration {
uint64_t>
CallGraphProfile;
bool AllowMultipleDefinition;
+ bool AllowShlibUndefined;
bool AndroidPackDynRelocs;
bool ARMHasBlx = false;
bool ARMHasMovtMovw = false;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 3c3750b213e..02a40ad5fb8 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -756,6 +756,8 @@ 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, true);
Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index 030a375ecb4..c3e5b2f4878 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -862,7 +862,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);
@@ -899,12 +899,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;
}
}
}
@@ -972,7 +976,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/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index c8a79beab8d..07f85e89b7b 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -332,6 +332,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) {
@@ -340,7 +341,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();
@@ -358,6 +359,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/lld/ELF/Options.td b/lld/ELF/Options.td
index bc203193661..b6889765712 100644
--- a/lld/ELF/Options.td
+++ b/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)",
+ "Do not allow unresolved references in shared libraries">;
+
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/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index 7a801245d9f..e25682f809c 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -92,7 +92,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/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h
index 7180ccb483f..9822ed75c3f 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -79,6 +79,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);
@@ -106,9 +109,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/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 70cdbf952e9..6465a619d06 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1682,6 +1682,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/lld/test/ELF/allow-shlib-undefined.s b/lld/test/ELF/allow-shlib-undefined.s
index abb0351db72..a6c8b62e423 100644
--- a/lld/test/ELF/allow-shlib-undefined.s
+++ b/lld/test/ELF/allow-shlib-undefined.s
@@ -1,26 +1,28 @@
# REQUIRES: x86
-# --allow-shlib-undefined and --no-allow-shlib-undefined are fully
-# ignored in linker implementation.
# --allow-shlib-undefined is set by default
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
-# RUN: %p/Inputs/allow-shlib-undefined.s -o %t
-# RUN: ld.lld -shared %t -o %t.so
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
+# RUN: %p/Inputs/allow-shlib-undefined.s -o %t1.o
+# RUN: ld.lld -shared %t1.o -o %t.so
+
+# RUN: ld.lld %t.o %t.so -o /dev/null
+# RUN: ld.lld --allow-shlib-undefined %t.o %t.so -o /dev/null
+# RUN: not ld.lld --no-allow-shlib-undefined %t.o %t.so -o /dev/null 2>&1 | FileCheck %s
-# Executable: should link with DSO containing undefined symbols in any case.
-# RUN: ld.lld %t1 %t.so -o %t2
-# RUN: ld.lld --no-allow-shlib-undefined %t1 %t.so -o %t2
-# RUN: ld.lld --allow-shlib-undefined %t1 %t.so -o %t2
+# RUN: echo | llvm-mc -filetype=obj -triple=x86_64-unknown-linux -o %tempty.o
+# RUN: ld.lld -shared %tempty.o -o %tempty.so
+# RUN: ld.lld -shared %t1.o %tempty.so -o %t2.so
+# RUN: ld.lld --no-allow-shlib-undefined %t.o %t2.so -o /dev/null
# DSO with undefines:
# should link with or without any of these options.
-# RUN: ld.lld -shared %t -o %t.so
-# RUN: ld.lld -shared --allow-shlib-undefined %t -o %t.so
-# RUN: ld.lld -shared --no-allow-shlib-undefined %t -o %t.so
-
-# Executable still should not link when have undefines inside.
-# RUN: not ld.lld %t -o %t.so
+# RUN: ld.lld -shared %t1.o -o /dev/null
+# RUN: ld.lld -shared --allow-shlib-undefined %t1.o -o /dev/null
+# RUN: ld.lld -shared --no-allow-shlib-undefined %t1.o -o /dev/null
.globl _start
_start:
callq _shared@PLT
+
+# CHECK: undefined reference to _unresolved
OpenPOWER on IntegriCloud