Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -37,6 +37,7 @@ std::string RPath; std::vector InputSearchPaths; bool AllowMultipleDefinition; + bool AsNeeded = false; bool DiscardAll; bool DiscardLocals; bool DiscardNone; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -141,9 +141,12 @@ case file_magic::archive: Symtab.addFile(make_unique(MBRef)); return; - case file_magic::elf_shared_object: - Symtab.addFile(createELFInputFile(MBRef)); + case file_magic::elf_shared_object: { + std::unique_ptr File = createELFInputFile(MBRef); + cast(File.get())->AsNeeded = Config->AsNeeded; + Symtab.addFile(std::move(File)); return; + } default: Symtab.addFile(createELFInputFile(MBRef)); } @@ -204,6 +207,12 @@ case OPT_INPUT: 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 @@ -194,6 +194,11 @@ static bool classof(const InputFile *F) { return F->kind() == SharedKind; } StringRef getSoName() const { return SoName; } virtual void parseSoName() = 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 @@ -280,7 +280,7 @@ error(NameOrErr.getError()); StringRef Name = *NameOrErr; - SymbolBodies.emplace_back(Name, Sym); + SymbolBodies.emplace_back(this, Name, Sym); } } Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -129,12 +129,15 @@ void LinkerScript::readAsNeeded() { expect("("); + bool Orig = Config->AsNeeded; + Config->AsNeeded = true; for (;;) { StringRef Tok = next(); if (Tok == ")") - return; + break; Driver->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">; @@ -106,14 +110,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 @@ -323,11 +323,12 @@ if (FiniArraySec) NumEntries += 2; - const std::vector> &SharedFiles = - SymTab.getSharedFiles(); - for (const std::unique_ptr &File : SharedFiles) + for (const std::unique_ptr &File : SymTab.getSharedFiles()) { + if (!File->isNeeded()) + continue; Out::DynStrTab->add(File->getSoName()); - NumEntries += SharedFiles.size(); + ++NumEntries; + } if (Symbol *S = SymTab.getSymbols().lookup(Config->Init)) InitSym = dyn_cast>(S->Body); @@ -401,10 +402,9 @@ WriteArray(DT_INIT_ARRAY, DT_INIT_ARRAYSZ, InitArraySec); WriteArray(DT_FINI_ARRAY, DT_FINI_ARRAYSZ, FiniArraySec); - const std::vector> &SharedFiles = - SymTab.getSharedFiles(); - for (const std::unique_ptr &File : SharedFiles) - WriteVal(DT_NEEDED, Out::DynStrTab->getFileOff(File->getSoName())); + for (const std::unique_ptr &File : SymTab.getSharedFiles()) + if (File->isNeeded()) + WriteVal(DT_NEEDED, Out::DynStrTab->getFileOff(File->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(). @@ -260,8 +261,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 @@ -233,12 +233,16 @@ 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}); + + // Set "used" bit for --as-needed. + if (auto *S = dyn_cast>(Body)) + S->File->IsUsed = true; } } 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