fix(objc): box resilient value types to fix missing x86_64 symbols#8037
Conversation
Without @Frozen, the Swift compiler treats these types as resilient on x86_64 under -enable-library-evolution (BUILD_LIBRARY_FOR_DISTRIBUTION). This causes the compiler to use runtime class realization (CMt) instead of emitting a static _OBJC_CLASS_$_ symbol, which results in linker failures for consumers of the SentryObjC dynamic framework. Fixes #8034
This reverts commit 0682bc8.
📲 Install BuildsiOS
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8037 +/- ##
=============================================
- Coverage 87.333% 87.313% -0.021%
=============================================
Files 553 554 +1
Lines 31983 31987 +4
Branches 13158 13159 +1
=============================================
- Hits 27932 27929 -3
- Misses 4004 4010 +6
- Partials 47 48 +1
... and 6 files with indirect coverage changes Continue to review full report in Codecov by Harness.
|
Under -enable-library-evolution (BUILD_LIBRARY_FOR_DISTRIBUTION), the Swift compiler treats cross-module value types as resilient — their size is unknown at compile time. On x86_64, this forces the compiler to use runtime class realization (CMt) instead of emitting a static _OBJC_CLASS_$_ symbol, causing linker failures for consumers. Wrapping the resilient stored properties in a Box<T> class makes the ivar pointer-sized (always known), so the compiler emits a static class layout on all architectures. Affected wrappers: SentryObjCAttributeContent, SentryObjCUnit, SentryObjCMetricValue, SentryObjCMetric. Fixes #8034
The internal property was renamed during the boxing refactor.
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 6c64f68 | 1235.80 ms | 1272.35 ms | 36.55 ms |
| ccf353a | 1233.79 ms | 1268.77 ms | 34.98 ms |
| 58e87fd | 1234.27 ms | 1262.09 ms | 27.82 ms |
| 9da166b | 1222.40 ms | 1250.24 ms | 27.84 ms |
| adef457 | 1229.45 ms | 1262.67 ms | 33.22 ms |
| 5e4d0a6 | 1230.40 ms | 1255.44 ms | 25.04 ms |
| ffb6adc | 1218.60 ms | 1247.47 ms | 28.87 ms |
| 09627e8 | 1223.68 ms | 1261.95 ms | 38.27 ms |
| bdd8e0e | 1233.35 ms | 1266.96 ms | 33.60 ms |
| e3147fd | 1227.15 ms | 1257.79 ms | 30.63 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 6c64f68 | 24.14 KiB | 1.17 MiB | 1.14 MiB |
| ccf353a | 24.14 KiB | 1.17 MiB | 1.15 MiB |
| 58e87fd | 24.14 KiB | 1.17 MiB | 1.14 MiB |
| 9da166b | 24.14 KiB | 1.16 MiB | 1.14 MiB |
| adef457 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 5e4d0a6 | 24.14 KiB | 1.17 MiB | 1.15 MiB |
| ffb6adc | 24.14 KiB | 1.15 MiB | 1.12 MiB |
| 09627e8 | 24.14 KiB | 1.17 MiB | 1.14 MiB |
| bdd8e0e | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| e3147fd | 24.14 KiB | 1.17 MiB | 1.14 MiB |
philprime
left a comment
There was a problem hiding this comment.
LGTM, but we will have to expand this to other types as well when we start to refactor the underlying SDK to use Swift-only classes or even Swift data structures. I'll create a script to detect a drift between architectures in a follow-up PR
Add validate-xcframework-symbols.sh that compares ObjC class symbols across architectures within fat binaries. Detects when resilient value types cause symbols to be emitted on arm64 but missing on x86_64. Wired into all XCFramework validation points: local builds, CI assembly workflows, and release validation.
🚨 Detected changes in high risk code 🚨High-risk code can easily blow up and is hard to test. We had severe bugs in the past. Be extra careful when changing these files, and have an extra careful look at these:
|
This reverts commit 78a4c5f.
Description
Fixes #8034 — SentryObjC dynamic framework is missing
_OBJC_CLASS_$_symbols for 4 classes in the x86_64 architecture slice:SentryObjCAttributeContentSentryObjCUnitSentryObjCMetricSentryObjCMetricValueRoot cause
These 4 wrapper classes in
SentryObjCCompatstore properties of pure Swift value types (SentryAttributeContent,SentryUnit,SentryMetricValue,SentryMetric) from theSentrySwiftmodule.When building with
BUILD_LIBRARY_FOR_DISTRIBUTION=YES, the Swift compiler treatsSentrySwifttypes as resilient — their in-memory size is unknown at compile time. On x86_64, this causes the compiler to generate a "class metadata template" (CMt) for runtime class realization instead of emitting a static_OBJC_CLASS_$_linker symbol. Consumers then get undefined symbol errors at link time.On arm64, the compiler emits a statically-laid-out class, so the issue only manifests on x86_64.
The other 41 working wrapper classes all wrap ObjC classes (pointer-sized, always known layout), which is why they aren't affected.
Fix
Wrap the resilient stored properties in a
Box<T>indirection class (new file:Box.swift). This makes the ivar pointer-sized (always known at compile time), so the compiler emits a static class layout on all architectures. The box adds one heap allocation per wrapper instance, which is negligible since these are alreadyNSObjectsubclasses.Verification
Built
SentryObjCfor macOS Release withBUILD_LIBRARY_FOR_DISTRIBUTION=YESand confirmed vianmthat all 4_OBJC_CLASS_$_symbols are now present in both arm64 and x86_64 object files, with noCMt(class metadata template) symbols.