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

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 15:52:33 PDT 2026


Author: Dave MacLachlan
Date: 2026-09-10T15:52:28-07:00
New Revision: ec5a61a1454082773a9071c8a9db0472306a2517

URL: https://github.com/llvm/llvm-project/commit/ec5a61a1454082773a9071c8a9db0472306a2517
DIFF: https://github.com/llvm/llvm-project/commit/ec5a61a1454082773a9071c8a9db0472306a2517.diff

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

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.

Added: 
    lld/test/MachO/warn-missing-subsections-via-symbols.s

Modified: 
    lld/MachO/Config.h
    lld/MachO/Driver.cpp
    lld/MachO/InputFiles.cpp
    lld/MachO/Options.td
    lld/docs/MachO/ld64-vs-lld.md
    lld/docs/ReleaseNotes.md

Removed: 
    


################################################################################
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..0097ce6aa567e 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:
+```asm
+#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..52ff5f505759d
--- /dev/null
+++ b/lld/test/MachO/warn-missing-subsections-via-symbols.s
@@ -0,0 +1,33 @@
+# REQUIRES: x86
+# RUN: rm -rf %t; split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/no_subsections.s -o %t/no_subsections.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/with_subsections.s -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
+
+#--- no_subsections.s
+.text
+.globl _foo
+_foo:
+  ret
+
+#--- with_subsections.s
+.text
+.globl _bar
+_bar:
+  ret
+.subsections_via_symbols


        


More information about the llvm-commits mailing list