[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:
parent
88d288489e
commit
7e8a9020b1
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
----------------
|
||||
|
448
lld/test/ELF/linkerscript/section-class.test
Normal file
448
lld/test/ELF/linkerscript/section-class.test
Normal 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/
|
Loading…
x
Reference in New Issue
Block a user