llvm / llvm/llvm-project

clang: GNUstep-runtime ObjC codegen (CGObjCGNU.cpp) has no Mach-O support - COMDAT crash + missing __start_/__stop_ equivalent

Open
#217,260 4 comments 0 reactions 0 assignees View on GitHub
confirmed crash-on-valid llvm:mc platform:macos
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary

Clang's GNUstep-runtime Objective-C code generator (`CGObjCGNU.cpp`,
`CGObjCGNUstep2`/related classes) appears to have never had Mach-O object-format
support added, despite accepting `-fobjc-runtime=` flags for the GNU-family
runtimes (`gcc`, `gnustep-1.7`, `gnustep-1.9`, etc. — everything except
`gnustep-2.0`, which is explicitly rejected for Mach-O with a clear diagnostic)
on a Mach-O target without any other complaint at the driver level.

In practice this means: **any Objective-C source that references a selector
(i.e. essentially any real Objective-C code) crashes the backend when compiled
with a GNU-family `-fobjc-runtime=` flag for a Mach-O target**, and even code
that avoided that crash would fail to *link*, because a separate mechanism the
same file relies on (`__start_`/`__stop_` boundary symbols)
is an ELF/GNU-linker-only convention with no Mach-O equivalent implemented here.

## Reproduction

```objc
// selrepro.m
#include
SEL get_sel(void) { return @selector(copy); }
```

```
$ clang -fobjc-runtime=gnustep-1.9 -c selrepro.m
fatal error: error in backend: MachO doesn't support COMDATs, '.objc_sel_namecopy' cannot be lowered.
clang: error: clang frontend command failed with exit code 70
```

Reproduces identically with `-fobjc-runtime=gnustep-1.7` and
`-fobjc-runtime=gcc` as well — every GNU-family runtime flag, not just one.
Tested with both Apple's Xcode/CommandLineTools clang (21.0.0) and a plain
upstream build (`clang version 22.1.8`, built via MacPorts' `llvm-22` port
from `llvmorg-22.1.8`, `-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld"`,
no Apple-specific patches applied to this file) — identical crash on both,
confirming this isn't specific to Apple's fork.

Target tested: `arm64-apple-darwin25.4.0` (Apple Silicon macOS 26).

## Root cause (traced in `clang/lib/CodeGen/CGObjCGNU.cpp`, tag `llvmorg-22.1.8`)

1. **`setComdat()` is called unconditionally at ~11 sites** in this file with
no check on the target object format anywhere (`grep -c isOSBinFormatMachO
CGObjCGNU.cpp` → 0):
- `ExportUniqueString` (~line 217) — selector name strings, EH typeinfo
names; this is the one the repro above hits.
- `GenerateProtocolRef` (~line 1345)
- `GenerateProtocolRef` (protocol definition, ~line 1436)
- `GetTypeString` (~line 1474)
- `GetConstantSelector` (~line 1494)
- `GetSectionBounds`'s COFF-only `Sym` helper (~line 1523) — COFF-specific,
not itself a Mach-O gap, but confirms the pattern this file already uses
elsewhere for platform-specific comdat/linkage choices
- `ModuleInitFunction`'s load function, init struct, and ctor variable
(~lines 1551, 1569, 1599)
- `createNullGlobal`'s placeholder globals (~line 1614)

Several sibling code paths in the same file (e.g. line 938, 1036, 1053,
1127, 1247, 1510, 1589, 1627, 2543) already special-case
`CGM.getTriple().isOSBinFormatCOFF()` for Windows — Mach-O never got an
equivalent branch anywhere.

2. **`GetSectionBounds`** (used by `ModuleInitFunction` so the runtime's
`+load`-time init can enumerate every compiled-in selector/class/
category/protocol) has exactly two paths: a COFF-specific one using
zero-sized sentinel symbols at each end of a section, and a fallback used
for everything else (in practice: only ever ELF) that emits bare
references to `__start_`/`__stop_` and relies on the
target linker auto-defining them — a real, but ELF/GNU-linker-specific,
convention. Apple's `ld64` does not implement it. Mach-O does have its own
equivalent (`section$start$SEGMENT$section` /
`section$end$SEGMENT$section` synthesized symbols), but nothing in this
function emits that form.

Together these mean a Mach-O target can't get past `setComdat()` on the first
real Objective-C file today, and — going by the code as read, not tested
further, since nothing gets that far — would then fail to *link* on the
`__start_`/`__stop_` symbols even once the comdat crash is worked around.

## Context

Investigating this while trying to build a modern GNUstep (`gnustep-base`/
`gnustep-gui`/`gnustep-back`, GNUstep's portable Foundation/AppKit-equivalent
libraries) for Apple Silicon macOS using the GNU-family Objective-C runtime
(GNUstep's own `libobjc2`, or the ABI-compatible parts of it). GNUstep's own
tooling (`gnustep-make`) has a documented, intentional `apple-gnu-gnu`
library-combo specifically for pairing Apple's native runtime with
GNUstep's own Foundation/AppKit — but Apple's runtime lacks the
`__objc_msg_forward2`-style hook GNUstep's invocation/DO machinery needs, so
a real build needs the GNU-family runtime and therefore needs this codegen
path working on Mach-O. This looks like the underlying reason nobody has
gotten GNUstep's own Objective-C runtime working on modern macOS — not a
GNUstep-side problem, but a gap in Clang's Mach-O support for this codegen
family. Happy to help test a fix, or provide more detail (a smaller
project reproducing this end-to-end can be shared if useful).

Contributor guide

Open the contributing guide

Research direction

Start with clang/lib/CodeGen/CGObjCGNU.cpp and reproduce the failure using the selrepro.m example and the gnustep-1.9 Mach-O compile command. Trace the listed setComdat() calls and GetSectionBounds platform paths; done means GNU-family Objective-C code no longer hits the Mach-O COMDAT crash and uses a Mach-O-compatible section-boundary mechanism so it can link.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, objective-c
Domain
compilers, operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.