blob: b1569a177a205e4eb884539ba5e89dbd33306338 [file] [log] [blame]
commit e47bbd28f8e9dfea4e019e27e9d371c144c3302e
Author: Fangrui Song <maskray@google.com>
Date: Tue Sep 24 11:48:31 2019 +0000
[ELF] Make MergeInputSection merging aware of output sections
Fixes PR38748
mergeSections() calls getOutputSectionName() to get output section
names. Two MergeInputSections may be merged even if they are made
different by SECTIONS commands.
This patch moves mergeSections() after processSectionCommands() and
addOrphanSections() to fix the issue. The new pass is renamed to
OutputSection::finalizeInputSections().
processSectionCommands() and addorphanSections() are changed to add
sections to InputSectionDescription::sectionBases.
finalizeInputSections() merges MergeInputSections and migrates
`sectionBases` to `sections`.
For the -r case, we drop an optimization that tries keeping sh_entsize
non-zero. This is for the simplicity of addOrphanSections(). The
updated merge-entsize2.s reflects the change.
Reviewed By: grimar
Differential Revision: https://reviews.llvm.org/D67504
llvm-svn: 372734
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index bae04791ec4..f4a4d1d3fd1 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1889,8 +1889,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
"feature detected");
}
- // This adds a .comment section containing a version string. We have to add it
- // before mergeSections because the .comment section is a mergeable section.
+ // This adds a .comment section containing a version string.
if (!config->relocatable)
inputSections.push_back(createCommentSection());
@@ -1902,7 +1901,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
splitSections<ELFT>();
markLive<ELFT>();
demoteSharedSymbols();
- mergeSections();
// Make copies of any input sections that need to be copied into each
// partition.
@@ -1926,6 +1924,16 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// they are assigned to output sections by the default rule. Process that.
script->addOrphanSections();
+ // Migrate InputSectionDescription::sectionBases to sections. This includes
+ // merging MergeInputSections into a single MergeSyntheticSection. From this
+ // point onwards InputSectionDescription::sections should be used instead of
+ // sectionBases.
+ for (BaseCommand *base : script->sectionCommands)
+ if (auto *sec = dyn_cast<OutputSection>(base))
+ sec->finalizeInputSections();
+ llvm::erase_if(inputSections,
+ [](InputSectionBase *s) { return isa<MergeInputSection>(s); });
+
// Two input sections with different output sections should not be folded.
// ICF runs after processSectionCommands() so that we know the output sections.
if (config->icf != ICFLevel::None) {
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index cd02e779e7a..48f6c38cce6 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -341,19 +341,19 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
}
// A helper function for the SORT() command.
-static bool matchConstraints(ArrayRef<InputSection *> sections,
+static bool matchConstraints(ArrayRef<InputSectionBase *> sections,
ConstraintKind kind) {
if (kind == ConstraintKind::NoConstraint)
return true;
bool isRW = llvm::any_of(
- sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; });
+ sections, [](InputSectionBase *sec) { return sec->flags & SHF_WRITE; });
return (isRW && kind == ConstraintKind::ReadWrite) ||
(!isRW && kind == ConstraintKind::ReadOnly);
}
-static void sortSections(MutableArrayRef<InputSection *> vec,
+static void sortSections(MutableArrayRef<InputSectionBase *> vec,
SortSectionPolicy k) {
auto alignmentComparator = [](InputSectionBase *a, InputSectionBase *b) {
// ">" is not a mistake. Sections with larger alignments are placed
@@ -392,7 +392,7 @@ static void sortSections(MutableArrayRef<InputSection *> vec,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
-static void sortInputSections(MutableArrayRef<InputSection *> vec,
+static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
const SectionPattern &pat) {
if (pat.sortOuter == SortSectionPolicy::None)
return;
@@ -405,9 +405,9 @@ static void sortInputSections(MutableArrayRef<InputSection *> vec,
}
// Compute and remember which sections the InputSectionDescription matches.
-std::vector<InputSection *>
+std::vector<InputSectionBase *>
LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
- std::vector<InputSection *> ret;
+ std::vector<InputSectionBase *> ret;
// Collects all sections that satisfy constraints of Cmd.
for (const SectionPattern &pat : cmd->sectionPatterns) {
@@ -422,10 +422,8 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
// 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.
- //
- // It is safe to assume that Sec is an InputSection because mergeable or
- // EH input sections have already been handled and eliminated.
- if (cast<InputSection>(sec)->getRelocatedSection())
+ if (isa<InputSection>(sec) &&
+ cast<InputSection>(sec)->getRelocatedSection())
continue;
std::string filename = getFilename(sec->file);
@@ -434,42 +432,41 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
!pat.sectionPat.match(sec->name))
continue;
- ret.push_back(cast<InputSection>(sec));
+ ret.push_back(sec);
sec->assigned = true;
}
- sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore),
- pat);
+ sortInputSections(
+ MutableArrayRef<InputSectionBase *>(ret).slice(sizeBefore), pat);
}
return ret;
}
-void LinkerScript::discard(ArrayRef<InputSection *> v) {
- for (InputSection *s : v) {
- if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
- error("discarding " + s->name + " section is not allowed");
-
- // You can discard .hash and .gnu.hash sections by linker scripts. Since
- // they are synthesized sections, we need to handle them differently than
- // other regular sections.
- if (s == mainPart->gnuHashTab)
- mainPart->gnuHashTab = nullptr;
- if (s == mainPart->hashTab)
- mainPart->hashTab = nullptr;
-
- s->markDead();
- discard(s->dependentSections);
- }
+void LinkerScript::discard(InputSectionBase *s) {
+ if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
+ error("discarding " + s->name + " section is not allowed");
+
+ // You can discard .hash and .gnu.hash sections by linker scripts. Since
+ // they are synthesized sections, we need to handle them differently than
+ // other regular sections.
+ if (s == mainPart->gnuHashTab)
+ mainPart->gnuHashTab = nullptr;
+ if (s == mainPart->hashTab)
+ mainPart->hashTab = nullptr;
+
+ s->markDead();
+ for (InputSection *ds : s->dependentSections)
+ discard(ds);
}
-std::vector<InputSection *>
+std::vector<InputSectionBase *>
LinkerScript::createInputSectionList(OutputSection &outCmd) {
- std::vector<InputSection *> ret;
+ std::vector<InputSectionBase *> ret;
for (BaseCommand *base : outCmd.sectionCommands) {
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
- cmd->sections = computeInputSections(cmd);
- ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end());
+ cmd->sectionBases = computeInputSections(cmd);
+ ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
}
}
return ret;
@@ -480,12 +477,13 @@ void LinkerScript::processSectionCommands() {
size_t i = 0;
for (BaseCommand *base : sectionCommands) {
if (auto *sec = dyn_cast<OutputSection>(base)) {
- std::vector<InputSection *> v = createInputSectionList(*sec);
+ std::vector<InputSectionBase *> v = createInputSectionList(*sec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
if (sec->name == "/DISCARD/") {
- discard(v);
+ for (InputSectionBase *s : v)
+ discard(s);
sec->sectionCommands.clear();
continue;
}
@@ -513,15 +511,9 @@ void LinkerScript::processSectionCommands() {
s->alignment = subalign;
}
- // Some input sections may be removed from the list after ICF.
- for (InputSection *s : v)
- sec->addSection(s);
-
sec->sectionIndex = i++;
- if (sec->noload)
- sec->type = SHT_NOBITS;
- if (sec->nonAlloc)
- sec->flags &= ~(uint64_t)SHF_ALLOC;
+ for (InputSectionBase *s : v)
+ s->parent = sec;
}
}
}
@@ -565,7 +557,7 @@ static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
static OutputSection *createSection(InputSectionBase *isec,
StringRef outsecName) {
OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
- sec->addSection(cast<InputSection>(isec));
+ sec->recordSection(isec);
return sec;
}
@@ -594,7 +586,7 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
OutputSection *out = sec->getRelocatedSection()->getOutputSection();
if (out->relocationSection) {
- out->relocationSection->addSection(sec);
+ out->relocationSection->recordSection(sec);
return nullptr;
}
@@ -602,12 +594,6 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
return out->relocationSection;
}
- // When control reaches here, mergeable sections have already been merged into
- // synthetic sections. For relocatable case we want to create one output
- // section per syntetic section so that they have a valid sh_entsize.
- if (config->relocatable && (isec->flags & SHF_MERGE))
- return createSection(isec, outsecName);
-
// The ELF spec just says
// ----------------------------------------------------------------
// In the first phase, input sections that match in name, type and
@@ -654,7 +640,7 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
for (OutputSection *sec : v) {
if (sec->partition != isec->partition)
continue;
- sec->addSection(cast<InputSection>(isec));
+ sec->recordSection(isec);
return nullptr;
}
@@ -680,13 +666,14 @@ void LinkerScript::addOrphanSections() {
warn(toString(s) + " is being placed in '" + name + "'");
if (OutputSection *sec = findByName(sectionCommands, name)) {
- sec->addSection(cast<InputSection>(s));
+ sec->recordSection(s);
return;
}
if (OutputSection *os = addInputSec(map, s, name))
v.push_back(os);
- assert(s->getOutputSection()->sectionIndex == UINT32_MAX);
+ assert(isa<MergeInputSection>(s) ||
+ s->getOutputSection()->sectionIndex == UINT32_MAX);
};
// For futher --emit-reloc handling code we need target output section
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 4ac9e2a3909..621b8baeaae 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -168,6 +168,12 @@ struct InputSectionDescription : BaseCommand {
// will be associated with this InputSectionDescription.
std::vector<SectionPattern> sectionPatterns;
+ // Includes InputSections and MergeInputSections. Used temporarily during
+ // assignment of input sections to output sections.
+ std::vector<InputSectionBase *> sectionBases;
+
+ // Used after the finalizeInputSections() pass. MergeInputSections have been
+ // merged into MergeSyntheticSections.
std::vector<InputSection *> sections;
// Temporary record of synthetic ThunkSection instances and the pass that
@@ -226,10 +232,10 @@ class LinkerScript final {
void expandOutputSection(uint64_t size);
void expandMemoryRegions(uint64_t size);
- std::vector<InputSection *>
+ std::vector<InputSectionBase *>
computeInputSections(const InputSectionDescription *);
- std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
+ std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
std::vector<size_t> getPhdrIndices(OutputSection *sec);
@@ -259,7 +265,7 @@ public:
bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
uint64_t getDot() { return dot; }
- void discard(ArrayRef<InputSection *> v);
+ void discard(InputSectionBase *s);
ExprValue getSymbolValue(StringRef name, const Twine &loc);
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index a89bd509bc9..d6164103867 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -83,12 +83,32 @@ static bool canMergeToProgbits(unsigned type) {
type == SHT_NOTE;
}
-void OutputSection::addSection(InputSection *isec) {
+// Record that isec will be placed in the OutputSection. isec does not become
+// permanent until finalizeInputSections() is called. The function should not be
+// used after finalizeInputSections() is called. If you need to add an
+// InputSection post finalizeInputSections(), then you must do the following:
+//
+// 1. Find or create an InputSectionDescription to hold InputSection.
+// 2. Add the InputSection to the InputSectionDesciption::sections.
+// 3. Call commitSection(isec).
+void OutputSection::recordSection(InputSectionBase *isec) {
+ partition = isec->partition;
+ isec->parent = this;
+ if (sectionCommands.empty() ||
+ !isa<InputSectionDescription>(sectionCommands.back()))
+ sectionCommands.push_back(make<InputSectionDescription>(""));
+ auto *isd = cast<InputSectionDescription>(sectionCommands.back());
+ isd->sectionBases.push_back(isec);
+}
+
+// Update fields (type, flags, alignment, etc) according to the InputSection
+// isec. Also check whether the InputSection flags and type are consistent with
+// other InputSections.
+void OutputSection::commitSection(InputSection *isec) {
if (!hasInputSections) {
// If IS is the first section to be added to this section,
- // initialize Partition, Type, Entsize and flags from IS.
+ // initialize type, entsize and flags from isec.
hasInputSections = true;
- partition = isec->partition;
type = isec->type;
entsize = isec->entsize;
flags = isec->flags;
@@ -110,6 +130,8 @@ void OutputSection::addSection(InputSection *isec) {
type = SHT_PROGBITS;
}
}
+ if (noload)
+ type = SHT_NOBITS;
isec->parent = this;
uint64_t andMask =
@@ -118,6 +140,8 @@ void OutputSection::addSection(InputSection *isec) {
uint64_t andFlags = (flags & isec->flags) & andMask;
uint64_t orFlags = (flags | isec->flags) & orMask;
flags = andFlags | orFlags;
+ if (nonAlloc)
+ flags &= ~(uint64_t)SHF_ALLOC;
alignment = std::max(alignment, isec->alignment);
@@ -126,15 +150,69 @@ void OutputSection::addSection(InputSection *isec) {
// set sh_entsize to 0.
if (entsize != isec->entsize)
entsize = 0;
+}
+
+// This function scans over the InputSectionBase list sectionBases to create
+// InputSectionDescription::sections.
+//
+// It removes MergeInputSections from the input section array and adds
+// 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() {
+ std::vector<MergeSyntheticSection *> mergeSections;
+ for (BaseCommand *base : sectionCommands) {
+ auto *cmd = dyn_cast<InputSectionDescription>(base);
+ if (!cmd)
+ continue;
+ cmd->sections.reserve(cmd->sectionBases.size());
+ for (InputSectionBase *s : cmd->sectionBases) {
+ MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
+ if (!ms) {
+ cmd->sections.push_back(cast<InputSection>(s));
+ continue;
+ }
+
+ // We do not want to handle sections that are not alive, so just remove
+ // them instead of trying to merge.
+ if (!ms->isLive())
+ continue;
+
+ auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
+ // While we could create a single synthetic section for two different
+ // values of Entsize, it is better to take Entsize into consideration.
+ //
+ // With a single synthetic section no two pieces with different Entsize
+ // could be equal, so we may as well have two sections.
+ //
+ // Using Entsize in here also allows us to propagate it to the synthetic
+ // section.
+ //
+ // SHF_STRINGS section with different alignments should not be merged.
+ return sec->flags == ms->flags && sec->entsize == ms->entsize &&
+ (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
+ });
+ if (i == mergeSections.end()) {
+ MergeSyntheticSection *syn =
+ createMergeSynthetic(name, ms->type, ms->flags, ms->alignment);
+ mergeSections.push_back(syn);
+ i = std::prev(mergeSections.end());
+ syn->entsize = ms->entsize;
+ cmd->sections.push_back(syn);
+ }
+ (*i)->addSection(ms);
+ }
+
+ // sectionBases should not be used from this point onwards. Clear it to
+ // catch misuses.
+ cmd->sectionBases.clear();
- if (!isec->assigned) {
- isec->assigned = true;
- if (sectionCommands.empty() ||
- !isa<InputSectionDescription>(sectionCommands.back()))
- sectionCommands.push_back(make<InputSectionDescription>(""));
- auto *isd = cast<InputSectionDescription>(sectionCommands.back());
- isd->sections.push_back(isec);
+ // Some input sections may be removed from the list after ICF.
+ for (InputSection *s : cmd->sections)
+ commitSection(s);
}
+ for (auto *ms : mergeSections)
+ ms->finalizeContents();
}
static void sortByOrder(MutableArrayRef<InputSection *> in,
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index fff8327ea37..a24294eedf3 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -71,7 +71,9 @@ public:
uint64_t addr = 0;
uint32_t shName = 0;
- void addSection(InputSection *isec);
+ void recordSection(InputSectionBase *isec);
+ void commitSection(InputSection *isec);
+ void finalizeInputSections();
// The following members are normally only used in linker scripts.
MemoryRegion *memRegion = nullptr;
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 42f0a873909..442ffdbb2f8 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -567,10 +567,16 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
bool isRO = isReadOnly<ELFT>(ss);
BssSection *sec =
make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
- if (isRO)
- in.bssRelRo->getParent()->addSection(sec);
- else
- in.bss->getParent()->addSection(sec);
+ OutputSection *osec = (isRO ? in.bssRelRo : in.bss)->getParent();
+
+ // At this point, sectionBases has been migrated to sections. Append sec to
+ // sections.
+ if (osec->sectionCommands.empty() ||
+ !isa<InputSectionDescription>(osec->sectionCommands.back()))
+ osec->sectionCommands.push_back(make<InputSectionDescription>(""));
+ auto *isd = cast<InputSectionDescription>(osec->sectionCommands.back());
+ isd->sections.push_back(sec);
+ osec->commitSection(sec);
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index e323ecaa9d7..1948da93095 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -3125,10 +3125,9 @@ void MergeNoTailSection::finalizeContents() {
});
}
-static MergeSyntheticSection *createMergeSynthetic(StringRef name,
- uint32_t type,
- uint64_t flags,
- uint32_t alignment) {
+MergeSyntheticSection *elf::createMergeSynthetic(StringRef name, uint32_t type,
+ uint64_t flags,
+ uint32_t alignment) {
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
if (shouldTailMerge)
return make<MergeTailSection>(name, type, flags, alignment);
@@ -3146,63 +3145,6 @@ template <class ELFT> void elf::splitSections() {
});
}
-// This function scans over the inputsections to create mergeable
-// synthetic sections.
-//
-// It removes MergeInputSections from the input section array and adds
-// 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 elf::mergeSections() {
- std::vector<MergeSyntheticSection *> mergeSections;
- for (InputSectionBase *&s : inputSections) {
- MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
- if (!ms)
- continue;
-
- // We do not want to handle sections that are not alive, so just remove
- // them instead of trying to merge.
- if (!ms->isLive()) {
- s = nullptr;
- continue;
- }
-
- StringRef outsecName = getOutputSectionName(ms);
-
- auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
- // While we could create a single synthetic section for two different
- // values of Entsize, it is better to take Entsize into consideration.
- //
- // With a single synthetic section no two pieces with different Entsize
- // could be equal, so we may as well have two sections.
- //
- // Using Entsize in here also allows us to propagate it to the synthetic
- // section.
- //
- // SHF_STRINGS section with different alignments should not be merged.
- return sec->name == outsecName && sec->flags == ms->flags &&
- sec->entsize == ms->entsize &&
- (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
- });
- if (i == mergeSections.end()) {
- MergeSyntheticSection *syn =
- createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment);
- mergeSections.push_back(syn);
- i = std::prev(mergeSections.end());
- s = syn;
- syn->entsize = ms->entsize;
- } else {
- s = nullptr;
- }
- (*i)->addSection(ms);
- }
- for (auto *ms : mergeSections)
- ms->finalizeContents();
-
- std::vector<InputSectionBase *> &v = inputSections;
- v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
-}
-
MipsRldMapSection::MipsRldMapSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
".rld_map") {}
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 68463978950..7442f33f0b0 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -1102,8 +1102,9 @@ public:
InputSection *createInterpSection();
MergeInputSection *createCommentSection();
+MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
+ uint64_t flags, uint32_t alignment);
template <class ELFT> void splitSections();
-void mergeSections();
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index f0334470e5e..1a5504b2624 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -705,7 +705,7 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
});
if (i == sec->sectionCommands.end())
continue;
- InputSection *isec = cast<InputSectionDescription>(*i)->sections[0];
+ InputSectionBase *isec = cast<InputSectionDescription>(*i)->sections[0];
// Relocations are not using REL[A] section symbols.
if (isec->type == SHT_REL || isec->type == SHT_RELA)
diff --git a/lld/test/ELF/linkerscript/merge-output-sections.s b/lld/test/ELF/linkerscript/merge-output-sections.s
new file mode 100644
index 00000000000..b15596f84cf
--- /dev/null
+++ b/lld/test/ELF/linkerscript/merge-output-sections.s
@@ -0,0 +1,35 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+## SHF_MERGE sections within the same output section can be freely merged.
+# RUN: echo 'SECTIONS { .rodata : { *(.rodata.*) }}' > %t.script
+# RUN: ld.lld %t.o -T %t.script -o %t
+# RUN: llvm-readelf -x .rodata %t | FileCheck --check-prefix=SAME %s --implicit-check-not=section
+
+# SAME: section '.rodata':
+# SAME-NEXT: 0x00000000 01000200 0300
+
+## SHF_MERGE sections with different output sections cannot be merged.
+# RUN: echo 'SECTIONS { \
+# RUN: .rodata.foo : { *(.rodata.foo) } \
+# RUN: .rodata.bar : { *(.rodata.bar) } \
+# RUN: }' > %t2.script
+# RUN: ld.lld %t.o -T %t2.script -o %t2
+# RUN: llvm-readelf -x .rodata.foo -x .rodata.bar %t2 | FileCheck --check-prefix=DIFF %s --implicit-check-not=section
+
+# DIFF: section '.rodata.foo':
+# DIFF-NEXT: 0x00000000 01000200 0300
+# DIFF: section '.rodata.bar':
+# DIFF-NEXT: 0x00000006 0100
+
+.section .rodata.foo,"aM",@progbits,2,unique,0
+.short 1
+.short 2
+.section .rodata.foo,"aM",@progbits,2,unique,1
+.short 1
+.short 3
+
+.section .rodata.bar,"aM",@progbits,2,unique,0
+.short 1
+.section .rodata.bar,"aM",@progbits,2,unique,1
+.short 1
diff --git a/lld/test/ELF/linkerscript/merge-sections.s b/lld/test/ELF/linkerscript/merge-sections.s
index b9ebfe79714..ea53ba3e420 100644
--- a/lld/test/ELF/linkerscript/merge-sections.s
+++ b/lld/test/ELF/linkerscript/merge-sections.s
@@ -17,7 +17,7 @@
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x[[ADDR1:.*]]
# CHECK-NEXT: Offset: 0x[[ADDR1]]
-# CHECK-NEXT: Size: 14
+# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 2
@@ -28,7 +28,7 @@
# CHECK-NEXT: Value: 0x[[ADDR1]]
# CHECK: Name: end
-# CHECK-NEXT: Value: 0x236
+# CHECK-NEXT: Value: 0x230
# Check that we don't crash with --gc-sections
# RUN: ld.lld --gc-sections -o %t2 --script %t.script %t -shared
diff --git a/lld/test/ELF/merge-entsize2.s b/lld/test/ELF/merge-entsize2.s
index 26e40d3a55e..25036beeea8 100644
--- a/lld/test/ELF/merge-entsize2.s
+++ b/lld/test/ELF/merge-entsize2.s
@@ -6,8 +6,8 @@
# RUN: llvm-readelf -x .cst %t | FileCheck --check-prefix=HEX %s
# RUN: ld.lld -O0 -r %t.o -o %t1.o
-# RUN: llvm-readelf -S %t1.o | FileCheck --check-prefix=SEC-R %s
-# RUN: llvm-readelf -x .cst %t1.o | FileCheck --check-prefix=HEX-R %s
+# RUN: llvm-readelf -S %t1.o | FileCheck --check-prefix=SEC %s
+# RUN: llvm-readelf -x .cst %t1.o | FileCheck --check-prefix=HEX %s
## Check that SHF_MERGE sections with the same name, sh_flags and sh_entsize
## are grouped together and can be merged within the group.
@@ -17,20 +17,10 @@
# SEC: Name Type {{.*}} Size ES Flg Lk Inf Al
# SEC: .cst PROGBITS {{.*}} 000020 00 AM 0 0 8
-## .cst 0 and .cst 1 are merged, but emitted as a separate output section.
-# SEC-R: .cst PROGBITS {{.*}} 00000c 04 AM 0 0 4
-# SEC-R: .cst PROGBITS {{.*}} 000010 08 AM 0 0 8
-
# HEX: Hex dump of section '.cst':
# HEX-NEXT: 0x{{[0-9a-f]+}} 01000000 00000000 02000000 00000000
# HEX-NEXT: 0x{{[0-9a-f]+}} 01000000 00000000 03000000 00000000
-# HEX-R: Hex dump of section '.cst':
-# HEX-R-NEXT: 0x00000000 01000000 00000000 02000000
-# HEX-R-EMPTY:
-# HEX-R-NEXT: Hex dump of section '.cst':
-# HEX-R-NEXT: 0x00000000 01000000 00000000 03000000 00000000
-
.section .cst,"aM",@progbits,4,unique,0
.align 2
.long 1