[LLD] Add CLASS syntax to SECTIONS (#95323)

This allows the input section matching algorithm to be separated from
output section descriptions. This allows a group of sections to be
assigned to multiple output sections, providing an explicit version of
--enable-non-contiguous-regions's spilling that doesn't require altering
global linker script matching behavior with a flag. It also makes the
linker script language more expressive even if spilling is not intended,
since input section matching can be done in a different order than
sections are placed in an output section.

The implementation reuses the backend mechanism provided by
--enable-non-contiguous-regions, so it has roughly similar semantics and
limitations. In particular, sections cannot be spilled into or out of
INSERT, OVERWRITE_SECTIONS, or /DISCARD/. The former two aren't
intrinsic, so it may be possible to relax those restrictions later.
This commit is contained in:
Daniel Thornburgh 2024-08-05 13:06:45 -07:00 committed by GitHub
parent 88d288489e
commit 7e8a9020b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 777 additions and 102 deletions

View File

@ -194,6 +194,8 @@ uint64_t SectionBase::getOffset(uint64_t offset) const {
// For output sections we treat offset -1 as the end of the section.
return offset == uint64_t(-1) ? os->size : offset;
}
case Class:
llvm_unreachable("section classes do not have offsets");
case Regular:
case Synthetic:
case Spill:

View File

@ -61,7 +61,7 @@ template <class ELFT> struct RelsOrRelas {
// sections.
class SectionBase {
public:
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output };
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output, Class };
Kind kind() const { return (Kind)sectionKind; }
@ -148,7 +148,9 @@ public:
uint32_t addralign, ArrayRef<uint8_t> data, StringRef name,
Kind sectionKind);
static bool classof(const SectionBase *s) { return s->kind() != Output; }
static bool classof(const SectionBase *s) {
return s->kind() != Output && s->kind() != Class;
}
// The file which contains this section. Its dynamic type is usually
// ObjFile<ELFT>, but may be an InputFile of InternalKind (for a synthetic

View File

