Skip to content

Commit 2008e1b

Browse files
committed
[LLD] Add CLASS syntax to SECTIONS
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 c9d5800 commit 2008e1b

File tree

10 files changed

+693
-97
lines changed

10 files changed

+693
-97
lines changed

lld/ELF/InputSection.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ uint64_t SectionBase::getOffset(uint64_t offset) const {
159159
// For output sections we treat offset -1 as the end of the section.
160160
return offset == uint64_t(-1) ? os->size : offset;
161161
}
162+
case Class:
163+
llvm_unreachable("section classes do not have offsets");
162164
case Regular:
163165
case Synthetic:
164166
case Spill:

lld/ELF/InputSection.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ template <class ELFT> struct RelsOrRelas {
4848
// sections.
4949
class SectionBase {
5050
public:
51-
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output };
51+
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output, Class };
5252

5353
Kind kind() const { return (Kind)sectionKind; }
5454

@@ -132,7 +132,9 @@ class InputSectionBase : public SectionBase {
132132
uint32_t addralign, ArrayRef<uint8_t> data, StringRef name,
133133
Kind sectionKind);
134134

135-
static bool classof(const SectionBase *s) { return s->kind() != Output; }
135+
static bool classof(const SectionBase *s) {
136+
return s->kind() != Output && s->kind() != Class;
137+
}
136138

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

lld/ELF/LinkerScript.cpp

Lines changed: 161 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ getSymbolAssignmentValues(ArrayRef<SectionCommand *> sectionCommands) {
276276
assign->sym->value));
277277
continue;
278278
}
279+
if (isa<SectionClassDesc>(cmd))
280+
continue;
279281
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
280282
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
281283
if (assign->sym)
@@ -347,6 +349,8 @@ void LinkerScript::declareSymbols() {
347349
declareSymbol(assign);
348350
continue;
349351
}
352+
if (isa<SectionClassDesc>(cmd))
353+
continue;
350354

351355
// If the output section directive has constraints,
352356
// we can't say for sure if it is going to be included or not.
@@ -490,99 +494,130 @@ static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
490494
SmallVector<InputSectionBase *, 0>
491495
LinkerScript::computeInputSections(const InputSectionDescription *cmd,
492496
ArrayRef<InputSectionBase *> sections,
493-
const OutputSection &outCmd) {
497+
const SectionBase &outCmd) {
494498
SmallVector<InputSectionBase *, 0> ret;
495-
SmallVector<size_t, 0> indexes;
496-
DenseSet<size_t> seen;
497499
DenseSet<InputSectionBase *> spills;
498-
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
499-
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
500-
for (size_t i = begin; i != end; ++i)
501-
ret[i] = sections[indexes[i]];
502-
sortInputSections(
503-
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
504-
config->sortSection, SortSectionPolicy::None);
500+
501+
const auto flagsMatch = [cmd](InputSectionBase *sec) {
502+
return (sec->flags & cmd->withFlags) == cmd->withFlags &&
503+
(sec->flags & cmd->withoutFlags) == 0;
505504
};
506505

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

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

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

552-
// Skip if the section was already matched by a different input section
553-
// description within this output section.
554-
if (sec->parent == &outCmd)
543+
if (!cmd->matchesFile(sec->file) || pat.excludesFile(sec->file) ||
544+
!flagsMatch(sec))
555545
continue;
556546

557-
spills.insert(sec);
547+
if (sec->parent) {
548+
// Skip if not allowing multiple matches.
549+
if (!config->enableNonContiguousRegions)
550+
continue;
551+
552+
// Disallow spilling out of or into section classes; that's already a
553+
// mechanism for spilling.
554+
if (isa<SectionClass>(sec->parent) || isa<SectionClass>(outCmd))
555+
continue;
556+
557+
// Disallow spilling into /DISCARD/; special handling would be needed
558+
// for this in address assignment, and the semantics are nebulous.
559+
if (outCmd.name == "/DISCARD/")
560+
continue;
561+
562+
// Skip if the section was already matched by a different input
563+
// section description within this output section or class.
564+
if (sec->parent == &outCmd)
565+
continue;
566+
567+
spills.insert(sec);
568+
}
569+
570+
ret.push_back(sec);
571+
indexes.push_back(i);
572+
seen.insert(i);
558573
}
559574

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

565-
if (pat.sortOuter == SortSectionPolicy::Default)
566-
continue;
594+
// Matched sections after the last SORT* are sorted by (--sort-alignment,
595+
// input order).
596+
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
597+
} else {
598+
SectionClassDesc *scd = script->sectionClasses.lookup(cmd->className);
599+
if (!scd) {
600+
error("undefined section class '" + cmd->className + "'");
601+
return ret;
602+
}
603+
if (!scd->sc.assigned) {
604+
error("section class '" + cmd->className + "' used before assigned");
605+
return ret;
606+
}
567607

568-
// Matched sections are ordered by radix sort with the keys being (SORT*,
569-
// --sort-section, input order), where SORT* (if present) is most
570-
// significant.
571-
//
572-
// Matched sections between the previous SORT* and this SORT* are sorted by
573-
// (--sort-alignment, input order).
574-
sortByPositionThenCommandLine(sizeAfterPrevSort, sizeBeforeCurrPat);
575-
// Matched sections by this SORT* pattern are sorted using all 3 keys.
576-
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
577-
// just sort by sortOuter and sortInner.
578-
sortInputSections(
579-
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
580-
pat.sortOuter, pat.sortInner);
581-
sizeAfterPrevSort = ret.size();
608+
for (InputSectionDescription *isd : scd->sc.commands) {
609+
for (InputSectionBase *sec : isd->sectionBases) {
610+
if (sec->parent == &outCmd || !flagsMatch(sec))
611+
continue;
612+
bool isSpill = sec->parent && isa<OutputSection>(sec->parent);
613+
if (!sec->parent || (isSpill && outCmd.name == "/DISCARD/"))
614+
error("section '" + sec->name + "' cannot spill from/to /DISCARD/");
615+
if (isSpill)
616+
spills.insert(sec);
617+
ret.push_back(sec);
618+
}
619+
}
582620
}
583-
// Matched sections after the last SORT* are sorted by (--sort-alignment,
584-
// input order).
585-
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
586621

