Skip to content

Commit 7e8a902

Browse files
authored
[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.
1 parent 88d2884 commit 7e8a902

10 files changed

+780
-105
lines changed

lld/ELF/InputSection.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ uint64_t SectionBase::getOffset(uint64_t offset) const {
194194
// For output sections we treat offset -1 as the end of the section.
195195
return offset == uint64_t(-1) ? os->size : offset;
196196
}
197+
case Class:
198+
llvm_unreachable("section classes do not have offsets");
197199
case Regular:
198200
case Synthetic:
199201
case Spill:

lld/ELF/InputSection.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ template <class ELFT> struct RelsOrRelas {
6161
// sections.
6262
class SectionBase {
6363
public:
64-
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output };
64+
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output, Class };
6565

6666
Kind kind() const { return (Kind)sectionKind; }
6767

@@ -148,7 +148,9 @@ class InputSectionBase : public SectionBase {
148148
uint32_t addralign, ArrayRef<uint8_t> data, StringRef name,
149149
Kind sectionKind);
150150

151-
static bool classof(const SectionBase *s) { return s->kind() != Output; }
151+
static bool classof(const SectionBase *s) {
152+
return s->kind() != Output && s->kind() != Class;
153+
}
152154

153155
// The file which contains this section. Its dynamic type is usually
154156
// ObjFile<ELFT>, but may be an InputFile of InternalKind (for a synthetic

lld/ELF/LinkerScript.cpp

Lines changed: 182 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ getSymbolAssignmentValues(ArrayRef<SectionCommand *> sectionCommands) {
277277
assign->sym->value));
278278
continue;
279279
}
280+
if (isa<SectionClassDesc>(cmd))
281+
continue;
280282
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
281283
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
282284
if (assign->sym)
@@ -348,6 +350,8 @@ void LinkerScript::declareSymbols() {
348350
declareSymbol(assign);
349351
continue;
350352
}
353+
if (isa<SectionClassDesc>(cmd))
354+
continue;
351355

