Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -41,6 +41,7 @@ std::string RPath; std::vector SearchPaths; bool AllowMultipleDefinition; + bool AsNeeded = false; bool DiscardAll; bool DiscardLocals; bool DiscardNone; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -94,9 +94,12 @@ } Files.push_back(make_unique(MBRef)); return; - case file_magic::elf_shared_object: - Files.push_back(createELFFile(MBRef)); + case file_magic::elf_shared_object: { + std::unique_ptr File = createELFFile(MBRef); + cast(File.get())->AsNeeded = Config->AsNeeded; + Files.push_back(std::move(File)); return; + } default: Files.push_back(createELFFile(MBRef)); } @@ -181,6 +184,12 @@ case OPT_script: addFile(Arg->getValue()); break; + case OPT_as_needed: + Config->AsNeeded = true; + break; + case OPT_no_as_needed: + Config->AsNeeded = false; + break; case OPT_Bstatic: Config->Static = true; break; Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -146,7 +146,7 @@ uint32_t FirstNonLocal = this->Symtab->sh_info; if (SymbolIndex < FirstNonLocal) return nullptr; - return SymbolBodies[SymbolIndex - FirstNonLocal]->repl(); + return SymbolBodies[SymbolIndex - FirstNonLocal]; } Elf_Sym_Range getLocalSymbols(); @@ -198,6 +198,11 @@ StringRef getSoName() const { return SoName; } virtual void parseSoName() = 0; virtual void parse() = 0; + + // Used for --as-needed + bool AsNeeded = false; + bool IsUsed = false; + bool isNeeded() const { return !AsNeeded || IsUsed; } }; template Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -329,7 +329,7 @@ error(NameOrErr.getError()); StringRef Name = *NameOrErr; - SymbolBodies.emplace_back(Name, Sym); + SymbolBodies.emplace_back(this, Name, Sym); } } Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -46,7 +46,7 @@ continue; SymVA = getLocalSymVA(Sym, File); } else { - SymbolBody &Body = *File.getSymbolBody(SymIndex); + SymbolBody &Body = *File.getSymbolBody(SymIndex)->repl(); SymVA = getSymVA(Body); if (Target->relocNeedsPlt(Type, Body)) { SymVA = Out::Plt->getEntryAddr(Body); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -155,12 +155,15 @@ void LinkerScript::readAsNeeded() { expect("("); + bool Orig = Config->AsNeeded; + Config->AsNeeded = true; for (;;) { StringRef Tok = next(); if (Tok == ")") - return; + break; addFile(Tok); } + Config->AsNeeded = Orig; } void LinkerScript::readEntry() { Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -14,6 +14,8 @@ def allow_shlib_undefined : Flag<["--", "-"], "allow-shlib-undefined">; +def as_needed : Flag<["--"], "as-needed">; + def disable_new_dtags : Flag<["--"], "disable-new-dtags">, HelpText<"Disable new dynamic tags">; @@ -52,6 +54,8 @@ def no_allow_shlib_undefined : Flag<["--"], "no-allow-shlib-undefined">; +def no_as_needed : Flag<["--"], "no-as-needed">; + def no_whole_archive : Flag<["--"], "no-whole-archive">, HelpText<"Restores the default behavior of loading archive members">; @@ -111,14 +115,12 @@ // Options listed below are silently ignored now. def O3 : Flag<["-"], "O3">; -def as_needed : Flag<["--"], "as-needed">; def build_id : Flag<["--"], "build-id">; def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">; def end_group : Flag<["--"], "end-group">; def gc_sections : Flag<["--"], "gc-sections">; def hash_style : Joined<["--"], "hash-style=">; def no_add_needed : Flag<["--"], "no-add-needed">; -def no_as_needed : Flag<["--"], "no-as-needed">; def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">; def start_group : Flag<["--"], "start-group">; def strip_all : Flag<["--"], "strip-all">; Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -116,8 +116,10 @@ OutputSection *OutSec = C.getOutputSection(); uint32_t SymIndex = RI.getSymbol(IsMips64EL); const ObjectFile &File = *C.getFile(); - const SymbolBody *Body = File.getSymbolBody(SymIndex); + SymbolBody *Body = File.getSymbolBody(SymIndex); const ELFFile &Obj = File.getObj(); + if (Body) + Body = Body->repl(); uint32_t Type = RI.getType(IsMips64EL); @@ -279,6 +281,8 @@ NumEntries += 2; for (const std::unique_ptr> &F : SymTab.getSharedFiles()) { + if (!F->isNeeded()) + continue; Out::DynStrTab->add(F->getSoName()); ++NumEntries; } @@ -356,7 +360,8 @@ WriteArray(DT_FINI_ARRAY, DT_FINI_ARRAYSZ, FiniArraySec); for (const std::unique_ptr> &F : SymTab.getSharedFiles()) - WriteVal(DT_NEEDED, Out::DynStrTab->getFileOff(F->getSoName())); + if (F->isNeeded()) + WriteVal(DT_NEEDED, Out::DynStrTab->getFileOff(F->getSoName())); if (InitSym) WritePtr(DT_INIT, getSymVA(*InitSym)); Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -24,6 +24,7 @@ class SymbolBody; template class ObjectFile; template class OutputSection; +template class SharedFile; // Initializes global objects defined in this file. // Called at the beginning of main(). @@ -263,8 +264,10 @@ return S->kind() == Base::SharedKind; } - SharedSymbol(StringRef Name, const Elf_Sym &Sym) - : Defined(Base::SharedKind, Name, Sym) {} + SharedSymbol(SharedFile *F, StringRef Name, const Elf_Sym &Sym) + : Defined(Base::SharedKind, Name, Sym), File(F) {} + + SharedFile *File; }; // This class represents a symbol defined in an archive file. It is Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -174,6 +174,14 @@ uint32_t SymIndex = RI.getSymbol(IsMips64EL); SymbolBody *Body = File.getSymbolBody(SymIndex); uint32_t Type = RI.getType(IsMips64EL); + + // Set "used" bit for --as-needed. + if (Body && Body->isUndefined() && !Body->isWeak()) + if (auto *S = dyn_cast>(Body->repl())) + S->File->IsUsed = true; + + if (Body) + Body = Body->repl(); if (Body) { if (Target->relocNeedsPlt(Type, *Body)) { if (Body->isInPlt()) @@ -186,12 +194,13 @@ Out::Got->addEntry(Body); } } - if (canBePreempted(Body)) { + + bool CBP = canBePreempted(Body); + if (!CBP && (!Config->Shared || Target->isRelRelative(Type))) + continue; + if (CBP) Body->setUsedInDynamicReloc(); - Out::RelaDyn->addReloc({C, RI}); - } else if (Config->Shared && !Target->isRelRelative(Type)) { - Out::RelaDyn->addReloc({C, RI}); - } + Out::RelaDyn->addReloc({C, RI}); } } Index: test/elf2/Inputs/shared2.s =================================================================== --- /dev/null +++ test/elf2/Inputs/shared2.s @@ -0,0 +1,6 @@ +.global bar2 +.type bar2, @function +bar2: + +.global zed2 +zed2: Index: test/elf2/as-needed.s =================================================================== --- /dev/null +++ test/elf2/as-needed.s @@ -0,0 +1,34 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared.s -o %t2.o +// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared2.s -o %t3.o +// RUN: ld.lld2 -shared %t2.o -soname shared1 -o %t2.so +// RUN: ld.lld2 -shared %t3.o -soname shared2 -o %t3.so + +// RUN: ld.lld2 %t.o %t2.so %t3.so -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s + +// RUN: ld.lld2 --as-needed %t.o %t2.so %t3.so -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s + +// RUN: ld.lld2 --as-needed %t.o %t2.so --no-as-needed %t3.so -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s + +// RUN: echo "GROUP(" %t2.so %t3.so ")" > %t.script +// RUN: ld.lld2 %t.o %t.script -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s + +// RUN: echo "GROUP(AS_NEEDED(" %t2.so %t3.so "))" > %t.script +// RUN: ld.lld2 %t.o %t.script -o %t2 +// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s + +// CHECK: NEEDED SharedLibrary (shared1) +// CHECK: NEEDED SharedLibrary (shared2) + +// CHECK2: NEEDED SharedLibrary (shared1) +// CHECK2-NOT: NEEDED SharedLibrary (shared2) + +.global _start +_start: +.long bar +.long zed