From be9868bd2a5538a4fa4432ac2c181d78d6005a6e Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Mon, 4 Dec 2023 14:05:37 -0800 Subject: [PATCH 1/8] [LLD] Implement --enable-non-contiguous-regions 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. --- lld/ELF/Config.h | 1 + lld/ELF/Driver.cpp | 4 +- lld/ELF/InputSection.cpp | 7 + lld/ELF/InputSection.h | 24 +- lld/ELF/LinkerScript.cpp | 183 +++++++++++- lld/ELF/LinkerScript.h | 16 +- lld/ELF/Options.td | 3 + lld/ELF/OutputSections.cpp | 4 +- lld/ELF/OutputSections.h | 2 +- lld/ELF/SyntheticSections.cpp | 7 + lld/ELF/SyntheticSections.h | 4 + lld/ELF/Writer.cpp | 12 + lld/docs/ELF/linker_script.rst | 11 + lld/docs/ReleaseNotes.rst | 11 + ...able-non-contiguous-regions-arm-exidx.test | 54 ++++ .../enable-non-contiguous-regions.test | 279 ++++++++++++++++++ 16 files changed, 609 insertions(+), 13 deletions(-) create mode 100644 lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test create mode 100644 lld/test/ELF/linkerscript/enable-non-contiguous-regions.test diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 33bfa42b0fcbf..bb5d7001f59fb 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -237,6 +237,7 @@ struct Config { bool emitLLVM; bool emitRelocs; bool enableNewDtags; + bool enableNonContiguousRegions; bool executeOnly; bool exportDynamic; bool fixCortexA53Errata843419; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index a5b47f020f872..915184c1aa263 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1250,6 +1250,8 @@ static void readConfigs(opt::InputArgList &args) { config->emitRelocs = args.hasArg(OPT_emit_relocs); config->enableNewDtags = args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true); + config->enableNonContiguousRegions = + args.hasArg(OPT_enable_non_contiguous_regions); config->entry = args.getLastArgValue(OPT_entry); errorHandler().errorHandlingScript = @@ -3077,7 +3079,7 @@ template void LinkerDriver::link(opt::InputArgList &args) { // sectionBases. for (SectionCommand *cmd : script->sectionCommands) if (auto *osd = dyn_cast(cmd)) - osd->osec.finalizeInputSections(); + osd->osec.finalizeInputSections(script.get()); } // Two input sections with different output sections should not be folded. diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index fa48552b8f7a1..c5f050fffdcb3 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -161,6 +161,7 @@ uint64_t SectionBase::getOffset(uint64_t offset) const { } case Regular: case Synthetic: + case Spill: return cast(this)->outSecOff + offset; case EHFrame: { // 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 { .str(); } +SpillInputSection::SpillInputSection(InputSectionBase *source, + InputSectionDescription *isd) + : InputSection(source->file, source->flags, source->type, source->addralign, + {}, source->name, SectionBase::Spill), + isd(isd) {} + InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef(), ""); InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type, diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 1fb7077ca435b..3d75a9633a3cc 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -48,7 +48,7 @@ template struct RelsOrRelas { // sections. class SectionBase { public: - enum Kind { Regular, Synthetic, EHFrame, Merge, Output }; + enum Kind { Regular, Synthetic, EHFrame, Merge, Output, Spill }; Kind kind() const { return (Kind)sectionKind; } @@ -382,7 +382,8 @@ class InputSection : public InputSectionBase { static bool classof(const SectionBase *s) { return s->kind() == SectionBase::Regular || - s->kind() == SectionBase::Synthetic; + s->kind() == SectionBase::Synthetic || + s->kind() == SectionBase::Spill; } // Write this section to a mmap'ed file, assuming Buf is pointing to @@ -425,6 +426,25 @@ class InputSection : public InputSectionBase { template void copyShtGroup(uint8_t *buf); }; +// A marker for a potential spill location for another input section. This +// broadly acts as if it were the original section until address assignment. +// Then it is either replaced with the real input section or removed. +class SpillInputSection : public InputSection { +public: + // The containing input section description; used to quickly replace this stub + // with the actual section. + InputSectionDescription *isd; + + // Next spill location for the same source input section. + SpillInputSection *next = nullptr; + + SpillInputSection(InputSectionBase *source, InputSectionDescription *cmd); + + static bool classof(const SectionBase *sec) { + return sec->kind() == InputSectionBase::Spill; + } +}; + static_assert(sizeof(InputSection) <= 160, "InputSection is too big"); class SyntheticSection : public InputSection { diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index f815b3ac6feed..8db8e4abc595f 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -304,6 +304,9 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) { void LinkerScript::processInsertCommands() { SmallVector moves; for (const InsertCommand &cmd : insertCommands) { + if (config->enableNonContiguousRegions) + error("INSERT cannot be used with --enable-non-contiguous-regions"); + for (StringRef name : cmd.names) { // If base is empty, it may have been discarded by // adjustOutputSections(). We do not handle such output sections. @@ -486,10 +489,12 @@ static void sortInputSections(MutableArrayRef vec, // Compute and remember which sections the InputSectionDescription matches. SmallVector LinkerScript::computeInputSections(const InputSectionDescription *cmd, - ArrayRef sections) { + ArrayRef sections, + const OutputSection &outCmd) { SmallVector ret; SmallVector indexes; DenseSet seen; + DenseSet spills; auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) { llvm::sort(MutableArrayRef(indexes).slice(begin, end - begin)); for (size_t i = begin; i != end; ++i) @@ -505,12 +510,33 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, 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 input - // section description or a previous pattern. + // 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() || sec->parent || seen.contains(i)) + if (!sec->isLive() || seen.contains(i)) continue; + 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; + + // Skip if the section's first match was /DISCARD/; such sections are + // always discarded. + if (sec->parent->name == "/DISCARD/") + continue; + + // Skip if the section was already matched by a different input section + // description within this output section. + if (sec->parent == &outCmd) + 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. @@ -530,6 +556,8 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, continue; ret.push_back(sec); + if (sec->parent) + spills.insert(sec); indexes.push_back(i); seen.insert(i); } @@ -555,6 +583,28 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, // Matched sections after the last SORT* are sorted by (--sort-alignment, // input order). sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size()); + + // Replace matches after the first with potential spill sections. + if (!spills.empty()) { + for (InputSectionBase *&sec : ret) { + if (!spills.contains(sec)) + continue; + + SpillInputSection *sis = make( + sec, const_cast(cmd)); + + // Append the spill input section to the list for the input section, + // creating it if necessary. + auto res = spillLists.try_emplace(sec, SpillList{sis, sis}); + if (!res.second) { + SpillInputSection *&tail = res.first->second.tail; + tail = tail->next = sis; + } + + sec = sis; + } + } + return ret; } @@ -577,7 +627,7 @@ void LinkerScript::discardSynthetic(OutputSection &outCmd) { part.armExidx->exidxSections.end()); for (SectionCommand *cmd : outCmd.commands) if (auto *isd = dyn_cast(cmd)) - for (InputSectionBase *s : computeInputSections(isd, secs)) + for (InputSectionBase *s : computeInputSections(isd, secs, outCmd)) discard(*s); } } @@ -588,7 +638,7 @@ LinkerScript::createInputSectionList(OutputSection &outCmd) { for (SectionCommand *cmd : outCmd.commands) { if (auto *isd = dyn_cast(cmd)) { - isd->sectionBases = computeInputSections(isd, ctx.inputSections); + isd->sectionBases = computeInputSections(isd, ctx.inputSections, outCmd); for (InputSectionBase *s : isd->sectionBases) s->parent = &outCmd; ret.insert(ret.end(), isd->sectionBases.begin(), isd->sectionBases.end()); @@ -644,6 +694,9 @@ void LinkerScript::processSectionCommands() { // Process OVERWRITE_SECTIONS first so that it can overwrite the main script // or orphans. + if (config->enableNonContiguousRegions && !overwriteSections.empty()) + error("OVERWRITE_SECTIONS cannot be used with " + "--enable-non-contiguous-regions"); DenseMap map; size_t i = 0; for (OutputDesc *osd : overwriteSections) { @@ -911,6 +964,13 @@ void LinkerScript::diagnoseMissingSGSectionAddress() const { error("no address assigned to the veneers output section " + sec->name); } +void LinkerScript::copySpillList(InputSectionBase *dst, InputSectionBase *src) { + auto i = spillLists.find(src); + if (i == spillLists.end()) + return; + spillLists.try_emplace(dst, i->second); +} + // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned in the first member of the pair. Otherwise, a nullptr is returned. @@ -1066,8 +1126,16 @@ void LinkerScript::assignOffsets(OutputSection *sec) { // Handle a single input section description command. // It calculates and assigns the offsets for each section and also // updates the output section size. - for (InputSection *isec : cast(cmd)->sections) { + + DenseSet spills; + auto §ions = cast(cmd)->sections; + for (InputSection *isec : sections) { assert(isec->getParent() == sec); + + // Skip all possible spills. + if (isa(isec)) + continue; + const uint64_t pos = dot; dot = alignToPowerOf2(dot, isec->addralign); isec->outSecOff = dot - sec->addr; @@ -1364,6 +1432,107 @@ const Defined *LinkerScript::assignAddresses() { return getChangedSymbolAssignment(oldValues); } +static bool isRegionOverflowed(MemoryRegion *mr) { + if (!mr) + return false; + return mr->curPos - mr->getOrigin() > mr->getLength(); +} + +// Spill input sections in reverse order of address assignment to (potentially) +// bring memory regions out of overflow. The size savings of a spill can only be +// estimated, since general linker script arithmetic may occur afterwards. +// Under-estimates may cause unnecessary spills, but over-estimates can always +// be corrected on the next pass. +bool LinkerScript::spillSections() { + if (!config->enableNonContiguousRegions) + return false; + + bool spilled = false; + for (SectionCommand *cmd : reverse(sectionCommands)) { + auto *od = dyn_cast(cmd); + if (!od) + continue; + OutputSection *osec = &od->osec; + if (!osec->size || !osec->memRegion) + continue; + + DenseSet spills; + for (SectionCommand *cmd : reverse(osec->commands)) { + if (!isRegionOverflowed(osec->memRegion) && + !isRegionOverflowed(osec->lmaRegion)) + break; + + auto *is = dyn_cast(cmd); + if (!is) + continue; + for (InputSection *isec : reverse(is->sections)) { + // Potential spill locations cannot be spilled. + if (isa(isec)) + continue; + + // Find the next spill location. + auto it = spillLists.find(isec); + if (it == spillLists.end()) + continue; + + spilled = true; + SpillList &list = it->second; + + SpillInputSection *spill = list.head; + if (!spill->next) + spillLists.erase(isec); + else + list.head = spill->next; + + spills.insert(isec); + + // Replace the next spill location with the spilled section and adjust + // its properties to match the new location. + *llvm::find(spill->isd->sections, spill) = isec; + isec->parent = spill->parent; + // The alignment of the spill section may have diverged from the + // original, but correct assignment requires the spill's alignment, + // not the original. + isec->addralign = spill->addralign; + + // Record the reduction in overage. + osec->memRegion->curPos -= isec->getSize(); + if (osec->lmaRegion) + osec->lmaRegion->curPos -= isec->getSize(); + if (!isRegionOverflowed(osec->memRegion) && + !isRegionOverflowed(osec->lmaRegion)) + break; + } + // Remove any spilled sections. + if (!spills.empty()) + llvm::erase_if(is->sections, [&](InputSection *isec) { + return spills.contains(isec); + }); + } + } + + return spilled; +} + +// Erase any potential spill sections that were not used. +void LinkerScript::eraseSpillSections() { + if (spillLists.empty()) + return; + + // Collect the set of input section descriptions that contain potential + // spills. + DenseSet isds; + for (const auto &[_, list] : spillLists) + for (SpillInputSection *s = list.head; s; s = s->next) + isds.insert(s->isd); + + for (InputSectionDescription *isd : isds) + llvm::erase_if(isd->sections, + [](InputSection *s) { return isa(s); }); + + spillLists.clear(); +} + // Creates program headers as instructed by PHDRS linker script command. SmallVector LinkerScript::createPhdrs() { SmallVector ret; diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index fa7c6eb9c0d8f..554df0ea88571 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -10,6 +10,7 @@ #define LLD_ELF_LINKER_SCRIPT_H #include "Config.h" +#include "InputSection.h" #include "Writer.h" #include "lld/Common/LLVM.h" #include "lld/Common/Strings.h" @@ -287,7 +288,8 @@ class LinkerScript final { SmallVector computeInputSections(const InputSectionDescription *, - ArrayRef); + ArrayRef, + const OutputSection &outCmd); SmallVector createInputSectionList(OutputSection &cmd); @@ -312,6 +314,15 @@ class LinkerScript final { uint64_t dot; + // List of potential spill locations (SpillInputSection) for an input + // section. + struct SpillList { + // Never nullptr. + SpillInputSection *head; + SpillInputSection *tail; + }; + llvm::DenseMap spillLists; + public: OutputDesc *createOutputSection(StringRef name, StringRef location); OutputDesc *getOrCreateOutputSection(StringRef name); @@ -325,6 +336,7 @@ class LinkerScript final { void addOrphanSections(); void diagnoseOrphanHandling() const; void diagnoseMissingSGSectionAddress() const; + void copySpillList(InputSectionBase *dst, InputSectionBase *src); void adjustOutputSections(); void adjustSectionsAfterSorting(); @@ -333,6 +345,8 @@ class LinkerScript final { bool shouldKeep(InputSectionBase *s); const Defined *assignAddresses(); + bool spillSections(); + void eraseSpillSections(); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 72eaf157a181c..0e5d15124d260 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -196,6 +196,9 @@ def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; def enable_new_dtags: F<"enable-new-dtags">, HelpText<"Enable new dynamic tags (default)">; +def enable_non_contiguous_regions : FF<"enable-non-contiguous-regions">, + HelpText<"Spill input sections to later matching output sections to avoid memory region overflow">; + def end_group: F<"end-group">, HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index eadab9d745d68..1a6c5b7a09f4c 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -186,7 +186,7 @@ static MergeSyntheticSection *createMergeSynthetic(StringRef name, // new synthetic sections at the location of the first input section // that it replaces. It then finalizes each synthetic section in order // to compute an output offset for each piece of each input section. -void OutputSection::finalizeInputSections() { +void OutputSection::finalizeInputSections(LinkerScript *script) { std::vector mergeSections; for (SectionCommand *cmd : commands) { auto *isd = dyn_cast(cmd); @@ -226,6 +226,8 @@ void OutputSection::finalizeInputSections() { i = std::prev(mergeSections.end()); syn->entsize = ms->entsize; isd->sections.push_back(syn); + if (script) + script->copySpillList(syn, ms); } (*i)->addSection(ms); } diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index 421a0181feb5d..78fede48a23f2 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -75,7 +75,7 @@ class OutputSection final : public SectionBase { void recordSection(InputSectionBase *isec); void commitSection(InputSection *isec); - void finalizeInputSections(); + void finalizeInputSections(LinkerScript *script = nullptr); // The following members are normally only used in linker scripts. MemoryRegion *memRegion = nullptr; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 7b9ada40c0f67..298c714adb3b4 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -4074,6 +4074,13 @@ static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) { // InputSection with the highest address and any InputSections that have // mergeable .ARM.exidx table entries are removed from it. void ARMExidxSyntheticSection::finalizeContents() { + // Ensure that any fixed-point iterations after the first see the original set + // of sections. + if (!originalExecutableSections.empty()) + executableSections = originalExecutableSections; + else if (config->enableNonContiguousRegions) + originalExecutableSections = executableSections; + // The executableSections and exidxSections that we use to derive the final // contents of this SyntheticSection are populated before // processSectionCommands() and ICF. A /DISCARD/ entry in SECTIONS command or diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h index 995fd4b344b07..34949025a45f7 100644 --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -1255,6 +1255,10 @@ class ARMExidxSyntheticSection : public SyntheticSection { // either find the .ARM.exidx section or know that we need to generate one. SmallVector executableSections; + // Value of executableSecitons before finalizeContents(), so that it can be + // run repeateadly during fixed point iteration. + SmallVector originalExecutableSections; + // The executable InputSection with the highest address to use for the // sentinel. We store separately from ExecutableSections as merging of // duplicate entries may mean this InputSection is removed from diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 240c16a4d8f69..4ef027704aa32 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1419,6 +1419,8 @@ template void Writer::finalizeAddressDependentContent() { for (;;) { bool changed = target->needsThunks ? tc.createThunks(pass, outputSections) : target->relaxOnce(pass); + bool spilled = script->spillSections(); + changed |= spilled; ++pass; // With Thunk Size much smaller than branch range we expect to @@ -1464,6 +1466,12 @@ template void Writer::finalizeAddressDependentContent() { " does not converge"); break; } + } else if (spilled) { + // Spilling can change relative section order, so recompute anything that + // depends on it. + for (Partition &part : partitions) + finalizeSynthetic(part.armExidx.get()); + resolveShfLinkOrder(); } } if (!config->relocatable) @@ -1483,6 +1491,10 @@ template void Writer::finalizeAddressDependentContent() { osec->name + " is not a multiple of alignment (" + Twine(osec->addralign) + ")"); } + + // Sizes are no longer allowed to grow, so all allowable spills have been + // taken. Remove any leftover potential spills. + script->eraseSpillSections(); } // If Input Sections have been shrunk (basic block sections) then diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst index 3606ef4fe4b8e..7a35534be096c 100644 --- a/lld/docs/ELF/linker_script.rst +++ b/lld/docs/ELF/linker_script.rst @@ -197,3 +197,14 @@ 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``. + +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. diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index a7ed49726fd99..0c558a13c434b 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -35,6 +35,17 @@ ELF Improvements * ``--debug-names`` is added to create a merged ``.debug_names`` index from input ``.debug_names`` sections. Type units are not handled yet. (`#86508 `_) +* ``--fat-lto-objects`` option is added to support LLVM FatLTO. + Without ``--fat-lto-objects``, LLD will link LLVM FatLTO objects using the + relocatable object file. (`D146778 `_) +* common-page-size can now be larger than the system page-size. + (`#57618 `_) +* ``--enable-non-contiguous-regions`` option allows automatically packing input + sections into memory regions by automatically spilling to later matches if a + region would overflow. This reduces the toil of manually packing regions + (typical for embedded). It also makes full LTO feasible in such cases, since + IR merging currently prevents the linker script from referring to input + files. Breaking changes ---------------- diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test new file mode 100644 index 0000000000000..8ead54f7cd8bd --- /dev/null +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test @@ -0,0 +1,54 @@ +# If a spilling reorders input sections, the .ARM.exidx table must be rebuilt +# using the new order. + +RUN: split-file %s %t +RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %t/test.s -o %t/test.o +RUN: ld.lld -T %t/test.ld %t/test.o -o %t/test --enable-non-contiguous-regions +RUN: llvm-readobj -x .ARM.exidx %t/test | FileCheck %s + +CHECK: 20000000 08849780 1c000000 10849880 +CHECK-NEXT: 18000000 20849980 14000000 01000000 + +#--- test.ld +MEMORY { + exidx : ORIGIN = 0, LENGTH = 32 + a : ORIGIN = 32, LENGTH = 4 + b : ORIGIN = 36, LENGTH = 4 + c : ORIGIN = 40, LENGTH = 4 +} + +SECTIONS { + .ARM.exidx : { *(.ARM.exidx) } >exidx + .first_chance : { *(.text .text.f2) } >a + .text.f1 : { *(.text.f1) } >b + .last_chance : { *(.text.f2) } >c +} + +#--- test.s + .syntax unified + .section .text, "ax",%progbits + .globl _start +_start: + .fnstart + bx lr + .save {r7, lr} + .setfp r7, sp, #0 + .fnend + + .section .text.f1, "ax", %progbits + .globl f1 +f1: + .fnstart + bx lr + .save {r8, lr} + .setfp r8, sp, #0 + .fnend + + .section .text.f2, "ax", %progbits + .globl f2 +f2: + .fnstart + bx lr + .save {r9, lr} + .setfp r9, sp, #0 + .fnend diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test new file mode 100644 index 0000000000000..d59c80b6f9220 --- /dev/null +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test @@ -0,0 +1,279 @@ +REQUIRES: x86 + +RUN: split-file %s %t +RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/spill.s -o %t/spill.o + +# An input section must spill to a later match if the region of its first match +# would overflow. + +RUN: ld.lld -T %t/spill.ld %t/spill.o -o %t/spill --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/spill | FileCheck %s -check-prefix=SPILL + +SPILL: Name Type Address Off Size +SPILL: .first_chance PROGBITS 0000000000000000 001000 000001 +SPILL-NEXT: .last_chance PROGBITS 0000000000000002 001002 000002 + +# A spill off the end must still fails the link. + +RUN: not ld.lld -T %t/spill-fail.ld %t/spill.o -o %t/spill-fail --enable-non-contiguous-regions 2>&1 |\ +RUN: FileCheck %s -check-prefix=SPILL-FAIL + +SPILL-FAIL: error: section '.last_chance' will not fit in region 'b': overflowed by 2 bytes + +# The above spill must still occur if the LMA would overflow, even if the VMA +# would fit. + +RUN: ld.lld -T %t/spill-lma.ld %t/spill.o -o %t/spill-lma --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/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 + +# A spill must be able to occur to an additional match after the first. + +RUN: ld.lld -T %t/spill-later.ld %t/spill.o -o %t/spill-later --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/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 + +# A later overflow must be able to cause an earlier section to spill. + +RUN: ld.lld -T %t/spill-earlier.ld %t/spill.o -o %t/spill-earlier --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/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 + +# An additional match in /DISCARD/ must have no effect. + +RUN: not ld.lld -T %t/no-spill-into-discard.ld %t/spill.o -o %t/no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ +RUN: FileCheck %s -check-prefix=NO-SPILL-INTO-DISCARD + +NO-SPILL-INTO-DISCARD: error: section '.osec' will not fit in region 'a': overflowed by 1 bytes + +# An additional match after /DISCARD/ must have no effect. + +RUN: ld.lld -T %t/no-spill-from-discard.ld %t/spill.o -o %t/no-spill-from-discard --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/no-spill-from-discard | FileCheck %s -check-prefix=NO-SPILL-FROM-DISCARD + +NO-SPILL-FROM-DISCARD: Name Type Address Off Size +NO-SPILL-FROM-DISCARD-NOT: .osec + +# A spill must use the alignment of the later match. + +RUN: ld.lld -T %t/spill-align.ld %t/spill.o -o %t/spill-align --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/spill-align | FileCheck %s -check-prefix=SPILL-ALIGN + +SPILL-ALIGN: Name Type Address Off Size +SPILL-ALIGN: .first_chance PROGBITS 0000000000000000 000158 000000 +SPILL-ALIGN-NEXT: .last_chance PROGBITS 0000000000000008 001008 00000a + +# SHF_MERGEd sections must be spilled according to the matches of the first +# merged input section (the one giving the resulting section its name). + +RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/merge.s -o %t/merge.o +RUN: ld.lld -T %t/spill-merge.ld %t/merge.o -o %t/spill-merge --enable-non-contiguous-regions +RUN: llvm-readelf -S %t/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 + +# An error must be reported for INSERT. + +RUN: not ld.lld -T %t/insert.ld %t/spill.o -o %t/insert --enable-non-contiguous-regions 2>&1 |\ +RUN: FileCheck %s -check-prefix=INSERT + +INSERT: error: INSERT cannot be used with --enable-non-contiguous-regions + +# An error must be reported for OVERWRITE_SECTIONS. + +RUN: not ld.lld -T %t/overwrite-sections.ld %t/spill.o -o %t/overwrite-sections --enable-non-contiguous-regions 2>&1 |\ +RUN: FileCheck %s -check-prefix=OVERWRITE_SECTIONS + +OVERWRITE_SECTIONS: error: OVERWRITE_SECTIONS cannot be used with --enable-non-contiguous-regions + +# SHF_LINK_ORDER must be reordered if spilling changes relative section order. + +RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/link-order.s -o %t/link-order.o +RUN: ld.lld -T %t/link-order.ld %t/link-order.o -o %t/link-order --enable-non-contiguous-regions +RUN: llvm-readobj -x .order %t/link-order | FileCheck %s -check-prefix=LINK-ORDER + +LINK-ORDER: 0201 + +#--- spill.s +.section .one_byte_section,"a",@progbits +.fill 1 + +.section .two_byte_section,"a",@progbits +.fill 2 + +#--- spill.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 2 + b : ORIGIN = 2, LENGTH = 2 +} + +SECTIONS { + .first_chance : { *(.one_byte_section) *(.two_byte_section) } >a + .last_chance : { *(.two_byte_section) } >b +} + +#--- spill-fail.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 1 + b : ORIGIN = 2, LENGTH = 0 +} + +SECTIONS { + .first_chance : { *(.one_byte_section) *(.two_byte_section) } >a + .last_chance : { *(.two_byte_section) } >b +} + +#--- spill-lma.ld +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 { + .first_chance : { *(.one_byte_section) *(.two_byte_section) } >vma_a AT>lma_a + .last_chance : { *(.two_byte_section) } >vma_b AT>lma_b +} + +#--- spill-later.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 2 + b : ORIGIN = 2, LENGTH = 1 + c : ORIGIN = 3, LENGTH = 2 +} + +SECTIONS { + .first_chance : { *(.one_byte_section) *(.two_byte_section) } >a + .second_chance : { *(.two_byte_section) } >b + .last_chance : { *(.two_byte_section) } >c +} + +#--- spill-earlier.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 2 + b : ORIGIN = 2, LENGTH = 1 +} + +SECTIONS { + .first_chance : { *(.one_byte_section) *(.two_byte_section) } >a + .last_chance : { *(.one_byte_section) } >b +} + +#--- no-spill-into-discard.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 1 +} + +SECTIONS { + .osec : { *(.two_byte_section) } >a + /DISCARD/ : { *(.one_byte_section) *(.two_byte_section) } +} + +#--- no-spill-from-discard.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 2 +} + +SECTIONS { + /DISCARD/ : { *(.one_byte_section) *(.two_byte_section) } + .osec : { *(.two_byte_section) } >a +} + +#--- spill-align.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 0 + b : ORIGIN = 2, LENGTH = 16 +} + +SECTIONS { + .first_chance : SUBALIGN(1) { *(.two_byte_section) } >a + .last_chance : SUBALIGN(8) { *(.one_byte_section) *(.two_byte_section) } >b +} + +#--- merge.s +.section .a,"aM",@progbits,1 +.byte 0x12, 0x34 + +.section .b,"aM",@progbits,1 +.byte 0x12 + +#--- spill-merge.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 1 + b : ORIGIN = 1, LENGTH = 2 + c : ORIGIN = 3, LENGTH = 2 +} + +SECTIONS { + .first : { *(.a) *(.b) } >a + .second : { *(.a) } >b + .third : { *(.b) } >c +} + +#--- insert.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 1 +} + +SECTIONS { + .a : { *(.two_byte_section) } >a +} + +SECTIONS { + .b : { *(.one_byte_section) } >a +} INSERT AFTER .a; + +#--- overwrite-sections.ld +MEMORY { + a : ORIGIN = 0, LENGTH = 1 +} + +SECTIONS { + .a : { *(.two_byte_section) } >a +} + +OVERWRITE_SECTIONS { + .b : { *(.one_byte_section) } >a +} + +#--- link-order.s +.section .a,"a",@progbits +.fill 1 + +.section .b,"a",@progbits +.fill 1 + +.section .link_order.a,"ao",@progbits,.a +.byte 1 + +.section .link_order.b,"ao",@progbits,.b +.byte 2 + +#--- link-order.ld +MEMORY { + order : ORIGIN = 0, LENGTH = 2 + potential_a : ORIGIN = 2, LENGTH = 0 + b : ORIGIN = 2, LENGTH = 1 + actual_a : ORIGIN = 3, LENGTH = 1 +} + +SECTIONS { + .order : { *(.link_order.*) } > order + .potential_a : { *(.a) } >potential_a + .b : { *(.b) } >b + .actual_a : { *(.a) } >actual_a +} From eba58e5a99314ef2fba47bca69a760e8118dae27 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Tue, 30 Apr 2024 11:23:23 -0700 Subject: [PATCH 2/8] Address round of review comments - Add no-op `--enable-non-contiguous-regions-warnings`. - Add help text to LLD man page. - Additional implementation comments. - Remove dead variable. - Remove release note merge chaff. - Rename `isRegionOverflowed` to `hasRegionOverflowed`. - Rename variable `is` to `isd` for InputSectionDescription. --- lld/ELF/LinkerScript.cpp | 51 +++++++++++++++++++++------------- lld/ELF/Options.td | 2 ++ lld/ELF/OutputSections.cpp | 2 ++ lld/docs/ELF/linker_script.rst | 2 +- lld/docs/ReleaseNotes.rst | 5 ---- lld/docs/ld.lld.1 | 4 +++ 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 8db8e4abc595f..43070c308dd0c 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -584,7 +584,10 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, // input order). sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size()); - // Replace matches after the first with potential spill sections. + // 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. if (!spills.empty()) { for (InputSectionBase *&sec : ret) { if (!spills.contains(sec)) @@ -1127,12 +1130,11 @@ void LinkerScript::assignOffsets(OutputSection *sec) { // It calculates and assigns the offsets for each section and also // updates the output section size. - DenseSet spills; auto §ions = cast(cmd)->sections; for (InputSection *isec : sections) { assert(isec->getParent() == sec); - // Skip all possible spills. + // Skip all potential spill locations. if (isa(isec)) continue; @@ -1432,7 +1434,7 @@ const Defined *LinkerScript::assignAddresses() { return getChangedSymbolAssignment(oldValues); } -static bool isRegionOverflowed(MemoryRegion *mr) { +static bool hasRegionOverflowed(MemoryRegion *mr) { if (!mr) return false; return mr->curPos - mr->getOrigin() > mr->getLength(); @@ -1456,16 +1458,19 @@ bool LinkerScript::spillSections() { if (!osec->size || !osec->memRegion) continue; - DenseSet spills; + // Input sections that have replaced a potential spill and should be removed + // from their input section description. + DenseSet spilledInputSections; + for (SectionCommand *cmd : reverse(osec->commands)) { - if (!isRegionOverflowed(osec->memRegion) && - !isRegionOverflowed(osec->lmaRegion)) + if (!hasRegionOverflowed(osec->memRegion) && + !hasRegionOverflowed(osec->lmaRegion)) break; - auto *is = dyn_cast(cmd); - if (!is) + auto *isd = dyn_cast(cmd); + if (!isd) continue; - for (InputSection *isec : reverse(is->sections)) { + for (InputSection *isec : reverse(isd->sections)) { // Potential spill locations cannot be spilled. if (isa(isec)) continue; @@ -1484,29 +1489,35 @@ bool LinkerScript::spillSections() { else list.head = spill->next; - spills.insert(isec); + spilledInputSections.insert(isec); // Replace the next spill location with the spilled section and adjust // its properties to match the new location. *llvm::find(spill->isd->sections, spill) = isec; isec->parent = spill->parent; + // The alignment of the spill section may have diverged from the - // original, but correct assignment requires the spill's alignment, - // not the original. + // original due to e.g. a SUBALIGN. Correct assignment requires the + // spill's alignment to be used, not the original. isec->addralign = spill->addralign; - // Record the reduction in overage. + // Record the (potential) reduction in the region's end position. osec->memRegion->curPos -= isec->getSize(); if (osec->lmaRegion) osec->lmaRegion->curPos -= isec->getSize(); - if (!isRegionOverflowed(osec->memRegion) && - !isRegionOverflowed(osec->lmaRegion)) + + // Spilling continues until the end position no longer overflows the + // region. Then, another round of address assignment will either confirm + // the spill's success or lead to yet more spilling. + if (!hasRegionOverflowed(osec->memRegion) && + !hasRegionOverflowed(osec->lmaRegion)) break; } - // Remove any spilled sections. - if (!spills.empty()) - llvm::erase_if(is->sections, [&](InputSection *isec) { - return spills.contains(isec); + + // Remove any spilled input sections to complete their move. + if (!spilledInputSections.empty()) + llvm::erase_if(isd->sections, [&](InputSection *isec) { + return spilledInputSections.contains(isec); }); } } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index 0e5d15124d260..bad73dbb94d38 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -198,6 +198,8 @@ def enable_new_dtags: F<"enable-new-dtags">, def enable_non_contiguous_regions : FF<"enable-non-contiguous-regions">, HelpText<"Spill input sections to later matching output sections to avoid memory region overflow">; +def enable_non_contiguous_regions_warnings : FF<"enable-non-contiguous-regions-warnings">, + HelpText<"No effect; included for GNU LD compatibility">; def end_group: F<"end-group">, HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 1a6c5b7a09f4c..d0bfd3b9ddc8c 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -226,6 +226,8 @@ void OutputSection::finalizeInputSections(LinkerScript *script) { i = std::prev(mergeSections.end()); syn->entsize = ms->entsize; isd->sections.push_back(syn); + // The merge synthetic section inherits the potential spill locations of + // its first contained section. if (script) script->copySpillList(syn, ms); } diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst index 7a35534be096c..e9319db900220 100644 --- a/lld/docs/ELF/linker_script.rst +++ b/lld/docs/ELF/linker_script.rst @@ -206,5 +206,5 @@ 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 +GNU flag ``--enable-non-contiguous-regions-warnings`` has no effect, as it exists to warn about such occurrences. diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index 0c558a13c434b..4755e0415fe91 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -35,11 +35,6 @@ ELF Improvements * ``--debug-names`` is added to create a merged ``.debug_names`` index from input ``.debug_names`` sections. Type units are not handled yet. (`#86508 `_) -* ``--fat-lto-objects`` option is added to support LLVM FatLTO. - Without ``--fat-lto-objects``, LLD will link LLVM FatLTO objects using the - relocatable object file. (`D146778 `_) -* common-page-size can now be larger than the system page-size. - (`#57618 `_) * ``--enable-non-contiguous-regions`` option allows automatically packing input sections into memory regions by automatically spilling to later matches if a region would overflow. This reduces the toil of manually packing regions diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 3861120915e8b..1e82572d0de4b 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -222,6 +222,10 @@ segment header. Generate relocations in the output. .It Fl -enable-new-dtags Enable new dynamic tags. +.It Fl -enable-non-contiguous-regions +Spill input sections to later matching output sections to avoid memory region overflow. +.It Fl -enable-non-contiguous-regions-warnings +No effect; included for GNU LD compatibility. .It Fl -end-lib End a grouping of objects that should be treated as if they were together in an archive. From ef9f456bce8c70427a71ab7736cd00d2e2e6cef3 Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Tue, 30 Apr 2024 15:21:32 -0700 Subject: [PATCH 3/8] Rename SpillInputSection (etc.) to PotentialSpillSection --- lld/ELF/InputSection.cpp | 4 ++-- lld/ELF/InputSection.h | 8 +++---- lld/ELF/LinkerScript.cpp | 49 ++++++++++++++++++++------------------ lld/ELF/LinkerScript.h | 14 +++++------ lld/ELF/OutputSections.cpp | 2 +- lld/ELF/Writer.cpp | 2 +- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index c5f050fffdcb3..da41141066eb2 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -310,8 +310,8 @@ std::string InputSectionBase::getObjMsg(uint64_t off) const { .str(); } -SpillInputSection::SpillInputSection(InputSectionBase *source, - InputSectionDescription *isd) +PotentialSpillSection::PotentialSpillSection(InputSectionBase *source, + InputSectionDescription *isd) : InputSection(source->file, source->flags, source->type, source->addralign, {}, source->name, SectionBase::Spill), isd(isd) {} diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 3d75a9633a3cc..a81565810f88d 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -429,16 +429,16 @@ class InputSection : public InputSectionBase { // A marker for a potential spill location for another input section. This // broadly acts as if it were the original section until address assignment. // Then it is either replaced with the real input section or removed. -class SpillInputSection : public InputSection { +class PotentialSpillSection : public InputSection { public: // The containing input section description; used to quickly replace this stub // with the actual section. InputSectionDescription *isd; - // Next spill location for the same source input section. - SpillInputSection *next = nullptr; + // Next potential spill location for the same source input section. + PotentialSpillSection *next = nullptr; - SpillInputSection(InputSectionBase *source, InputSectionDescription *cmd); + PotentialSpillSection(InputSectionBase *source, InputSectionDescription *cmd); static bool classof(const SectionBase *sec) { return sec->kind() == InputSectionBase::Spill; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 43070c308dd0c..872b80208006d 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -593,18 +593,19 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, if (!spills.contains(sec)) continue; - SpillInputSection *sis = make( + PotentialSpillSection *pss = make( sec, const_cast(cmd)); // Append the spill input section to the list for the input section, // creating it if necessary. - auto res = spillLists.try_emplace(sec, SpillList{sis, sis}); + auto res = + potentialSpillLists.try_emplace(sec, PotentialSpillList{pss, pss}); if (!res.second) { - SpillInputSection *&tail = res.first->second.tail; - tail = tail->next = sis; + PotentialSpillSection *&tail = res.first->second.tail; + tail = tail->next = pss; } - sec = sis; + sec = pss; } } @@ -967,11 +968,12 @@ void LinkerScript::diagnoseMissingSGSectionAddress() const { error("no address assigned to the veneers output section " + sec->name); } -void LinkerScript::copySpillList(InputSectionBase *dst, InputSectionBase *src) { - auto i = spillLists.find(src); - if (i == spillLists.end()) +void LinkerScript::copyPotentialSpillList(InputSectionBase *dst, + InputSectionBase *src) { + auto i = potentialSpillLists.find(src); + if (i == potentialSpillLists.end()) return; - spillLists.try_emplace(dst, i->second); + potentialSpillLists.try_emplace(dst, i->second); } // This function searches for a memory region to place the given output @@ -1135,7 +1137,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) { assert(isec->getParent() == sec); // Skip all potential spill locations. - if (isa(isec)) + if (isa(isec)) continue; const uint64_t pos = dot; @@ -1472,20 +1474,20 @@ bool LinkerScript::spillSections() { continue; for (InputSection *isec : reverse(isd->sections)) { // Potential spill locations cannot be spilled. - if (isa(isec)) + if (isa(isec)) continue; // Find the next spill location. - auto it = spillLists.find(isec); - if (it == spillLists.end()) + auto it = potentialSpillLists.find(isec); + if (it == potentialSpillLists.end()) continue; spilled = true; - SpillList &list = it->second; + PotentialSpillList &list = it->second; - SpillInputSection *spill = list.head; + PotentialSpillSection *spill = list.head; if (!spill->next) - spillLists.erase(isec); + potentialSpillLists.erase(isec); else list.head = spill->next; @@ -1526,22 +1528,23 @@ bool LinkerScript::spillSections() { } // Erase any potential spill sections that were not used. -void LinkerScript::eraseSpillSections() { - if (spillLists.empty()) +void LinkerScript::erasePotentialSpillSections() { + if (potentialSpillLists.empty()) return; // Collect the set of input section descriptions that contain potential // spills. DenseSet isds; - for (const auto &[_, list] : spillLists) - for (SpillInputSection *s = list.head; s; s = s->next) + for (const auto &[_, list] : potentialSpillLists) + for (PotentialSpillSection *s = list.head; s; s = s->next) isds.insert(s->isd); for (InputSectionDescription *isd : isds) - llvm::erase_if(isd->sections, - [](InputSection *s) { return isa(s); }); + llvm::erase_if(isd->sections, [](InputSection *s) { + return isa(s); + }); - spillLists.clear(); + potentialSpillLists.clear(); } // Creates program headers as instructed by PHDRS linker script command. diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 554df0ea88571..56bad3bbbd0d6 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -314,14 +314,14 @@ class LinkerScript final { uint64_t dot; - // List of potential spill locations (SpillInputSection) for an input + // List of potential spill locations (PotentialSpillSection) for an input // section. - struct SpillList { + struct PotentialSpillList { // Never nullptr. - SpillInputSection *head; - SpillInputSection *tail; + PotentialSpillSection *head; + PotentialSpillSection *tail; }; - llvm::DenseMap spillLists; + llvm::DenseMap potentialSpillLists; public: OutputDesc *createOutputSection(StringRef name, StringRef location); @@ -336,7 +336,7 @@ class LinkerScript final { void addOrphanSections(); void diagnoseOrphanHandling() const; void diagnoseMissingSGSectionAddress() const; - void copySpillList(InputSectionBase *dst, InputSectionBase *src); + void copyPotentialSpillList(InputSectionBase *dst, InputSectionBase *src); void adjustOutputSections(); void adjustSectionsAfterSorting(); @@ -346,7 +346,7 @@ class LinkerScript final { bool shouldKeep(InputSectionBase *s); const Defined *assignAddresses(); bool spillSections(); - void eraseSpillSections(); + void erasePotentialSpillSections(); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index d0bfd3b9ddc8c..a7ed3ff59e6f1 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -229,7 +229,7 @@ void OutputSection::finalizeInputSections(LinkerScript *script) { // The merge synthetic section inherits the potential spill locations of // its first contained section. if (script) - script->copySpillList(syn, ms); + script->copyPotentialSpillList(syn, ms); } (*i)->addSection(ms); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 4ef027704aa32..abd448b47b8fe 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1494,7 +1494,7 @@ template void Writer::finalizeAddressDependentContent() { // Sizes are no longer allowed to grow, so all allowable spills have been // taken. Remove any leftover potential spills. - script->eraseSpillSections(); + script->erasePotentialSpillSections(); } // If Input Sections have been shrunk (basic block sections) then From 2d40b1ebd8ad54c7f7c98d0d09e9154bfb0dac3f Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Wed, 8 May 2024 13:30:07 -0700 Subject: [PATCH 4/8] Address exidx test review comments - Update example to demonstrate merging - Make test require ARM - Two hashes for comments; one for non-split-file content - Fixed typo --- ...able-non-contiguous-regions-arm-exidx.test | 21 +-- .../enable-non-contiguous-regions.test | 140 +++++++++--------- 2 files changed, 81 insertions(+), 80 deletions(-) diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test index 8ead54f7cd8bd..39643e946aa01 100644 --- a/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test @@ -1,13 +1,14 @@ -# If a spilling reorders input sections, the .ARM.exidx table must be rebuilt -# using the new order. +## If a spilling reorders input sections, the .ARM.exidx table must be rebuilt +## using the new order. -RUN: split-file %s %t -RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %t/test.s -o %t/test.o -RUN: ld.lld -T %t/test.ld %t/test.o -o %t/test --enable-non-contiguous-regions -RUN: llvm-readobj -x .ARM.exidx %t/test | FileCheck %s +# REQUIRES: arm +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %t/test.s -o %t/test.o +# RUN: ld.lld -T %t/test.ld %t/test.o -o %t/test --enable-non-contiguous-regions +# RUN: llvm-readobj -x .ARM.exidx %t/test | FileCheck %s -CHECK: 20000000 08849780 1c000000 10849880 -CHECK-NEXT: 18000000 20849980 14000000 01000000 +# CHECK: 20000000 08849780 1c000000 10849880 +# CHECK-NEXT: 1c000000 01000000 #--- test.ld MEMORY { @@ -49,6 +50,6 @@ f1: f2: .fnstart bx lr - .save {r9, lr} - .setfp r9, sp, #0 + .save {r8, lr} + .setfp r8, sp, #0 .fnend diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test index d59c80b6f9220..907323b697d38 100644 --- a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test @@ -1,111 +1,111 @@ -REQUIRES: x86 +# REQUIRES: x86 -RUN: split-file %s %t -RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/spill.s -o %t/spill.o +# RUN: split-file %s %t +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/spill.s -o %t/spill.o -# An input section must spill to a later match if the region of its first match -# would overflow. +## An input section must spill to a later match if the region of its first match +## would overflow. -RUN: ld.lld -T %t/spill.ld %t/spill.o -o %t/spill --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill | FileCheck %s -check-prefix=SPILL +# RUN: ld.lld -T %t/spill.ld %t/spill.o -o %t/spill --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/spill | FileCheck %s -check-prefix=SPILL -SPILL: Name Type Address Off Size -SPILL: .first_chance PROGBITS 0000000000000000 001000 000001 -SPILL-NEXT: .last_chance PROGBITS 0000000000000002 001002 000002 +# SPILL: Name Type Address Off Size +# SPILL: .first_chance PROGBITS 0000000000000000 001000 000001 +# SPILL-NEXT: .last_chance PROGBITS 0000000000000002 001002 000002 -# A spill off the end must still fails the link. +## A spill off the end must still fail the link. -RUN: not ld.lld -T %t/spill-fail.ld %t/spill.o -o %t/spill-fail --enable-non-contiguous-regions 2>&1 |\ -RUN: FileCheck %s -check-prefix=SPILL-FAIL +# RUN: not ld.lld -T %t/spill-fail.ld %t/spill.o -o %t/spill-fail --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s -check-prefix=SPILL-FAIL -SPILL-FAIL: error: section '.last_chance' will not fit in region 'b': overflowed by 2 bytes +# SPILL-FAIL: error: section '.last_chance' will not fit in region 'b': overflowed by 2 bytes -# The above spill must still occur if the LMA would overflow, even if the VMA -# would fit. +## The above spill must still occur if the LMA would overflow, even if the VMA +## would fit. -RUN: ld.lld -T %t/spill-lma.ld %t/spill.o -o %t/spill-lma --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill-lma | FileCheck %s -check-prefix=SPILL-LMA +# RUN: ld.lld -T %t/spill-lma.ld %t/spill.o -o %t/spill-lma --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/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-LMA: Name Type Address Off Size +# SPILL-LMA: .first_chance PROGBITS 0000000000000000 001000 000001 +# SPILL-LMA-NEXT: .last_chance PROGBITS 0000000000000003 001003 000002 -# A spill must be able to occur to an additional match after the first. +## A spill must be able to occur to an additional match after the first. -RUN: ld.lld -T %t/spill-later.ld %t/spill.o -o %t/spill-later --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill-later | FileCheck %s -check-prefix=SPILL-LATER +# RUN: ld.lld -T %t/spill-later.ld %t/spill.o -o %t/spill-later --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/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-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 -# A later overflow must be able to cause an earlier section to spill. +## A later overflow must be able to cause an earlier section to spill. -RUN: ld.lld -T %t/spill-earlier.ld %t/spill.o -o %t/spill-earlier --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill-earlier | FileCheck %s -check-prefix=SPILL-EARLIER +# RUN: ld.lld -T %t/spill-earlier.ld %t/spill.o -o %t/spill-earlier --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/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 +# SPILL-EARLIER: Name Type Address Off Size +# SPILL-EARLIER: .first_chance PROGBITS 0000000000000000 001000 000002 +# SPILL-EARLIER-NEXT: .last_chance PROGBITS 0000000000000002 001002 000001 -# An additional match in /DISCARD/ must have no effect. +## An additional match in /DISCARD/ must have no effect. -RUN: not ld.lld -T %t/no-spill-into-discard.ld %t/spill.o -o %t/no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ -RUN: FileCheck %s -check-prefix=NO-SPILL-INTO-DISCARD +# RUN: not ld.lld -T %t/no-spill-into-discard.ld %t/spill.o -o %t/no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s -check-prefix=NO-SPILL-INTO-DISCARD -NO-SPILL-INTO-DISCARD: error: section '.osec' will not fit in region 'a': overflowed by 1 bytes +# NO-SPILL-INTO-DISCARD: error: section '.osec' will not fit in region 'a': overflowed by 1 bytes -# An additional match after /DISCARD/ must have no effect. +## An additional match after /DISCARD/ must have no effect. -RUN: ld.lld -T %t/no-spill-from-discard.ld %t/spill.o -o %t/no-spill-from-discard --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/no-spill-from-discard | FileCheck %s -check-prefix=NO-SPILL-FROM-DISCARD +# RUN: ld.lld -T %t/no-spill-from-discard.ld %t/spill.o -o %t/no-spill-from-discard --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/no-spill-from-discard | FileCheck %s -check-prefix=NO-SPILL-FROM-DISCARD -NO-SPILL-FROM-DISCARD: Name Type Address Off Size -NO-SPILL-FROM-DISCARD-NOT: .osec +# NO-SPILL-FROM-DISCARD: Name Type Address Off Size +# NO-SPILL-FROM-DISCARD-NOT: .osec -# A spill must use the alignment of the later match. +## A spill must use the alignment of the later match. -RUN: ld.lld -T %t/spill-align.ld %t/spill.o -o %t/spill-align --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill-align | FileCheck %s -check-prefix=SPILL-ALIGN +# RUN: ld.lld -T %t/spill-align.ld %t/spill.o -o %t/spill-align --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/spill-align | FileCheck %s -check-prefix=SPILL-ALIGN -SPILL-ALIGN: Name Type Address Off Size -SPILL-ALIGN: .first_chance PROGBITS 0000000000000000 000158 000000 -SPILL-ALIGN-NEXT: .last_chance PROGBITS 0000000000000008 001008 00000a +# SPILL-ALIGN: Name Type Address Off Size +# SPILL-ALIGN: .first_chance PROGBITS 0000000000000000 000158 000000 +# SPILL-ALIGN-NEXT: .last_chance PROGBITS 0000000000000008 001008 00000a -# SHF_MERGEd sections must be spilled according to the matches of the first -# merged input section (the one giving the resulting section its name). +## SHF_MERGEd sections must be spilled according to the matches of the first +## merged input section (the one giving the resulting section its name). -RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/merge.s -o %t/merge.o -RUN: ld.lld -T %t/spill-merge.ld %t/merge.o -o %t/spill-merge --enable-non-contiguous-regions -RUN: llvm-readelf -S %t/spill-merge | FileCheck %s -check-prefix=SPILL-MERGE +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/merge.s -o %t/merge.o +# RUN: ld.lld -T %t/spill-merge.ld %t/merge.o -o %t/spill-merge --enable-non-contiguous-regions +# RUN: llvm-readelf -S %t/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 +# 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 -# An error must be reported for INSERT. +## An error must be reported for INSERT. -RUN: not ld.lld -T %t/insert.ld %t/spill.o -o %t/insert --enable-non-contiguous-regions 2>&1 |\ -RUN: FileCheck %s -check-prefix=INSERT +# RUN: not ld.lld -T %t/insert.ld %t/spill.o -o %t/insert --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s -check-prefix=INSERT -INSERT: error: INSERT cannot be used with --enable-non-contiguous-regions +# INSERT: error: INSERT cannot be used with --enable-non-contiguous-regions -# An error must be reported for OVERWRITE_SECTIONS. +## An error must be reported for OVERWRITE_SECTIONS. -RUN: not ld.lld -T %t/overwrite-sections.ld %t/spill.o -o %t/overwrite-sections --enable-non-contiguous-regions 2>&1 |\ -RUN: FileCheck %s -check-prefix=OVERWRITE_SECTIONS +# RUN: not ld.lld -T %t/overwrite-sections.ld %t/spill.o -o %t/overwrite-sections --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s -check-prefix=OVERWRITE_SECTIONS -OVERWRITE_SECTIONS: error: OVERWRITE_SECTIONS cannot be used with --enable-non-contiguous-regions +# OVERWRITE_SECTIONS: error: OVERWRITE_SECTIONS cannot be used with --enable-non-contiguous-regions # SHF_LINK_ORDER must be reordered if spilling changes relative section order. -RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/link-order.s -o %t/link-order.o -RUN: ld.lld -T %t/link-order.ld %t/link-order.o -o %t/link-order --enable-non-contiguous-regions -RUN: llvm-readobj -x .order %t/link-order | FileCheck %s -check-prefix=LINK-ORDER +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/link-order.s -o %t/link-order.o +# RUN: ld.lld -T %t/link-order.ld %t/link-order.o -o %t/link-order --enable-non-contiguous-regions +# RUN: llvm-readobj -x .order %t/link-order | FileCheck %s -check-prefix=LINK-ORDER -LINK-ORDER: 0201 +# LINK-ORDER: 0201 #--- spill.s .section .one_byte_section,"a",@progbits From 03ec16f6c3fd01f59aca25dca2efea4f5a849f3f Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Fri, 10 May 2024 14:46:41 -0700 Subject: [PATCH 5/8] Address review comments - Remove split-file directory and cd into it. - Consolidate logic in computeInputSections(). - Make spill logic denser and group comments. - Create finalizeOrderDependentContent lambda. - Spill zero-size sections. - Remove warnings flag. - Make potentialSpillLists public; remove copySpillLists. - implicit-check-not: error: - Reword test descriptions to remove "must". - Merge spill and subalign test. - Missing hash before SHF_LINK_ORDER comment. - Add PR reference to release notes - Take potentialSpillSection constructor params by ref --- lld/ELF/InputSection.cpp | 10 +- lld/ELF/InputSection.h | 5 +- lld/ELF/LinkerScript.cpp | 83 ++++++-------- lld/ELF/LinkerScript.h | 18 +-- lld/ELF/Options.td | 2 - lld/ELF/OutputSections.cpp | 7 +- lld/ELF/Writer.cpp | 20 ++-- lld/docs/ELF/linker_script.rst | 2 +- lld/docs/ReleaseNotes.rst | 2 +- lld/docs/ld.lld.1 | 2 - ...able-non-contiguous-regions-arm-exidx.test | 12 +- .../enable-non-contiguous-regions.test | 108 +++++++----------- 12 files changed, 120 insertions(+), 151 deletions(-) diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index da41141066eb2..ab661c18f0cf4 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -310,11 +310,11 @@ std::string InputSectionBase::getObjMsg(uint64_t off) const { .str(); } -PotentialSpillSection::PotentialSpillSection(InputSectionBase *source, - InputSectionDescription *isd) - : InputSection(source->file, source->flags, source->type, source->addralign, - {}, source->name, SectionBase::Spill), - isd(isd) {} +PotentialSpillSection::PotentialSpillSection(const InputSectionBase &source, + InputSectionDescription &isd) + : InputSection(source.file, source.flags, source.type, source.addralign, {}, + source.name, SectionBase::Spill), + isd(&isd) {} InputSection InputSection::discarded(nullptr, 0, 0, 0, ArrayRef(), ""); diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index a81565810f88d..58e5306fd6dcd 100644 --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -48,7 +48,7 @@ template struct RelsOrRelas { // sections. class SectionBase { public: - enum Kind { Regular, Synthetic, EHFrame, Merge, Output, Spill }; + enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output }; Kind kind() const { return (Kind)sectionKind; } @@ -438,7 +438,8 @@ class PotentialSpillSection : public InputSection { // Next potential spill location for the same source input section. PotentialSpillSection *next = nullptr; - PotentialSpillSection(InputSectionBase *source, InputSectionDescription *cmd); + PotentialSpillSection(const InputSectionBase &source, + InputSectionDescription &isd); static bool classof(const SectionBase *sec) { return sec->kind() == InputSectionBase::Spill; diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 872b80208006d..75e5132042d1f 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -516,6 +516,24 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, 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(sec) && + cast(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) @@ -535,29 +553,11 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, // description within this output section. if (sec->parent == &outCmd) 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(sec) && - cast(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; + spills.insert(sec); + } ret.push_back(sec); - if (sec->parent) - spills.insert(sec); indexes.push_back(i); seen.insert(i); } @@ -593,18 +593,16 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, if (!spills.contains(sec)) continue; - PotentialSpillSection *pss = make( - sec, const_cast(cmd)); - // Append the spill input section to the list for the input section, // creating it if necessary. - auto res = + PotentialSpillSection *pss = make( + *sec, const_cast(*cmd)); + auto [it, inserted] = potentialSpillLists.try_emplace(sec, PotentialSpillList{pss, pss}); - if (!res.second) { - PotentialSpillSection *&tail = res.first->second.tail; + if (!inserted) { + PotentialSpillSection *&tail = it->second.tail; tail = tail->next = pss; } - sec = pss; } } @@ -968,14 +966,6 @@ void LinkerScript::diagnoseMissingSGSectionAddress() const { error("no address assigned to the veneers output section " + sec->name); } -void LinkerScript::copyPotentialSpillList(InputSectionBase *dst, - InputSectionBase *src) { - auto i = potentialSpillLists.find(src); - if (i == potentialSpillLists.end()) - return; - potentialSpillLists.try_emplace(dst, i->second); -} - // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned in the first member of the pair. Otherwise, a nullptr is returned. @@ -1457,7 +1447,7 @@ bool LinkerScript::spillSections() { if (!od) continue; OutputSection *osec = &od->osec; - if (!osec->size || !osec->memRegion) + if (!osec->memRegion) continue; // Input sections that have replaced a potential spill and should be removed @@ -1477,30 +1467,25 @@ bool LinkerScript::spillSections() { if (isa(isec)) continue; - // Find the next spill location. + // Find the next potential spill location and remove it from the list. auto it = potentialSpillLists.find(isec); if (it == potentialSpillLists.end()) continue; - - spilled = true; PotentialSpillList &list = it->second; - PotentialSpillSection *spill = list.head; if (!spill->next) potentialSpillLists.erase(isec); else list.head = spill->next; - spilledInputSections.insert(isec); - // Replace the next spill location with the spilled section and adjust - // its properties to match the new location. + // its properties to match the new location. Note that the alignment of + // the spill section may have diverged from the original due to e.g. a + // SUBALIGN. Correct assignment requires the spill's alignment to be + // used, not the original. + spilledInputSections.insert(isec); *llvm::find(spill->isd->sections, spill) = isec; isec->parent = spill->parent; - - // The alignment of the spill section may have diverged from the - // original due to e.g. a SUBALIGN. Correct assignment requires the - // spill's alignment to be used, not the original. isec->addralign = spill->addralign; // Record the (potential) reduction in the region's end position. @@ -1517,10 +1502,12 @@ bool LinkerScript::spillSections() { } // Remove any spilled input sections to complete their move. - if (!spilledInputSections.empty()) + if (!spilledInputSections.empty()) { + spilled = true; llvm::erase_if(isd->sections, [&](InputSection *isec) { return spilledInputSections.contains(isec); }); + } } } diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 56bad3bbbd0d6..1a527cd4a7e6e 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -314,15 +314,6 @@ class LinkerScript final { uint64_t dot; - // List of potential spill locations (PotentialSpillSection) for an input - // section. - struct PotentialSpillList { - // Never nullptr. - PotentialSpillSection *head; - PotentialSpillSection *tail; - }; - llvm::DenseMap potentialSpillLists; - public: OutputDesc *createOutputSection(StringRef name, StringRef location); OutputDesc *getOrCreateOutputSection(StringRef name); @@ -414,6 +405,15 @@ class LinkerScript final { // // then provideMap should contain the mapping: 'v' -> ['a', 'b', 'c'] llvm::MapVector> provideMap; + + // List of potential spill locations (PotentialSpillSection) for an input + // section. + struct PotentialSpillList { + // Never nullptr. + PotentialSpillSection *head; + PotentialSpillSection *tail; + }; + llvm::DenseMap potentialSpillLists; }; LLVM_LIBRARY_VISIBILITY extern std::unique_ptr script; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td index bad73dbb94d38..0e5d15124d260 100644 --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -198,8 +198,6 @@ def enable_new_dtags: F<"enable-new-dtags">, def enable_non_contiguous_regions : FF<"enable-non-contiguous-regions">, HelpText<"Spill input sections to later matching output sections to avoid memory region overflow">; -def enable_non_contiguous_regions_warnings : FF<"enable-non-contiguous-regions-warnings">, - HelpText<"No effect; included for GNU LD compatibility">; def end_group: F<"end-group">, HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">; diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index a7ed3ff59e6f1..b04b3c7d02e5f 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -228,8 +228,11 @@ void OutputSection::finalizeInputSections(LinkerScript *script) { isd->sections.push_back(syn); // The merge synthetic section inherits the potential spill locations of // its first contained section. - if (script) - script->copyPotentialSpillList(syn, ms); + if (script) { + auto it = script->potentialSpillLists.find(ms); + if (it != script->potentialSpillLists.end()) + script->potentialSpillLists.try_emplace(syn, it->second); + } } (*i)->addSection(ms); } diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index abd448b47b8fe..e4492d331a1fc 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1403,13 +1403,18 @@ template void Writer::finalizeAddressDependentContent() { AArch64Err843419Patcher a64p; ARMErr657417Patcher a32p; script->assignAddresses(); + // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they // do require the relative addresses of OutputSections because linker scripts // can assign Virtual Addresses to OutputSections that are not monotonically - // increasing. - for (Partition &part : partitions) - finalizeSynthetic(part.armExidx.get()); - resolveShfLinkOrder(); + // increasing. Anything here must be repeatable, since spilling may change + // section order. + const auto finalizeOrderDependentContent = [this] { + for (Partition &part : partitions) + finalizeSynthetic(part.armExidx.get()); + resolveShfLinkOrder(); + }; + finalizeOrderDependentContent(); // Converts call x@GDPLT to call __tls_get_addr if (config->emachine == EM_HEXAGON) @@ -1467,11 +1472,8 @@ template void Writer::finalizeAddressDependentContent() { break; } } else if (spilled) { - // Spilling can change relative section order, so recompute anything that - // depends on it. - for (Partition &part : partitions) - finalizeSynthetic(part.armExidx.get()); - resolveShfLinkOrder(); + // Spilling can change relative section order. + finalizeOrderDependentContent(); } } if (!config->relocatable) diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst index e9319db900220..7a35534be096c 100644 --- a/lld/docs/ELF/linker_script.rst +++ b/lld/docs/ELF/linker_script.rst @@ -206,5 +206,5 @@ 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`` has no effect, as it +GNU flag ``--enable-non-contiguous-regions-warnings`` is not implemented, as it exists to warn about such occurrences. diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index 4755e0415fe91..711cb7493a44e 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -40,7 +40,7 @@ ELF Improvements region would overflow. This reduces the toil of manually packing regions (typical for embedded). It also makes full LTO feasible in such cases, since IR merging currently prevents the linker script from referring to input - files. + files. (`#90007 `_) Breaking changes ---------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 1e82572d0de4b..6441f494ffac0 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -224,8 +224,6 @@ Generate relocations in the output. Enable new dynamic tags. .It Fl -enable-non-contiguous-regions Spill input sections to later matching output sections to avoid memory region overflow. -.It Fl -enable-non-contiguous-regions-warnings -No effect; included for GNU LD compatibility. .It Fl -end-lib End a grouping of objects that should be treated as if they were together in an archive. diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test index 39643e946aa01..3f7b9c4e5f8b8 100644 --- a/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test @@ -1,11 +1,11 @@ -## If a spilling reorders input sections, the .ARM.exidx table must be rebuilt -## using the new order. +## When spilling reorders input sections, the .ARM.exidx table is rebuilt using +## the new order. # REQUIRES: arm -# RUN: split-file %s %t -# RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %t/test.s -o %t/test.o -# RUN: ld.lld -T %t/test.ld %t/test.o -o %t/test --enable-non-contiguous-regions -# RUN: llvm-readobj -x .ARM.exidx %t/test | FileCheck %s +# RUN: rm -rf %t && split-file %s %t && cd %t +# RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi test.s -o test.o +# RUN: ld.lld -T test.ld test.o -o test --enable-non-contiguous-regions +# RUN: llvm-readobj -x .ARM.exidx test | FileCheck %s # CHECK: 20000000 08849780 1c000000 10849880 # CHECK-NEXT: 1c000000 01000000 diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test index 907323b697d38..00d49dd57bb89 100644 --- a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test @@ -1,109 +1,100 @@ # REQUIRES: x86 -# RUN: split-file %s %t -# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/spill.s -o %t/spill.o +# RUN: rm -rf %t && split-file %s %t && cd %t +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 spill.s -o spill.o -## An input section must spill to a later match if the region of its first match -## would overflow. +## An input section spills to a later match when the region of its first match +## would overflow. The spill uses the alignment of the later match. -# RUN: ld.lld -T %t/spill.ld %t/spill.o -o %t/spill --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill | FileCheck %s -check-prefix=SPILL +# RUN: ld.lld -T spill.ld spill.o -o spill --enable-non-contiguous-regions +# 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 0000000000000002 001002 000002 +# SPILL-NEXT: .last_chance PROGBITS 0000000000000008 001008 000002 -## A spill off the end must still fail the link. +## A spill off the end still fails the link. -# RUN: not ld.lld -T %t/spill-fail.ld %t/spill.o -o %t/spill-fail --enable-non-contiguous-regions 2>&1 |\ -# RUN: FileCheck %s -check-prefix=SPILL-FAIL +# RUN: not ld.lld -T spill-fail.ld spill.o -o spill-fail --enable-non-contiguous-regions 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 -## The above spill must still occur if the LMA would overflow, even if the VMA -## would fit. +## The above spill still occurs when the LMA would overflow, even though the +## VMA would fit. -# RUN: ld.lld -T %t/spill-lma.ld %t/spill.o -o %t/spill-lma --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill-lma | FileCheck %s -check-prefix=SPILL-LMA +# RUN: ld.lld -T spill-lma.ld spill.o -o spill-lma --enable-non-contiguous-regions +# 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 -## A spill must be able to occur to an additional match after the first. +## A spill occurs to an additional match after the first. -# RUN: ld.lld -T %t/spill-later.ld %t/spill.o -o %t/spill-later --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill-later | FileCheck %s -check-prefix=SPILL-LATER +# RUN: ld.lld -T spill-later.ld spill.o -o spill-later --enable-non-contiguous-regions +# 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 -## A later overflow must be able to cause an earlier section to spill. +## A later overflow causes an earlier section to spill. -# RUN: ld.lld -T %t/spill-earlier.ld %t/spill.o -o %t/spill-earlier --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill-earlier | FileCheck %s -check-prefix=SPILL-EARLIER +# RUN: ld.lld -T spill-earlier.ld spill.o -o spill-earlier --enable-non-contiguous-regions +# 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 -## An additional match in /DISCARD/ must have no effect. +## An additional match in /DISCARD/ has no effect. -# RUN: not ld.lld -T %t/no-spill-into-discard.ld %t/spill.o -o %t/no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ -# RUN: FileCheck %s -check-prefix=NO-SPILL-INTO-DISCARD +# RUN: not ld.lld -T no-spill-into-discard.ld spill.o -o no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s --check-prefix=NO-SPILL-INTO-DISCARD --implicit-check-not=error: # NO-SPILL-INTO-DISCARD: error: section '.osec' will not fit in region 'a': overflowed by 1 bytes -## An additional match after /DISCARD/ must have no effect. +## An additional match after /DISCARD/ has no effect. -# RUN: ld.lld -T %t/no-spill-from-discard.ld %t/spill.o -o %t/no-spill-from-discard --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/no-spill-from-discard | FileCheck %s -check-prefix=NO-SPILL-FROM-DISCARD +# RUN: ld.lld -T no-spill-from-discard.ld spill.o -o no-spill-from-discard --enable-non-contiguous-regions +# RUN: llvm-readelf -S no-spill-from-discard | FileCheck %s --check-prefix=NO-SPILL-FROM-DISCARD # NO-SPILL-FROM-DISCARD: Name Type Address Off Size # NO-SPILL-FROM-DISCARD-NOT: .osec -## A spill must use the alignment of the later match. +## SHF_MERGEd sections are spilled according to the matches of the first merged +## input section (the one giving the resulting section its name). -# RUN: ld.lld -T %t/spill-align.ld %t/spill.o -o %t/spill-align --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill-align | FileCheck %s -check-prefix=SPILL-ALIGN - -# SPILL-ALIGN: Name Type Address Off Size -# SPILL-ALIGN: .first_chance PROGBITS 0000000000000000 000158 000000 -# SPILL-ALIGN-NEXT: .last_chance PROGBITS 0000000000000008 001008 00000a - -## SHF_MERGEd sections must be spilled according to the matches of the first -## merged input section (the one giving the resulting section its name). - -# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/merge.s -o %t/merge.o -# RUN: ld.lld -T %t/spill-merge.ld %t/merge.o -o %t/spill-merge --enable-non-contiguous-regions -# RUN: llvm-readelf -S %t/spill-merge | FileCheck %s -check-prefix=SPILL-MERGE +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 merge.s -o merge.o +# RUN: ld.lld -T spill-merge.ld merge.o -o spill-merge --enable-non-contiguous-regions +# 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 -## An error must be reported for INSERT. +## An error is reported for INSERT. -# RUN: not ld.lld -T %t/insert.ld %t/spill.o -o %t/insert --enable-non-contiguous-regions 2>&1 |\ -# RUN: FileCheck %s -check-prefix=INSERT +# RUN: not ld.lld -T insert.ld spill.o -o insert --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s --check-prefix=INSERT # INSERT: error: INSERT cannot be used with --enable-non-contiguous-regions -## An error must be reported for OVERWRITE_SECTIONS. +## An error is reported for OVERWRITE_SECTIONS. -# RUN: not ld.lld -T %t/overwrite-sections.ld %t/spill.o -o %t/overwrite-sections --enable-non-contiguous-regions 2>&1 |\ -# RUN: FileCheck %s -check-prefix=OVERWRITE_SECTIONS +# RUN: not ld.lld -T overwrite-sections.ld spill.o -o overwrite-sections --enable-non-contiguous-regions 2>&1 |\ +# RUN: FileCheck %s --check-prefix=OVERWRITE_SECTIONS # OVERWRITE_SECTIONS: error: OVERWRITE_SECTIONS cannot be used with --enable-non-contiguous-regions -# SHF_LINK_ORDER must be reordered if spilling changes relative section order. +## SHF_LINK_ORDER is reordered when spilling changes relative section order. -# RUN: llvm-mc -n -filetype=obj -triple=x86_64 %t/link-order.s -o %t/link-order.o -# RUN: ld.lld -T %t/link-order.ld %t/link-order.o -o %t/link-order --enable-non-contiguous-regions -# RUN: llvm-readobj -x .order %t/link-order | FileCheck %s -check-prefix=LINK-ORDER +# RUN: llvm-mc -n -filetype=obj -triple=x86_64 link-order.s -o link-order.o +# RUN: ld.lld -T link-order.ld link-order.o -o link-order --enable-non-contiguous-regions +# RUN: llvm-readobj -x .order link-order | FileCheck %s --check-prefix=LINK-ORDER # LINK-ORDER: 0201 @@ -117,12 +108,12 @@ #--- spill.ld MEMORY { a : ORIGIN = 0, LENGTH = 2 - b : ORIGIN = 2, LENGTH = 2 + b : ORIGIN = 2, LENGTH = 16 } SECTIONS { - .first_chance : { *(.one_byte_section) *(.two_byte_section) } >a - .last_chance : { *(.two_byte_section) } >b + .first_chance : SUBALIGN(1) { *(.one_byte_section) *(.two_byte_section) } >a + .last_chance : SUBALIGN(8) { *(.two_byte_section) } >b } #--- spill-fail.ld @@ -193,17 +184,6 @@ SECTIONS { .osec : { *(.two_byte_section) } >a } -#--- spill-align.ld -MEMORY { - a : ORIGIN = 0, LENGTH = 0 - b : ORIGIN = 2, LENGTH = 16 -} - -SECTIONS { - .first_chance : SUBALIGN(1) { *(.two_byte_section) } >a - .last_chance : SUBALIGN(8) { *(.one_byte_section) *(.two_byte_section) } >b -} - #--- merge.s .section .a,"aM",@progbits,1 .byte 0x12, 0x34 From d7a4f4327e1ae71cfa8fccc11463d4cd1bb4b70a Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Fri, 10 May 2024 16:36:36 -0700 Subject: [PATCH 6/8] Remove trivial comment and blank lines; remove dead function declaration --- lld/ELF/LinkerScript.cpp | 3 --- lld/ELF/LinkerScript.h | 1 - 2 files changed, 4 deletions(-) diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index 75e5132042d1f..b3dbedbfc41b4 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1125,11 +1125,8 @@ void LinkerScript::assignOffsets(OutputSection *sec) { auto §ions = cast(cmd)->sections; for (InputSection *isec : sections) { assert(isec->getParent() == sec); - - // Skip all potential spill locations. if (isa(isec)) continue; - const uint64_t pos = dot; dot = alignToPowerOf2(dot, isec->addralign); isec->outSecOff = dot - sec->addr; diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 1a527cd4a7e6e..9e95d2923bd60 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -327,7 +327,6 @@ class LinkerScript final { void addOrphanSections(); void diagnoseOrphanHandling() const; void diagnoseMissingSGSectionAddress() const; - void copyPotentialSpillList(InputSectionBase *dst, InputSectionBase *src); void adjustOutputSections(); void adjustSectionsAfterSorting(); From 915fb2c203fe011a1e79e2dbf6229fec40f4170b Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Fri, 10 May 2024 16:36:36 -0700 Subject: [PATCH 7/8] Review comments - Swap true and false branches to remove condition negation - No -o for failure tests - Add a third link order section to show both normal sorting and reordering due to spilling. --- lld/ELF/LinkerScript.cpp | 6 ++--- .../enable-non-contiguous-regions.test | 26 ++++++++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index b3dbedbfc41b4..12cbb8e00cf16 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1470,10 +1470,10 @@ bool LinkerScript::spillSections() { continue; PotentialSpillList &list = it->second; PotentialSpillSection *spill = list.head; - if (!spill->next) - potentialSpillLists.erase(isec); - else + if (spill->next) list.head = spill->next; + else + potentialSpillLists.erase(isec); // Replace the next spill location with the spilled section and adjust // its properties to match the new location. Note that the alignment of diff --git a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test index 00d49dd57bb89..392106fd476fd 100644 --- a/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test @@ -15,7 +15,7 @@ ## A spill off the end still fails the link. -# RUN: not ld.lld -T spill-fail.ld spill.o -o spill-fail --enable-non-contiguous-regions 2>&1 |\ +# RUN: not ld.lld -T spill-fail.ld spill.o --enable-non-contiguous-regions 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 @@ -51,7 +51,7 @@ ## An additional match in /DISCARD/ has no effect. -# RUN: not ld.lld -T no-spill-into-discard.ld spill.o -o no-spill-into-discard --enable-non-contiguous-regions 2>&1 |\ +# RUN: not ld.lld -T no-spill-into-discard.ld spill.o --enable-non-contiguous-regions 2>&1 |\ # RUN: FileCheck %s --check-prefix=NO-SPILL-INTO-DISCARD --implicit-check-not=error: # NO-SPILL-INTO-DISCARD: error: section '.osec' will not fit in region 'a': overflowed by 1 bytes @@ -78,14 +78,14 @@ ## An error is reported for INSERT. -# RUN: not ld.lld -T insert.ld spill.o -o insert --enable-non-contiguous-regions 2>&1 |\ +# RUN: not ld.lld -T insert.ld spill.o --enable-non-contiguous-regions 2>&1 |\ # RUN: FileCheck %s --check-prefix=INSERT # INSERT: error: INSERT cannot be used with --enable-non-contiguous-regions ## An error is reported for OVERWRITE_SECTIONS. -# RUN: not ld.lld -T overwrite-sections.ld spill.o -o overwrite-sections --enable-non-contiguous-regions 2>&1 |\ +# RUN: not ld.lld -T overwrite-sections.ld spill.o --enable-non-contiguous-regions 2>&1 |\ # RUN: FileCheck %s --check-prefix=OVERWRITE_SECTIONS # OVERWRITE_SECTIONS: error: OVERWRITE_SECTIONS cannot be used with --enable-non-contiguous-regions @@ -96,7 +96,7 @@ # RUN: ld.lld -T link-order.ld link-order.o -o link-order --enable-non-contiguous-regions # RUN: llvm-readobj -x .order link-order | FileCheck %s --check-prefix=LINK-ORDER -# LINK-ORDER: 0201 +# LINK-ORDER: 020301 #--- spill.s .section .one_byte_section,"a",@progbits @@ -237,23 +237,29 @@ OVERWRITE_SECTIONS { .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 + #--- link-order.ld MEMORY { - order : ORIGIN = 0, LENGTH = 2 - potential_a : ORIGIN = 2, LENGTH = 0 - b : ORIGIN = 2, LENGTH = 1 - actual_a : ORIGIN = 3, LENGTH = 1 + order : ORIGIN = 0, LENGTH = 3 + potential_a : ORIGIN = 3, LENGTH = 0 + bc : ORIGIN = 3, LENGTH = 2 + actual_a : ORIGIN = 5, LENGTH = 1 } SECTIONS { .order : { *(.link_order.*) } > order .potential_a : { *(.a) } >potential_a - .b : { *(.b) } >b + .bc : { *(.b) *(.c) } >bc .actual_a : { *(.a) } >actual_a } From 62e9896c86b6ea27f117fa05b115987300155d6e Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Mon, 13 May 2024 10:29:52 -0700 Subject: [PATCH 8/8] Remove if check around script; should never be hit --- lld/ELF/OutputSections.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index b04b3c7d02e5f..881246203cd70 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -228,11 +228,9 @@ void OutputSection::finalizeInputSections(LinkerScript *script) { isd->sections.push_back(syn); // The merge synthetic section inherits the potential spill locations of // its first contained section. - if (script) { - auto it = script->potentialSpillLists.find(ms); - if (it != script->potentialSpillLists.end()) - script->potentialSpillLists.try_emplace(syn, it->second); - } + auto it = script->potentialSpillLists.find(ms); + if (it != script->potentialSpillLists.end()) + script->potentialSpillLists.try_emplace(syn, it->second); } (*i)->addSection(ms); }