352356
// If the output section directive has constraints,
353357
// 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,
491495
SmallVector<InputSectionBase *, 0>
492496
LinkerScript::computeInputSections(const InputSectionDescription *cmd,
493497
ArrayRef<InputSectionBase *> sections,
494-
const OutputSection &outCmd) {
498+
const SectionBase &outCmd) {
495499
SmallVector<InputSectionBase *, 0> ret;
496-
SmallVector<size_t, 0> indexes;
497-
DenseSet<size_t> seen;
498500
DenseSet<InputSectionBase *> spills;
499-
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
500-
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
501-
for (size_t i = begin; i != end; ++i)
502-
ret[i] = sections[indexes[i]];
503-
sortInputSections(
504-
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
505-
config->sortSection, SortSectionPolicy::None);
501+
502+
// Returns whether an input section's flags match the input section
503+
// description's specifiers.
504+
auto flagsMatch = [cmd](InputSectionBase *sec) {
505+
return (sec->flags & cmd->withFlags) == cmd->withFlags &&
506+
(sec->flags & cmd->withoutFlags) == 0;
506507
};
507508

508509
// Collects all sections that satisfy constraints of Cmd.
509-
size_t sizeAfterPrevSort = 0;
510-
for (const SectionPattern &pat : cmd->sectionPatterns) {
511-
size_t sizeBeforeCurrPat = ret.size();
512-
513-
for (size_t i = 0, e = sections.size(); i != e; ++i) {
514-
// Skip if the section is dead or has been matched by a previous pattern
515-
// in this input section description.
516-
InputSectionBase *sec = sections[i];
517-
if (!sec->isLive() || seen.contains(i))
518-
continue;
519-
520-
// For --emit-relocs we have to ignore entries like
521-
// .rela.dyn : { *(.rela.data) }
522-
// which are common because they are in the default bfd script.
523-
// We do not ignore SHT_REL[A] linker-synthesized sections here because
524-
// want to support scripts that do custom layout for them.
525-
if (isa<InputSection>(sec) &&
526-
cast<InputSection>(sec)->getRelocatedSection())
527-
continue;
528-
529-
// Check the name early to improve performance in the common case.
530-
if (!pat.sectionPat.match(sec->name))
531-
continue;
532-
533-
if (!cmd->matchesFile(sec->file) || pat.excludesFile(sec->file) ||
534-
(sec->flags & cmd->withFlags) != cmd->withFlags ||
535-
(sec->flags & cmd->withoutFlags) != 0)
536-
continue;
537-
538-
if (sec->parent) {
539-
// Skip if not allowing multiple matches.
540-
if (!config->enableNonContiguousRegions)
510+
if (cmd->classRef.empty()) {
511+
DenseSet<size_t> seen;
512+
size_t sizeAfterPrevSort = 0;
513+
SmallVector<size_t, 0> indexes;
514+
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
515+
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
516+
for (size_t i = begin; i != end; ++i)
517+
ret[i] = sections[indexes[i]];
518+
sortInputSections(
519+
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
520+
config->sortSection, SortSectionPolicy::None);
521+
};
522+
523+
for (const SectionPattern &pat : cmd->sectionPatterns) {
524+
size_t sizeBeforeCurrPat = ret.size();
525+
526+
for (size_t i = 0, e = sections.size(); i != e; ++i) {
527+
// Skip if the section is dead or has been matched by a previous pattern
528+
// in this input section description.
529+
InputSectionBase *sec = sections[i];
530+
if (!sec->isLive() || seen.contains(i))
541531
continue;
542532

543-
// Disallow spilling into /DISCARD/; special handling would be needed
544-
// for this in address assignment, and the semantics are nebulous.
545-
if (outCmd.name == "/DISCARD/")
533+
// For --emit-relocs we have to ignore entries like
534+
// .rela.dyn : { *(.rela.data) }
535+
// which are common because they are in the default bfd script.
536+
// We do not ignore SHT_REL[A] linker-synthesized sections here because
537+
// want to support scripts that do custom layout for them.
538+
if (isa<InputSection>(sec) &&
539+
cast<InputSection>(sec)->getRelocatedSection())
546540
continue;
547541

548-
// Skip if the section's first match was /DISCARD/; such sections are
549-
// always discarded.
550-
if (sec->parent->name == "/DISCARD/")
542+
// Check the name early to improve performance in the common case.
543+
if (!pat.sectionPat.match(sec->name))
551544
continue;
552545

553-
// Skip if the section was already matched by a different input section
554-
// description within this output section.
555-
if (sec->parent == &outCmd)
546+
if (!cmd->matchesFile(sec->file) || pat.excludesFile(sec->file) ||
547+
sec->parent == &outCmd || !flagsMatch(sec))
556548
continue;
557549

558-
spills.insert(sec);
550+
if (sec->parent) {
551+
// Skip if not allowing multiple matches.
552+
if (!config->enableNonContiguousRegions)
553+
continue;
554+
555+
// Disallow spilling into /DISCARD/; special handling would be needed
556+
// for this in address assignment, and the semantics are nebulous.
557+
if (outCmd.name == "/DISCARD/")
558+
continue;
559+
560+
// Class definitions cannot contain spills, nor can a class definition
561+
// generate a spill in a subsequent match. Those behaviors belong to
562+
// class references and additional matches.
563+
if (!isa<SectionClass>(outCmd) && !isa<SectionClass>(sec->parent))
564+
spills.insert(sec);
565+
}
566+
567+
ret.push_back(sec);
568+
indexes.push_back(i);
569+
seen.insert(i);
559570
}
560571

561-
ret.push_back(sec);
562-
indexes.push_back(i);
563-
seen.insert(i);
572+
if (pat.sortOuter == SortSectionPolicy::Default)
573+
continue;
574+
575+
// Matched sections are ordered by radix sort with the keys being (SORT*,
576+
// --sort-section, input order), where SORT* (if present) is most
577+
// significant.
578+
//
579+
// Matched sections between the previous SORT* and this SORT* are sorted
580+
// by (--sort-alignment, input order).
581+
sortByPositionThenCommandLine(sizeAfterPrevSort, sizeBeforeCurrPat);
582+
// Matched sections by this SORT* pattern are sorted using all 3 keys.
583+
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
584+
// just sort by sortOuter and sortInner.
585+
sortInputSections(
586+
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
587+
pat.sortOuter, pat.sortInner);
588+
sizeAfterPrevSort = ret.size();
564589
}
565590

566-
if (pat.sortOuter == SortSectionPolicy::Default)
567-
continue;
591+
// Matched sections after the last SORT* are sorted by (--sort-alignment,
592+
// input order).
593+
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
594+
} else {
595+
SectionClassDesc *scd =
596+
script->sectionClasses.lookup(CachedHashStringRef(cmd->classRef));
597+
if (!scd) {
598+
errorOrWarn("undefined section class '" + cmd->classRef + "'");
599+
return ret;
600+
}
601+
if (!scd->sc.assigned) {
602+
errorOrWarn("section class '" + cmd->classRef + "' referenced by '" +
603+
outCmd.name + "' before class definition");
604+
return ret;
605+
}
568606

569-
// Matched sections are ordered by radix sort with the keys being (SORT*,
570-
// --sort-section, input order), where SORT* (if present) is most
571-
// significant.
572-
//
573-
// Matched sections between the previous SORT* and this SORT* are sorted by
574-
// (--sort-alignment, input order).
575-
sortByPositionThenCommandLine(sizeAfterPrevSort, sizeBeforeCurrPat);
576-
// Matched sections by this SORT* pattern are sorted using all 3 keys.
577-
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
578-
// just sort by sortOuter and sortInner.
579-
sortInputSections(
580-
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
581-
pat.sortOuter, pat.sortInner);
582-
sizeAfterPrevSort = ret.size();
607+
for (InputSectionDescription *isd : scd->sc.commands) {
608+
for (InputSectionBase *sec : isd->sectionBases) {
609+
if (sec->parent == &outCmd || !flagsMatch(sec))
610+
continue;
611+
bool isSpill = sec->parent && isa<OutputSection>(sec->parent);
612+
if (!sec->parent || (isSpill && outCmd.name == "/DISCARD/")) {
613+
errorOrWarn("section '" + sec->name +
614+
"' cannot spill from/to /DISCARD/");
615+
continue;
616+
}
617+
if (isSpill)
618+
spills.insert(sec);
619+
ret.push_back(sec);
620+
}
621+
}
583622
}
584-
// Matched sections after the last SORT* are sorted by (--sort-alignment,
585-
// input order).
586-
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
587-
588-
// The flag --enable-non-contiguous-regions may cause sections to match an
589-
// InputSectionDescription in more than one OutputSection. Matches after the
590-
// first were collected in the spills set, so replace these with potential
591-
// spill sections.
623+
624+
// The flag --enable-non-contiguous-regions or the section CLASS syntax may
625+
// cause sections to match an InputSectionDescription in more than one
626+
// OutputSection. Matches after the first were collected in the spills set, so
627+
// replace these with potential spill sections.
592628
if (!spills.empty()) {
593629
for (InputSectionBase *&sec : ret) {
594630
if (!spills.contains(sec))
@@ -708,7 +744,7 @@ void LinkerScript::processSectionCommands() {
708744
!map.try_emplace(CachedHashStringRef(osec->name), osd).second)
709745
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
710746
}
711-
for (SectionCommand *&base : sectionCommands)
747+
for (SectionCommand *&base : sectionCommands) {
712748
if (auto *osd = dyn_cast<OutputDesc>(base)) {
713749
OutputSection *osec = &osd->osec;
714750
if (OutputDesc *overwrite = map.lookup(CachedHashStringRef(osec->name))) {
@@ -718,14 +754,72 @@ void LinkerScript::processSectionCommands() {
718754
} else if (process(osec)) {
719755
osec->sectionIndex = i++;
720756
}
757+
} else if (auto *sc = dyn_cast<SectionClassDesc>(base)) {
758+
for (InputSectionDescription *isd : sc->sc.commands) {
759+
isd->sectionBases =
760+
computeInputSections(isd, ctx.inputSections, sc->sc);
761+
for (InputSectionBase *s : isd->sectionBases) {
762+
// A section class containing a section with different parent isn't
763+
// necessarily an error due to --enable-non-contiguous-regions. Such
764+
// sections all become potential spills when the class is referenced.
765+
if (!s->parent)
766+
s->parent = &sc->sc;
767+
}
768+
}
769+
sc->sc.assigned = true;
770+
}
771+
}
772+
773+
// Check that input sections cannot spill into or out of INSERT,
774+
// since the semantics are nebulous. This is also true for OVERWRITE_SECTIONS,
775+
// but no check is needed, since the order of processing ensures they cannot
776+
// legally reference classes.
777+
if (!potentialSpillLists.empty()) {
778+
DenseSet<StringRef> insertNames;
779+
for (InsertCommand &ic : insertCommands)
780+
insertNames.insert(ic.names.begin(), ic.names.end());
781+
for (SectionCommand *&base : sectionCommands) {
782+
auto *osd = dyn_cast<OutputDesc>(base);
783+
if (!osd)
784+
continue;
785+
OutputSection *os = &osd->osec;
786+
if (!insertNames.contains(os->name))
787+
continue;
788+
for (SectionCommand *sc : os->commands) {
789+
auto *isd = dyn_cast<InputSectionDescription>(sc);
790+
if (!isd)
791+
continue;
792+
for (InputSectionBase *isec : isd->sectionBases)
793+
if (isa<PotentialSpillSection>(isec) ||
794+
potentialSpillLists.contains(isec))
795+
errorOrWarn("section '" + isec->name +
796+
"' cannot spill from/to INSERT section '" + os->name +
797+
"'");
798+
}
721799
}
800+
}
722801

723802
// If an OVERWRITE_SECTIONS specified output section is not in
724803
// sectionCommands, append it to the end. The section will be inserted by
725804
// orphan placement.
726805
for (OutputDesc *osd : overwriteSections)
727806
if (osd->osec.partition == 1 && osd->osec.sectionIndex == UINT32_MAX)
728807
sectionCommands.push_back(osd);
808+
809+
// Input sections cannot have a section class parent past this point; they
810+
// must have been assigned to an output section.
811+
for (const auto &[_, sc] : sectionClasses) {
812+
for (InputSectionDescription *isd : sc->sc.commands) {
813+
for (InputSectionBase *sec : isd->sectionBases) {
814+
if (sec->parent && isa<SectionClass>(sec->parent)) {
815+
errorOrWarn("section class '" + sec->parent->name +
816+
"' is unreferenced");
817+
goto nextClass;
818+
}
819+
}
820+
}
821+
nextClass:;
822+
}
729823
}
730824

731825
void LinkerScript::processSymbolAssignments() {
@@ -746,8 +840,8 @@ void LinkerScript::processSymbolAssignments() {
746840
for (SectionCommand *cmd : sectionCommands) {
747841
if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
748842
addSymbol(assign);
749-
else
750-
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
843+
else if (auto *osd = dyn_cast<OutputDesc>(cmd))
844+
for (SectionCommand *subCmd : osd->osec.commands)
751845
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
752846
addSymbol(assign);
753847
}
@@ -1417,6 +1511,8 @@ LinkerScript::assignAddresses() {
14171511
assign->size = dot - assign->addr;
14181512
continue;
14191513
}
1514+
if (isa<SectionClassDesc>(cmd))
1515+
continue;
14201516
if (assignOffsets(&cast<OutputDesc>(cmd)->osec) && !changedOsec)
14211517
changedOsec = &cast<OutputDesc>(cmd)->osec;
14221518
}
@@ -1437,15 +1533,15 @@ static bool hasRegionOverflowed(MemoryRegion *mr) {
14371533
// Under-estimates may cause unnecessary spills, but over-estimates can always
14381534
// be corrected on the next pass.
14391535
bool LinkerScript::spillSections() {
1440-
if (!config->enableNonContiguousRegions)
1536+
if (potentialSpillLists.empty())
14411537
return false;
14421538

14431539
bool spilled = false;
14441540
for (SectionCommand *cmd : reverse(sectionCommands)) {
1445-
auto *od = dyn_cast<OutputDesc>(cmd);
1446-
if (!od)
1541+
auto *osd = dyn_cast<OutputDesc>(cmd);
1542+
if (!osd)
14471543
continue;
1448-
OutputSection *osec = &od->osec;
1544+
OutputSection *osec = &osd->osec;
14491545
if (!osec->memRegion)
14501546
continue;
14511547

0 commit comments

Comments
 (0)