ELF: Change how to handle KEEP linker script command.

You can instruct the linker to not discard sections even if they
are unused and --gc-sections option is given. The linker script
command for doing that is KEEP. The syntax is KEEP(foo) where foo
is a section name. KEEP commands are written in SECTIONS command,
so you can specify the order of sections *and* which sections
will be kept.

Each sub-command in SECTIONS command are translated into SectionRule
object. Previously, each SectionRule has `Keep` bit. However,
if you think about it, this hid information in too deep in elements
of a list. Semantically, KEEP commands aren't really related to
SECTIONS subcommands. We can keep the section list for KEEP in a
separate list. This patch does that.

llvm-svn: 267065
This commit is contained in:
Rui Ueyama 2016-04-21 22:00:51 +00:00
parent 243b71fd8b
commit 8ec77e64fc
3 changed files with 53 additions and 47 deletions

View File

@ -35,6 +35,8 @@ using namespace lld::elf;
ScriptConfiguration *elf::ScriptConfig; ScriptConfiguration *elf::ScriptConfig;
static bool matchStr(StringRef S, StringRef T);
static uint64_t getInteger(StringRef S) { static uint64_t getInteger(StringRef S) {
uint64_t V; uint64_t V;
if (S.getAsInteger(0, V)) { if (S.getAsInteger(0, V)) {
@ -159,17 +161,11 @@ static uint64_t evaluate(ArrayRef<StringRef> Tokens, uint64_t Dot) {
} }
template <class ELFT> template <class ELFT>
SectionRule *LinkerScript<ELFT>::find(InputSectionBase<ELFT> *S) { StringRef LinkerScript<ELFT>::getOutputSection(InputSectionBase<ELFT> *S) {
for (SectionRule &R : Opt.Sections) for (SectionRule &R : Opt.Sections)
if (R.match(S)) if (R.match(S))
return &R; return R.Dest;
return nullptr; return "";
}
template <class ELFT>
StringRef LinkerScript<ELFT>::getOutputSection(InputSectionBase<ELFT> *S) {
SectionRule *R = find(S);
return R ? R->Dest : "";
} }
template <class ELFT> template <class ELFT>
@ -179,8 +175,10 @@ bool LinkerScript<ELFT>::isDiscarded(InputSectionBase<ELFT> *S) {
template <class ELFT> template <class ELFT>
bool LinkerScript<ELFT>::shouldKeep(InputSectionBase<ELFT> *S) { bool LinkerScript<ELFT>::shouldKeep(InputSectionBase<ELFT> *S) {
SectionRule *R = find(S); for (StringRef Pat : Opt.KeptSections)
return R && R->Keep; if (matchStr(Pat, S->getSectionName()))
return true;
return false;
} }
template <class ELFT> template <class ELFT>
@ -326,7 +324,7 @@ private:
void readLocationCounterValue(); void readLocationCounterValue();
void readOutputSectionDescription(); void readOutputSectionDescription();
void readSectionPatterns(StringRef OutSec, bool Keep); void readSectionPatterns(StringRef OutSec);
const static StringMap<Handler> Cmd; const static StringMap<Handler> Cmd;
ScriptConfiguration &Opt = *ScriptConfig; ScriptConfiguration &Opt = *ScriptConfig;
@ -497,10 +495,10 @@ void ScriptParser::readSections() {
} }
} }
void ScriptParser::readSectionPatterns(StringRef OutSec, bool Keep) { void ScriptParser::readSectionPatterns(StringRef OutSec) {
expect("("); expect("(");
while (!Error && !skip(")")) while (!Error && !skip(")"))
Opt.Sections.emplace_back(OutSec, next(), Keep); Opt.Sections.emplace_back(OutSec, next());
} }
void ScriptParser::readLocationCounterValue() { void ScriptParser::readLocationCounterValue() {
@ -523,19 +521,28 @@ void ScriptParser::readOutputSectionDescription() {
Opt.Commands.push_back({SectionKind, {}, OutSec}); Opt.Commands.push_back({SectionKind, {}, OutSec});
expect(":"); expect(":");
expect("{"); expect("{");
while (!Error && !skip("}")) { while (!Error && !skip("}")) {
StringRef Tok = next(); StringRef Tok = next();
if (Tok == "*") { if (Tok == "*") {
readSectionPatterns(OutSec, false); expect("(");
while (!Error && !skip(")"))
Opt.Sections.emplace_back(OutSec, next());
} else if (Tok == "KEEP") { } else if (Tok == "KEEP") {
expect("("); expect("(");
next(); // Skip * expect("*");
readSectionPatterns(OutSec, true); expect("(");
while (!Error && !skip(")")) {
StringRef Sec = next();
Opt.Sections.emplace_back(OutSec, Sec);
Opt.KeptSections.push_back(Sec);
}
expect(")"); expect(")");
} else { } else {
setError("unknown command " + Tok); setError("unknown command " + Tok);
} }
} }
StringRef Tok = peek(); StringRef Tok = peek();
if (Tok.startswith("=")) { if (Tok.startswith("=")) {
if (!Tok.startswith("=0x")) { if (!Tok.startswith("=0x")) {

View File

@ -30,17 +30,14 @@ template <class ELFT> class OutputSectionBase;
// This class represents each rule in SECTIONS command. // This class represents each rule in SECTIONS command.
class SectionRule { class SectionRule {
public: public:
SectionRule(StringRef D, StringRef S, bool Keep) SectionRule(StringRef D, StringRef S)
: Dest(D), Keep(Keep), SectionPattern(S) {} : Dest(D), SectionPattern(S) {}
// Returns true if S should be in Dest section. // Returns true if S should be in Dest section.
template <class ELFT> bool match(InputSectionBase<ELFT> *S); template <class ELFT> bool match(InputSectionBase<ELFT> *S);
StringRef Dest; StringRef Dest;
// KEEP command saves unused sections even if --gc-sections is specified.
bool Keep = false;
private: private:
StringRef SectionPattern; StringRef SectionPattern;
}; };
@ -70,6 +67,10 @@ struct ScriptConfiguration {
bool DoLayout = false; bool DoLayout = false;
llvm::BumpPtrAllocator Alloc; llvm::BumpPtrAllocator Alloc;
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
std::vector<StringRef> KeptSections;
}; };
extern ScriptConfiguration *ScriptConfig; extern ScriptConfiguration *ScriptConfig;

View File

@ -37,37 +37,35 @@
# RUN: .keep : { KEEP(*(.keep)) } \ # RUN: .keep : { KEEP(*(.keep)) } \
# RUN: .nokeep : { *(.keep) }}" > %t.script # RUN: .nokeep : { *(.keep) }}" > %t.script
# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t # RUN: ld.lld --gc-sections -o %t1 --script %t.script %t
# RUN: llvm-objdump -section-headers %t1 | \ # RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED1 %s
# RUN: FileCheck -check-prefix=KEEP-AT-FIRST %s # MIXED1: Sections:
# KEEP-AT-FIRST: Sections: # MIXED1-NEXT: Idx Name Size Address Type
# KEEP-AT-FIRST-NEXT: Idx Name Size Address Type # MIXED1-NEXT: 0 00000000 0000000000000000
# KEEP-AT-FIRST-NEXT: 0 00000000 0000000000000000 # MIXED1-NEXT: 1 .keep 00000004 0000000000000120 DATA
# KEEP-AT-FIRST-NEXT: 1 .keep 00000004 0000000000000120 DATA # MIXED1-NEXT: 2 .temp 00000004 0000000000000124 DATA
# KEEP-AT-FIRST-NEXT: 2 .temp 00000004 0000000000000124 DATA # MIXED1-NEXT: 3 .text 00000007 0000000000000128 TEXT DATA
# KEEP-AT-FIRST-NEXT: 3 .text 00000007 0000000000000128 TEXT DATA # MIXED1-NEXT: 4 .symtab 00000060 0000000000000000
# KEEP-AT-FIRST-NEXT: 4 .symtab 00000060 0000000000000000 # MIXED1-NEXT: 5 .shstrtab 0000002d 0000000000000000
# KEEP-AT-FIRST-NEXT: 5 .shstrtab 0000002d 0000000000000000 # MIXED1-NEXT: 6 .strtab 00000012 0000000000000000
# KEEP-AT-FIRST-NEXT: 6 .strtab 00000012 0000000000000000
## The same, but now section without KEEP is at first place. ## The same, but now section without KEEP is at first place.
## It will be collected then. ## gold and bfd linkers disagree here. gold collects .keep while
## This test checks that lld behavior is equal to gold linker. ## bfd keeps it. Our current behavior is compatible with bfd although
## ld.bfd has different behavior, it prevents the section .keep ## we can choose either way.
## from collecting in this case either.
# RUN: echo "SECTIONS { \ # RUN: echo "SECTIONS { \
# RUN: .nokeep : { *(.keep) } \ # RUN: .nokeep : { *(.keep) } \
# RUN: .keep : { KEEP(*(.keep)) }}" > %t.script # RUN: .keep : { KEEP(*(.keep)) }}" > %t.script
# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t # RUN: ld.lld --gc-sections -o %t1 --script %t.script %t
# RUN: llvm-objdump -section-headers %t1 | \ # RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED2 %s
# RUN: FileCheck -check-prefix=KEEP-AT-SECOND %s # MIXED2: Sections:
# KEEP-AT-SECOND: Sections: # MIXED2-NEXT: Idx Name Size Address Type
# KEEP-AT-SECOND-NEXT: Idx Name Size Address Type # MIXED2-NEXT: 0 00000000 0000000000000000
# KEEP-AT-SECOND-NEXT: 0 00000000 0000000000000000 # MIXED2-NEXT: 1 .nokeep 00000004 0000000000000120 DATA
# KEEP-AT-SECOND-NEXT: 1 .temp 00000004 0000000000000120 DATA # MIXED2-NEXT: 2 .temp 00000004 0000000000000124 DATA
# KEEP-AT-SECOND-NEXT: 2 .text 00000007 0000000000000124 TEXT DATA # MIXED2-NEXT: 3 .text 00000007 0000000000000128 TEXT DATA
# KEEP-AT-SECOND-NEXT: 3 .symtab 00000048 0000000000000000 # MIXED2-NEXT: 4 .symtab 00000060 0000000000000000
# KEEP-AT-SECOND-NEXT: 4 .shstrtab 00000027 0000000000000000 # MIXED2-NEXT: 5 .shstrtab 0000002f 0000000000000000
# KEEP-AT-SECOND-NEXT: 5 .strtab 0000000d 0000000000000000 # MIXED2-NEXT: 6 .strtab 00000012 0000000000000000
.global _start .global _start
_start: _start: