Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -20,6 +20,7 @@ llvm::StringRef DynamicLinker; std::string RPath; bool Shared = false; + bool DiscardAll = false; }; extern Configuration *Config; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ 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: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -102,6 +102,8 @@ uint16_t getEMachine() const { return getObj()->getHeader()->e_machine; } + StringRef getStringTable() const { return StringTable; } + protected: std::unique_ptr> ELFObj; const Elf_Shdr *Symtab = nullptr; @@ -138,6 +140,8 @@ return SymbolBodies[SymbolIndex - FirstNonLocal]->getReplacement(); } + Elf_Sym_Range getLocalSymbols(); + private: void initializeChunks(); void initializeSymbols(); Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -72,6 +72,26 @@ return llvm::make_range(Syms.begin() + FirstNonLocal, Syms.end()); } +// XXX: Factor out common code between getLocalSymbols() +// and getNonLocalSymbols(). +template +typename ObjectFile::Elf_Sym_Range ObjectFile::getLocalSymbols() { + if (!this->Symtab) + return Elf_Sym_Range(nullptr, nullptr); + Elf_Sym_Range Syms = this->ELFObj->symbols(this->Symtab); + uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); + uint32_t LastLocal = this->Symtab->sh_info - 1; + if (LastLocal > NumSymbols) + error("Invalid sh_info in symbol table"); + + // No local symbols. + if (LastLocal == 0) + return Elf_Sym_Range(nullptr, nullptr); + + // Skip over dummy symbol. + return llvm::make_range(Syms.begin() + 1, Syms.begin() + LastLocal + 1); +} + template void elf2::ObjectFile::parse() { this->openELF(MB); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ 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: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -158,6 +158,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) @@ -169,9 +170,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; } @@ -179,15 +177,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; } @@ -197,6 +198,7 @@ SymbolTable &Table; StringTableSection &StrTabSec; unsigned NumVisible = 0; + unsigned NumLocals = 0; const Writer &W; }; @@ -363,6 +365,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; Writer(SymbolTable *T) : SymTabSec(*this, *T, StrTabSec), DynSymSec(*this, *T, DynStrSec), HashSec(DynSymSec), DynamicSec(*T, HashSec) {} @@ -533,9 +536,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; @@ -596,9 +618,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 @@ -676,6 +697,14 @@ const SymbolTable &Symtab = SymTabSec.getSymTable(); 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: test/elf2/basic64be.s =================================================================== --- test/elf2/basic64be.s +++ 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: test/elf2/local-dynamic.s =================================================================== --- test/elf2/local-dynamic.s +++ 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 -s %t1.so | FileCheck %s +// REQUIRES: x86 + +// CHECK: Name: .dynsym (35) +// CHECK-NEXT: Type: SHT_DYNSYM (0xB) +// CHECK-NEXT: Flags [ (0x2) +// CHECK-NEXT: SHF_ALLOC (0x2) +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: 1 + +// CHECK: Name: .symtab (71) +// CHECK-NEXT: Type: SHT_SYMTAB (0x2) +// CHECK-NEXT: Flags [ (0x0) +// 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: (0) +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined (0x0) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: blah (49) +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined (0x0) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: foo (31) +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined (0x0) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: goo (27) +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local (0x0) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined (0x0) +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start (7) +// CHECK-NEXT: Value: 0x1000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global (0x1) +// CHECK-NEXT: Type: None (0x0) +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text (0x1) +// CHECK-NEXT: } +// CHECK-NEXT: ] + +.global _start +_start: + +blah: +foo: +goo: + + Index: test/elf2/local.s =================================================================== --- test/elf2/local.s +++ 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: test/elf2/resolution.s =================================================================== --- test/elf2/resolution.s +++ 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