[ELF] - Linkerscript: support complex section pattern grammar.

This is PR30442.
Previously we were failed to parce complex expressions like:
foo : { *(SORT_BY_NAME(bar) zed) }

Main idea of patch that globs and excludes can be wrapped in a SORT.
There is a difference in semanics of ld/gold:
ld likes:
*(SORT(EXCLUDE_FILE (*file1.o) .foo.1))

gold likes:
*(EXCLUDE_FILE (*file1.o) SORT(.foo.1))

Patch implements ld grammar, complex expressions like 
next is not a problem anymore:
.abc : { *(SORT(.foo.* EXCLUDE_FILE (*file1.o) .bar.*) .bar.*) }


Differential revision: https://reviews.llvm.org/D24758

llvm-svn: 282078
This commit is contained in:
George Rimar 2016-09-21 15:56:44 +00:00
parent 1049085c78
commit 07171f21d1
3 changed files with 99 additions and 42 deletions

View File

@ -156,10 +156,10 @@ static bool matchConstraints(ArrayRef<InputSectionBase<ELFT> *> Sections,
});
}
static void sortSections(std::vector<InputSectionData *> &Sections,
static void sortSections(InputSectionData **Begin, InputSectionData **End,
SortSectionPolicy K) {
if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None)
std::stable_sort(Sections.begin(), Sections.end(), getComparator(K));
std::stable_sort(Begin, End, getComparator(K));
}
// Compute and remember which sections the InputSectionDescription matches.
@ -168,6 +168,7 @@ void LinkerScript<ELFT>::computeInputSections(InputSectionDescription *I) {
// Collects all sections that satisfy constraints of I
// and attach them to I.
for (SectionPattern &Pat : I->SectionPatterns) {
size_t SizeBefore = I->Sections.size();
for (ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles()) {
StringRef Filename = sys::path::filename(F->getName());
if (!I->FileRe.match(Filename) || Pat.ExcludedFileRe.match(Filename))
@ -179,25 +180,27 @@ void LinkerScript<ELFT>::computeInputSections(InputSectionDescription *I) {
if (Pat.SectionRe.match("COMMON"))
I->Sections.push_back(CommonInputSection<ELFT>::X);
}
}
// Sort sections as instructed by SORT-family commands and --sort-section
// option. Because SORT-family commands can be nested at most two depth
// (e.g. SORT_BY_NAME(SORT_BY_ALIGNMENT(.text.*))) and because the command
// line option is respected even if a SORT command is given, the exact
// behavior we have here is a bit complicated. Here are the rules.
//
// 1. If two SORT commands are given, --sort-section is ignored.
// 2. If one SORT command is given, and if it is not SORT_NONE,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
if (I->SortOuter != SortSectionPolicy::None) {
if (I->SortInner == SortSectionPolicy::Default)
sortSections(I->Sections, Config->SortSection);
else
sortSections(I->Sections, I->SortInner);
sortSections(I->Sections, I->SortOuter);
// Sort sections as instructed by SORT-family commands and --sort-section
// option. Because SORT-family commands can be nested at most two depth
// (e.g. SORT_BY_NAME(SORT_BY_ALIGNMENT(.text.*))) and because the command
// line option is respected even if a SORT command is given, the exact
// behavior we have here is a bit complicated. Here are the rules.
//
// 1. If two SORT commands are given, --sort-section is ignored.
// 2. If one SORT command is given, and if it is not SORT_NONE,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
InputSectionData **Begin = I->Sections.data() + SizeBefore;
InputSectionData **End = I->Sections.data() + I->Sections.size();
if (Pat.SortOuter != SortSectionPolicy::None) {
if (Pat.SortInner == SortSectionPolicy::Default)
sortSections(Begin, End, Config->SortSection);
else
sortSections(Begin, End, Pat.SortInner);
sortSections(Begin, End, Pat.SortOuter);
}
}
// We do not add duplicate input sections, so mark them with a dummy output
@ -769,7 +772,7 @@ private:
std::vector<StringRef> readOutputSectionPhdrs();
InputSectionDescription *readInputSectionDescription(StringRef Tok);
Regex readFilePatterns();
void readSectionExcludes(InputSectionDescription *Cmd);
std::vector<SectionPattern> readInputSectionsList();
InputSectionDescription *readInputSectionRules(StringRef FilePattern);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
@ -1072,7 +1075,8 @@ SortSectionPolicy ScriptParser::readSortKind() {
// * Include .foo.1 from every file.
// * Include .foo.2 from every file but a.o
// * Include .foo.3 from every file but b.o
void ScriptParser::readSectionExcludes(InputSectionDescription *Cmd) {
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
std::vector<SectionPattern> Ret;
while (!Error && peek() != ")") {
Regex ExcludeFileRe;
if (skip("EXCLUDE_FILE")) {
@ -1085,38 +1089,49 @@ void ScriptParser::readSectionExcludes(InputSectionDescription *Cmd) {
V.push_back(next());
if (!V.empty())
Cmd->SectionPatterns.push_back(
{std::move(ExcludeFileRe), compileGlobPatterns(V)});
Ret.push_back({std::move(ExcludeFileRe), compileGlobPatterns(V)});
else
setError("section pattern is expected");
}
expect(")");
return Ret;
}
// Section pattern grammar can have complex expressions, for example:
// *(SORT(.foo.* EXCLUDE_FILE (*file1.o) .bar.*) .bar.* SORT(.zed.*))
// Generally is a sequence of globs and excludes that may be wrapped in a SORT()
// commands, like: SORT(glob0) glob1 glob2 SORT(glob4)
// This methods handles wrapping sequences of excluded files and section globs
// into SORT() if that needed and reads them all.
InputSectionDescription *
ScriptParser::readInputSectionRules(StringRef FilePattern) {
auto *Cmd = new InputSectionDescription(FilePattern);
expect("(");
// Read SORT().
SortSectionPolicy K1 = readSortKind();
if (K1 != SortSectionPolicy::Default) {
Cmd->SortOuter = K1;
expect("(");
SortSectionPolicy K2 = readSortKind();
if (K2 != SortSectionPolicy::Default) {
Cmd->SortInner = K2;
while (!HasError && !skip(")")) {
SortSectionPolicy Outer = readSortKind();
SortSectionPolicy Inner = SortSectionPolicy::Default;
std::vector<SectionPattern> V;
if (Outer != SortSectionPolicy::Default) {
expect("(");
Cmd->SectionPatterns.push_back({Regex(), readFilePatterns()});
Inner = readSortKind();
if (Inner != SortSectionPolicy::Default) {
expect("(");
V = readInputSectionsList();
expect(")");
} else {
V = readInputSectionsList();
}
expect(")");
} else {
Cmd->SectionPatterns.push_back({Regex(), readFilePatterns()});
V = readInputSectionsList();
}
expect(")");
return Cmd;
}
readSectionExcludes(Cmd);
for (SectionPattern &Pat : V) {
Pat.SortInner = Inner;
Pat.SortOuter = Outer;
}
std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns));
}
return Cmd;
}

View File

@ -99,6 +99,7 @@ struct OutputSectionCommand : BaseCommand {
// This struct represents one section match pattern in SECTIONS() command.
// It can optionally have negative match pattern for EXCLUDED_FILE command.
// Also it may be surrounded with SORT() command, so contains sorting rules.
struct SectionPattern {
SectionPattern(llvm::Regex &&Re1, llvm::Regex &&Re2)
: ExcludedFileRe(std::forward<llvm::Regex>(Re1)),
@ -107,10 +108,14 @@ struct SectionPattern {
SectionPattern(SectionPattern &&Other) {
std::swap(ExcludedFileRe, Other.ExcludedFileRe);
std::swap(SectionRe, Other.SectionRe);
std::swap(SortOuter, Other.SortOuter);
std::swap(SortInner, Other.SortInner);
}
llvm::Regex ExcludedFileRe;
llvm::Regex SectionRe;
SortSectionPolicy SortOuter;
SortSectionPolicy SortInner;
};
struct InputSectionDescription : BaseCommand {
@ -119,8 +124,6 @@ struct InputSectionDescription : BaseCommand {
FileRe(compileGlobPatterns({FilePattern})) {}
static bool classof(const BaseCommand *C);
llvm::Regex FileRe;
SortSectionPolicy SortOuter = SortSectionPolicy::Default;
SortSectionPolicy SortInner = SortSectionPolicy::Default;
// Input sections that matches at least one of SectionPatterns
// will be associated with this InputSectionDescription.

View File

@ -0,0 +1,39 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile1.o
# RUN: echo "SECTIONS { .abc : { *(SORT(.foo.*) .bar.*) } }" > %t1.script
# RUN: ld.lld -o %t1 --script %t1.script %tfile1.o
# RUN: llvm-objdump -s %t1 | FileCheck %s
# CHECK: Contents of section .abc:
# CHECK: 00e8 01000000 00000000 02000000 00000000
# CHECK: 00f8 03000000 00000000 04000000 00000000
# CHECK: 0108 06000000 00000000 05000000 00000000
# RUN: echo "SECTIONS { \
# RUN: .abc : { *(SORT(.foo.* EXCLUDE_FILE (*file1.o) .bar.*) .bar.*) } \
# RUN: }" > %t2.script
# RUN: ld.lld -o %t2 --script %t2.script %tfile1.o
# RUN: llvm-objdump -s %t2 | FileCheck %s
.text
.globl _start
_start:
.section .foo.2,"a"
.quad 2
.section .foo.3,"a"
.quad 3
.section .foo.1,"a"
.quad 1
.section .bar.4,"a"
.quad 4
.section .bar.6,"a"
.quad 6
.section .bar.5,"a"
.quad 5