Skip to content

Commit 66466ff

Browse files
committed
Reland: [LLD] Implement --enable-non-contiguous-regions (#90007)
When enabled, input sections that would otherwise overflow a memory region are instead spilled to the next matching output section. This feature parallels the one in GNU LD, but there are some differences from its documented behavior: - /DISCARD/ only matches previously-unmatched sections (i.e., the flag does not affect it). - If a section fails to fit at any of its matches, the link fails instead of discarding the section. - The flag --enable-non-contiguous-regions-warnings is not implemented, as it exists to warn about such occurrences. The implementation places stubs at possible spill locations, and replaces them with the original input section when effecting spills. Spilling decisions occur after address assignment. Sections are spilled in reverse order of assignment, with each spill naively decreasing the size of the affected memory regions. This continues until the memory regions are brought back under size. Spilling anything causes another pass of address assignment, and this continues to fixed point. Spilling after rather than during assignment allows the algorithm to consider the size effects of unspillable input sections that appear later in the assignment. Otherwise, such sections (e.g. thunks) may force an overflow, even if spilling something earlier could have avoided it. A few notable feature interactions occur: - Stubs affect alignment, ONLY_IF_RO, etc, broadly as if a copy of the input section were actually placed there. - SHF_MERGE synthetic sections use the spill list of their first contained input section (the one that gives the section its name). - ICF occurs oblivious to spill sections; spill lists for merged-away sections become inert and are removed after assignment. - SHF_LINK_ORDER and .ARM.exidx are ordered according to the final section ordering, after all spilling has completed. - INSERT BEFORE/AFTER and OVERWRITE_SECTIONS are explicitly disallowed.
1 parent 026686b commit 66466ff

17 files changed

+600
-17
lines changed

lld/ELF/Config.h

+1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ struct Config {
238238
bool emitLLVM;
239239
bool emitRelocs;
240240
bool enableNewDtags;
241+
bool enableNonContiguousRegions;
241242
bool executeOnly;
242243
bool exportDynamic;
243244
bool fixCortexA53Errata843419;

lld/ELF/Driver.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,8 @@ static void readConfigs(opt::InputArgList &args) {
12501250
config->emitRelocs = args.hasArg(OPT_emit_relocs);
12511251
config->enableNewDtags =
12521252
args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
1253+
config->enableNonContiguousRegions =
1254+
args.hasArg(OPT_enable_non_contiguous_regions);
12531255
config->entry = args.getLastArgValue(OPT_entry);
12541256

12551257
errorHandler().errorHandlingScript =
@@ -3085,7 +3087,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
30853087
// sectionBases.
30863088
for (SectionCommand *cmd : script->sectionCommands)
30873089
if (auto *osd = dyn_cast<OutputDesc>(cmd))
3088-
osd->osec.finalizeInputSections();
3090+
osd->osec.finalizeInputSections(&script.s);
30893091
}
30903092

30913093
// Two input sections with different output sections should not be folded.

lld/ELF/InputSection.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ uint64_t SectionBase::getOffset(uint64_t offset) const {
161161
}
162162
case Regular:
163163
case Synthetic:
164+
case Spill:
164165
return cast<InputSection>(this)->outSecOff + offset;
165166
case EHFrame: {
166167
// Two code paths may reach here. First, clang_rt.crtbegin.o and GCC
@@ -309,6 +310,12 @@ std::string InputSectionBase::getObjMsg(uint64_t off) const {
309310
.str();
310311
}
311312

313+
PotentialSpillSection::PotentialSpillSection(const InputSectionBase &source,
314+
InputSectionDescription &isd)
315+
: InputSection(source.file, source.flags, source.type, source.addralign, {},
316+
source.name, SectionBase::Spill),
317+
isd(&isd) {}
318+
312319
InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef<uint8_t>(), "");
313320

314321
InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type,

lld/ELF/InputSection.h

+23-2
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, EHFrame, Merge, Output };
51+
enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output };
5252

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

