[lld] [lld][MachO] Add `--warn-missing-subsections-via-symbols` flag (PR #221464)

Dave MacLachlan via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 5 09:59:33 PDT 2026


https://github.com/dmaclach updated https://github.com/llvm/llvm-project/pull/221464

>From b706f8a322f7c9c9d5c2059557c1ab7bcbcbfe44 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <dmaclach at gmail.com>
Date: Sat, 5 Sep 2026 09:37:17 -0700
Subject: [PATCH 1/2] [lld][MachO] Add `--warn-missing-subsections-via-symbols`
 flag

This change introduces:

* `--warn-missing-subsections-via-symbols`: Warns when an input object file with non-empty sections is missing `MH_SUBSECTIONS_VIA_SYMBOLS`.
* `--no-warn-missing-subsections-via-symbols`: Disables the warning (default).

Also adds documentation and lit tests.

Object files missing `MH_SUBSECTIONS_VIA_SYMBOLS` can prevent dead-stripping and subsection splitting.

We have seen a number of cases where we are compiling assembly, and this allows us to track them down.
---
 lld/MachO/Config.h                            |  1 +
 lld/MachO/Driver.cpp                          |  3 ++
 lld/MachO/InputFiles.cpp                      |  3 ++
 lld/MachO/Options.td                          |  3 ++
 lld/docs/MachO/ld64-vs-lld.md                 | 34 +++++++++++++++++++
 lld/docs/ReleaseNotes.md                      |  5 +++
 .../warn-missing-subsections-via-symbols.s    | 27 +++++++++++++++
 7 files changed, 76 insertions(+)
 create mode 100644 lld/test/MachO/warn-missing-subsections-via-symbols.s

diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index b90fa18780a8a..a939d7789a758 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -224,6 +224,7 @@ struct Configuration {
   llvm::StringRef csProfilePath;
   bool pgoWarnMismatch;
   bool warnThinArchiveMissingMembers;
+  bool warnMissingSubsectionsViaSymbols = false;
   bool disableVerify;
   bool separateCstringLiteralSections;
   bool tailMergeStrings;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 92d841dfd3ade..8edaff6e38657 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -2077,6 +2077,9 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
   config->warnThinArchiveMissingMembers =
       args.hasFlag(OPT_warn_thin_archive_missing_members,
                    OPT_no_warn_thin_archive_missing_members, true);
+  config->warnMissingSubsectionsViaSymbols =
+      args.hasFlag(OPT_warn_missing_subsections_via_symbols,
+                   OPT_no_warn_missing_subsections_via_symbols, false);
   config->generateUuid = !args.hasArg(OPT_no_uuid);
   config->disableVerify = args.hasArg(OPT_disable_verify);
   config->separateCstringLiteralSections =
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 5279eb0e1fea7..8faac4b0614a5 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -1054,6 +1054,9 @@ template <class LP> void ObjFile::parse() {
                           c->nsyms);
     const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
     bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS;
+    if (config->warnMissingSubsectionsViaSymbols && !subsectionsViaSymbols &&
+        !sectionHeaders.empty())
+      warn(toString(this) + ": missing MH_SUBSECTIONS_VIA_SYMBOLS");
     parseSymbols<LP>(sectionHeaders, nList, strtab, subsectionsViaSymbols);
   }
 
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index 0e1faaff139f1..32b6ce0a64a07 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -219,6 +219,9 @@ defm pgo_warn_mismatch: BB<"pgo-warn-mismatch",
 defm warn_thin_archive_missing_members : BB<"warn-thin-archive-missing-members",
   "Warn on missing object files referenced by thin archives (default)",
   "Do not warn on missing object files referenced by thin archives">, Group<grp_lld>;
+defm warn_missing_subsections_via_symbols : BB<"warn-missing-subsections-via-symbols",
+  "Warn if an input object file is missing MH_SUBSECTIONS_VIA_SYMBOLS",
+  "Do not warn if an input object file is missing MH_SUBSECTIONS_VIA_SYMBOLS (default)">, Group<grp_lld>;
 
 // This is a complete Options.td compiled from Apple's ld(1) manpage
 // dated 2018-03-07 and cross checked with ld64 source code in repo
diff --git a/lld/docs/MachO/ld64-vs-lld.md b/lld/docs/MachO/ld64-vs-lld.md
index 33be2e41dbe61..2dbea3090e714 100644
--- a/lld/docs/MachO/ld64-vs-lld.md
+++ b/lld/docs/MachO/ld64-vs-lld.md
@@ -52,3 +52,37 @@ environment variable. LLD flips this default to prefer hermetic builds, but
 allows disabling this behavior by setting `ZERO_AR_DATE=0`. Any other value
 of `ZERO_AR_DATE` will be ignored.
 
+## Subsections via Symbols and `--warn-missing-subsections-via-symbols`
+
+In Mach-O object files, the `MH_SUBSECTIONS_VIA_SYMBOLS` header flag (emitted via
+the `.subsections_via_symbols` assembly directive) indicates that sections can be
+divided into independent subsections along symbol boundaries.
+
+When an object file is missing `MH_SUBSECTIONS_VIA_SYMBOLS`:
+- LLD treats each section in that object file as a single monolithic block rather
+  than splitting it into individual symbol-level subsections.
+- **Dead code stripping (`-dead_strip`)**: Unreferenced functions or data in that
+  file cannot be stripped individually. If any symbol in the section is live, the
+  entire section is retained in the output binary.
+- **Identical Code Folding (ICF) and Section Ordering**: Functions cannot be
+  individually deduplicated or reordered via `-order_file`.
+- **Branch range extension thunks**: On architectures with limited direct branch
+  ranges (such as ARM64's $\pm 128\text{ MB}$ limit for `b` / `bl`), large
+  monolithic sections cannot have branch thunk islands inserted within them. In
+  large binaries, this can lead to thunk range overruns (e.g. `relocation
+  R_AARCH64_CALL26 out of range`).
+
+Handwritten assembly files frequently omit `.subsections_via_symbols`. To help
+identify object files causing dead-stripping or layout issues, LLD provides:
+- `--warn-missing-subsections-via-symbols`: Emits a warning when an input object
+  file with non-empty sections is missing `MH_SUBSECTIONS_VIA_SYMBOLS`.
+- `--no-warn-missing-subsections-via-symbols`: Disables the warning (default).
+
+To resolve the warning and allow subsection splitting, add `.subsections_via_symbols`
+to the assembly source:
+```assembly
+#if defined(__APPLE__)
+.subsections_via_symbols
+#endif
+```
+
diff --git a/lld/docs/ReleaseNotes.md b/lld/docs/ReleaseNotes.md
index 00c9f5c85af8f..827d09bec5776 100644
--- a/lld/docs/ReleaseNotes.md
+++ b/lld/docs/ReleaseNotes.md
@@ -41,6 +41,11 @@ from the [LLVM releases web site](https://llvm.org/releases/).
   call them, so that stubs reached from prioritized code are laid out together.
   This applies whenever section priorities exist, such as with `-order_file`.
 
+* Added `--warn-missing-subsections-via-symbols` and
+  `--no-warn-missing-subsections-via-symbols` to lld to warn when input object
+  files lack the `MH_SUBSECTIONS_VIA_SYMBOLS` flag, which prevents
+  dead-stripping and subsection splitting.
+
 ### WebAssembly Improvements
 
 * Added support for resolving and merging common data symbols (allocating them
diff --git a/lld/test/MachO/warn-missing-subsections-via-symbols.s b/lld/test/MachO/warn-missing-subsections-via-symbols.s
new file mode 100644
index 0000000000000..1435714d27d51
--- /dev/null
+++ b/lld/test/MachO/warn-missing-subsections-via-symbols.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %s -o %t/no_subsections.o
+# RUN: echo -e ".text\n.globl _bar\n_bar:\nret\n.subsections_via_symbols" | \
+# RUN:     llvm-mc -filetype=obj -triple=x86_64-apple-macos - -o %t/with_subsections.o
+
+## Default: no warning (even under -fatal_warnings)
+# RUN: %lld -dylib %t/no_subsections.o -o /dev/null 2>&1 | count 0
+
+## Warn when flag is passed
+# RUN: %no-fatal-warnings-lld -dylib %t/no_subsections.o --warn-missing-subsections-via-symbols -o /dev/null 2>&1 \
+# RUN:     | FileCheck %s --check-prefix=WARN
+
+## Overridden by --no-warn-missing-subsections-via-symbols
+# RUN: %lld -dylib %t/no_subsections.o --warn-missing-subsections-via-symbols \
+# RUN:     --no-warn-missing-subsections-via-symbols -o /dev/null 2>&1 | count 0
+
+## Object with .subsections_via_symbols should not trigger a warning even when flag is passed
+# RUN: %lld -dylib %t/with_subsections.o --warn-missing-subsections-via-symbols -o /dev/null 2>&1 | count 0
+
+# WARN: warning: {{.*}}no_subsections.o: missing MH_SUBSECTIONS_VIA_SYMBOLS
+
+.text
+.globl _foo
+_foo:
+  ret

>From e8da79f885daf6236c44a6f5e0736d42e600214c Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <dmaclach at gmail.com>
Date: Sat, 5 Sep 2026 09:59:05 -0700
Subject: [PATCH 2/2] Fixed `assembly` tag

---
 lld/docs/MachO/ld64-vs-lld.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/docs/MachO/ld64-vs-lld.md b/lld/docs/MachO/ld64-vs-lld.md
index 2dbea3090e714..0097ce6aa567e 100644
--- a/lld/docs/MachO/ld64-vs-lld.md
+++ b/lld/docs/MachO/ld64-vs-lld.md
@@ -80,7 +80,7 @@ identify object files causing dead-stripping or layout issues, LLD provides:
 
 To resolve the warning and allow subsection splitting, add `.subsections_via_symbols`
 to the assembly source:
-```assembly
+```asm
 #if defined(__APPLE__)
 .subsections_via_symbols
 #endif



More information about the llvm-commits mailing list