summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Driver.cpp14
-rw-r--r--lld/ELF/LinkerScript.cpp95
-rw-r--r--lld/ELF/LinkerScript.h12
-rw-r--r--lld/ELF/OutputSections.cpp98
-rw-r--r--lld/ELF/OutputSections.h4
-rw-r--r--lld/ELF/Relocations.cpp14
-rw-r--r--lld/ELF/SyntheticSections.cpp64
-rw-r--r--lld/ELF/SyntheticSections.h3
-rw-r--r--lld/ELF/Writer.cpp2
-rw-r--r--lld/test/ELF/linkerscript/merge-output-sections.s35
-rw-r--r--lld/test/ELF/linkerscript/merge-sections.s4
-rw-r--r--lld/test/ELF/merge-entsize2.s14
12 files changed, 207 insertions, 152 deletions
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
OpenPOWER on IntegriCloud