LinkerScript *elf2::Script;
template <class ELFT>
-StringRef LinkerScript::getOutputSection(InputSectionBase<ELFT> *S) {
+SectionRule *LinkerScript::find(InputSectionBase<ELFT> *S) {
for (SectionRule &R : Sections)
if (R.match(S))
- return R.Dest;
- return "";
+ return &R;
+ return nullptr;
+}
+
+template <class ELFT>
+StringRef LinkerScript::getOutputSection(InputSectionBase<ELFT> *S) {
+ SectionRule *R = find(S);
+ return R ? R->Dest : "";
}
template <class ELFT>
return getOutputSection(S) == "/DISCARD/";
}
+template <class ELFT> bool LinkerScript::shouldKeep(InputSectionBase<ELFT> *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.
void readSections();
void readOutputSectionDescription();
+ void readSectionPatterns(StringRef OutSec, bool Keep);
StringSaver Saver;
std::vector<StringRef> Tokens;
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);
+ }
}
}
template bool LinkerScript::isDiscarded(InputSectionBase<ELF64LE> *);
template bool LinkerScript::isDiscarded(InputSectionBase<ELF64BE> *);
+template bool LinkerScript::shouldKeep(InputSectionBase<ELF32LE> *);
+template bool LinkerScript::shouldKeep(InputSectionBase<ELF32BE> *);
+template bool LinkerScript::shouldKeep(InputSectionBase<ELF64LE> *);
+template bool LinkerScript::shouldKeep(InputSectionBase<ELF64BE> *);
+
template bool SectionRule::match(InputSectionBase<ELF32LE> *);
template bool SectionRule::match(InputSectionBase<ELF32BE> *);
template bool SectionRule::match(InputSectionBase<ELF64LE> *);
// 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 <class ELFT> bool match(InputSectionBase<ELFT> *S);
StringRef Dest;
+ // KEEP command saves unused sections even if --gc-sections is specified.
+ bool Keep = false;
+
private:
StringRef SectionPattern;
};
template <class ELFT> StringRef getOutputSection(InputSectionBase<ELFT> *S);
template <class ELFT> bool isDiscarded(InputSectionBase<ELFT> *S);
+ template <class ELFT> bool shouldKeep(InputSectionBase<ELFT> *S);
int compareSections(StringRef A, StringRef B);
private:
+ template <class ELFT> SectionRule *find(InputSectionBase<ELFT> *S);
+
// SECTIONS commands.
std::vector<SectionRule> Sections;
//===----------------------------------------------------------------------===//
#include "InputSection.h"
+#include "LinkerScript.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
}
}
- // Preserve special sections.
+ // Preserve special sections and those which are specified in linker
+ // script KEEP command.
for (const std::unique_ptr<ObjectFile<ELFT>> &F : Symtab->getObjectFiles())
for (InputSectionBase<ELFT> *Sec : F->getSections())
- if (Sec && Sec != &InputSection<ELFT>::Discarded && isReserved(Sec))
- Enqueue(Sec);
+ if (Sec && Sec != &InputSection<ELFT>::Discarded)
+ if (isReserved(Sec) || Script->shouldKeep<ELFT>(Sec))
+ Enqueue(Sec);
// Mark all reachable sections.
while (!Q.empty())
--- /dev/null
+# 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