Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -32,6 +32,9 @@ StringRef Dest; + // KEEP command saves unused sections even if --gc-sections is specified. + bool Keep = false; + private: StringRef SectionPattern; }; @@ -47,6 +50,7 @@ template StringRef getOutputSection(InputSectionBase *S); template bool isDiscarded(InputSectionBase *S); + template bool shouldKeep(InputSectionBase *S); int compareSections(StringRef A, StringRef B); private: Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -43,6 +43,14 @@ return getOutputSection(S) == "/DISCARD/"; } +template +bool LinkerScript::shouldKeep(InputSectionBase *S) { + for (SectionRule &R : Sections) + if (R.match(S)) + return R.Keep; + return false; +} + // A compartor to sort output sections. Returns -1 or 1 if both // A and B are mentioned in linker scripts. Otherwise, returns 0 // to use the default rule which is implemented in Writer.cpp. @@ -373,20 +381,37 @@ void ScriptParser::readSections() { expect("{"); - while (!Error && !skip("}")) + while (!skip("}")) readOutputSectionDescription(); } void ScriptParser::readOutputSectionDescription() { + auto ReadSections = [this](StringRef OutSec, bool Keep = false) { + expect("("); + while (!skip(")")) { + SectionRule R = { OutSec, next() }; + R.Keep = Keep; + Script->Sections.emplace_back(R); + } + }; + StringRef OutSec = next(); Script->SectionOrder.push_back(OutSec); expect(":"); expect("{"); - while (!Error && !skip("}")) { - next(); // Skip input file name. - expect("("); - while (!Error && !skip(")")) - Script->Sections.push_back({OutSec, next()}); + + while (!skip("}")) { + StringRef Cmd = next(); + if (Cmd == "*") { + ReadSections(OutSec); + } else if (Cmd == "KEEP") { + expect("("); + next(); // Skip * + ReadSections(OutSec, true); + expect(")"); + } else { + error("Unknown command " + Cmd); + } } } @@ -415,6 +440,11 @@ template bool LinkerScript::isDiscarded(InputSectionBase *); template bool LinkerScript::isDiscarded(InputSectionBase *); +template bool LinkerScript::shouldKeep(InputSectionBase *); +template bool LinkerScript::shouldKeep(InputSectionBase *); +template bool LinkerScript::shouldKeep(InputSectionBase *); +template bool LinkerScript::shouldKeep(InputSectionBase *); + template bool SectionRule::match(InputSectionBase *); template bool SectionRule::match(InputSectionBase *); template bool SectionRule::match(InputSectionBase *); Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -21,6 +21,7 @@ //===----------------------------------------------------------------------===// #include "InputSection.h" +#include "LinkerScript.h" #include "OutputSections.h" #include "SymbolTable.h" #include "Symbols.h" @@ -113,11 +114,13 @@ } } - // Preserve special sections. + // Preserve special sections and those which are specified in linker + // script KEEP command. for (const std::unique_ptr> &F : Symtab->getObjectFiles()) for (InputSectionBase *Sec : F->getSections()) - if (Sec && Sec != &InputSection::Discarded && isReserved(Sec)) - Enqueue(Sec); + if (Sec && Sec != &InputSection::Discarded) + if (isReserved(Sec) || Script->shouldKeep(Sec)) + Enqueue(Sec); // Mark all reachable sections. while (!Q.empty()) Index: test/ELF/linkerscript-sections-keep.s =================================================================== --- test/ELF/linkerscript-sections-keep.s +++ test/ELF/linkerscript-sections-keep.s @@ -0,0 +1,43 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +## First check that section "keep" is garbage collected without using KEEP +# RUN: echo "SECTIONS { \ +# RUN: .text : { *(.text) } \ +# RUN: .keep : { *(.keep) } \ +# RUN: .temp : { *(.temp) }}" > %t.script +# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | \ +# RUN: FileCheck -check-prefix=SECGC %s +# SECGC: Sections: +# SECGC-NEXT: Idx Name Size Address Type +# SECGC-NEXT: 0 00000000 0000000000000000 +# SECGC-NEXT: 1 .text 00000007 0000000000011000 TEXT DATA +# SECGC-NEXT: 2 .temp 00000004 0000000000012000 DATA + +## Now apply KEEP command to preserve the section. +# RUN: echo "SECTIONS { \ +# RUN: .text : { *(.text) } \ +# RUN: .keep : { KEEP(*(.keep)) } \ +# RUN: .temp : { *(.temp) }}" > %t.script +# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | \ +# RUN: FileCheck -check-prefix=SECNOGC %s +# SECNOGC: Sections: +# SECNOGC-NEXT: Idx Name Size Address Type +# SECNOGC-NEXT: 0 00000000 0000000000000000 +# SECNOGC-NEXT: 1 .text 00000007 0000000000011000 TEXT DATA +# SECNOGC-NEXT: 2 .keep 00000004 0000000000012000 DATA +# SECNOGC-NEXT: 3 .temp 00000004 0000000000012004 DATA + +.global _start +_start: + mov temp, %eax + +.section .keep, "a" +keep: + .long 1 + +.section .temp, "a" +temp: + .long 2