Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -54,6 +54,7 @@ OutputSectionBase &Section, typename llvm::object::ELFFile::uintX_t Value); void addIgnoredSym(StringRef Name); + bool isUndefined(StringRef Name); void scanShlibUndefined(); private: Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -88,6 +88,12 @@ resolve(Sym); } +template bool SymbolTable::isUndefined(StringRef Name) { + if (SymbolBody *Sym = find(Name)) + return Sym->isUndefined(); + return false; +} + template void SymbolTable::addELFFile(ELFFileBase *File) { if (auto *O = dyn_cast>(File)) Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/StringSaver.h" using namespace llvm; using namespace llvm::ELF; @@ -70,9 +71,11 @@ std::unique_ptr Buffer; SpecificBumpPtrAllocator> SecAlloc; + BumpPtrAllocator Alloc; std::vector *> OutputSections; unsigned getNumSections() const { return OutputSections.size() + 1; } + void addStartStopSymbols(OutputSection *Sec); void setPhdr(Elf_Phdr *PH, uint32_t Type, uint32_t Flags, uintX_t FileOff, uintX_t VA, uintX_t Align); void copyPhdr(Elf_Phdr *PH, OutputSectionBase *From); @@ -400,6 +403,8 @@ if (!isOutputDynamic()) Symtab.addIgnoredSym("__tls_get_addr"); + std::vector *> RegularSections; + for (const std::unique_ptr> &F : Symtab.getObjectFiles()) { for (InputSection *C : F->getSections()) { if (!C || C == &InputSection::Discarded) @@ -413,12 +418,16 @@ Sec = new (SecAlloc.Allocate()) OutputSection(Key.Name, Key.Type, Key.Flags); OutputSections.push_back(Sec); + RegularSections.push_back(Sec); } Sec->addSection(C); scanRelocs(*C); } } + for (OutputSection *Sec : RegularSections) + addStartStopSymbols(Sec); + Out::Dynamic->PreInitArraySec = Map.lookup({".preinit_array", SHT_PREINIT_ARRAY, SHF_WRITE | SHF_ALLOC}); Out::Dynamic->InitArraySec = @@ -501,6 +510,40 @@ Out::Opd = Map.lookup({".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC}); } +static bool isAlpha(char C) { + return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_'; +} + +static bool isAlnum(char C) { + return isAlpha(C) || ('0' <= C && C <= '9'); +} + +// Returns true if S is valid as a C identifier. +static bool isValidCIdentifier(StringRef S) { + if (S.empty() || !isAlpha(S[0])) + return false; + return std::all_of(S.begin() + 1, S.end(), isAlnum); +} + +// If a section name is valid as a C identifier (which is rare because of +// the leading '.'), linkers are expected to define __start_ and +// __stop_ symbols. They are at beginning and end of the section, +// respectively. This is not requested by the ELF standard, but GNU ld and +// gold provide this feature. +template +void Writer::addStartStopSymbols(OutputSection *Sec) { + StringRef S = Sec->getName(); + if (!isValidCIdentifier(S)) + return; + StringSaver Saver(Alloc); + StringRef Start = Saver.save("__start_" + S); + if (Symtab.isUndefined(Start)) + Symtab.addSyntheticSym(Start, *Sec, 0); + StringRef Stop = Saver.save("__stop_" + S); + if (Symtab.isUndefined(Stop)) + Symtab.addSyntheticSym(Stop, *Sec, Sec->getSize()); +} + template static bool needsPhdr(OutputSectionBase *Sec) { return Sec->getFlags() & SHF_ALLOC; Index: test/elf2/startstop.s =================================================================== --- /dev/null +++ test/elf2/startstop.s @@ -0,0 +1,54 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +// RUN: ld.lld2 %t -o %tout +// RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s +// RUN: llvm-readobj -symbols %tout | FileCheck -check-prefix=SYMBOL %s + +// DISASM: _start: +// DISASM: 11000: e8 0a 00 00 00 callq 10 +// DISASM: 11005: e8 08 00 00 00 callq 8 +// DISASM: 1100a: e8 03 00 00 00 callq 3 +// DISASM: Disassembly of section foo: +// DISASM: __start_foo: +// DISASM: 1100f: 90 nop +// DISASM: 11010: 90 nop +// DISASM: 11011: 90 nop +// DISASM: Disassembly of section bar: +// DISASM: __start_bar: +// DISASM: 11012: 90 nop +// DISASM: 11013: 90 nop +// DISASM: 11014: 90 nop + +// SYMBOL: Symbol { +// SYMBOL: Name: __start_bar +// SYMBOL: Value: 0x11012 +// SYMBOL: Section: bar +// SYMBOL: } +// SYMBOL-NOT: Section: __stop_bar +// SYMBOL: Symbol { +// SYMBOL: Name: __start_foo +// SYMBOL: Value: 0x1100F +// SYMBOL: Section: foo +// SYMBOL: } +// SYMBOL: Symbol { +// SYMBOL: Name: __stop_foo +// SYMBOL: Value: 0x11012 +// SYMBOL: Section: foo (0x2) +// SYMBOL: } + +.global _start +.text +_start: + call __start_foo + call __stop_foo + call __start_bar + +.section foo,"ax" + nop + nop + nop + +.section bar,"ax" + nop + nop + nop