fix(bridge): defer dylib init off the constructor (macOS 26 dyld init-order)#138
Conversation
…-order) Move all ObjC/Foundation/IMCore work out of the __attribute__((constructor)) into a bridgeBootstrap() dispatched on the main queue. macOS 26 tightened dyld initializer ordering for platform apps; touching Foundation at constructor time can run before libSystem finishes bootstrapping and abort Messages on launch. The constructor now only enqueues (a libdispatch call, no synchronous ObjC). Based on v0.11.0; constructor change only.
|
Codex review: needs maintainer review before merge. Reviewed June 6, 2026, 8:20 PM ET / 00:20 UTC. Summary Reproducibility: no. high-confidence reproduction of the exact macOS 26.5 crash was established in this review. Source inspection does confirm current main performs Foundation and IMCore work inside the dylib constructor, matching the proposed failure mode. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow constructor-deferral after normal maintainer review, preserving the existing delayed watcher startup and adding macOS 26.5 launch/status proof if a suitable host is available. Do we have a high-confidence way to reproduce the issue? No high-confidence reproduction of the exact macOS 26.5 crash was established in this review. Source inspection does confirm current main performs Foundation and IMCore work inside the dylib constructor, matching the proposed failure mode. Is this the best way to solve the issue? Yes. Deferring the existing bootstrap body behind an async main-queue hop is the narrowest maintainable fix I see, and the remaining macOS 26.5 validation gap is proof-related rather than a clear code blocker. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 041b40686a6d. Label changesLabel changes:
Label justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Maintainer pass on PR #138 complete. What changed beyond the original patch:
Proof run locally on macOS 26.5 build 25F71:
Live injection caveat:
CI on pushed head
|
Summary
Move all ObjC/Foundation/IMCore work out of the injected dylib's
__attribute__((constructor))(injectedInit) intobridgeBootstrap(), dispatched on the main queue. The constructor now only enqueues (a libdispatch call, no synchronous ObjC message dispatch).Why: macOS 26 tightened dyld initializer ordering for platform/system apps. Touching ObjC/Foundation/IMCore at constructor time can run before libSystem finishes bootstrapping ("dyld initialized but libSystem has not") and abort Messages.app on launch. Deferring onto the main queue runs the work only after the process is fully initialized.
Based on
v0.11.0; constructor change only (+16/-2).Verification
make build-dylibclean (arm64e).imsg launch --kill-only+--dylib <built>.vmmapconfirmed the new dylib mapped into the live Messages process;.imsg-bridge-readyrewritten;imsg status --json->advanced_features:true, v2_ready:true, "Connected to Messages.app";imsg chatsRPC roundtrip works; selectors identical to the released dylib (plus a bonuspollPayloadMessage:true). No crash, bridge fully up. Rolled back to the released dylib afterward.Scope / caveat
This proves no regression on macOS 26.4.x, where injection already works. It does not prove it fixes the macOS 26.5 injection/crash path (no 26.5 host with a working bridge available to test) — that remains the motivating hypothesis. The change is sound hardening regardless and is risk-free on hosts where the dylib already loads.