[lld] 7e8a902 - [LLD] Add CLASS syntax to SECTIONS (#95323)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 5 13:06:49 PDT 2024
Author: Daniel Thornburgh
Date: 2024-08-05T13:06:45-07:00
New Revision: 7e8a9020b1ae3dea28c19f0cd68743482dca13d9
URL: https://github.com/llvm/llvm-project/commit/7e8a9020b1ae3dea28c19f0cd68743482dca13d9
DIFF: https://github.com/llvm/llvm-project/commit/7e8a9020b1ae3dea28c19f0cd68743482dca13d9.diff
LOG: [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.
Added:
lld/test/ELF/linkerscript/section-class.test
Modified:
lld/ELF/InputSection.cpp
lld/ELF/InputSection.h
lld/ELF/LinkerScript.cpp
lld/ELF/LinkerScript.h
lld/ELF/MapFile.cpp
lld/ELF/OutputSections.h
lld/ELF/ScriptParser.cpp
lld/docs/ELF/linker_script.rst
lld/docs/ReleaseNotes.rst
Removed:
################################################################################
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index da4c90516ec30..cf34a814fed41 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -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:
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 6659530a9c9c2..e3b7af13d066d 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -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 @@ class InputSectionBase : public SectionBase {
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
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 577731164076b..0c4ba1abb4778 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -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();
-
- 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 --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)
+ 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 (const SectionPattern &pat : cmd->sectionPatterns) {
+ size_t sizeBeforeCurrPat = ret.size();
+
+ 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
diff erent 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
diff erent 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;
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index b86521a429f04..90090ce16de54 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -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 @@ class InputSectionDescription : public SectionCommand {
// 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 @@ class LinkerScript final {
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 {
diff --git a/lld/ELF/MapFile.cpp b/lld/ELF/MapFile.cpp
index c4f3fdde30f36..1bad529b40329 100644
--- a/lld/ELF/MapFile.cpp
+++ b/lld/ELF/MapFile.cpp
@@ -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);
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index bf48f808fdbcc..909b284a75a2e 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -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);
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 107b4c6e7af06..bdbce396cba1f 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -87,6 +87,8 @@ class ScriptParser final : ScriptLexer {
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;
diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst
index 7a35534be096c..c9cb47fc0553e 100644
--- a/lld/docs/ELF/linker_script.rst
+++ b/lld/docs/ELF/linker_script.rst
@@ -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.
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index e9d3c12b76545..6d09de10e7195 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -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
----------------
diff --git a/lld/test/ELF/linkerscript/section-class.test b/lld/test/ELF/linkerscript/section-class.test
new file mode 100644
index 0000000000000..7fce13bfe3e02
--- /dev/null
+++ b/lld/test/ELF/linkerscript/section-class.test
@@ -0,0 +1,448 @@
+# REQUIRES: x86
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+#--- matching.s
+.section .rodata.a,"a", at progbits
+.byte 1
+
+.section .rodata.b,"a", at progbits
+.byte 2
+
+.section .rodata.c,"ax", at progbits
+.byte 3
+
+.section .rodata.d,"a", at progbits
+.byte 4
+
+.section .rodata.e,"a", at progbits
+.byte 5
+
+.section .rodata.f,"a", at progbits
+.balign 2
+.byte 6
+
+.section .rodata.g,"a", at progbits
+.byte 7
+
+.section .rodata.h,"a", at 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
diff erent 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", at progbits
+.fill 1
+
+.section .two_byte_section,"a", at 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", at progbits,1
+.byte 0x12, 0x34
+
+.section .b,"aM", at 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", at progbits
+.fill 1
+
+.section .b,"a", at progbits
+.fill 1
+
+.section .c,"a", at progbits
+.fill 1
+
+.section .link_order.a,"ao", at progbits,.a
+.byte 1
+
+.section .link_order.b,"ao", at progbits,.b
+.byte 2
+
+.section .link_order.c,"ao", at 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/
More information about the llvm-commits
mailing list