[ELF] Add OVERWRITE_SECTIONS command

This implements https://sourceware.org/bugzilla/show_bug.cgi?id=26404

An `OVERWRITE_SECTIONS` command is a `SECTIONS` variant which contains several
output section descriptions. The output sections do not have specify an order.
Similar to `INSERT [BEFORE|AFTER]`, `LinkerScript::hasSectionsCommand` is not
set, so the built-in rules (see `docs/ELF/linker_script.rst`) still apply.
`OVERWRITE_SECTIONS` can be more convenient than `INSERT` because it does not
need an anchor section.

The initial syntax is intentionally narrow to facilitate backward compatible
extensions in the future. Symbol assignments cannot be used.

This feature is versatile. To list a few usage:

* Use `section : { KEEP(...) }` to retain input sections under GC
* Define encapsulation symbols (start/end) for an output section
* Use `section : ALIGN(...) : { ... }` to overalign an output section (similar to ld64 `-sectalign`)

When an output section is specified by both `OVERWRITE_SECTIONS` and
`INSERT`, `INSERT` is processed after overwrite sections. To make this work,
this patch changes `InsertCommand` to use name based matching instead of pointer
based matching. (This may cause a difference when `INSERT` moves one output
section more than once. Such duplicate commands should not be used in practice
(seems that in GNU ld the output sections may just disappear).)

A linker script can be used without -T/--script. The traditional `SECTIONS`
commands are concatenated, so a wrong rule can be more noticeable from the
section order. This feature if misused can be less noticeable, just like
`INSERT`.

Differential Revision: https://reviews.llvm.org/D103303
This commit is contained in:
Fangrui Song
2021-06-13 12:41:11 -07:00
parent 60e32a1f34
commit 899fdf548e
8 changed files with 320 additions and 59 deletions

View File

@@ -254,9 +254,13 @@ void LinkerScript::processInsertCommands() {
for (const InsertCommand &cmd : insertCommands) {
// If cmd.os is empty, it may have been discarded by
// adjustSectionsBeforeSorting(). We do not handle such output sections.
auto from = llvm::find(sectionCommands, cmd.os);
auto from = llvm::find_if(sectionCommands, [&](BaseCommand *base) {
return isa<OutputSection>(base) &&
cast<OutputSection>(base)->name == cmd.name;
});
if (from == sectionCommands.end())
continue;
OutputSection *osec = cast<OutputSection>(*from);
sectionCommands.erase(from);
auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) {
@@ -264,12 +268,12 @@ void LinkerScript::processInsertCommands() {
return to != nullptr && to->name == cmd.where;
});
if (insertPos == sectionCommands.end()) {
error("unable to insert " + cmd.os->name +
error("unable to insert " + osec->name +
(cmd.isAfter ? " after " : " before ") + cmd.where);
} else {
if (cmd.isAfter)
++insertPos;
sectionCommands.insert(insertPos, cmd.os);
sectionCommands.insert(insertPos, osec);
}
}
}
@@ -547,52 +551,73 @@ LinkerScript::createInputSectionList(OutputSection &outCmd) {
// Create output sections described by SECTIONS commands.
void LinkerScript::processSectionCommands() {
size_t i = 0;
for (BaseCommand *base : sectionCommands) {
if (auto *sec = dyn_cast<OutputSection>(base)) {
std::vector<InputSectionBase *> v = createInputSectionList(*sec);
auto process = [this](OutputSection *osec) {
std::vector<InputSectionBase *> v = createInputSectionList(*osec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
if (sec->name == "/DISCARD/") {
for (InputSectionBase *s : v)
discard(s);
discardSynthetic(*sec);
sec->sectionCommands.clear();
continue;
}
// This is for ONLY_IF_RO and ONLY_IF_RW. An output section directive
// ".foo : ONLY_IF_R[OW] { ... }" is handled only if all member input
// sections satisfy a given constraint. If not, a directive is handled
// as if it wasn't present from the beginning.
//
// Because we'll iterate over SectionCommands many more times, the easy
// way to "make it as if it wasn't present" is to make it empty.
if (!matchConstraints(v, sec->constraint)) {
for (InputSectionBase *s : v)
s->parent = nullptr;
sec->sectionCommands.clear();
continue;
}
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
// is given, input sections are aligned to that value, whether the
// given value is larger or smaller than the original section alignment.
if (sec->subalignExpr) {
uint32_t subalign = sec->subalignExpr().getValue();
for (InputSectionBase *s : v)
s->alignment = subalign;
}
// Set the partition field the same way OutputSection::recordSection()
// does. Partitions cannot be used with the SECTIONS command, so this is
// always 1.
sec->partition = 1;
sec->sectionIndex = i++;
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
if (osec->name == "/DISCARD/") {
for (InputSectionBase *s : v)
discard(s);
discardSynthetic(*osec);
osec->sectionCommands.clear();
return false;
}
}
// This is for ONLY_IF_RO and ONLY_IF_RW. An output section directive
// ".foo : ONLY_IF_R[OW] { ... }" is handled only if all member input
// sections satisfy a given constraint. If not, a directive is handled
// as if it wasn't present from the beginning.
//
// Because we'll iterate over SectionCommands many more times, the easy
// way to "make it as if it wasn't present" is to make it empty.
if (!matchConstraints(v, osec->constraint)) {
for (InputSectionBase *s : v)
s->parent = nullptr;
osec->sectionCommands.clear();
return false;
}
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
// is given, input sections are aligned to that value, whether the
// given value is larger or smaller than the original section alignment.
if (osec->subalignExpr) {
uint32_t subalign = osec->subalignExpr().getValue();
for (InputSectionBase *s : v)
s->alignment = subalign;
}
// Set the partition field the same way OutputSection::recordSection()
// does. Partitions cannot be used with the SECTIONS command, so this is
// always 1.
osec->partition = 1;
return true;
};
// Process OVERWRITE_SECTIONS first so that it can overwrite the main script
// or orphans.
DenseMap<StringRef, OutputSection *> map;
size_t i = 0;
for (OutputSection *osec : overwriteSections)
if (process(osec) && !map.try_emplace(osec->name, osec).second)
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
for (BaseCommand *&base : sectionCommands)
if (auto *osec = dyn_cast<OutputSection>(base)) {
if (OutputSection *overwrite = map.lookup(osec->name)) {
log(overwrite->location + " overwrites " + osec->name);
overwrite->sectionIndex = i++;
base = overwrite;
} else if (process(osec)) {
osec->sectionIndex = i++;
}
}
// If an OVERWRITE_SECTIONS specified output section is not in
// sectionCommands, append it to the end. The section will be inserted by
// orphan placement.
for (OutputSection *osec : overwriteSections)
if (osec->partition == 1 && osec->sectionIndex == UINT32_MAX)
sectionCommands.push_back(osec);
}
void LinkerScript::processSymbolAssignments() {