Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -20,6 +20,7 @@ llvm::StringRef DynamicLinker; std::string RPath; bool Shared = false; + bool DiscardAll = false; }; extern Configuration *Config; Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -82,6 +82,9 @@ if (Args.hasArg(OPT_shared)) Config->Shared = true; + if (Args.hasArg(OPT_discard_all)) + Config->DiscardAll = true; + // Create a list of input files. std::vector Inputs; Index: lld/trunk/ELF/InputFiles.h =================================================================== --- lld/trunk/ELF/InputFiles.h +++ lld/trunk/ELF/InputFiles.h @@ -102,11 +102,14 @@ uint16_t getEMachine() const { return getObj()->getHeader()->e_machine; } + StringRef getStringTable() const { return StringTable; } + protected: std::unique_ptr> ELFObj; const Elf_Shdr *Symtab = nullptr; StringRef StringTable; Elf_Sym_Range getNonLocalSymbols(); + Elf_Sym_Range getSymbolsHelper(bool); void openELF(MemoryBufferRef MB); }; @@ -138,6 +141,8 @@ return SymbolBodies[SymbolIndex - FirstNonLocal]->getReplacement(); } + Elf_Sym_Range getLocalSymbols(); + private: void initializeChunks(); void initializeSymbols(); Index: lld/trunk/ELF/InputFiles.cpp =================================================================== --- lld/trunk/ELF/InputFiles.cpp +++ lld/trunk/ELF/InputFiles.cpp @@ -55,21 +55,36 @@ } template -typename ELFData::Elf_Sym_Range ELFData::getNonLocalSymbols() { +typename ELFData::Elf_Sym_Range +ELFData::getSymbolsHelper(bool Local) { if (!Symtab) return Elf_Sym_Range(nullptr, nullptr); + Elf_Sym_Range Syms = ELFObj->symbols(Symtab); + uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); + uint32_t FirstNonLocal = Symtab->sh_info; + if (FirstNonLocal > NumSymbols) + error("Invalid sh_info in symbol table"); + if (!Local) + return llvm::make_range(Syms.begin() + FirstNonLocal, Syms.end()); + else + // Skip over dummy symbol. + return llvm::make_range(Syms.begin() + 1, Syms.begin() + FirstNonLocal); +} +template +typename ELFData::Elf_Sym_Range ELFData::getNonLocalSymbols() { + if (!Symtab) + return Elf_Sym_Range(nullptr, nullptr); ErrorOr StringTableOrErr = ELFObj->getStringTableForSymtab(*Symtab); error(StringTableOrErr.getError()); StringTable = *StringTableOrErr; + return getSymbolsHelper(false); +} - Elf_Sym_Range Syms = ELFObj->symbols(Symtab); - uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); - uint32_t FirstNonLocal = Symtab->sh_info; - if (FirstNonLocal > NumSymbols) - error("Invalid sh_info in symbol table"); - return llvm::make_range(Syms.begin() + FirstNonLocal, Syms.end()); +template +typename ObjectFile::Elf_Sym_Range ObjectFile::getLocalSymbols() { + return this->getSymbolsHelper(true); } template void elf2::ObjectFile::parse() { @@ -217,6 +232,7 @@ namespace lld { namespace elf2 { + template class elf2::ObjectFile; template class elf2::ObjectFile; template class elf2::ObjectFile; Index: lld/trunk/ELF/Options.td =================================================================== --- lld/trunk/ELF/Options.td +++ lld/trunk/ELF/Options.td @@ -15,3 +15,6 @@ def shared : Flag<["-"], "shared">, HelpText<"Build a shared object">; + +def discard_all : Flag<["-"], "discard-all">, + HelpText<"Delete all local symbols">; Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -210,6 +210,7 @@ class SymbolTableSection final : public OutputSectionBase { public: typedef typename ELFFile::Elf_Sym Elf_Sym; + typedef typename ELFFile::Elf_Sym_Range Elf_Sym_Range; typedef typename OutputSectionBase::uintX_t uintX_t; SymbolTableSection(Writer &W, SymbolTable &Table, StringTableSection &StrTabSec) @@ -221,9 +222,6 @@ typedef OutputSectionBase Base; typename Base::HeaderT &Header = this->Header; - // For now the only local symbol is going to be the one at index 0 - Header.sh_info = 1; - Header.sh_entsize = sizeof(Elf_Sym); Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; } @@ -231,15 +229,18 @@ void finalize() override { this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym); this->Header.sh_link = StrTabSec.getSectionIndex(); + this->Header.sh_info = NumLocals + 1; } void writeTo(uint8_t *Buf) override; const SymbolTable &getSymTable() const { return Table; } - void addSymbol(StringRef Name) { + void addSymbol(StringRef Name, bool isLocal = false) { StrTabSec.add(Name); ++NumVisible; + if (isLocal) + ++NumLocals; } StringTableSection &getStrTabSec() { return StrTabSec; } @@ -249,6 +250,7 @@ SymbolTable &Table; StringTableSection &StrTabSec; unsigned NumVisible = 0; + unsigned NumLocals = 0; const Writer &W; }; @@ -434,6 +436,7 @@ typedef typename ELFFile::Elf_Ehdr Elf_Ehdr; typedef typename ELFFile::Elf_Phdr Elf_Phdr; typedef typename ELFFile::Elf_Sym Elf_Sym; + typedef typename ELFFile::Elf_Sym_Range Elf_Sym_Range; typedef typename ELFFile::Elf_Rela Elf_Rela; Writer(SymbolTable *T) : SymTabSec(*this, *T, StrTabSec), DynSymSec(*this, *T, DynStrSec), @@ -623,9 +626,28 @@ } template void SymbolTableSection::writeTo(uint8_t *Buf) { - uint8_t *BufStart = Buf; - Buf += sizeof(Elf_Sym); + + // All symbols with STB_LOCAL binding precede the weak and global symbols. + // .dynsym only contains global symbols. + if (!Config->DiscardAll && !StrTabSec.isDynamic()) { + for (const std::unique_ptr &FileB : + Table.getObjectFiles()) { + auto &File = cast>(*FileB); + Elf_Sym_Range Syms = File.getLocalSymbols(); + for (const Elf_Sym &Sym : Syms) { + auto *ESym = reinterpret_cast(Buf); + ErrorOr SymName = Sym.getName(File.getStringTable()); + ESym->st_name = (SymName) ? StrTabSec.getFileOff(*SymName) : 0; + ESym->st_value = Sym.st_value; + ESym->st_size = Sym.st_size; + Buf += sizeof(Elf_Sym); + } + } + } + + uint8_t *GlobalStart = Buf; + for (auto &P : Table.getSymbols()) { StringRef Name = P.first; Symbol *Sym = P.second; @@ -686,9 +708,8 @@ // The default hashing of StringRef produces different results on 32 and 64 // bit systems so we sort by st_name. That is arbitrary but deterministic. // FIXME: Experiment with passing in a custom hashing instead. - auto *Syms = reinterpret_cast(BufStart); - ++Syms; - array_pod_sort(Syms, Syms + NumVisible, compareSym); + auto *Syms = reinterpret_cast(GlobalStart); + array_pod_sort(Syms, Syms + NumVisible - NumLocals, compareSym); } template @@ -819,6 +840,14 @@ for (const std::unique_ptr &FileB : Symtab.getObjectFiles()) { auto &File = cast>(*FileB); + if (!Config->DiscardAll) { + Elf_Sym_Range Syms = File.getLocalSymbols(); + for (const Elf_Sym &Sym : Syms) { + ErrorOr SymName = Sym.getName(File.getStringTable()); + if (SymName) + SymTabSec.addSymbol(*SymName, true); + } + } for (SectionChunk *C : File.getChunks()) { if (!C) continue; Index: lld/trunk/test/elf2/basic64be.s =================================================================== --- lld/trunk/test/elf2/basic64be.s +++ lld/trunk/test/elf2/basic64be.s @@ -1,5 +1,5 @@ # RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t -# RUN: lld -flavor gnu2 %t -o %t2 +# RUN: lld -flavor gnu2 -discard-all %t -o %t2 # RUN: llvm-readobj -file-headers -sections -program-headers %t2 | FileCheck %s # REQUIRES: ppc Index: lld/trunk/test/elf2/local-dynamic.s =================================================================== --- lld/trunk/test/elf2/local-dynamic.s +++ lld/trunk/test/elf2/local-dynamic.s @@ -0,0 +1,83 @@ +// Check that local symbols are not inserted into dynamic table. +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +// RUN: lld -flavor gnu2 %t -shared -o %t1.so +// RUN: llvm-readobj -t -dyn-symbols %t1.so | FileCheck %s +// REQUIRES: x86 + +// CHECK: Symbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: blah +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: foo +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: goo +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start +// CHECK-NEXT: Value: 0x1000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// CHECK: DynamicSymbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: @ +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start@ +// CHECK-NEXT: Value: 0x1000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: ] + +.global _start +_start: + +blah: +foo: +goo: + + Index: lld/trunk/test/elf2/local.s =================================================================== --- lld/trunk/test/elf2/local.s +++ lld/trunk/test/elf2/local.s @@ -0,0 +1,72 @@ +// Check that symbol table is correctly populated with local symbols. +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +// RUN: lld -flavor gnu2 %t -o %t1 +// RUN: llvm-readobj -t -s %t1 | FileCheck %s +// REQUIRES: x86 + +// Check that Info is equal to the number of local symbols. +// CHECK: Section { +// CHECK: Name: .symtab +// CHECK-NEXT: Type: SHT_SYMTAB +// CHECK-NEXT: Flags [ +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: 4 + +// CHECK: Symbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: blah +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: foo +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: goo +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start +// CHECK-NEXT: Value: 0x11000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: ] + +.global _start +_start: + +blah: +foo: +goo: Index: lld/trunk/test/elf2/resolution.s =================================================================== --- lld/trunk/test/elf2/resolution.s +++ lld/trunk/test/elf2/resolution.s @@ -1,6 +1,6 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/resolution.s -o %t2 -// RUN: lld -flavor gnu2 %t %t2 -o %t3 +// RUN: lld -flavor gnu2 -discard-all %t %t2 -o %t3 // RUN: llvm-readobj -t %t3 | FileCheck %s // REQUIRES: x86