Skip to content

[LLD] Add CLASS syntax to SECTIONS #95323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions lld/ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down Expand Up @@ -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
Expand Down
268 changes: 182 additions & 86 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 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))
Expand Down Expand Up @@ -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))) {
Expand All @@ -718,14 +754,72 @@ 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
// orphan placement.
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() {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;

Expand Down
Loading