Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -140,6 +140,9 @@ if (Symtab.getObjectFiles().empty()) error("no input files."); + for (auto *Arg : Args.filtered(OPT_undefined)) + Symtab.addUndefinedSym(Arg->getValue()); + // Write the result. const ELFFileBase *FirstObj = Symtab.getFirstELF(); switch (FirstObj->getELFKind()) { Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -42,12 +42,16 @@ def sysroot : Joined<["--"], "sysroot=">, HelpText<"Set the system root">; +def undefined : Joined<["--"], "undefined=">, + HelpText<"Force undefined symbol during linking">; + // Aliases def alias_L : Joined<["--"], "library-path=">, Alias; def alias_discard_all: Flag<["-"], "x">, Alias; def alias_discard_locals: Flag<["-"], "X">, Alias; def alias_entry : Separate<["-"], "e">, Alias; def alias_l : Joined<["--"], "library=">, Alias; +def alias_undefined : Separate<["-"], "u">, Alias; // Options listed below are silently ignored now. def as_needed : Flag<["--"], "as-needed">; Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -61,6 +61,8 @@ return EntrySym->getReplacement(); } + void addUndefinedSym(StringRef Name); + template void addSyntheticSym(StringRef Name, OutputSection &Section, typename llvm::object::ELFFile::uintX_t Value); @@ -71,6 +73,7 @@ void addELFFile(ELFFileBase *File); void addLazy(Lazy *New); void addMemberFile(Lazy *Body); + template void addUndefinedSym(StringRef Name); template void init(uint16_t EMachine); template void resolve(SymbolBody *Body); Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -59,6 +59,31 @@ error("Unknown target machine"); } +void SymbolTable::addUndefinedSym(StringRef Name) { + switch (getFirstELF()->getELFKind()) { + case ELF32LEKind: + addUndefinedSym(Name); + break; + case ELF32BEKind: + addUndefinedSym(Name); + break; + case ELF64LEKind: + addUndefinedSym(Name); + break; + case ELF64BEKind: + addUndefinedSym(Name); + break; + default: + llvm_unreachable("Invalid kind"); + } +} + +template void SymbolTable::addUndefinedSym(StringRef Name) { + auto Sym = + new (Alloc) Undefined(Name, Undefined::SyntheticOptional); + resolve(Sym); +} + template void SymbolTable::addSyntheticSym(StringRef Name, OutputSection &Section, typename ELFFile::uintX_t Value) { @@ -76,7 +101,7 @@ return; EntrySym = new (Alloc) Undefined( Config->Entry.empty() ? Target->getDefaultEntry() : Config->Entry, - Undefined::Synthetic); + Undefined::SyntheticRequired); resolve(EntrySym); // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol is magical Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -53,7 +53,6 @@ bool isWeak() const { return IsWeak; } bool isUndefined() const { return SymbolKind == UndefinedKind; } bool isDefined() const { return SymbolKind <= DefinedLast; } - bool isStrongUndefined() const { return !IsWeak && isUndefined(); } bool isCommon() const { return SymbolKind == DefinedCommonKind; } bool isLazy() const { return SymbolKind == LazyKind; } bool isShared() const { return SymbolKind == SharedKind; } @@ -235,7 +234,8 @@ typedef typename Base::Elf_Sym Elf_Sym; public: - static Elf_Sym Synthetic; + static Elf_Sym SyntheticRequired; + static Elf_Sym SyntheticOptional; Undefined(StringRef N, const Elf_Sym &Sym) : ELFSymbolBody(Base::UndefinedKind, N, Sym) {} @@ -243,10 +243,24 @@ static bool classof(const SymbolBody *S) { return S->kind() == Base::UndefinedKind; } + + bool canKeepUndefined() const { return &this->Sym == &SyntheticOptional; } }; +namespace { +template +static typename llvm::object::ELFFile::Elf_Sym initGlobalSym() { + typename llvm::object::ELFFile::Elf_Sym Sym; + Sym.setBinding(llvm::ELF::STB_GLOBAL); + return Sym; +} +} + +template +typename Undefined::Elf_Sym Undefined::SyntheticRequired; template -typename Undefined::Elf_Sym Undefined::Synthetic; +typename Undefined::Elf_Sym + Undefined::SyntheticOptional = initGlobalSym(); template class SharedSymbol : public Defined { typedef Defined Base; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -336,8 +336,10 @@ for (auto &P : Symtab.getSymbols()) { StringRef Name = P.first; SymbolBody *Body = P.second->Body; - if (Body->isStrongUndefined()) - undefError(Symtab, *Body); + if (auto *U = dyn_cast>(Body)) { + if (!U->isWeak() && !U->canKeepUndefined()) + undefError(Symtab, *Body); + } if (auto *C = dyn_cast>(Body)) CommonSymbols.push_back(C); Index: test/elf2/undefined-opt.s =================================================================== --- /dev/null +++ test/elf2/undefined-opt.s @@ -0,0 +1,59 @@ +# 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/abs.s -o %tabs.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +# RUN: %p/Inputs/shared.s -o %tshared.o +# RUN: rm -f %tar.a +# RUN: llvm-ar rcs %tar.a %tabs.o %tshared.o +# REQUIRES: x86 + +# Symbols from the archive are not in if not needed +# RUN: lld -flavor gnu2 -o %t1 %t.o %tar.a +# RUN: llvm-readobj --symbols %t1 | FileCheck --check-prefix=NO-UNDEFINED %s +# NO-UNDEFINED: Symbols [ +# NO-UNDEFINED-NOT: Name: abs +# NO-UNDEFINED-NOT: Name: big +# NO-UNDEFINED-NOT: Name: bar +# NO-UNDEFINED-NOT: Name: zed +# NO-UNDEFINED: ] + +# Symbols from the archive are in if needed, but only from the +# containing object file +# RUN: lld -flavor gnu2 -o %t2 %t.o %tar.a -u bar +# RUN: llvm-readobj --symbols %t2 | FileCheck --check-prefix=ONE-UNDEFINED %s +# ONE-UNDEFINED: Symbols [ +# ONE-UNDEFINED-NOT: Name: abs +# ONE-UNDEFINED-NOT: Name: big +# ONE-UNDEFINED: Name: bar +# ONE-UNDEFINED: Name: zed +# ONE-UNDEFINED: ] + +# Use the option couple of times, both short and long forms +# RUN: lld -flavor gnu2 -o %t3 %t.o %tar.a -u bar --undefined=abs +# RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=TWO-UNDEFINED %s +# TWO-UNDEFINED: Symbols [ +# TWO-UNDEFINED: Name: abs +# TWO-UNDEFINED: Name: big +# TWO-UNDEFINED: Name: bar +# TWO-UNDEFINED: Name: zed +# TWO-UNDEFINED: ] + +# Added undefined symbol may be left undefined without error +# RUN: lld -flavor gnu2 -o %t4 %t.o %tar.a -u unknown +# RUN: llvm-readobj --symbols %t4 | FileCheck --check-prefix=UNK-UNDEFINED %s +# UNK-UNDEFINED: Symbols [ +# UNK-UNDEFINED-NOT: Name: abs +# UNK-UNDEFINED-NOT: Name: big +# UNK-UNDEFINED-NOT: Name: bar +# UNK-UNDEFINED-NOT: Name: zed +# UNK-UNDEFINED: Name: unknown +# UNK-UNDEFINED-NEXT: Value: 0x0 +# UNK-UNDEFINED-NEXT: Size: 0 +# UNK-UNDEFINED-NEXT: Binding: Global +# UNK-UNDEFINED-NEXT: Type: None +# UNK-UNDEFINED-NEXT: Other: 0 +# UNK-UNDEFINED-NEXT: Section: Undefined +# UNK-UNDEFINED: ] + +.globl _start; +_start: