[lld] [LLD][COFF] Prevent merging .bss into shared sections (PR #202817)
Evgenii Kudriashov via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 26 07:51:41 PDT 2026
https://github.com/e-kud updated https://github.com/llvm/llvm-project/pull/202817
>From 776ac18f2d6dbb4813b0496da8c7cf942cd073bf Mon Sep 17 00:00:00 2001
From: Evgenii Kudriashov <evgenii.kudriashov at intel.com>
Date: Tue, 9 Jun 2026 17:02:49 -0700
Subject: [PATCH 1/3] [LLD][COFF] Prevent merging .bss into shared sections
This matches the behavior with MSVC linker. Generally the idea that .bss
section may contain security cookies that shouldn't be shared among
processes.
---
lld/COFF/Writer.cpp | 33 +++++++---
lld/test/COFF/bss-shared-no-merge.test | 85 ++++++++++++++++++++++++++
2 files changed, 111 insertions(+), 7 deletions(-)
create mode 100644 lld/test/COFF/bss-shared-no-merge.test
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 955a9897ac2b0..d8bdcc51afda0 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -216,6 +216,8 @@ class Writer {
void appendImportThunks();
void locateImportTables();
void createExportTable();
+ StringRef getMergeDestination(const StringRef sectionName,
+ const StringRef toName);
void mergeSection(const std::map<StringRef, StringRef>::value_type &p);
void mergeSections();
void sortECChunks();
@@ -1656,19 +1658,27 @@ void Writer::createSymbolAndStringTable() {
fileSize = alignTo(fileOff, ctx.config.fileAlign);
}
-void Writer::mergeSection(const std::map<StringRef, StringRef>::value_type &p) {
- StringRef toName = p.second;
- if (p.first == toName)
- return;
+StringRef Writer::getMergeDestination(const StringRef sectionName,
+ const StringRef destName) {
+ StringRef toName = destName;
StringSet<> names;
while (true) {
if (!names.insert(toName).second)
- Fatal(ctx) << "/merge: cycle found for section '" << p.first << "'";
+ Fatal(ctx) << "/merge: cycle found for section '" << sectionName << "'";
auto i = ctx.config.merge.find(toName);
if (i == ctx.config.merge.end())
break;
toName = i->second;
}
+ return toName;
+}
+
+void Writer::mergeSection(const std::map<StringRef, StringRef>::value_type &p) {
+ if (p.first == p.second)
+ return;
+
+ StringRef toName = getMergeDestination(p.first, p.second);
+
OutputSection *from = findSection(p.first);
OutputSection *to = findSection(toName);
if (!from)
@@ -1716,8 +1726,17 @@ void Writer::mergeSections() {
// whatever section it is being merged into (usually .data) so that the image
// need not actually contain all of the zeros.
auto it = ctx.config.merge.find(".bss");
- if (it != ctx.config.merge.end())
- mergeSection(*it);
+ if (it != ctx.config.merge.end()) {
+ // Resolve the final merge target name following the chain.
+ StringRef toName = getMergeDestination(it->first, it->second);
+ // Don't merge .bss into a shared section. MSVC link.exe keeps .bss
+ // separate when the target has IMAGE_SCN_MEM_SHARED, preventing unexpected
+ // sharing across processes.
+ auto secIt = ctx.config.section.find(toName);
+ if (secIt == ctx.config.section.end() ||
+ !(secIt->second & IMAGE_SCN_MEM_SHARED))
+ mergeSection({it->first, toName});
+ }
}
// EC targets may have chunks of various architectures mixed together at this
diff --git a/lld/test/COFF/bss-shared-no-merge.test b/lld/test/COFF/bss-shared-no-merge.test
new file mode 100644
index 0000000000000..497ed0c85ce05
--- /dev/null
+++ b/lld/test/COFF/bss-shared-no-merge.test
@@ -0,0 +1,85 @@
+# REQUIRES: x86
+# Test that .bss is not merged into .data when .data is marked shared.
+# MSVC link.exe keeps .bss separate to prevent unexpected sharing across
+# processes.
+
+# RUN: yaml2obj %s -o %t.obj
+
+## .bss should NOT be merged when .data is shared.
+# RUN: lld-link /out:%t.dll /dll /entry:DllMain /section:.data,rws %t.obj
+# RUN: llvm-readobj --sections %t.dll | FileCheck -check-prefix=SHARED %s
+
+# SHARED: Name: .bss
+# SHARED: Name: .data
+
+## .bss should still be merged when .data is not shared (default behavior).
+# RUN: lld-link /out:%t2.dll /dll /entry:DllMain %t.obj
+# RUN: llvm-readobj --sections %t2.dll | FileCheck -check-prefix=NOSHARED %s
+
+# NOSHARED-NOT: Name: .bss
+# NOSHARED: Name: .data
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 'C3'
+ SizeOfRawData: 1
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: '01000000'
+ SizeOfRawData: 4
+ - Name: .bss
+ Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ SizeOfRawData: 256
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 1
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 256
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: DllMain
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
>From d6b081b40beb3fcaf58192d99d99093a25f9c9a2 Mon Sep 17 00:00:00 2001
From: Evgenii Kudriashov <evgenii.kudriashov at intel.com>
Date: Wed, 10 Jun 2026 03:43:46 -0700
Subject: [PATCH 2/3] More canonic names
---
lld/COFF/Writer.cpp | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index d8bdcc51afda0..4b502b74ec5b2 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -216,8 +216,7 @@ class Writer {
void appendImportThunks();
void locateImportTables();
void createExportTable();
- StringRef getMergeDestination(const StringRef sectionName,
- const StringRef toName);
+ StringRef getMergeDestination(StringRef fromSection, StringRef toSection);
void mergeSection(const std::map<StringRef, StringRef>::value_type &p);
void mergeSections();
void sortECChunks();
@@ -1658,19 +1657,18 @@ void Writer::createSymbolAndStringTable() {
fileSize = alignTo(fileOff, ctx.config.fileAlign);
}
-StringRef Writer::getMergeDestination(const StringRef sectionName,
- const StringRef destName) {
- StringRef toName = destName;
+StringRef Writer::getMergeDestination(StringRef fromSection,
+ StringRef toSection) {
StringSet<> names;
while (true) {
- if (!names.insert(toName).second)
- Fatal(ctx) << "/merge: cycle found for section '" << sectionName << "'";
- auto i = ctx.config.merge.find(toName);
+ if (!names.insert(toSection).second)
+ Fatal(ctx) << "/merge: cycle found for section '" << fromSection << "'";
+ auto i = ctx.config.merge.find(toSection);
if (i == ctx.config.merge.end())
break;
- toName = i->second;
+ toSection = i->second;
}
- return toName;
+ return toSection;
}
void Writer::mergeSection(const std::map<StringRef, StringRef>::value_type &p) {
>From 4b9fea244d2b091f85d4b94f0fe3b7c347efa49a Mon Sep 17 00:00:00 2001
From: Evgenii Kudriashov <evgenii.kudriashov at intel.com>
Date: Wed, 10 Jun 2026 03:49:09 -0700
Subject: [PATCH 3/3] Propagate toSection
---
lld/COFF/Writer.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 4b502b74ec5b2..33826c9146c50 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1675,14 +1675,14 @@ void Writer::mergeSection(const std::map<StringRef, StringRef>::value_type &p) {
if (p.first == p.second)
return;
- StringRef toName = getMergeDestination(p.first, p.second);
+ StringRef toSection = getMergeDestination(p.first, p.second);
OutputSection *from = findSection(p.first);
- OutputSection *to = findSection(toName);
+ OutputSection *to = findSection(toSection);
if (!from)
return;
if (!to) {
- from->name = toName;
+ from->name = toSection;
return;
}
to->merge(from);
@@ -1726,14 +1726,14 @@ void Writer::mergeSections() {
auto it = ctx.config.merge.find(".bss");
if (it != ctx.config.merge.end()) {
// Resolve the final merge target name following the chain.
- StringRef toName = getMergeDestination(it->first, it->second);
+ StringRef toSection = getMergeDestination(it->first, it->second);
// Don't merge .bss into a shared section. MSVC link.exe keeps .bss
// separate when the target has IMAGE_SCN_MEM_SHARED, preventing unexpected
// sharing across processes.
- auto secIt = ctx.config.section.find(toName);
+ auto secIt = ctx.config.section.find(toSection);
if (secIt == ctx.config.section.end() ||
!(secIt->second & IMAGE_SCN_MEM_SHARED))
- mergeSection({it->first, toName});
+ mergeSection({it->first, toSection});
}
}
More information about the llvm-commits
mailing list