587622
// The flag --enable-non-contiguous-regions may cause sections to match an
588623
// InputSectionDescription in more than one OutputSection. Matches after the
@@ -707,7 +742,7 @@ void LinkerScript::processSectionCommands() {
707742
!map.try_emplace(CachedHashStringRef(osec->name), osd).second)
708743
warn("OVERWRITE_SECTIONS specifies duplicate " + osec->name);
709744
}
710-
for (SectionCommand *&base : sectionCommands)
745+
for (SectionCommand *&base : sectionCommands) {
711746
if (auto *osd = dyn_cast<OutputDesc>(base)) {
712747
OutputSection *osec = &osd->osec;
713748
if (OutputDesc *overwrite = map.lookup(CachedHashStringRef(osec->name))) {
@@ -717,14 +752,60 @@ void LinkerScript::processSectionCommands() {
717752
} else if (process(osec)) {
718753
osec->sectionIndex = i++;
719754
}
755+
} else if (auto *sc = dyn_cast<SectionClassDesc>(base)) {
756+
for (InputSectionDescription *isd : sc->sc.commands) {
757+
isd->sectionBases =
758+
computeInputSections(isd, ctx.inputSections, sc->sc);
759+
for (InputSectionBase *s : isd->sectionBases)
760+
s->parent = &sc->sc;
761+
}
762+
sc->sc.assigned = true;
720763
}
764+
}
765+
766+
// Check that input sections cannot spill into or out of INSERT,
767+
// since the semantics are nebulous. This is also true for OVERWRITE_SECTIONS,
768+
// but no check is needed, since the order of processing ensures they cannot
769+
// legally reference classes.
770+
if (!potentialSpillLists.empty()) {
771+
DenseSet<StringRef> insertNames;
772+
for (InsertCommand &ic : insertCommands)
773+
insertNames.insert(ic.names.begin(), ic.names.end());
774+
for (SectionCommand *&base : sectionCommands) {
775+
auto *osd = dyn_cast<OutputDesc>(base);
776+
if (!osd)
777+
continue;
778+
OutputSection *os = &osd->osec;
779+
if (!insertNames.contains(os->name))
780+
continue;
781+
for (SectionCommand *sc : os->commands) {
782+
auto *isd = dyn_cast<InputSectionDescription>(sc);
783+
if (!isd)
784+
continue;
785+
for (InputSectionBase *isec : isd->sectionBases)
786+
if (isa<PotentialSpillSection>(isec) ||
787+
potentialSpillLists.contains(isec))
788+
error("section '" + isec->name +
789+
"' cannot spill from/to INSERT section '" + os->name + "'");
790+
}
791+
}
792+
}
721793

722794
// If an OVERWRITE_SECTIONS specified output section is not in
723795
// sectionCommands, append it to the end. The section will be inserted by
724796
// orphan placement.
725797
for (OutputDesc *osd : overwriteSections)
726798
if (osd->osec.partition == 1 && osd->osec.sectionIndex == UINT32_MAX)
727799
sectionCommands.push_back(osd);
800+
801+
// Input sections cannot have a section class parent past this point; they
802+
// must have been assigned to an output section.
803+
for (const auto &[_, sc] : sectionClasses)
804+
for (InputSectionDescription *isd : sc->sc.commands)
805+
for (InputSectionBase *sec : isd->sectionBases)
806+
if (sec->parent && isa<SectionClass>(sec->parent))
807+
error("section '" + sec->name + "' assigned to class '" +
808+
sec->parent->name + "' but to no output section");
728809
}
729810

730811
void LinkerScript::processSymbolAssignments() {
@@ -745,8 +826,8 @@ void LinkerScript::processSymbolAssignments() {
745826
for (SectionCommand *cmd : sectionCommands) {
746827
if (auto *assign = dyn_cast<SymbolAssignment>(cmd))
747828
addSymbol(assign);
748-
else
749-
for (SectionCommand *subCmd : cast<OutputDesc>(cmd)->osec.commands)
829+
else if (auto *od = dyn_cast<OutputDesc>(cmd))
830+
for (SectionCommand *subCmd : od->osec.commands)
750831
if (auto *assign = dyn_cast<SymbolAssignment>(subCmd))
751832
addSymbol(assign);
752833
}
@@ -1421,6 +1502,8 @@ LinkerScript::assignAddresses() {
14211502
assign->size = dot - assign->addr;
14221503
continue;
14231504
}
1505+
if (isa<SectionClassDesc>(cmd))
1506+
continue;
14241507
if (assignOffsets(&cast<OutputDesc>(cmd)->osec) && !changedOsec)
14251508
changedOsec = &cast<OutputDesc>(cmd)->osec;
14261509
}
@@ -1441,7 +1524,7 @@ static bool hasRegionOverflowed(MemoryRegion *mr) {
14411524
// Under-estimates may cause unnecessary spills, but over-estimates can always
14421525
// be corrected on the next pass.
14431526
bool LinkerScript::spillSections() {
1444-
if (!config->enableNonContiguousRegions)
1527+
if (potentialSpillLists.empty())
14451528
return false;
14461529

14471530
bool spilled = false;

0 commit comments

Comments
 (0)