Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -254,6 +254,9 @@ uint64_t Dot; public: + std::vector + predictOutputSections(std::vector V); + OutputSection *createOutputSection(StringRef Name, StringRef Location); OutputSection *getOrCreateOutputSection(StringRef Name); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -372,6 +372,47 @@ sortSections(Vec, Pat.SortOuter); } +static bool matches(StringRef Name, std::vector &V) { + for (const SectionPattern &Pat : V) + if (Pat.SectionPat.match(Name)) + return true; + return false; +} + +// This function predicts the output section names. For the given list of input +// sections method returns the list of predicted output sections. +// It is used for creating mergeable sections and do only basic name matching +// logic. That allows us to create different output sections if that is +// specified in the script, but keep the whole linker logic simple, what is +// possible because we call mergeSections() early. Otherwise, we would need to +// merge sections during handling the linker script commands and handling orphan +// sections, that is not that convenient and does not seem to have any valuable +// benefits for users. +std::vector +LinkerScript::predictOutputSections(std::vector V) { + if (!Script->HasSectionsCommand) + return {}; + + std::vector Ret(V.size()); + for (BaseCommand *OutBase : SectionCommands) { + if (auto *OS = dyn_cast(OutBase)) { + for (BaseCommand *InBase : OS->SectionCommands) { + InputSectionDescription *Cmd = + dyn_cast(InBase); + if (!Cmd) + continue; + + for (size_t I = 0; I < V.size(); ++I) { + if (Ret[I] || !matches(V[I]->Name, Cmd->SectionPatterns)) + continue; + Ret[I] = OS; + } + } + } + } + return Ret; +} + // Compute and remember which sections the InputSectionDescription matches. std::vector LinkerScript::computeInputSections(const InputSectionDescription *Cmd) { Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -2928,7 +2928,13 @@ // to compute an output offset for each piece of each input section. void elf::mergeSections() { std::vector MergeSections; + std::vector PredictedOS = + Script->predictOutputSections(InputSections); + DenseMap SecToOutputName; + + size_t N = 0; for (InputSectionBase *&S : InputSections) { + ++N; MergeInputSection *MS = dyn_cast(S); if (!MS) continue; @@ -2940,7 +2946,9 @@ continue; } - StringRef OutsecName = getOutputSectionName(MS); + StringRef OutName = (PredictedOS.empty() || !PredictedOS[N - 1]) + ? getOutputSectionName(MS) + : PredictedOS[N - 1]->Name; uint32_t Alignment = std::max(MS->Alignment, MS->Entsize); auto I = llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) { @@ -2952,13 +2960,20 @@ // // Using Entsize in here also allows us to propagate it to the synthetic // section. - return Sec->Name == OutsecName && Sec->Flags == MS->Flags && - Sec->Entsize == MS->Entsize && Sec->Alignment == Alignment; + // + // We are trying to predict the output section name and do not merge the + // sections which we think will be in the different outputs. This is a + // simplification that allows us do merging early before scanning the + // linker script commands. + return Sec->Flags == MS->Flags && Sec->Entsize == MS->Entsize && + Sec->Alignment == Alignment && + SecToOutputName.lookup(Sec) == OutName; }); if (I == MergeSections.end()) { MergeSyntheticSection *Syn = - createMergeSynthetic(OutsecName, MS->Type, MS->Flags, Alignment); + createMergeSynthetic(MS->Name, MS->Type, MS->Flags, Alignment); MergeSections.push_back(Syn); + SecToOutputName.insert({Syn, OutName}); I = std::prev(MergeSections.end()); S = Syn; Syn->Entsize = MS->Entsize; Index: test/ELF/linkerscript/merge-sections-different-output.s =================================================================== --- test/ELF/linkerscript/merge-sections-different-output.s +++ test/ELF/linkerscript/merge-sections-different-output.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: echo "SECTIONS { \ +# RUN: .rodata_foo : { *(.rodata.foo) } \ +# RUN: .rodata_bar : { *(.rodata.bar) } \ +# RUN: }" > %t.script +# RUN: ld.lld -o %t -script %t.script %t.o +# RUN: llvm-readelf -S %t | FileCheck %s + +# CHECK: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# CHECK-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# CHECK-NEXT: [ 1] .rodata_foo PROGBITS 0000000000000000 001000 000004 04 AM 0 0 4 +# CHECK-NEXT: [ 2] .rodata_bar PROGBITS 0000000000000004 001004 000004 04 AM 0 0 4 + +.section .rodata.foo,"aM",@progbits,4 +.long 1 + +.section .rodata.bar,"aM",@progbits,4 +.long 1 Index: test/ELF/linkerscript/merge-sections.s =================================================================== --- test/ELF/linkerscript/merge-sections.s +++ test/ELF/linkerscript/merge-sections.s @@ -17,7 +17,7 @@ # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x[[ADDR1:.*]] # CHECK-NEXT: Offset: 0x[[ADDR1]] -# CHECK-NEXT: Size: 14 +# CHECK-NEXT: Size: 8 # CHECK-NEXT: Link: 0 # CHECK-NEXT: Info: 0 # CHECK-NEXT: AddressAlignment: 2 @@ -28,7 +28,7 @@ # CHECK-NEXT: Value: 0x[[ADDR1]] # CHECK: Name: end -# CHECK-NEXT: Value: 0x236 +# CHECK-NEXT: Value: 0x230 # Check that we don't crash with --gc-sections # RUN: ld.lld --gc-sections -o %t2 --script %t.script %t -shared