Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -24,6 +24,7 @@ llvm::StringRef SoName; llvm::StringRef Sysroot; std::string RPath; + std::vector ExcludeLibs; std::vector InputSearchPaths; bool AllowMultipleDefinition; bool DiscardAll; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -120,6 +120,12 @@ if (auto *Arg = Args.getLastArg(OPT_soname)) Config->SoName = Arg->getValue(); + if (auto *Arg = Args.getLastArg(OPT_exclude_libs)) { + SmallVector V; + ((StringRef)Arg->getValue()).split(V, ",", -1, false); + Config->ExcludeLibs.insert(Config->ExcludeLibs.end(), V.begin(), V.end()); + } + Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->DiscardAll = Args.hasArg(OPT_discard_all); Config->DiscardLocals = Args.hasArg(OPT_discard_locals); Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -139,6 +139,8 @@ explicit ObjectFile(MemoryBufferRef M); void parse() override; + void hideAllSymbols() { HideSymbols = true; } + ArrayRef *> getSections() const { return Sections; } SymbolBody *getSymbolBody(uint32_t SymbolIndex) const { @@ -163,6 +165,8 @@ std::vector *> Sections; ArrayRef SymtabSHNDX; + + bool HideSymbols = false; }; class ArchiveFile : public InputFile { @@ -179,6 +183,8 @@ llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } std::vector getMembers(); + bool SymbolsHidden = false; + private: std::unique_ptr File; std::vector LazySymbols; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -148,8 +148,18 @@ Elf_Sym_Range Syms = this->getNonLocalSymbols(); uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); SymbolBodies.reserve(NumSymbols); - for (const Elf_Sym &Sym : Syms) - SymbolBodies.push_back(createSymbolBody(this->StringTable, &Sym)); + for (const Elf_Sym &Sym : Syms) { + SymbolBody *Body = createSymbolBody(this->StringTable, &Sym); + if (HideSymbols) { + uint8_t Visibility = Body->getMostConstrainingVisibility(); + // the attributes, ordered from least to most constraining: STV_PROTECTED, + // STV_HIDDEN, STV_INTERNAL. + if (Visibility == STV_DEFAULT || Visibility == STV_PROTECTED) + Visibility = STV_HIDDEN; + Body->setMostConstrainingVisibility(Visibility); + } + SymbolBodies.push_back(Body); + } } template @@ -201,7 +211,8 @@ // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &Sym : File->symbols()) - LazySymbols.emplace_back(this, Sym); + LazySymbols.emplace_back(this, Sym, SymbolsHidden ? llvm::ELF::STV_HIDDEN + : llvm::ELF::STV_DEFAULT); } // Returns a buffer pointing to a member file containing a given symbol. Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -29,6 +29,9 @@ def entry : Separate<["--", "-"], "entry">, MetaVarName<"">, HelpText<"Name of entry point symbol">; +def exclude_libs : Separate<["--"], "exclude-libs">, MetaVarName<"">, + HelpText<"Specifies a list of archives from which symbols should not be automatically exported">; + def export_dynamic : Flag<["--", "-"], "export-dynamic">, HelpText<"Put symbols in the dynamic symbol table">; Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -31,7 +31,8 @@ public: SymbolTable(); - void addFile(std::unique_ptr File); + void addFile(std::unique_ptr File, + ArchiveFile *HeadArchive = nullptr); const ELFFileBase *getFirstELF() const { if (!ObjectFiles.empty()) @@ -71,8 +72,9 @@ Symbol *insert(SymbolBody *New); template void addELFFile(ELFFileBase *File); void addELFFile(ELFFileBase *File); - void addLazy(Lazy *New); + void addLazy(Lazy *New, ArchiveFile *HeadArchive); void addMemberFile(Lazy *Body); + void addArchiveMemberFile(Lazy *Body, ArchiveFile *HeadArchive); template void init(uint16_t EMachine); template void resolve(SymbolBody *Body); Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -12,6 +12,7 @@ #include "Error.h" #include "Symbols.h" #include "Target.h" +#include "llvm\Support\Path.h" using namespace llvm; using namespace llvm::object; @@ -27,18 +28,44 @@ return K == ELF64LEKind || K == ELF64BEKind; } -void SymbolTable::addFile(std::unique_ptr File) { +namespace { +bool shouldExcludeLibs(InputFile *FileP) { + if (Config->ExcludeLibs.empty()) + return false; + if (Config->ExcludeLibs.size() == 1 && Config->ExcludeLibs[0] == "ALL") + return true; + + StringRef FileName = llvm::sys::path::filename(FileP->getName()); + auto Found = + std::find_if(Config->ExcludeLibs.begin(), Config->ExcludeLibs.end(), + [&FileName](const StringRef &R) { return R == FileName; }); + return (Found != Config->ExcludeLibs.end()); +} + +template void setSymbolsHidden(InputFile *File) { + if (ObjectFile *O = dyn_cast>(File)) { + O->hideAllSymbols(); + return; + } + assert(!"Unexpected content of archive."); +} +} + +void SymbolTable::addFile(std::unique_ptr File, + ArchiveFile *HeadArchive) { if (auto *AF = dyn_cast(File.get())) { File.release(); ArchiveFiles.emplace_back(AF); if (Config->WholeArchive) { for (MemoryBufferRef &MBRef : AF->getMembers()) - addFile(createELFFile(MBRef)); + addFile(createELFFile(MBRef), HeadArchive); return; } + AF->SymbolsHidden = shouldExcludeLibs(HeadArchive ? HeadArchive : AF); AF->parse(); + ArchiveFile *Container = HeadArchive ? HeadArchive : AF; for (Lazy &Sym : AF->getLazySymbols()) - addLazy(&Sym); + addLazy(&Sym, Container); return; } if (auto *S = dyn_cast(File.get())) { @@ -221,7 +248,7 @@ return Sym; } -void SymbolTable::addLazy(Lazy *New) { +void SymbolTable::addLazy(Lazy *New, ArchiveFile *HeadArchive) { Symbol *Sym = insert(New); if (Sym->Body == New) return; @@ -230,7 +257,37 @@ return; Sym->Body = New; assert(Existing->isUndefined() && "Unexpected symbol kind."); - addMemberFile(New); + addArchiveMemberFile(New, HeadArchive); +} + +void SymbolTable::addArchiveMemberFile(Lazy *Body, ArchiveFile *HeadArchive) { + std::unique_ptr File = Body->getMember(); + // getMember returns nullptr if the member was already read from the library. + if (!File) + return; + + if (HeadArchive->SymbolsHidden) { + InputFile *FileP = File.get(); + switch (cast(FileP)->getELFKind()) { + case ELF32LEKind: + setSymbolsHidden(FileP); + break; + case ELF32BEKind: + setSymbolsHidden(FileP); + break; + case ELF64LEKind: + setSymbolsHidden(FileP); + break; + case ELF64BEKind: + setSymbolsHidden(FileP); + break; + default: + assert("Unsupported ELF kind."); + break; + } + } + + addFile(std::move(File), HeadArchive); } void SymbolTable::addMemberFile(Lazy *Body) { Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -68,6 +68,10 @@ return MostConstrainingVisibility; } + void setMostConstrainingVisibility(uint8_t V) { + MostConstrainingVisibility = V; + } + unsigned getDynamicSymbolTableIndex() const { return DynamicSymbolTableIndex; } @@ -268,9 +272,9 @@ // the same name, it will ask the Lazy to load a file. class Lazy : public SymbolBody { public: - Lazy(ArchiveFile *F, const llvm::object::Archive::Symbol S) - : SymbolBody(LazyKind, S.getName(), false, llvm::ELF::STV_DEFAULT), - File(F), Sym(S) {} + Lazy(ArchiveFile *F, const llvm::object::Archive::Symbol S, + uint8_t Visibility = llvm::ELF::STV_DEFAULT) + : SymbolBody(LazyKind, S.getName(), false, Visibility), File(F), Sym(S) {} static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; } Index: test/elf2/Inputs/exclude-libs.s =================================================================== --- test/elf2/Inputs/exclude-libs.s +++ test/elf2/Inputs/exclude-libs.s @@ -0,0 +1,3 @@ +.globl _usedFunction +.type _usedFunction, @function + _usedFunction: Index: test/elf2/exclude-libs.s =================================================================== --- test/elf2/exclude-libs.s +++ test/elf2/exclude-libs.s @@ -0,0 +1,37 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tmain.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +// RUN: %p/Inputs/exclude-libs.s -o %ta.o +// RUN: rm -f %t.a +// RUN: llvm-ar rcs %t.a %ta.o + +// without --exclude-libs flag symbol from archive should appear +// both in .symtab and .dynsym. +// RUN: lld -shared -flavor gnu2 %tmain.o %t.a -o %t.out +// RUN: llvm-readobj -symbols -dyn-symbols %t.out | FileCheck --check-prefix=YSYM %s + +// --exclude-libs hides symbols from all archives. Here check is performed for one existent. +// RUN: lld --exclude-libs ALL -shared -flavor gnu2 %tmain.o %t.a -o %t.out +// RUN: llvm-readobj -symbols -dyn-symbols %t.out | FileCheck --check-prefix=NSYM %s + +// after excluding fake archives symbols should not be hidden. +// RUN: lld --exclude-libs libFake1.a,libFake2.a,libFake3.a -shared -flavor gnu2 %tmain.o %t.a -o %t.out +// RUN: llvm-readobj -symbols -dyn-symbols %t.out | FileCheck --check-prefix=YSYM %s + +// YSYM: Symbols [ +// YSYM: Name: _usedFunction +// YSYM: ] +// YSYM: DynamicSymbols [ +// YSYM: Name: _usedFunction +// YSYM: ] + +// NSYM: Symbols [ +// NSYM-NOT: Name: _usedFunction +// NSYM: ] +// NSYM: DynamicSymbols [ +// NSYM-NOT: Name: _usedFunction +// NSYM: ] + +.globl _start +_start: + call _usedFunction