Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/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" @@ -41,6 +42,7 @@ llvm::StringRef SoName; llvm::StringRef Sysroot; std::string RPath; + llvm::MapVector> OutputSections; std::vector SearchPaths; std::vector Undefined; bool AllowMultipleDefinition; Index: lld/trunk/ELF/LinkerScript.cpp =================================================================== --- lld/trunk/ELF/LinkerScript.cpp +++ lld/trunk/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,9 @@ void readOutputArch(); void readOutputFormat(); void readSearchDir(); + void readSections(); + + void readOutputSectionDescription(); StringSaver Saver; std::vector Tokens; @@ -78,6 +82,8 @@ readOutputFormat(); } else if (Tok == "SEARCH_DIR") { readSearchDir(); + } else if (Tok == "SECTIONS") { + readSections(); } else { error("unknown directive: " + Tok); } @@ -133,11 +139,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 +270,26 @@ expect(")"); } +void LinkerScript::readSections() { + expect("{"); + while (!skip("}")) + readOutputSectionDescription(); +} + +void LinkerScript::readOutputSectionDescription() { + StringRef Name = next(); + std::vector &InputSections = Config->OutputSections[Name]; + + expect(":"); + expect("{"); + while (!skip("}")) { + next(); // Skip input file name. + expect("("); + while (!skip(")")) + InputSections.push_back(next()); + } +} + // 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: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -13,6 +13,7 @@ #include "SymbolTable.h" #include "Target.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/StringSaver.h" @@ -47,9 +48,12 @@ void scanRelocs(InputSection &C); void scanRelocs(InputSectionBase &S, const Elf_Shdr &RelSec); void assignAddresses(); + void buildSectionMap(); 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(); } @@ -82,6 +86,8 @@ uintX_t FileSize; uintX_t SectionHeaderOff; + + llvm::StringMap InputToOutputSection; }; } // anonymous namespace @@ -131,6 +137,7 @@ // The main function of the writer. template void Writer::run() { + buildSectionMap(); if (!Config->DiscardAll) copyLocalSymbols(); createSections(); @@ -461,7 +468,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.")) @@ -473,6 +485,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. @@ -485,7 +518,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; @@ -498,7 +531,7 @@ if (OutType == SHT_PROGBITS && C->getSectionName() == ".eh_frame" && Config->EMachine == EM_X86_64) OutType = SHT_X86_64_UNWIND; - SectionKey Key{getOutputName(C->getSectionName()), + SectionKey Key{getOutputSectionName(C->getSectionName()), OutType, OutFlags, EntSize}; OutputSectionBase *&Sec = Map[Key]; if (!Sec) { @@ -577,14 +610,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 && S->isLive()) - scanRelocs(*S); - } else if (auto *S = dyn_cast_or_null>(B)) { + for (InputSectionBase *C : F->getSections()) { + if (isDiscarded(C)) + continue; + if (auto *S = dyn_cast>(C)) + scanRelocs(*S); + else if (auto *S = dyn_cast>(C)) if (S->RelocSection) scanRelocs(*S, *S->RelocSection); - } } } @@ -656,7 +689,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; @@ -952,6 +985,13 @@ PH->p_align = From->getAlign(); } +template void Writer::buildSectionMap() { + for (const std::pair> &OutSec : + Config->OutputSections) + for (StringRef Name : OutSec.second) + InputToOutputSection[Name] = OutSec.first; +} + template void lld::elf2::writeResult(SymbolTable *Symtab); template void lld::elf2::writeResult(SymbolTable *Symtab); template void lld::elf2::writeResult(SymbolTable *Symtab); Index: lld/trunk/test/elf2/linkerscript-sections.s =================================================================== --- lld/trunk/test/elf2/linkerscript-sections.s +++ lld/trunk/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