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..ab661c18f0cf4 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(); } +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(), ""); InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type, diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h index 1fb7077ca435b..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 }; + enum Kind { Regular, Synthetic, Spill, EHFrame, Merge, Output }; 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,26 @@ 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 PotentialSpillSection : public InputSection { +public: + // The containing input section description; used to quickly replace this stub + // with the actual section. + InputSectionDescription *isd; + + // Next potential spill location for the same source input section. + PotentialSpillSection *next = nullptr; + + PotentialSpillSection(const InputSectionBase &source, + InputSectionDescription &isd); + + 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..12cbb8e00cf16 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,10 +510,10 @@ 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; // For --emit-relocs we have to ignore entries like @@ -529,6 +534,29 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, (sec->flags & cmd->withoutFlags) != 0) 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; + + spills.insert(sec); + } + ret.push_back(sec); indexes.push_back(i); seen.insert(i); @@ -555,6 +583,30 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd, // Matched sections after the last SORT* are sorted by (--sort-alignment, // input order). sortByPositionThenCommandLine(sizeAfterPrevSort, ret.size()); + + // The flag --enable-non-contiguous-regions may cause sections to match an + // InputSectionDescription in more than one OutputSection. Matches after the + // first were collected in the spills set, so replace these with potential + // spill sections. + if (!spills.empty()) { + for (InputSectionBase *&sec : ret) { + if (!spills.contains(sec)) + continue; + + // Append the spill input section to the list for the input section, + // creating it if necessary. + PotentialSpillSection *pss = make( + *sec, const_cast(*cmd)); + auto [it, inserted] = + potentialSpillLists.try_emplace(sec, PotentialSpillList{pss, pss}); + if (!inserted) { + PotentialSpillSection *&tail = it->second.tail; + tail = tail->next = pss; + } + sec = pss; + } + } + return ret; } @@ -577,7 +629,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 +640,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 +696,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) { @@ -1066,8 +1121,12 @@ 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) { + + auto §ions = cast(cmd)->sections; + for (InputSection *isec : sections) { assert(isec->getParent() == sec); + if (isa(isec)) + continue; const uint64_t pos = dot; dot = alignToPowerOf2(dot, isec->addralign); isec->outSecOff = dot - sec->addr; @@ -1364,6 +1423,114 @@ const Defined *LinkerScript::assignAddresses() { return getChangedSymbolAssignment(oldValues); } +static bool hasRegionOverflowed(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->memRegion) + continue; + + // 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 (!hasRegionOverflowed(osec->memRegion) && + !hasRegionOverflowed(osec->lmaRegion)) + break; + + auto *isd = dyn_cast(cmd); + if (!isd) + continue; + for (InputSection *isec : reverse(isd->sections)) { + // Potential spill locations cannot be spilled. + if (isa(isec)) + continue; + + // Find the next potential spill location and remove it from the list. + auto it = potentialSpillLists.find(isec); + if (it == potentialSpillLists.end()) + continue; + PotentialSpillList &list = it->second; + PotentialSpillSection *spill = list.head; + 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 + // 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; + isec->addralign = spill->addralign; + + // Record the (potential) reduction in the region's end position. + osec->memRegion->curPos -= isec->getSize(); + if (osec->lmaRegion) + osec->lmaRegion->curPos -= isec->getSize(); + + // 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 input sections to complete their move. + if (!spilledInputSections.empty()) { + spilled = true; + llvm::erase_if(isd->sections, [&](InputSection *isec) { + return spilledInputSections.contains(isec); + }); + } + } + } + + return spilled; +} + +// Erase any potential spill sections that were not used. +void LinkerScript::erasePotentialSpillSections() { + if (potentialSpillLists.empty()) + return; + + // Collect the set of input section descriptions that contain potential + // spills. + DenseSet isds; + 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); + }); + + potentialSpillLists.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..9e95d2923bd60 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); @@ -333,6 +335,8 @@ class LinkerScript final { bool shouldKeep(InputSectionBase *s); const Defined *assignAddresses(); + bool spillSections(); + void erasePotentialSpillSections(); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); @@ -400,6 +404,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 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..881246203cd70 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,11 @@ void OutputSection::finalizeInputSections() { 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. + 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/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..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) @@ -1419,6 +1424,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 +1471,9 @@ template void Writer::finalizeAddressDependentContent() { " does not converge"); break; } + } else if (spilled) { + // Spilling can change relative section order. + finalizeOrderDependentContent(); } } if (!config->relocatable) @@ -1483,6 +1493,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->erasePotentialSpillSections(); } // 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..711cb7493a44e 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -35,6 +35,12 @@ 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 `_) +* ``--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. (`#90007 `_) Breaking changes ---------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index 3861120915e8b..6441f494ffac0 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -222,6 +222,8 @@ 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 -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 new file mode 100644 index 0000000000000..3f7b9c4e5f8b8 --- /dev/null +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test @@ -0,0 +1,55 @@ +## When spilling reorders input sections, the .ARM.exidx table is rebuilt using +## the new order. + +# REQUIRES: arm +# 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 + +#--- 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 {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 new file mode 100644 index 0000000000000..392106fd476fd --- /dev/null +++ b/lld/test/ELF/linkerscript/enable-non-contiguous-regions.test @@ -0,0 +1,265 @@ +# REQUIRES: x86 + +# 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 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 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 0000000000000008 001008 000002 + +## A spill off the end still fails the link. + +# 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 + +## The above spill still occurs when the LMA would overflow, even though the +## VMA would fit. + +# 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 occurs to an additional match after the first. + +# 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 causes an earlier section to spill. + +# 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/ has no effect. + +# 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 + +## An additional match after /DISCARD/ has no effect. + +# 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 + +## SHF_MERGEd sections are 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 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 is reported for INSERT. + +# 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 --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 is reordered when spilling changes relative section 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: 020301 + +#--- 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 = 16 +} + +SECTIONS { + .first_chance : SUBALIGN(1) { *(.one_byte_section) *(.two_byte_section) } >a + .last_chance : SUBALIGN(8) { *(.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 +} + +#--- 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 .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 = 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 + .bc : { *(.b) *(.c) } >bc + .actual_a : { *(.a) } >actual_a +}