@@ -382,7 +382,8 @@ class InputSection : public InputSectionBase {
382382

383383
static bool classof(const SectionBase *s) {
384384
return s->kind() == SectionBase::Regular ||
385-
s->kind() == SectionBase::Synthetic;
385+
s->kind() == SectionBase::Synthetic ||
386+
s->kind() == SectionBase::Spill;
386387
}
387388

388389
// Write this section to a mmap'ed file, assuming Buf is pointing to
@@ -425,6 +426,26 @@ class InputSection : public InputSectionBase {
425426
template <class ELFT> void copyShtGroup(uint8_t *buf);
426427
};
427428

429+
// A marker for a potential spill location for another input section. This
430+
// broadly acts as if it were the original section until address assignment.
431+
// Then it is either replaced with the real input section or removed.
432+
class PotentialSpillSection : public InputSection {
433+
public:
434+
// The containing input section description; used to quickly replace this stub
435+
// with the actual section.
436+
InputSectionDescription *isd;
437+
438+
// Next potential spill location for the same source input section.
439+
PotentialSpillSection *next = nullptr;
440+
441+
PotentialSpillSection(const InputSectionBase &source,
442+
InputSectionDescription &isd);
443+
444+
static bool classof(const SectionBase *sec) {
445+
return sec->kind() == InputSectionBase::Spill;
446+
}
447+
};
448+
428449
static_assert(sizeof(InputSection) <= 160, "InputSection is too big");
429450

430451
class SyntheticSection : public InputSection {

lld/ELF/LinkerScript.cpp

+174-7
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
304304
void LinkerScript::processInsertCommands() {
305305
SmallVector<OutputDesc *, 0> moves;
306306
for (const InsertCommand &cmd : insertCommands) {
307+
if (config->enableNonContiguousRegions)
308+
error("INSERT cannot be used with --enable-non-contiguous-regions");
309+
307310
for (StringRef name : cmd.names) {
308311
// If base is empty, it may have been discarded by
309312
// adjustOutputSections(). We do not handle such output sections.
@@ -486,10 +489,12 @@ static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
486489
// Compute and remember which sections the InputSectionDescription matches.
487490
SmallVector<InputSectionBase *, 0>
488491
LinkerScript::computeInputSections(const InputSectionDescription *cmd,
489-
ArrayRef<InputSectionBase *> sections) {
492+
ArrayRef<InputSectionBase *> sections,
493+
const OutputSection &outCmd) {
490494
SmallVector<InputSectionBase *, 0> ret;
491495
SmallVector<size_t, 0> indexes;
492496
DenseSet<size_t> seen;
497+
DenseSet<InputSectionBase *> spills;
493498
auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
494499
llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
495500
for (size_t i = begin; i != end; ++i)
@@ -505,10 +510,10 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
505510
size_t sizeBeforeCurrPat = ret.size();
506511

507512
for (size_t i = 0, e = sections.size(); i != e; ++i) {
508-
// Skip if the section is dead or has been matched by a previous input
509-
// section description or a previous pattern.
513+
// Skip if the section is dead or has been matched by a previous pattern
514+
// in this input section description.
510515
InputSectionBase *sec = sections[i];
511-
if (!sec->isLive() || sec->parent || seen.contains(i))
516+
if (!sec->isLive() || seen.contains(i))
512517
continue;
513518

514519
// For --emit-relocs we have to ignore entries like
@@ -529,6 +534,29 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
529534
(sec->flags & cmd->withoutFlags) != 0)
530535
continue;
531536

537+
if (sec->parent) {
538+
// Skip if not allowing multiple matches.
539+
if (!config->enableNonContiguousRegions)
540+
continue;
541+
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/")
545+
continue;
546+
547+
// Skip if the section's first match was /DISCARD/; such sections are
548+
// always discarded.
549+
if (sec->parent->name == "/DISCARD/")
550+
continue;
551+
552+
// Skip if the section was already matched by a different input section
553+
// description within this output section.
554+
if (sec->parent == &outCmd)
555+
continue;
556+
557+
spills.insert(sec);
558+
}
559+
532560
ret.push_back(sec);
533561
indexes.push_back(i);
534562
seen.insert(i);
@@ -555,6 +583,30 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
555583
// Matched sections after the last SORT* are sorted by (--sort-alignment,
556584
// input order).
557585
sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size());
586+
587+
// The flag --enable-non-contiguous-regions may cause sections to match an
588+
// InputSectionDescription in more than one OutputSection. Matches after the
589+
// first were collected in the spills set, so replace these with potential
590+
// spill sections.
591+
if (!spills.empty()) {
592+
for (InputSectionBase *&sec : ret) {
593+
if (!spills.contains(sec))
594+
continue;
595+
596+
// Append the spill input section to the list for the input section,
597+
// creating it if necessary.
598+
PotentialSpillSection *pss = make<PotentialSpillSection>(
599+
*sec, const_cast<InputSectionDescription &>(*cmd));
600+
auto [it, inserted] =
601+
potentialSpillLists.try_emplace(sec, PotentialSpillList{pss, pss});
602+
if (!inserted) {
603+
PotentialSpillSection *&tail = it->second.tail;
604+
tail = tail->next = pss;
605+
}
606+
sec = pss;
607+
}
608+
}
609+
558610
return ret;
559611
}
560612

@@ -577,7 +629,7 @@ void LinkerScript::discardSynthetic(OutputSection &outCmd) {
577629
part.armExidx->exidxSections.end());
578630
for (SectionCommand *cmd : outCmd.commands)
579631
if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
580-
for (InputSectionBase *s : computeInputSections(isd, secs))
632+
for (InputSectionBase *s : computeInputSections(isd, secs, outCmd))
581633
discard(*s);
582634
}
583635
}
@@ -588,7 +640,7 @@ LinkerScript::createInputSectionList(OutputSection &outCmd) {
588640

589641
for (SectionCommand *cmd : outCmd.commands) {
590642
if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
591-
isd->sectionBases = computeInputSections(isd, ctx.inputSections);
643+
isd->sectionBases = computeInputSections(isd, ctx.inputSections, outCmd);
592644
for (InputSectionBase *s : isd->sectionBases)
593645
s->parent = &outCmd;
594646
ret.insert(ret.end(), isd->sectionBases.begin(), isd->sectionBases.end());
@@ -644,6 +696,9 @@ void LinkerScript::processSectionCommands() {
644696

645697
// Process OVERWRITE_SECTIONS first so that it can overwrite the main script
646698
// or orphans.
699+
if (config->enableNonContiguousRegions && !overwriteSections.empty())
700+
error("OVERWRITE_SECTIONS cannot be used with "
701+
"--enable-non-contiguous-regions");
647702
DenseMap<CachedHashStringRef, OutputDesc *> map;
648703
size_t i = 0;
649704
for (OutputDesc *osd : overwriteSections) {
@@ -1066,8 +1121,12 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
10661121
// Handle a single input section description command.
10671122
// It calculates and assigns the offsets for each section and also
10681123
// updates the output section size.
1069-
for (InputSection *isec : cast<InputSectionDescription>(cmd)->sections) {
1124+
1125+
auto &sections = cast<InputSectionDescription>(cmd)->sections;
1126+
for (InputSection *isec : sections) {
10701127
assert(isec->getParent() == sec);
1128+
if (isa<PotentialSpillSection>(isec))
1129+
continue;
10711130
const uint64_t pos = dot;
10721131
dot = alignToPowerOf2(dot, isec->addralign);
10731132
isec->outSecOff = dot - sec->addr;
@@ -1364,6 +1423,114 @@ const Defined *LinkerScript::assignAddresses() {
13641423
return getChangedSymbolAssignment(oldValues);
13651424
}
13661425

1426+
static bool hasRegionOverflowed(MemoryRegion *mr) {
1427+
if (!mr)
1428+
return false;
1429+
return mr->curPos - mr->getOrigin() > mr->getLength();
1430+
}
1431+
1432+
// Spill input sections in reverse order of address assignment to (potentially)
1433+
// bring memory regions out of overflow. The size savings of a spill can only be
1434+
// estimated, since general linker script arithmetic may occur afterwards.
1435+
// Under-estimates may cause unnecessary spills, but over-estimates can always
1436+
// be corrected on the next pass.
1437+
bool LinkerScript::spillSections() {
1438+
if (!config->enableNonContiguousRegions)
1439+
return false;
1440+
1441+
bool spilled = false;
1442+
for (SectionCommand *cmd : reverse(sectionCommands)) {
1443+
auto *od = dyn_cast<OutputDesc>(cmd);
1444+
if (!od)
1445+
continue;
1446+
OutputSection *osec = &od->osec;
1447+
if (!osec->memRegion)
1448+
continue;
1449+
1450+
// Input sections that have replaced a potential spill and should be removed
1451+
// from their input section description.
1452+
DenseSet<InputSection *> spilledInputSections;
1453+
1454+
for (SectionCommand *cmd : reverse(osec->commands)) {
1455+
if (!hasRegionOverflowed(osec->memRegion) &&
1456+
!hasRegionOverflowed(osec->lmaRegion))
1457+
break;
1458+
1459+
auto *isd = dyn_cast<InputSectionDescription>(cmd);
1460+
if (!isd)
1461+
continue;
1462+
for (InputSection *isec : reverse(isd->sections)) {
1463+
// Potential spill locations cannot be spilled.
1464+
if (isa<PotentialSpillSection>(isec))
1465+
continue;
1466+
1467+
// Find the next potential spill location and remove it from the list.
1468+
auto it = potentialSpillLists.find(isec);
1469+
if (it == potentialSpillLists.end())
1470+
continue;
1471+
PotentialSpillList &list = it->second;
1472+
PotentialSpillSection *spill = list.head;
1473+
if (spill->next)
1474+
list.head = spill->next;
1475+
else
1476+
potentialSpillLists.erase(isec);
1477+
1478+
// Replace the next spill location with the spilled section and adjust
1479+
// its properties to match the new location. Note that the alignment of
1480+
// the spill section may have diverged from the original due to e.g. a
1481+
// SUBALIGN. Correct assignment requires the spill's alignment to be
1482+
// used, not the original.
1483+
spilledInputSections.insert(isec);
1484+
*llvm::find(spill->isd->sections, spill) = isec;
1485+
isec->parent = spill->parent;
1486+
isec->addralign = spill->addralign;
1487+
1488+
// Record the (potential) reduction in the region's end position.
1489+
osec->memRegion->curPos -= isec->getSize();
1490+
if (osec->lmaRegion)
1491+
osec->lmaRegion->curPos -= isec->getSize();
1492+
1493+
// Spilling continues until the end position no longer overflows the
1494+
// region. Then, another round of address assignment will either confirm
1495+
// the spill's success or lead to yet more spilling.
1496+
if (!hasRegionOverflowed(osec->memRegion) &&
1497+
!hasRegionOverflowed(osec->lmaRegion))
1498+
break;
1499+
}
1500+
1501+
// Remove any spilled input sections to complete their move.
1502+
if (!spilledInputSections.empty()) {
1503+
spilled = true;
1504+
llvm::erase_if(isd->sections, [&](InputSection *isec) {
1505+
return spilledInputSections.contains(isec);
1506+
});
1507+
}
1508+
}
1509+
}
1510+
1511+
return spilled;
1512+
}
1513+
1514+
// Erase any potential spill sections that were not used.
1515+
void LinkerScript::erasePotentialSpillSections() {
1516+
if (potentialSpillLists.empty())
1517+
return;
1518+
1519+
// Collect the set of input section descriptions that contain potential
1520+
// spills.
1521+
DenseSet<InputSectionDescription *> isds;
1522+
for (const auto &[_, list] : potentialSpillLists)
1523+
for (PotentialSpillSection *s = list.head; s; s = s->next)
1524+
isds.insert(s->isd);
1525+
1526+
for (InputSectionDescription *isd : isds)
1527+
llvm::erase_if(isd->sections, [](InputSection *s) {
1528+
return isa<PotentialSpillSection>(s);
1529+
});
1530+
1531+
potentialSpillLists.clear();
1532+
}
1533+
13671534
// Creates program headers as instructed by PHDRS linker script command.
13681535
SmallVector<PhdrEntry *, 0> LinkerScript::createPhdrs() {
13691536
SmallVector<PhdrEntry *, 0> ret;

lld/ELF/LinkerScript.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLD_ELF_LINKER_SCRIPT_H
1111

1212
#include "Config.h"
13+
#include "InputSection.h"
1314
#include "Writer.h"
1415
#include "lld/Common/LLVM.h"
1516
#include "lld/Common/Strings.h"
@@ -287,7 +288,8 @@ class LinkerScript final {
287288

288289
SmallVector<InputSectionBase *, 0>
289290
computeInputSections(const InputSectionDescription *,
290-
ArrayRef<InputSectionBase *>);
291+
ArrayRef<InputSectionBase *>,
292+
const OutputSection &outCmd);
291293

292294
SmallVector<InputSectionBase *, 0> createInputSectionList(OutputSection &cmd);
293295

@@ -333,6 +335,8 @@ class LinkerScript final {
333335

334336
bool shouldKeep(InputSectionBase *s);
335337
const Defined *assignAddresses();
338+
bool spillSections();
339+
void erasePotentialSpillSections();
336340
void allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs);
337341
void processSectionCommands();
338342
void processSymbolAssignments();
@@ -400,6 +404,15 @@ class LinkerScript final {
400404
//
401405
// then provideMap should contain the mapping: 'v' -> ['a', 'b', 'c']
402406
llvm::MapVector<StringRef, SmallVector<StringRef, 0>> provideMap;
407+
408+
// List of potential spill locations (PotentialSpillSection) for an input
409+
// section.
410+
struct PotentialSpillList {
411+
// Never nullptr.
412+
PotentialSpillSection *head;
413+
PotentialSpillSection *tail;
414+
};
415+
llvm::DenseMap<InputSectionBase *, PotentialSpillList> potentialSpillLists;
403416
};
404417

405418
struct ScriptWrapper {

0 commit comments

Comments
 (0)