Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -10,6 +10,7 @@ #ifndef LLD_ELF_CONFIG_H #define LLD_ELF_CONFIG_H +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ELF.h" @@ -29,6 +30,11 @@ ELF64BEKind }; +struct OutputSectionDescription { + llvm::StringRef Name; + std::vector InputSectionNames; +}; + struct Configuration { SymbolBody *EntrySym = nullptr; InputFile *FirstElf = nullptr; @@ -41,6 +47,7 @@ llvm::StringRef SoName; llvm::StringRef Sysroot; std::string RPath; + llvm::MapVector OutputSections; std::vector SearchPaths; std::vector Undefined; bool AllowMultipleDefinition; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -36,6 +36,7 @@ static std::vector tokenize(StringRef S); static StringRef skipSpace(StringRef S); StringRef next(); + bool skip(StringRef Tok); bool atEOF() { return Tokens.size() == Pos; } void expect(StringRef Expect); @@ -50,6 +51,10 @@ void readOutputArch(); void readOutputFormat(); void readSearchDir(); + void readSections(); + + void readInputSectionDescription(OutputSectionDescription &OutSec); + void readOutputSectionDescription(); StringSaver Saver; std::vector Tokens; @@ -78,6 +83,8 @@ readOutputFormat(); } else if (Tok == "SEARCH_DIR") { readSearchDir(); + } else if (Tok == "SECTIONS") { + readSections(); } else { error("unknown directive: " + Tok); } @@ -133,11 +140,20 @@ } StringRef LinkerScript::next() { - if (Pos == Tokens.size()) + if (atEOF()) error("unexpected EOF"); return Tokens[Pos++]; } +bool LinkerScript::skip(StringRef Tok) { + if (atEOF()) + error("unexpected EOF"); + if (Tok != Tokens[Pos]) + return false; + ++Pos; + return true; +} + void LinkerScript::expect(StringRef Expect) { StringRef Tok = next(); if (Tok != Expect) @@ -255,6 +271,31 @@ expect(")"); } +void LinkerScript::readSections() { + expect("{"); + while (!skip("}")) + readOutputSectionDescription(); +} + +void LinkerScript::readInputSectionDescription( + OutputSectionDescription &OutSec) { + next(); // Skip input file name. + expect("("); + while (!skip(")")) + OutSec.InputSectionNames.push_back(next()); +} + +void LinkerScript::readOutputSectionDescription() { + StringRef Name = next(); + OutputSectionDescription &OutSec = Config->OutputSections[Name]; + OutSec.Name = Name; + + expect(":"); + expect("{"); + while (!skip("}")) + readInputSectionDescription(OutSec); +} + // Entry point. The other functions or classes are private to this file. void lld::elf2::readLinkerScript(BumpPtrAllocator *A, MemoryBufferRef MB) { LinkerScript(A, MB.getBuffer()).run(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -14,6 +14,7 @@ #include "Target.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/StringSaver.h" @@ -36,7 +37,9 @@ 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 &S) : Symtab(S) {} + Writer(SymbolTable &S) : Symtab(S) { + parseSectionDescriptions(); + } void run(); private: @@ -50,6 +53,8 @@ void openFile(StringRef OutputPath); void writeHeader(); void writeSections(); + bool isDiscarded(InputSectionBase *IS) const; + StringRef getOutputSectionName(StringRef S) const; bool needsInterpSection() const { return !Symtab.getSharedFiles().empty() && !Config->DynamicLinker.empty(); } @@ -72,12 +77,15 @@ void setPhdr(Elf_Phdr *PH, uint32_t Type, uint32_t Flags, uintX_t FileOff, uintX_t VA, uintX_t Size, uintX_t Align); void copyPhdr(Elf_Phdr *PH, OutputSectionBase *From); + void parseSectionDescriptions(); SymbolTable &Symtab; std::vector Phdrs; uintX_t FileSize; uintX_t SectionHeaderOff; + + llvm::StringMap InputToOutputSection; }; } // anonymous namespace @@ -418,7 +426,12 @@ Out::Bss->setSize(Off); } -static StringRef getOutputName(StringRef S) { +template +StringRef Writer::getOutputSectionName(StringRef S) const { + auto It = InputToOutputSection.find(S); + if (It != std::end(InputToOutputSection)) + return It->second; + if (S.startswith(".text.")) return ".text"; if (S.startswith(".rodata.")) @@ -430,6 +443,27 @@ return S; } +template +bool Writer::isDiscarded(InputSectionBase *IS) const { + if (!IS || !IS->isLive() || IS == &InputSection::Discarded) + return true; + return InputToOutputSection.lookup(IS->getSectionName()) == "/DISCARD/"; +} + +template +static bool compareSections(OutputSectionBase *A, + OutputSectionBase *B) { + auto ItA = Config->OutputSections.find(A->getName()); + auto ItEnd = std::end(Config->OutputSections); + if (ItA == ItEnd) + return compareOutputSections(A, B); + auto ItB = Config->OutputSections.find(B->getName()); + if (ItB == ItEnd) + return compareOutputSections(A, B); + + return std::distance(ItA, ItB) > 0; +} + // Create output section objects and add them to OutputSections. template void Writer::createSections() { // .interp needs to be on the first page in the output file. @@ -446,7 +480,7 @@ for (const std::unique_ptr> &F : Symtab.getObjectFiles()) { for (InputSectionBase *C : F->getSections()) { - if (!C || !C->isLive() || C == &InputSection::Discarded) + if (isDiscarded(C)) continue; const Elf_Shdr *H = C->getSectionHdr(); uintX_t OutFlags = H->sh_flags & ~SHF_GROUP; @@ -455,7 +489,7 @@ // mapping from input to output. auto *IS = dyn_cast>(C); uintX_t EntSize = IS ? 0 : H->sh_entsize; - SectionKey Key{getOutputName(C->getSectionName()), + SectionKey Key{getOutputSectionName(C->getSectionName()), H->sh_type, OutFlags, EntSize}; OutputSectionBase *&Sec = Map[Key]; if (!Sec) { @@ -513,12 +547,14 @@ // Scan relocations. This must be done after every symbol is declared so that // we can correctly decide if a dynamic relocation is needed. - for (const std::unique_ptr> &F : Symtab.getObjectFiles()) - for (InputSectionBase *B : F->getSections()) - if (auto *S = dyn_cast_or_null>(B)) - if (S != &InputSection::Discarded) - if (S->isLive()) - scanRelocs(*S); + for (const std::unique_ptr> &F : Symtab.getObjectFiles()) { + for (InputSectionBase *C : F->getSections()) { + if (isDiscarded(C)) + continue; + if (auto *S = dyn_cast>(C)) + scanRelocs(*S); + } + } std::vector *> CommonSymbols; std::vector *> SharedCopySymbols; @@ -573,7 +609,7 @@ OutputSections.push_back(Out::Plt); std::stable_sort(OutputSections.begin(), OutputSections.end(), - compareOutputSections); + compareSections); for (unsigned I = 0, N = OutputSections.size(); I < N; ++I) OutputSections[I]->SectionIndex = I + 1; @@ -867,6 +903,13 @@ PH->p_align = From->getAlign(); } +template void Writer::parseSectionDescriptions() { + for (const std::pair &OutSec : + Config->OutputSections) + for (StringRef Name : OutSec.second.InputSectionNames) + InputToOutputSection[Name] = OutSec.second.Name; +} + template void lld::elf2::writeResult(SymbolTable *Symtab); template void lld::elf2::writeResult(SymbolTable *Symtab); template void lld::elf2::writeResult(SymbolTable *Symtab); Index: test/elf2/linkerscript-sections.s =================================================================== --- /dev/null +++ test/elf2/linkerscript-sections.s @@ -0,0 +1,119 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +# Empty SECTIONS command. +# RUN: echo "SECTIONS {}" > %t.script +# RUN: ld.lld2 -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | \ +# RUN: FileCheck -check-prefix=SEC-DEFAULT %s + +# SECTIONS command with the same order as default. +# RUN: echo "SECTIONS { \ +# RUN: .text : { *(.text) } \ +# RUN: .data : { *(.data) } }" \ > %t.script +# RUN: ld.lld2 -o %t2 --script %t.script %t +# RUN: llvm-objdump -section-headers %t2 | \ +# RUN: FileCheck -check-prefix=SEC-DEFAULT %s + +# Idx Name Size +# SEC-DEFAULT: 1 .text 0000000e {{[0-9a-f]*}} TEXT DATA +# SEC-DEFAULT: 2 .data 00000020 {{[0-9a-f]*}} DATA +# SEC-DEFAULT: 3 other 00000003 {{[0-9a-f]*}} DATA +# SEC-DEFAULT: 4 .bss 00000002 {{[0-9a-f]*}} BSS +# SEC-DEFAULT: 5 .shstrtab 00000002 {{[0-9a-f]*}} +# SEC-DEFAULT: 6 .symtab 00000030 {{[0-9a-f]*}} +# SEC-DEFAULT: 7 .shstrtab 00000032 {{[0-9a-f]*}} +# SEC-DEFAULT: 8 .strtab 00000008 {{[0-9a-f]*}} + +# Sections are put in order specified in linker script. +# RUN: echo "SECTIONS { \ +# RUN: .bss : { *(.bss) } \ +# RUN: other : { *(other) } \ +# RUN: .shstrtab : { *(.shstrtab) } \ +# RUN: .symtab : { *(.symtab) } \ +# RUN: .strtab : { *(.strtab) } \ +# RUN: .data : { *(.data) } \ +# RUN: .text : { *(.text) } }" \ > %t.script +# RUN: ld.lld2 -o %t3 --script %t.script %t +# RUN: llvm-objdump -section-headers %t3 | \ +# RUN: FileCheck -check-prefix=SEC-ORDER %s + +# Idx Name Size +# SEC-ORDER: 1 .bss 00000002 {{[0-9a-f]*}} BSS +# SEC-ORDER: 2 other 00000003 {{[0-9a-f]*}} DATA +# SEC-ORDER: 3 .shstrtab 00000002 {{[0-9a-f]*}} +# SEC-ORDER: 4 .shstrtab 00000032 {{[0-9a-f]*}} +# SEC-ORDER: 5 .symtab 00000030 {{[0-9a-f]*}} +# SEC-ORDER: 6 .strtab 00000008 {{[0-9a-f]*}} +# SEC-ORDER: 7 .data 00000020 {{[0-9a-f]*}} DATA +# SEC-ORDER: 8 .text 0000000e {{[0-9a-f]*}} TEXT DATA + +# .text and .data have swapped names but proper sizes and types. +# RUN: echo "SECTIONS { \ +# RUN: .data : { *(.text) } \ +# RUN: .text : { *(.data) } }" \ > %t.script +# RUN: ld.lld2 -o %t4 --script %t.script %t +# RUN: llvm-objdump -section-headers %t4 | \ +# RUN: FileCheck -check-prefix=SEC-SWAP-NAMES %s + +# Idx Name Size +# SEC-SWAP-NAMES: 1 .data 0000000e {{[0-9a-f]*}} TEXT DATA +# SEC-SWAP-NAMES: 2 .text 00000020 {{[0-9a-f]*}} DATA +# SEC-SWAP-NAMES: 3 other 00000003 {{[0-9a-f]*}} DATA +# SEC-SWAP-NAMES: 4 .bss 00000002 {{[0-9a-f]*}} BSS +# SEC-SWAP-NAMES: 5 .shstrtab 00000002 {{[0-9a-f]*}} +# SEC-SWAP-NAMES: 6 .symtab 00000030 {{[0-9a-f]*}} +# SEC-SWAP-NAMES: 7 .shstrtab 00000032 {{[0-9a-f]*}} +# SEC-SWAP-NAMES: 8 .strtab 00000008 {{[0-9a-f]*}} + +# .shstrtab from the input object file is discarded. +# RUN: echo "SECTIONS { \ +# RUN: /DISCARD/ : { *(.shstrtab) } }" \ > %t.script +# RUN: ld.lld2 -o %t5 --script %t.script %t +# RUN: llvm-objdump -section-headers %t5 | \ +# RUN: FileCheck -check-prefix=SEC-DISCARD %s + +# Idx Name Size +# SEC-DISCARD: 1 .text 0000000e {{[0-9a-f]*}} TEXT DATA +# SEC-DISCARD: 2 .data 00000020 {{[0-9a-f]*}} DATA +# SEC-DISCARD: 3 other 00000003 {{[0-9a-f]*}} DATA +# SEC-DISCARD: 4 .bss 00000002 {{[0-9a-f]*}} BSS +# SEC-DISCARD: 5 .symtab 00000030 {{[0-9a-f]*}} +# SEC-DISCARD: 6 .shstrtab 00000032 {{[0-9a-f]*}} +# SEC-DISCARD: 7 .strtab 00000008 {{[0-9a-f]*}} + +# Multiple SECTIONS command specifying additional input section descriptions +# for the same output section description - input sections are merged into +# one output section. +# RUN: echo "SECTIONS { \ +# RUN: .text : { *(.text) } \ +# RUN: .data : { *(.data) } } \ +# RUN: SECTIONS { \ +# RUN: .data : { *(other) } }" \ > %t.script +# RUN: ld.lld2 -o %t6 --script %t.script %t +# RUN: llvm-objdump -section-headers %t6 | \ +# RUN: FileCheck -check-prefix=SEC-MULTI %s + +# Idx Name Size +# SEC-MULTI: 1 .text 0000000e {{[0-9a-f]*}} TEXT DATA +# SEC-MULTI: 2 .data 00000023 {{[0-9a-f]*}} DATA +# SEC-MULTI: 3 .bss 00000002 {{[0-9a-f]*}} BSS +# SEC-MULTI: 4 .shstrtab 00000002 {{[0-9a-f]*}} +# SEC-MULTI: 5 .symtab 00000030 {{[0-9a-f]*}} +# SEC-MULTI: 6 .shstrtab 0000002c {{[0-9a-f]*}} +# SEC-MULTI: 7 .strtab 00000008 {{[0-9a-f]*}} + +.globl _start; +_start: + mov $60, %rax + mov $42, %rdi + +.section .data,"aw" +.quad 10, 10, 20, 20 +.section other,"aw" +.short 10 +.byte 20 +.section .shstrtab,"" +.short 20 +.section .bss,"",@nobits +.short 0