Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -83,6 +83,7 @@ struct InputSectionDescription : BaseCommand { InputSectionDescription() : BaseCommand(InputSectionKind) {} static bool classof(const BaseCommand *C); + bool Sort = false; std::vector ExcludedFiles; std::vector Patterns; }; @@ -133,7 +134,7 @@ bool hasPhdrsCommands(); private: - std::vector> + std::vector>> getSectionMap(); std::vector *> Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -74,20 +74,19 @@ return false; } -// Create a vector of (, ). -// For example, if a returned vector contains (".text" (".foo.*" ".bar.*")), -// input sections start with ".foo." or ".bar." should be added to -// ".text" section. +// Create a vector of (, list of ). template -std::vector> +std::vector>> LinkerScript::getSectionMap() { - std::vector> Ret; + std::vector>> Ret; for (const std::unique_ptr &Base1 : Opt.Commands) - if (auto *Cmd1 = dyn_cast(Base1.get())) + if (auto *Cmd1 = dyn_cast(Base1.get())) { + std::vector Commands; for (const std::unique_ptr &Base2 : Cmd1->Commands) - if (auto *Cmd2 = dyn_cast(Base2.get())) - Ret.emplace_back(Cmd1->Name, Cmd2); + Commands.push_back(Base2.get()); + Ret.emplace_back(Cmd1->Name, std::move(Commands)); + } return Ret; } @@ -109,32 +108,58 @@ return Ret; } +// Add input section to output section. If there is no output section yet, +// then create it and add to output section list. +template +static void addSection(OutputSectionFactory &Factory, + std::vector *> &Out, + InputSectionBase *C, StringRef Name) { + OutputSectionBase *Sec; + bool IsNew; + std::tie(Sec, IsNew) = Factory.create(C, Name); + if (IsNew) + Out.push_back(Sec); + Sec->addSection(C); +} + +template +static void addSections(OutputSectionFactory &Factory, + std::vector *> &Out, + std::vector *> &In, + StringRef Name, bool Sort) { + if (Sort) + std::stable_sort(In.begin(), In.end(), + [](InputSectionBase *A, InputSectionBase *B) { + return A->getSectionName() < B->getSectionName(); + }); + + for (InputSectionBase *I : In) + addSection(Factory, Out, I, Name); +} + template std::vector *> LinkerScript::createSections(OutputSectionFactory &Factory) { std::vector *> Ret; - // Add input section to output section. If there is no output section yet, - // then create it and add to output section list. - auto Add = [&](InputSectionBase *C, StringRef Name) { - OutputSectionBase *Sec; - bool IsNew; - std::tie(Sec, IsNew) = Factory.create(C, Name); - if (IsNew) - Ret.push_back(Sec); - Sec->addSection(C); - }; - for (auto &P : getSectionMap()) { StringRef OutputName = P.first; - const InputSectionDescription *I = P.second; - for (InputSectionBase *S : getInputSections(I)) { - if (OutputName == "/DISCARD/") { - S->Live = false; - reportDiscarded(S); + std::vector &Commands = P.second; + for (const BaseCommand *Cmd : Commands) { + auto *InCmd = dyn_cast(Cmd); + if (!InCmd) continue; + + std::vector *> Candidates; + for (InputSectionBase *S : getInputSections(InCmd)) { + if (OutputName == "/DISCARD/") { + S->Live = false; + reportDiscarded(S); + continue; + } + Candidates.push_back(S); } - Add(S, OutputName); + addSections(Factory, Ret, Candidates, OutputName, InCmd->Sort); } } @@ -143,7 +168,7 @@ Symtab::X->getObjectFiles()) for (InputSectionBase *S : F->getSections()) if (!isDiscarded(S) && !S->OutSec) - Add(S, getOutputSectionName(S)); + addSection(Factory, Ret, S, getOutputSectionName(S)); // Remove from the output all the sections which did not meet // the optional constraints. @@ -424,9 +449,7 @@ void readAsNeeded(); void readEntry(); void readExtern(); - std::unique_ptr readFilePattern(); void readGroup(); - void readKeep(OutputSectionCommand *Cmd); void readInclude(); void readNothing() {} void readOutput(); @@ -439,6 +462,7 @@ SymbolAssignment *readAssignment(StringRef Name); void readOutputSectionDescription(StringRef OutSec); std::vector readOutputSectionPhdrs(); + void readInputSectionDescription(InputSectionDescription *InCmd, bool Keep); unsigned readPhdrType(); void readProvide(bool Hidden); void readAlign(OutputSectionCommand *Cmd); @@ -667,32 +691,29 @@ .Default(-1); } -std::unique_ptr ScriptParser::readFilePattern() { - expect("*"); - expect("("); - - auto InCmd = llvm::make_unique(); +void ScriptParser::readInputSectionDescription(InputSectionDescription *InCmd, + bool Keep) { + if (skip("SORT")) { + expect("("); + InCmd->Sort = true; + readInputSectionDescription(InCmd, Keep); + expect(")"); + return; + } if (skip("EXCLUDE_FILE")) { expect("("); while (!Error && !skip(")")) InCmd->ExcludedFiles.push_back(next()); - InCmd->Patterns.push_back(next()); - expect(")"); - } else { - while (!Error && !skip(")")) - InCmd->Patterns.push_back(next()); + readInputSectionDescription(InCmd, Keep); + return; } - return InCmd; -} -void ScriptParser::readKeep(OutputSectionCommand *Cmd) { - expect("("); - std::unique_ptr InCmd = readFilePattern(); - Opt.KeptSections.insert(Opt.KeptSections.end(), InCmd->Patterns.begin(), - InCmd->Patterns.end()); - Cmd->Commands.push_back(std::move(InCmd)); - expect(")"); + while (!Error && !skip(")")) { + if (Keep) + Opt.KeptSections.push_back(peek()); + InCmd->Patterns.push_back(next()); + } } void ScriptParser::readAlign(OutputSectionCommand *Cmd) { @@ -723,16 +744,27 @@ expect("{"); while (!Error && !skip("}")) { - StringRef Tok = next(); - if (Tok == "*") { + if (peek() == "*" || peek() == "KEEP") { auto *InCmd = new InputSectionDescription(); Cmd->Commands.emplace_back(InCmd); - expect("("); - while (!Error && !skip(")")) - InCmd->Patterns.push_back(next()); - } else if (Tok == "KEEP") { - readKeep(Cmd); - } else if (Tok == "PROVIDE") { + // Input section wildcard can be surrounded by KEEP. + // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep + if (skip("KEEP")) { + expect("("); + expect("*"); + expect("("); + readInputSectionDescription(InCmd, true); + expect(")"); + } else { + expect("*"); + expect("("); + readInputSectionDescription(InCmd, false); + } + continue; + } + + StringRef Tok = next(); + if (Tok == "PROVIDE") { readProvide(false); } else if (Tok == "PROVIDE_HIDDEN") { readProvide(true); Index: test/ELF/linkerscript/Inputs/linkerscript-sort.s =================================================================== --- test/ELF/linkerscript/Inputs/linkerscript-sort.s +++ test/ELF/linkerscript/Inputs/linkerscript-sort.s @@ -0,0 +1,14 @@ +.section .aaa.5, "a" +.quad 0x55 + +.section .aaa.1, "a" +.quad 0x11 + +.section .aaa.3, "a" +.quad 0x33 + +.section .aaa.2, "a" +.quad 0x22 + +.section .aaa.4, "a" +.quad 0x44 Index: test/ELF/linkerscript/linkerscript-sort.s =================================================================== --- test/ELF/linkerscript/linkerscript-sort.s +++ test/ELF/linkerscript/linkerscript-sort.s @@ -0,0 +1,61 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +# RUN: %p/Inputs/linkerscript-sort.s -o %t2.o + +# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t1.script +# RUN: ld.lld -o %t1 --script %t1.script %t2.o %t1.o +# RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=UNSORTED %s +# UNSORTED: Contents of section .aaa: +# UNSORTED-NEXT: 0120 55000000 00000000 11000000 00000000 +# UNSORTED-NEXT: 0130 33000000 00000000 22000000 00000000 +# UNSORTED-NEXT: 0140 44000000 00000000 05000000 00000000 +# UNSORTED-NEXT: 0150 01000000 00000000 03000000 00000000 +# UNSORTED-NEXT: 0160 02000000 00000000 04000000 00000000 + +## Check that SORT works (sorted by name of section). +# RUN: echo "SECTIONS { .aaa : { *(SORT(.aaa.*)) } }" > %t2.script +# RUN: ld.lld -o %t2 --script %t2.script %t2.o %t1.o +# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SORTED_A %s +# SORTED_A: Contents of section .aaa: +# SORTED_A-NEXT: 0120 11000000 00000000 01000000 00000000 +# SORTED_A-NEXT: 0130 22000000 00000000 02000000 00000000 +# SORTED_A-NEXT: 0140 33000000 00000000 03000000 00000000 +# SORTED_A-NEXT: 0150 44000000 00000000 04000000 00000000 +# SORTED_A-NEXT: 0160 55000000 00000000 05000000 00000000 + +## When we switch the order of files, check that sorting by +## section names is stable. +# RUN: echo "SECTIONS { .aaa : { *(SORT(.aaa.*)) } }" > %t3.script +# RUN: ld.lld -o %t3 --script %t3.script %t1.o %t2.o +# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_B %s +# SORTED_B: Contents of section .aaa: +# SORTED_B-NEXT: 0120 01000000 00000000 11000000 00000000 +# SORTED_B-NEXT: 0130 02000000 00000000 22000000 00000000 +# SORTED_B-NEXT: 0140 03000000 00000000 33000000 00000000 +# SORTED_B-NEXT: 0150 04000000 00000000 44000000 00000000 +# SORTED_B-NEXT: 0160 05000000 00000000 55000000 00000000 + +## Check that SORT surrounded with KEEP also works. +# RUN: echo "SECTIONS { .aaa : { KEEP (*(SORT(.aaa.*))) } }" > %t3.script +# RUN: ld.lld -o %t3 --script %t3.script %t2.o %t1.o +# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_A %s + +.global _start +_start: + nop + +.section .aaa.5, "a" +.quad 5 + +.section .aaa.1, "a" +.quad 1 + +.section .aaa.3, "a" +.quad 3 + +.section .aaa.2, "a" +.quad 2 + +.section .aaa.4, "a" +.quad 4