[lld] [LLD] Implement --enable-non-contiguous-regions (PR #90007)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 24 17:00:37 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld

Author: Daniel Thornburgh (mysterymath)

<details>
<summary>Changes</summary>

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.

---

Patch is 34.34 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/90007.diff


17 Files Affected:

- (modified) lld/ELF/Config.h (+1) 
- (modified) lld/ELF/Driver.cpp (+3-1) 
- (modified) lld/ELF/ICF.cpp (+1) 
- (modified) lld/ELF/InputSection.cpp (+8) 
- (modified) lld/ELF/InputSection.h (+22-2) 
- (modified) lld/ELF/LinkerScript.cpp (+177-9) 
- (modified) lld/ELF/LinkerScript.h (+15-1) 
- (modified) lld/ELF/Options.td (+3) 
- (modified) lld/ELF/OutputSections.cpp (+3-1) 
- (modified) lld/ELF/OutputSections.h (+1-1) 
- (modified) lld/ELF/SyntheticSections.cpp (+7) 
- (modified) lld/ELF/SyntheticSections.h (+4) 
- (modified) lld/ELF/Writer.cpp (+12) 
- (modified) lld/docs/ELF/linker_script.rst (+11) 
- (modified) lld/docs/ReleaseNotes.rst (+11) 
- (added) lld/test/ELF/linkerscript/enable-non-contiguous-regions-arm-exidx.test (+54) 
- (added) lld/test/ELF/linkerscript/enable-non-contiguous-regions.test (+279) 


``````````diff
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 33bfa42b0fcbf0..bb5d7001f59fbb 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 a5b47f020f8726..915184c1aa2632 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 <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
     // sectionBases.
     for (SectionCommand *cmd : script->sectionCommands)
       if (auto *osd = dyn_cast<OutputDesc>(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/ICF.cpp b/lld/ELF/ICF.cpp
index bfc605c793a92c..677f325e854065 100644
--- a/lld/ELF/ICF.cpp
+++ b/lld/ELF/ICF.cpp
@@ -75,6 +75,7 @@
 #include "ICF.h"
 #include "Config.h"
 #include "InputFiles.h"
+#include "InputSection.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
 #include "SymbolTable.h"
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index fa48552b8f7a12..aa5e8f11177fd4 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -9,6 +9,7 @@
 #include "InputSection.h"
 #include "Config.h"
 #include "InputFiles.h"
+#include "LinkerScript.h"
 #include "OutputSections.h"
 #include "Relocations.h"
 #include "SymbolTable.h"
@@ -161,6 +162,7 @@ uint64_t SectionBase::getOffset(uint64_t offset) const {
   }
   case Regular:
   case Synthetic:
+  case Spill:
     return cast<InputSection>(this)->outSecOff + offset;
   case EHFrame: {
     // Two code paths may reach here. First, clang_rt.crtbegin.o and GCC
@@ -309,6 +311,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<uint8_t>(), "");
 
 InputSection::InputSection(InputFile *f, uint64_t flags, uint32_t type,
diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index 1fb7077ca435bd..3d75a9633a3ccf 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -48,7 +48,7 @@ template <class ELFT> 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 <class ELFT> 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 f815b3ac6feeda..41827015868496 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -304,6 +304,9 @@ getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
 void LinkerScript::processInsertCommands() {
   SmallVector<OutputDesc *, 0> 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.
@@ -484,12 +487,13 @@ static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
 }
 
 // Compute and remember which sections the InputSectionDescription matches.
-SmallVector<InputSectionBase *, 0>
-LinkerScript::computeInputSections(const InputSectionDescription *cmd,
-                                   ArrayRef<InputSectionBase *> sections) {
+SmallVector<InputSectionBase *, 0> LinkerScript::computeInputSections(
+    const InputSectionDescription *cmd, ArrayRef<InputSectionBase *> sections,
+    const OutputSection &outCmd) {
   SmallVector<InputSectionBase *, 0> ret;
   SmallVector<size_t, 0> indexes;
   DenseSet<size_t> seen;
+  DenseSet<InputSectionBase *> spills;
   auto sortByPositionThenCommandLine = [&](size_t begin, size_t end) {
     llvm::sort(MutableArrayRef<size_t>(indexes).slice(begin, end - begin));
     for (size_t i = begin; i != end; ++i)
@@ -505,12 +509,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 +555,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 +582,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<SpillInputSection>(
+          sec, const_cast<InputSectionDescription *>(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 +626,7 @@ void LinkerScript::discardSynthetic(OutputSection &outCmd) {
         part.armExidx->exidxSections.end());
     for (SectionCommand *cmd : outCmd.commands)
       if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
-        for (InputSectionBase *s : computeInputSections(isd, secs))
+        for (InputSectionBase *s : computeInputSections(isd, secs, outCmd))
           discard(*s);
   }
 }
@@ -588,7 +637,7 @@ LinkerScript::createInputSectionList(OutputSection &outCmd) {
 
   for (SectionCommand *cmd : outCmd.commands) {
     if (auto *isd = dyn_cast<InputSectionDescription>(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 +693,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<CachedHashStringRef, OutputDesc *> map;
   size_t i = 0;
   for (OutputDesc *osd : overwriteSections) {
@@ -911,6 +963,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 +1125,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<InputSectionDescription>(cmd)->sections) {
+
+    DenseSet<InputSection*> spills;
+    auto &sections = cast<InputSectionDescription>(cmd)->sections;
+    for (InputSection *isec : sections) {
       assert(isec->getParent() == sec);
+
+      // Skip all possible spills.
+      if (isa<SpillInputSection>(isec))
+        continue;
+
       const uint64_t pos = dot;
       dot = alignToPowerOf2(dot, isec->addralign);
       isec->outSecOff = dot - sec->addr;
@@ -1364,6 +1431,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<OutputDesc>(cmd);
+    if (!od)
+      continue;
+    OutputSection *osec = &od->osec;
+    if (!osec->size || !osec->memRegion)
+      continue;
+
+    DenseSet<InputSection *> spills;
+    for (SectionCommand *cmd : reverse(osec->commands)) {
+      if (!isRegionOverflowed(osec->memRegion) &&
+          !isRegionOverflowed(osec->lmaRegion))
+        break;
+
+      auto *is = dyn_cast<InputSectionDescription>(cmd);
+      if (!is)
+        continue;
+      for (InputSection *isec : reverse(is->sections)) {
+        // Potential spill locations cannot be spilled.
+        if (isa<SpillInputSection>(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<InputSectionDescription *> 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<SpillInputSection>(s); });
+
+  spillLists.clear();
+}
+
 // Creates program headers as instructed by PHDRS linker script command.
 SmallVector<PhdrEntry *, 0> LinkerScript::createPhdrs() {
   SmallVector<PhdrEntry *, 0> ret;
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index fa7c6eb9c0d8f7..920eadfdb82b3d 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<InputSectionBase *, 0>
   computeInputSections(const InputSectionDescription *,
-                       ArrayRef<InputSectionBase *>);
+                       ArrayRef<InputSectionBase *>,
+                       const OutputSection& outCmd);
 
   SmallVector<InputSectionBase *, 0> 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<InputSectionBase*, SpillList> 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<PhdrEntry *, 0> &phdrs);
   void processSectionCommands();
   void processSymbolAssignments();
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 72eaf157a181cf..0e5d15124d260b 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 eadab9d745d687..1a6c5b7a09f4ce 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<MergeSyntheticSection *> mergeSections;
   for (SectionCommand *cmd : commands) {
     auto *isd = dyn_cast<InputSectionDescription>(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 421a0181feb5df..78fede48a23f25 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 7b9ada40c0f67b..298c714adb3b43 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...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/90007


More information about the llvm-commits mailing list