@ -277,6 +277,8 @@ getSymbolAssignmentValues(ArrayRef<SectionCommand *> sectionCommands) {
assign->sym->value));
continue;
}
if (isa<SectionClassDesc>(cmd))
continue;
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
if (assign->sym)
@ -348,6 +350,8 @@ void LinkerScript::declareSymbols() {
declareSymbol(assign);
continue;
}
if (isa<SectionClassDesc>(cmd))
continue;
// If the output section directive has constraints,
// we can't say for sure if it is going to be included or not.
@ -491,104 +495,136 @@ static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
SmallVector<InputSectionBase *, 0>
LinkerScript::computeInputSections(const InputSectionDescription *cmd,
ArrayRef<InputSectionBase *> sections,
const OutputSection &outCmd) {
const SectionBase &outCmd) {
SmallVector<InputSectionBase *, 0> ret;
SmallVector<size_t, 0> indexes;
DenseSet<size_t> seen;
DenseSet<InputSectionBase *> spills;
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
for (size_t i = begin; i != end; ++i)
ret[i] = sections[indexes[i]];
sortInputSections(
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
config->sortSection, SortSectionPolicy::None);
// Returns whether an input section's flags match the input section
// description's specifiers.
auto flagsMatch = [cmd](InputSectionBase *sec) {
return (sec->flags & cmd->withFlags) == cmd->withFlags &&
(sec->flags & cmd->withoutFlags) == 0;
};
// Collects all sections that satisfy constraints of Cmd.
size_t sizeAfterPrevSort = 0;
for (const SectionPattern &pat : cmd->sectionPatterns) {
size_t sizeBeforeCurrPat = ret.size();
if (cmd->classRef.empty()) {
DenseSet<size_t> seen;
size_t sizeAfterPrevSort = 0;
SmallVector<size_t, 0> indexes;
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
for (size_t i = begin; i != end; ++i)
ret[i] = sections[indexes[i]];
sortInputSections(
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
config->sortSection, SortSectionPolicy::None);
};
for (size_t i = 0, e = sections.size(); i != e; ++i) {
// Skip if the section is dead or has been matched by a previous pattern
// in this input section description.
InputSectionBase *sec = sections[i];
if (!sec->isLive() || seen.contains(i))
continue;
for (const SectionPattern &pat : cmd->sectionPatterns) {
size_t sizeBeforeCurrPat = ret.size();
// For --emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them.
if (isa<InputSection>(sec) &&
cast<InputSection>(sec)->getRelocatedSection())
continue;
// Check the name early to improve performance in the common case.
if (!pat.sectionPat.match(sec->name))
continue;
if (!cmd->matchesFile(sec->file) || pat.excludesFile(sec->file) ||
(sec->flags & cmd->withFlags) != cmd->withFlags ||
(sec->flags & cmd->withoutFlags) != 0)
continue;
if (sec->parent) {
// Skip if not allowing multiple matches.
if (!config->enableNonContiguousRegions)
for (size_t i = 0, e = sections.size(); i != e; ++i) {
// Skip if the section is dead or has been matched by a previous pattern
// in this input section description.
InputSectionBase *sec = sections[i];
if (!sec->isLive() || seen.contains(i))
continue;
// Disallow spilling into /DISCARD/; special handling would be needed
// for this in address assignment, and the semantics are nebulous.
if (outCmd.name == "/DISCARD/")
// For --emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them.
if (isa<InputSection>(sec) &&
cast<InputSection>(sec)->getRelocatedSection())
continue;
// Skip if the section's first match was /DISCARD/; such sections are
// always discarded.
if (sec->parent->name == "/DISCARD/")
// Check the name early to improve performance in the common case.
if (!pat.sectionPat.match(sec->name))
continue;
// Skip if the section was already matched by a different input section
// description within this output section.
if (sec->parent == &outCmd)
if (!cmd->matchesFile(sec->file) || pat.excludesFile(sec->file) ||
sec->parent == &outCmd || !flagsMatch(sec))
continue;
spills.insert(sec);
if (sec->parent) {
// Skip if not allowing multiple matches.
if (!config->enableNonContiguousRegions)
continue;
// Disallow spilling into /DISCARD/; special handling would be needed
// for this in address assignment, and the semantics are nebulous.
if (outCmd.name == "/DISCARD/")
continue;
// Class definitions cannot contain spills, nor can a class definition
// generate a spill in a subsequent match. Those behaviors belong to
// class references and additional matches.
if (!isa<SectionClass>(outCmd) && !isa<SectionClass>(sec->parent))
spills.insert(sec);
}
ret.push_back(sec);
indexes.push_back(i);
seen.insert(i);
}
ret.push_back(sec);
indexes.push_back(i);
seen.insert(i);
if (pat.sortOuter == SortSectionPolicy::Default)
continue;
// Matched sections are ordered by radix sort with the keys being (SORT*,
// --sort-section, input order), where SORT* (if present) is most
// significant.
//
// Matched sections between the previous SORT* and this SORT* are sorted
// by (--sort-alignment, input order).
sortByPositionThenCommandLine(sizeAfterPrevSort, sizeBeforeCurrPat);
// Matched sections by this SORT* pattern are sorted using all 3 keys.
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
// just sort by sortOuter and sortInner.
sortInputSections(
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
pat.sortOuter, pat.sortInner);
sizeAfterPrevSort = ret.size();
}
if (pat.sortOuter == SortSectionPolicy::Default)
continue;
// Matched sections after the last SORT* are sorted by (--sort-alignment,
// input order).
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
} else {
SectionClassDesc *scd =
script->sectionClasses.lookup(CachedHashStringRef(cmd->classRef));
if (!scd) {
errorOrWarn("undefined section class '" + cmd->classRef + "'");
return ret;
}
if (!scd->sc.assigned) {
errorOrWarn("section class '" + cmd->classRef + "' referenced by '" +
outCmd.name + "' before class definition");
return ret;
}
// Matched sections are ordered by radix sort with the keys being (SORT*,
// --sort-section, input order), where SORT* (if present) is most
// significant.
//
// Matched sections between the previous SORT* and this SORT* are sorted by
// (--sort-alignment, input order).
sortByPositionThenCommandLine(sizeAfterPrevSort, sizeBeforeCurrPat);
// Matched sections by this SORT* pattern are sorted using all 3 keys.
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
// just sort by sortOuter and sortInner.
sortInputSections(
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
pat.sortOuter, pat.sortInner);
sizeAfterPrevSort = ret.size();
for (InputSectionDescription *isd : scd->sc.commands) {
for (InputSectionBase *sec : isd->sectionBases) {
if (sec->parent == &outCmd || !flagsMatch(sec))
continue;
bool isSpill = sec->parent && isa<OutputSection>(sec->parent);
if (!sec->parent || (isSpill && outCmd.name == "/DISCARD/")) {
errorOrWarn("section '" + sec->name +
"' cannot spill from/to /DISCARD/");
continue;
}
if (isSpill)
spills.insert(sec);
ret.push_back(sec);
}
}
}
// Matched sections after the last SORT* are sorted by (--sort-alignment,
// input order).
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
// The flag --enable-non-contiguous-regions may cause sections to match an
// InputSectionDescription in more than one OutputSection. Matches after the
// first were collected in the spills set, so replace these with potential
// spill sections.
// The flag --enable-non-contiguous-regions or the section CLASS syntax may
// cause sections to match an InputSectionDescription in more than one
// OutputSection. Matches after the first were collected in the spills set, so
// replace these with potential spill sections.
if (!spills.empty()) {
for (InputSectionBase *&sec : ret) {
if (!spills.contains(sec))
@ -708,7 +744,7 @@ void LinkerScript::processSectionCommands() {
!map.try_emplace(CachedHashStringRef(osec->name), osd).second)
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
}
for (SectionCommand *&base : sectionCommands)
for (SectionCommand *&base : sectionCommands) {
if (auto *osd = dyn_cast<OutputDesc>(base)) {
OutputSection *osec = &osd->osec;
if (OutputDesc *overwrite = map.lookup(CachedHashStringRef(osec->name))) {
@ -718,7 +754,50 @@ void LinkerScript::processSectionCommands() {
} else if (process(osec)) {
osec->sectionIndex = i++;
}
} else if (auto *sc = dyn_cast<SectionClassDesc>(base)) {
for (InputSectionDescription *isd : sc->sc.commands) {
isd->sectionBases =
computeInputSections(isd, ctx.inputSections, sc->sc);
for (InputSectionBase *s : isd->sectionBases) {
// A section class containing a section with different parent isn't
// necessarily an error due to --enable-non-contiguous-regions. Such
// sections all become potential spills when the class is referenced.
if (!s->parent)
s->parent = &sc->sc;
}
}
sc->sc.assigned = true;
}
}
// Check that input sections cannot spill into or out of INSERT,
// since the semantics are nebulous. This is also true for OVERWRITE_SECTIONS,
// but no check is needed, since the order of processing ensures they cannot
// legally reference classes.
if (!potentialSpillLists.empty()) {
DenseSet<StringRef> insertNames;
for (InsertCommand &ic : insertCommands)
insertNames.insert(ic.names.begin(), ic.names.end());
for (SectionCommand *&base : sectionCommands) {
auto *osd = dyn_cast<OutputDesc>(base);
if (!osd)
continue;
OutputSection *os = &osd->osec;
if (!insertNames.contains(os->name))
continue;
for (SectionCommand *sc : os->commands) {
auto *isd = dyn_cast<InputSectionDescription>(sc);
if (!isd)
continue;
for (InputSectionBase *isec : isd->sectionBases)
if (isa<PotentialSpillSection>(isec) ||
potentialSpillLists.contains(isec))
errorOrWarn("section '" + isec->name +
"' cannot spill from/to INSERT section '" + os->name +
"'");
}
}
}
// If an OVERWRITE_SECTIONS specified output section is not in
// sectionCommands, append it to the end. The section will be inserted by
@ -726,6 +805,21 @@ void LinkerScript::processSectionCommands() {
for (OutputDesc *osd : overwriteSections)
if (osd->osec.partition == 1 && osd->osec.sectionIndex == UINT32_MAX)
sectionCommands.push_back(osd);
// Input sections cannot have a section class parent past this point; they
// must have been assigned to an output section.
for (const auto &[_, sc] : sectionClasses) {
for (InputSectionDescription *isd : sc->sc.commands) {
for (InputSectionBase *sec : isd->sectionBases) {
if (sec->parent && isa<SectionClass>(sec->parent)) {
errorOrWarn("section class '" + sec->parent->name +
"' is unreferenced");
goto nextClass;
}
}
}
nextClass:;
}
}
void LinkerScript::processSymbolAssignments() {
@ -746,8 +840,8 @@ void LinkerScript::processSymbolAssignments() {
for (SectionCommand *cmd : sectionCommands) {
if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
addSymbol(assign);
else
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
else if (auto *osd = dyn_cast<OutputDesc>(cmd))
for (SectionCommand *subCmd : osd->osec.commands)
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
addSymbol(assign);
}
@ -1417,6 +1511,8 @@ LinkerScript::assignAddresses() {
assign->size = dot - assign->addr;
continue;
}
if (isa<SectionClassDesc>(cmd))
continue;
if (assignOffsets(&cast<OutputDesc>(cmd)->osec) && !changedOsec)
changedOsec = &cast<OutputDesc>(cmd)->osec;
}
@ -1437,15 +1533,15 @@ static bool hasRegionOverflowed(MemoryRegion *mr) {
// Under-estimates may cause unnecessary spills, but over-estimates can always
// be corrected on the next pass.
bool LinkerScript::spillSections() {
if (!config->enableNonContiguousRegions)
if (potentialSpillLists.empty())
return false;
bool spilled = false;
for (SectionCommand *cmd : reverse(sectionCommands)) {
auto *od = dyn_cast<OutputDesc>(cmd);
if (!od)
auto *osd = dyn_cast<OutputDesc>(cmd);
if (!osd)
continue;
OutputSection *osec = &od->osec;
OutputSection *osec = &osd->osec;
if (!osec->memRegion)
continue;

View File

@ -35,6 +35,8 @@ class OutputSection;
class SectionBase;
class ThunkSection;
struct OutputDesc;
struct SectionClass;
struct SectionClassDesc;
// This represents an r-value in the linker script.
struct ExprValue {
@ -78,7 +80,8 @@ enum SectionsCommandKind {
AssignmentKind, // . = expr or <sym> = expr
OutputSectionKind,
InputSectionKind,
ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
ByteKind, // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
ClassKind, // CLASS(class_name)
};
struct SectionCommand {
@ -198,9 +201,12 @@ class InputSectionDescription : public SectionCommand {
public:
InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0,
uint64_t withoutFlags = 0)
uint64_t withoutFlags = 0, StringRef classRef = {})
: SectionCommand(InputSectionKind), filePat(filePattern),
withFlags(withFlags), withoutFlags(withoutFlags) {}
classRef(classRef), withFlags(withFlags), withoutFlags(withoutFlags) {
assert((filePattern.empty() || classRef.empty()) &&
"file pattern and class reference are mutually exclusive");
}
static bool classof(const SectionCommand *c) {
return c->kind == InputSectionKind;
@ -212,6 +218,10 @@ public:
// will be associated with this InputSectionDescription.
SmallVector<SectionPattern, 0> sectionPatterns;
// If present, input section matching uses class membership instead of file
// and section patterns (mutually exclusive).
StringRef classRef;
// Includes InputSections and MergeInputSections. Used temporarily during
// assignment of input sections to output sections.
SmallVector<InputSectionBase *, 0> sectionBases;
@ -298,8 +308,7 @@ class LinkerScript final {
SmallVector<InputSectionBase *, 0>
computeInputSections(const InputSectionDescription *,
ArrayRef<InputSectionBase *>,
const OutputSection &outCmd);
ArrayRef<InputSectionBase *>, const SectionBase &outCmd);
SmallVector<InputSectionBase *, 0> createInputSectionList(OutputSection &cmd);
@ -429,6 +438,11 @@ public:
PotentialSpillSection *tail;
};
llvm::DenseMap<InputSectionBase *, PotentialSpillList> potentialSpillLists;
// Named lists of input sections that can be collectively referenced in output
// section descriptions. Multiple references allow for sections to spill from
// one output section to another.
llvm::DenseMap<llvm::CachedHashStringRef, SectionClassDesc *> sectionClasses;
};
struct ScriptWrapper {

View File

@ -167,6 +167,8 @@ static void writeMapFile(raw_fd_ostream &os) {
os << assign->commandString << '\n';
continue;
}
if (isa<SectionClassDesc>(cmd))
continue;
osec = &cast<OutputDesc>(cmd)->osec;
writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->addralign);

View File

@ -143,6 +143,25 @@ struct OutputDesc final : SectionCommand {
}
};
// This represents a CLASS(class_name) { ... } that can be referenced by output
// section descriptions. If referenced more than once, the sections can be
// spilled to the next reference like --enable-non-contiguous-regions.
struct SectionClass final : public SectionBase {
SmallVector<InputSectionDescription *, 0> commands;
bool assigned = false;
SectionClass(StringRef name) : SectionBase(Class, name, 0, 0, 0, 0, 0, 0) {}
static bool classof(const SectionBase *s) { return s->kind() == Class; }
};
struct SectionClassDesc : SectionCommand {
SectionClass sc;
SectionClassDesc(StringRef name) : SectionCommand(ClassKind), sc(name) {}
static bool classof(const SectionCommand *c) { return c->kind == ClassKind; }
};
int getPriority(StringRef s);
InputSection *getFirstInputSection(const OutputSection *os);

View File

@ -87,6 +87,8 @@ private:
OutputDesc *readOverlaySectionDescription();
OutputDesc *readOutputSectionDescription(StringRef outSec);
SmallVector<SectionCommand *, 0> readOverlay();
SectionClassDesc *readSectionClassDescription();
StringRef readSectionClassName();
SmallVector<StringRef, 0> readOutputSectionPhdrs();
std::pair<uint64_t, uint64_t> readInputSectionFlags();
InputSectionDescription *readInputSectionDescription(StringRef tok);
@ -605,6 +607,33 @@ SmallVector<SectionCommand *, 0> ScriptParser::readOverlay() {
return v;
}
SectionClassDesc *ScriptParser::readSectionClassDescription() {
StringRef name = readSectionClassName();
SectionClassDesc *desc = make<SectionClassDesc>(name);
if (!script->sectionClasses.insert({CachedHashStringRef(name), desc}).second)
setError("section class '" + name + "' already defined");
expect("{");
while (auto tok = till("}")) {
if (tok == "(" || tok == ")") {
setError("expected filename pattern");
} else if (peek() == "(") {
InputSectionDescription *isd = readInputSectionDescription(tok);
if (!isd->classRef.empty())
setError("section class '" + name + "' references class '" +
isd->classRef + "'");
desc->sc.commands.push_back(isd);
}
}
return desc;
}
StringRef ScriptParser::readSectionClassName() {
expect("(");
StringRef name = unquote(next());
expect(")");
return name;
}
void ScriptParser::readOverwriteSections() {
expect("{");
while (auto tok = till("}"))
@ -619,7 +648,12 @@ void ScriptParser::readSections() {
for (SectionCommand *cmd : readOverlay())
v.push_back(cmd);
continue;
} else if (tok == "INCLUDE") {
}
if (tok == "CLASS") {
v.push_back(readSectionClassDescription());
continue;
}
if (tok == "INCLUDE") {
readInclude();
continue;
}
@ -822,8 +856,14 @@ ScriptParser::readInputSectionDescription(StringRef tok) {
expect("(");
if (consume("INPUT_SECTION_FLAGS"))
std::tie(withFlags, withoutFlags) = readInputSectionFlags();
InputSectionDescription *cmd =
readInputSectionRules(next(), withFlags, withoutFlags);
tok = next();
InputSectionDescription *cmd;
if (tok == "CLASS")
cmd = make<InputSectionDescription>(StringRef{}, withFlags, withoutFlags,
readSectionClassName());
else
cmd = readInputSectionRules(tok, withFlags, withoutFlags);
expect(")");
script->keptSections.push_back(cmd);
return cmd;
@ -832,6 +872,9 @@ ScriptParser::readInputSectionDescription(StringRef tok) {
std::tie(withFlags, withoutFlags) = readInputSectionFlags();
tok = next();
}
if (tok == "CLASS")
return make<InputSectionDescription>(StringRef{}, withFlags, withoutFlags,
readSectionClassName());
return readInputSectionRules(tok, withFlags, withoutFlags);
}
@ -951,8 +994,12 @@ OutputDesc *ScriptParser::readOverlaySectionDescription() {
std::tie(withFlags, withoutFlags) = readInputSectionFlags();
tok = till("");
}
osd->osec.commands.push_back(
readInputSectionRules(tok, withFlags, withoutFlags));
if (tok == "CLASS")
osd->osec.commands.push_back(make<InputSectionDescription>(
StringRef{}, withFlags, withoutFlags, readSectionClassName()));
else
osd->osec.commands.push_back(
readInputSectionRules(tok, withFlags, withoutFlags));
}
osd->osec.phdrs = readOutputSectionPhdrs();
return osd;

View File

@ -198,13 +198,52 @@ the current location to a max-page-size boundary, ensuring that the next
LLD will insert ``.relro_padding`` immediately before the symbol assignment
using ``DATA_SEGMENT_RELRO_END``.
Section Classes
~~~~~~~~~~~~~~~
The ``CLASS`` keyword inside a ``SECTIONS`` command defines classes of input
sections:
::
SECTIONS {
CLASS(class_name) {
input-section-description
input-section-description
...
}
}
Input section descriptions refer to a class using ``CLASS(class_name)``
instead of the usual filename and section name patterns. For example:
::
SECTIONS {
CLASS(c) { *(.rodata.earlier) }
.rodata { *(.rodata) CLASS(c) (*.rodata.later) }
}
Input sections that are assigned to a class are not matched by later patterns,
just as if they had been assigned to an earlier output section. If a class is
referenced in multiple output sections, when a memory region would overflow,
the linker spills input sections from a reference to later references rather
than failing the link.
Classes cannot reference other classes; an input section is assigned to at most
one class.
Sections cannot be specified to possibly spill into or out of
``INSERT [AFTER|BEFORE]``, ``OVERWRITE_SECTIONS``, or ``/DISCARD/``.
Non-contiguous regions
~~~~~~~~~~~~~~~~~~~~~~
The flag ``--enable-non-contiguous-regions`` allows input sections to spill to
later matches rather than causing the link to fail by overflowing a memory
region. Unlike GNU ld, ``/DISCARD/`` only matches previously-unmatched sections
(i.e., the flag does not affect it). Also, if a section fails to fit at any of
its matches, the link fails instead of discarding the section. Accordingly, the
GNU flag ``--enable-non-contiguous-regions-warnings`` is not implemented, as it
exists to warn about such occurrences.
The flag ``--enable-non-contiguous-regions`` provides a version of the above
spilling functionality that is more compatible with GNU LD. It allows input
sections to spill to later pattern matches. (This globally changes the behavior
of patterns.) Unlike GNU ld, ``/DISCARD/`` only matches previously-unmatched
sections (i.e., the flag does not affect it). Also, if a section fails to fit
at any of its matches, the link fails instead of discarding the section.
Accordingly, the GNU flag ``--enable-non-contiguous-regions-warnings`` is not
implemented, as it exists to warn about such occurrences.

View File

@ -29,6 +29,12 @@ ELF Improvements
* ``-z nosectionheader`` has been implemented to omit the section header table.
The operation is similar to ``llvm-objcopy --strip-sections``.
(`#101286 <https://github.com/llvm/llvm-project/pull/101286>`_)
* Section ``CLASS`` linker script syntax binds input sections to named classes,
which are referenced later one or more times. This provides access to the
automatic spilling mechanism of `--enable-non-contiguous-regions` without
globally changing the semantics of section matching. It also independently
increases the expressive power of linker scripts.
(`#95323 <https://github.com/llvm/llvm-project/pull/95323>`_)
Breaking changes
----------------

View File

@ -0,0 +1,448 @@
# REQUIRES: x86
# RUN: rm -rf %t && split-file %s %t && cd %t
#--- matching.s
.section .rodata.a,"a",@progbits
.byte 1
.section .rodata.b,"a",@progbits
.byte 2
.section .rodata.c,"ax",@progbits
.byte 3
.section .rodata.d,"a",@progbits
.byte 4
.section .rodata.e,"a",@progbits
.byte 5
.section .rodata.f,"a",@progbits
.balign 2
.byte 6
.section .rodata.g,"a",@progbits
.byte 7
.section .rodata.h,"a",@progbits
.byte 8
# RUN: llvm-mc -n -filetype=obj -triple=x86_64 matching.s -o matching.o
#--- matching.lds
## CLASS definitions match sections in linker script order. The sections may be
## placed in a different order. Classes may derive from one another. Class
## references can be restricted by INPUT_SECTION_FLAGS. Classes can be referenced
## in /DISCARD/ and INSERT.
SECTIONS {
CLASS(a) { *(.rodata.a) }
CLASS(cd) { *(.rodata.c) *(.rodata.d) }
CLASS(ef) { *(SORT_BY_ALIGNMENT(.rodata.e .rodata.f)) }
CLASS(g) { *(.rodata.g) }
CLASS("h)") { *(.rodata.h) }
.rodata : {
*(.rodata.*)
INPUT_SECTION_FLAGS(SHF_EXECINSTR) CLASS( cd)
CLASS(a)CLASS(ef )
}
OVERLAY : { .rodata.d { INPUT_SECTION_FLAGS(!SHF_EXECINSTR) CLASS(cd) } }
/DISCARD/ : { CLASS(g) }
}
SECTIONS {
.rodata.h : { CLASS("h)") }
} INSERT AFTER .rodata;
# RUN: ld.lld -T matching.lds matching.o -o matching
# RUN: llvm-objdump -s matching |\
# RUN: FileCheck %s --check-prefix=MATCHING
# MATCHING: .rodata
# MATCHING-NEXT: 020301cc 0605 ......{{$}}
# MATCHING: .rodata.h
# MATCHING-NEXT: 08 .{{$}}
# MATCHING: .rodata.d
# MATCHING-NEXT: 04 .{{$}}
#--- already-defined.lds
## A section class has more than one description.
SECTIONS {
CLASS(a) { *(.rodata.a) }
CLASS(a) { *(.rodata.b) }
CLASS(b) { *(.rodata.c) }
CLASS(b) { *(.rodata.d) }
}
# RUN: not ld.lld -T already-defined.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=ALREADY-DEFINED --implicit-check-not=error:
# ALREADY-DEFINED: error: already-defined.lds:4: section class 'a' already defined
#--- missing-filename-pattern-1.lds
## A filename pattern is missing in a section class description.
SECTIONS {
CLASS(a) { (.rodata.a) }
}
#--- missing-filename-pattern-2.lds
## A filename pattern is missing in a section class description.
SECTIONS {
CLASS(a) { .rodata.a) }
}
# RUN: not ld.lld -T missing-filename-pattern-1.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=MISSING-FILENAME-PATTERN --implicit-check-not=error:
# RUN: not ld.lld -T missing-filename-pattern-2.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=MISSING-FILENAME-PATTERN --implicit-check-not=error:
# MISSING-FILENAME-PATTERN: error: missing-filename-pattern-{{[1-2]}}.lds:3: expected filename pattern
#--- multiple-class-names.lds
## More than one class is mentioned in a reference.
SECTIONS {
CLASS(a) { *(.rodata.a) }
CLASS(b) { *(.rodata.b) }
.rodata : { CLASS(a b) }
}
# RUN: not ld.lld -T multiple-class-names.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=MULTIPLE-CLASS-NAMES --implicit-check-not=error:
# MULTIPLE-CLASS-NAMES: error: multiple-class-names.lds:5: ) expected, but got b
#--- undefined.lds
## A section class is referenced but never defined
SECTIONS {
.rodata : { CLASS(a) }
}
# RUN: not ld.lld -T undefined.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=UNDEFINED --implicit-check-not=error:
# UNDEFINED: error: undefined section class 'a'
#--- referenced-before-defined.lds
## The content of section classes is demanded before its definition is processed.
SECTIONS {
.rodata : { CLASS(a) }
CLASS(a) { *(.rodata.a) }
}
# RUN: not ld.lld -T referenced-before-defined.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=REFERENCED-BEFORE-DEFINED
# RUN: ld.lld -T referenced-before-defined.lds matching.o -o out --noinhibit-exec 2>&1 | \
# RUN: FileCheck %s --check-prefix=REFERENCED-BEFORE-DEFINED-WARN
# REFERENCED-BEFORE-DEFINED: error: section class 'a' referenced by '.rodata' before class definition
# REFERENCED-BEFORE-DEFINED-WARN: warning: section class 'a' referenced by '.rodata' before class definition
#--- unreferenced.lds
## An input section is bound to a section class but is not referenced.
SECTIONS {
CLASS(a) { *(.rodata.*) }
}
# RUN: not ld.lld -T unreferenced.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=UNREFERENCED -implicit-check-not=error:
# RUN: ld.lld -T unreferenced.lds matching.o -o out --noinhibit-exec 2>&1 | \
# RUN: FileCheck %s --check-prefix=UNREFERENCED-WARN -implicit-check-not=error:
# UNREFERENCED: error: section class 'a' is unreferenced
# UNREFERENCED-WARN: warning: section class 'a' is unreferenced
#--- class-references-class.lds
## One section class references another.
SECTIONS {
CLASS(a) { *(.rodata.a) }
CLASS(b) { CLASS(a) }
}
# RUN: not ld.lld -T class-references-class.lds matching.o 2>&1 | \
# RUN: FileCheck %s --check-prefix=CLASS-REFERENCES-CLASS --implicit-check-not=error:
# CLASS-REFERENCES-CLASS: error: class-references-class.lds:4: section class 'b' references class 'a'
#--- spill.s
.section .one_byte_section,"a",@progbits
.fill 1
.section .two_byte_section,"a",@progbits
.fill 2
# RUN: llvm-mc -n -filetype=obj -triple=x86_64 spill.s -o spill.o
#--- spill.lds
## An input section in a class spills to a later class ref when the region of
## its first ref would overflow. The spill uses the alignment of the later ref.
MEMORY {
a : ORIGIN = 0, LENGTH = 2
b : ORIGIN = 2, LENGTH = 16
}
SECTIONS {
CLASS(c) { *(.two_byte_section) }
.first_chance : SUBALIGN(1) { *(.one_byte_section) CLASS(c) } >a
.last_chance : SUBALIGN(8) { CLASS (c) } >b
}
# RUN: ld.lld -T spill.lds spill.o -o spill
# RUN: llvm-readelf -S spill | FileCheck %s --check-prefix=SPILL
# SPILL: Name Type Address Off Size
# SPILL: .first_chance PROGBITS 0000000000000000 001000 000001
# SPILL-NEXT: .last_chance PROGBITS 0000000000000008 001008 000002
#--- spill-fail.lds
## A spill off the end still fails the link.
MEMORY {
a : ORIGIN = 0, LENGTH = 1
b : ORIGIN = 2, LENGTH = 0
}
SECTIONS {
CLASS(c) { *(.two_byte_section) }
.first_chance : { *(.one_byte_section) CLASS(c) } >a
.last_chance : { CLASS(c) } >b
}
# RUN: not ld.lld -T spill-fail.lds spill.o 2>&1 |\
# RUN: FileCheck %s --check-prefix=SPILL-FAIL --implicit-check-not=error:
# SPILL-FAIL: error: section '.last_chance' will not fit in region 'b': overflowed by 2 bytes
#--- spill-lma.lds
## The above spill still occurs when the LMA would overflow, even though the
## VMA would fit.
MEMORY {
vma_a : ORIGIN = 0, LENGTH = 3
vma_b : ORIGIN = 3, LENGTH = 3
lma_a : ORIGIN = 6, LENGTH = 2
lma_b : ORIGIN = 8, LENGTH = 2
}
SECTIONS {
CLASS(c) { *(.two_byte_section) }
.first_chance : { *(.one_byte_section) CLASS(c) } >vma_a AT>lma_a
.last_chance : { CLASS(c) } >vma_b AT>lma_b
}
# RUN: ld.lld -T spill-lma.lds spill.o -o spill-lma
# RUN: llvm-readelf -S spill-lma | FileCheck %s --check-prefix=SPILL-LMA
# SPILL-LMA: Name Type Address Off Size
# SPILL-LMA: .first_chance PROGBITS 0000000000000000 001000 000001
# SPILL-LMA-NEXT: .last_chance PROGBITS 0000000000000003 001003 000002
#--- spill-later.lds
## A spill occurs to an additional class ref after the first.
MEMORY {
a : ORIGIN = 0, LENGTH = 2
b : ORIGIN = 2, LENGTH = 1
c : ORIGIN = 3, LENGTH = 2
}
SECTIONS {
CLASS(c) { *(.two_byte_section) }
.first_chance : { *(.one_byte_section) CLASS(c) } >a
.second_chance : { CLASS(c) } >b
.last_chance : { CLASS(c) } >c
}
# RUN: ld.lld -T spill-later.lds spill.o -o spill-later
# RUN: llvm-readelf -S spill-later | FileCheck %s --check-prefix=SPILL-LATER
# SPILL-LATER: Name Type Address Off Size
# SPILL-LATER: .first_chance PROGBITS 0000000000000000 001000 000001
# SPILL-LATER-NEXT: .second_chance PROGBITS 0000000000000002 001001 000000
# SPILL-LATER-NEXT: .last_chance PROGBITS 0000000000000003 001003 000002
#--- spill-earlier.lds
## A later overflow causes an earlier section to spill.
MEMORY {
a : ORIGIN = 0, LENGTH = 2
b : ORIGIN = 2, LENGTH = 1
}
SECTIONS {
CLASS(c) { *(.one_byte_section) }
.first_chance : { CLASS(c) *(.two_byte_section) } >a
.last_chance : { CLASS(c) } >b
}
# RUN: ld.lld -T spill-earlier.lds spill.o -o spill-earlier
# RUN: llvm-readelf -S spill-earlier | FileCheck %s --check-prefix=SPILL-EARLIER
# SPILL-EARLIER: Name Type Address Off Size
# SPILL-EARLIER: .first_chance PROGBITS 0000000000000000 001000 000002
# SPILL-EARLIER-NEXT: .last_chance PROGBITS 0000000000000002 001002 000001
#--- enable-non-contiguous-regions.lds
## Class definitions do not preclude additional matches when used with
## --enable-non-contiguous-regions, and additional matches in class
## definitions become spills at class references.
MEMORY {
a : ORIGIN = 0, LENGTH = 1
b : ORIGIN = 1, LENGTH = 2
c : ORIGIN = 3, LENGTH = 1
}
SECTIONS {
.first_chance : { *(.two_byte_section) } >a
/* An additional match in a class defers a spill. */
CLASS(two) { *(.two_byte_section) }
/* A class references actualizes deferred spills. */
.last_chance : { CLASS(two) } >b
/* Section classes do not preclude other matches. */
CLASS(one) { *(.one_byte_section) }
.one_byte_section : { *(.one_byte_section) } >c
}
# RUN: ld.lld -T enable-non-contiguous-regions.lds spill.o -o enable-non-contiguous-regions --enable-non-contiguous-regions
# RUN: llvm-readelf -S enable-non-contiguous-regions | FileCheck %s --check-prefix=ENABLE-NON-CONTIGUOUS-REGIONS
# ENABLE-NON-CONTIGUOUS-REGIONS: Name Type Address Off Size
# ENABLE-NON-CONTIGUOUS-REGIONS: .first_chance PROGBITS 0000000000000000 000190 000000
# ENABLE-NON-CONTIGUOUS-REGIONS-NEXT: .last_chance PROGBITS 0000000000000001 001001 000002
# ENABLE-NON-CONTIGUOUS-REGIONS-NEXT: .one_byte_section PROGBITS 0000000000000003 001003 000001
#--- merge.s
.section .a,"aM",@progbits,1
.byte 0x12, 0x34
.section .b,"aM",@progbits,1
.byte 0x12
# RUN: llvm-mc -n -filetype=obj -triple=x86_64 merge.s -o merge.o
#--- spill-merge.lds
## SHF_MERGE sections are spilled according to the class refs of the first
## merged input section (the one giving the resulting section its name).
MEMORY {
a : ORIGIN = 0, LENGTH = 1
b : ORIGIN = 1, LENGTH = 2
c : ORIGIN = 3, LENGTH = 2
}
SECTIONS {
CLASS(a) { *(.a) }
CLASS(b) { *(.b) }
.first : { CLASS(a) CLASS(b) } >a
.second : { CLASS(a) } >b
.third : { CLASS(b) } >c
}
# RUN: ld.lld -T spill-merge.lds merge.o -o spill-merge
# RUN: llvm-readelf -S spill-merge | FileCheck %s --check-prefix=SPILL-MERGE
# SPILL-MERGE: Name Type Address Off Size
# SPILL-MERGE: .first PROGBITS 0000000000000000 000190 000000
# SPILL-MERGE-NEXT: .second PROGBITS 0000000000000001 001001 000002
# SPILL-MERGE-NEXT: .third PROGBITS 0000000000000003 001003 000000
#--- link-order.s
.section .a,"a",@progbits
.fill 1
.section .b,"a",@progbits
.fill 1
.section .c,"a",@progbits
.fill 1
.section .link_order.a,"ao",@progbits,.a
.byte 1
.section .link_order.b,"ao",@progbits,.b
.byte 2
.section .link_order.c,"ao",@progbits,.c
.byte 3
# RUN: llvm-mc -n -filetype=obj -triple=x86_64 link-order.s -o link-order.o
#--- link-order.lds
## SHF_LINK_ORDER is reordered when spilling changes relative section order.
MEMORY {
order : ORIGIN = 0, LENGTH = 3
potential_a : ORIGIN = 3, LENGTH = 0
bc : ORIGIN = 3, LENGTH = 2
actual_a : ORIGIN = 5, LENGTH = 1
}
SECTIONS {
CLASS(a) { *(.a) }
.order : { *(.link_order.*) } > order
.potential_a : { CLASS(a) } >potential_a
.bc : { *(.b) *(.c) } >bc
.actual_a : { CLASS(a) } >actual_a
}
# RUN: ld.lld -T link-order.lds link-order.o -o link-order
# RUN: llvm-objdump -s link-order | FileCheck %s --check-prefix=LINK-ORDER
# LINK-ORDER: 020301 ...{{$}}
#--- from-insert.lds
## A section might spill from INSERT.
SECTIONS {
CLASS(class) { *(.two_byte_section) }
.a : { *(.one_byte_section) }
}
SECTIONS { .b : { CLASS(class) } } INSERT AFTER .a;
SECTIONS { .c : { CLASS(class) } }
# RUN: not ld.lld -T from-insert.lds spill.o 2>&1 |\
# RUN: FileCheck %s --check-prefix=FROM-INSERT
# RUN: ld.lld -T from-insert.lds spill.o -o out --noinhibit-exec 2>&1 |\
# RUN: FileCheck %s --check-prefix=FROM-INSERT-WARN
# FROM-INSERT: error: section '.two_byte_section' cannot spill from/to INSERT section '.b'
# FROM-INSERT-WARN: warning: section '.two_byte_section' cannot spill from/to INSERT section '.b'
#--- to-insert.lds
## A section might spill to INSERT.
SECTIONS {
CLASS(class) { *(.two_byte_section) }
.a : { CLASS(class) *(.one_byte_section) }
}
SECTIONS { .b : { CLASS(class) } } INSERT AFTER .a;
# RUN: not ld.lld -T to-insert.lds spill.o 2>&1 |\
# RUN: FileCheck %s --check-prefix=TO-INSERT
# RUN: ld.lld -T to-insert.lds spill.o -o out --noinhibit-exec 2>&1 |\
# RUN: FileCheck %s --check-prefix=TO-INSERT-WARN
# TO-INSERT: error: section '.two_byte_section' cannot spill from/to INSERT section '.b'
# TO-INSERT-WARN: warning: section '.two_byte_section' cannot spill from/to INSERT section '.b'
#--- from-discard.lds
## A section might spill from /DISCARD/.
SECTIONS {
CLASS(class) { *(.two_byte_section) }
/DISCARD/ : { CLASS(class) }
.c : { CLASS(class) }
}
# RUN: not ld.lld -T from-discard.lds spill.o 2>&1 |\
# RUN: FileCheck %s --check-prefix=FROM-DISCARD
# RUN: ld.lld -T from-discard.lds spill.o -o out --noinhibit-exec 2>&1 |\
# RUN: FileCheck %s --check-prefix=FROM-DISCARD-WARN
# FROM-DISCARD: error: section '.two_byte_section' cannot spill from/to /DISCARD/
# FROM-DISCARD-WARN: warning: section '.two_byte_section' cannot spill from/to /DISCARD/
#--- to-discard.lds
## A section might spill to /DISCARD/.
SECTIONS {
CLASS(class) { *(.two_byte_section) }
.a : { CLASS(class) }
/DISCARD/ : { CLASS(class) }
}
# RUN: not ld.lld -T to-discard.lds spill.o 2>&1 |\
# RUN: FileCheck %s --check-prefix=TO-DISCARD
# RUN: ld.lld -T to-discard.lds spill.o -o out --noinhibit-exec 2>&1 |\
# RUN: FileCheck %s --check-prefix=TO-DISCARD-WARN
# TO-DISCARD: error: section '.two_byte_section' cannot spill from/to /DISCARD/
# TO-DISCARD-WARN: warning: section '.two_byte_section' cannot spill from/to /DISCARD/