Index: lld/trunk/ELF/LinkerScript.h =================================================================== --- lld/trunk/ELF/LinkerScript.h +++ lld/trunk/ELF/LinkerScript.h @@ -25,13 +25,17 @@ // This class represents each rule in SECTIONS command. class SectionRule { public: - SectionRule(StringRef D, StringRef S) : Dest(D), SectionPattern(S) {} + SectionRule(StringRef D, StringRef S, bool Keep) + : Dest(D), Keep(Keep), SectionPattern(S) {} // Returns true if S should be in Dest section. template bool match(InputSectionBase *S); StringRef Dest; + // KEEP command saves unused sections even if --gc-sections is specified. + bool Keep = false; + private: StringRef SectionPattern; }; @@ -47,9 +51,12 @@ template StringRef getOutputSection(InputSectionBase *S); template bool isDiscarded(InputSectionBase *S); + template bool shouldKeep(InputSectionBase *S); int compareSections(StringRef A, StringRef B); private: + template SectionRule *find(InputSectionBase *S); + // SECTIONS commands. std::vector Sections; Index: lld/trunk/ELF/LinkerScript.cpp =================================================================== --- lld/trunk/ELF/LinkerScript.cpp +++ lld/trunk/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,11 @@ return getOutputSection(S) == "/DISCARD/"; } +template bool LinkerScript::shouldKeep(InputSectionBase *S) { + SectionRule *R = find(S); + return R && R->Keep; +} + // 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. @@ -124,6 +135,7 @@ void readSections(); void readOutputSectionDescription(); + void readSectionPatterns(StringRef OutSec, bool Keep); StringSaver Saver; std::vector Tokens; @@ -374,16 +386,29 @@ readOutputSectionDescription(); } +void ScriptParser::readSectionPatterns(StringRef OutSec, bool Keep) { + expect("("); + while (!Error && !skip(")")) + Script->Sections.emplace_back(OutSec, next(), Keep); +} + 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 Tok = next(); + if (Tok == "*") { + readSectionPatterns(OutSec, false); + } else if (Tok == "KEEP") { + expect("("); + next(); // Skip * + readSectionPatterns(OutSec, true); + expect(")"); + } else { + setError("Unknown command " + Tok); + } } } @@ -412,6 +437,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: lld/trunk/ELF/MarkLive.cpp =================================================================== --- lld/trunk/ELF/MarkLive.cpp +++ lld/trunk/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: lld/trunk/test/ELF/linkerscript-sections-keep.s =================================================================== --- lld/trunk/test/ELF/linkerscript-sections-keep.s +++ lld/trunk/test/ELF/linkerscript-sections-keep.s @@ -0,0 +1,82 @@ +# 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 + +## A section name matches two entries in the SECTIONS directive. The +## first one doesn't have KEEP, the second one does. If section that have +## KEEP is the first in order then section is NOT collected. +# RUN: echo "SECTIONS { \ +# RUN: .keep : { KEEP(*(.keep)) } \ +# RUN: .nokeep : { *(.keep) }}" > %t.script +# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | \ +# RUN: FileCheck -check-prefix=KEEP-AT-FIRST %s +# KEEP-AT-FIRST: Sections: +# KEEP-AT-FIRST-NEXT: Idx Name Size Address Type +# KEEP-AT-FIRST-NEXT: 0 00000000 0000000000000000 +# KEEP-AT-FIRST-NEXT: 1 .keep 00000004 0000000000010120 DATA +# KEEP-AT-FIRST-NEXT: 2 .temp 00000004 0000000000010124 DATA +# KEEP-AT-FIRST-NEXT: 3 .text 00000007 0000000000011000 TEXT DATA +# KEEP-AT-FIRST-NEXT: 4 .symtab 00000060 0000000000000000 +# KEEP-AT-FIRST-NEXT: 5 .shstrtab 0000002d 0000000000000000 +# KEEP-AT-FIRST-NEXT: 6 .strtab 00000012 0000000000000000 + +## The same, but now section without KEEP is at first place. +## It will be collected then. +## This test checks that lld behavior is equal to gold linker. +## ld.bfd has different behavior, it prevents the section .keep +## from collecting in this case either. +# RUN: echo "SECTIONS { \ +# RUN: .nokeep : { *(.keep) } \ +# RUN: .keep : { KEEP(*(.keep)) }}" > %t.script +# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | \ +# RUN: FileCheck -check-prefix=KEEP-AT-SECOND %s +# KEEP-AT-SECOND: Sections: +# KEEP-AT-SECOND-NEXT: Idx Name Size Address Type +# KEEP-AT-SECOND-NEXT: 0 00000000 0000000000000000 +# KEEP-AT-SECOND-NEXT: 1 .temp 00000004 0000000000010120 DATA +# KEEP-AT-SECOND-NEXT: 2 .text 00000007 0000000000011000 TEXT DATA +# KEEP-AT-SECOND-NEXT: 3 .symtab 00000048 0000000000000000 +# KEEP-AT-SECOND-NEXT: 4 .shstrtab 00000027 0000000000000000 +# KEEP-AT-SECOND-NEXT: 5 .strtab 0000000d 0000000000000000 + +.global _start +_start: + mov temp, %eax + +.section .keep, "a" +keep: + .long 1 + +.section .temp, "a" +temp: + .long 2