[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