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,9 +50,13 @@ template StringRef getOutputSection(InputSectionBase *S); template bool isDiscarded(InputSectionBase *S); + template bool shouldKeep(InputSectionBase *S); int compareSections(StringRef A, StringRef B); private: + template + SectionRule *LinkerScript::find(InputSectionBase *S); + // SECTIONS commands. std::vector Sections; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -31,11 +31,17 @@ LinkerScript *elf2::Script; template -StringRef LinkerScript::getOutputSection(InputSectionBase *S) { +SectionRule *LinkerScript::find(InputSectionBase *S) { for (SectionRule &R : Sections) if (R.match(S)) - return R.Dest; - return ""; + return &R; + return nullptr; +} + +template +StringRef LinkerScript::getOutputSection(InputSectionBase *S) { + SectionRule *R = find(S); + return R ? R->Dest : ""; } template @@ -43,6 +49,12 @@ return getOutputSection(S) == "/DISCARD/"; } +template +bool LinkerScript::shouldKeep(InputSectionBase *S) { + SectionRule *R = find(S); + return R ? R->Keep : 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. @@ -111,6 +123,7 @@ void readSections(); void readOutputSectionDescription(); + void readSectionPatterns(StringRef OutSec, bool Keep); StringSaver Saver; std::vector Tokens; @@ -377,16 +390,32 @@ readOutputSectionDescription(); } +void ScriptParser::readSectionPatterns(StringRef OutSec, bool Keep) { + expect("("); + while (!skip(")")) { + SectionRule R = {OutSec, next()}; + R.Keep = Keep; + Script->Sections.emplace_back(R); + } +} + void ScriptParser::readOutputSectionDescription() { 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()}); + StringRef Cmd = next(); + if (Cmd == "*") { + readSectionPatterns(OutSec, false); + } else if (Cmd == "KEEP") { + expect("("); + next(); // Skip * + readSectionPatterns(OutSec, true); + expect(")"); + } else { + setError("Unknown command " + Cmd); + } } } @@ -415,6 +444,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