[msbuild] Don't turn missing Objective-C classes into hard link errors when inlining Class.GetHandle. Fixes #26268 - #26302
Conversation
…s when inlining Class.GetHandle. The Class.GetHandle inlining feature (compatibility/strict InlineClassGetHandle) emits a direct native reference ([X class] -> _OBJC_CLASS_$_X) for every surviving Class.GetHandle call. If the native Objective-C class doesn't actually exist in any linked library, the native link fails with 'Undefined symbols: _OBJC_CLASS_$_X', even if the class is never used at runtime. This happens for third-party bindings that declare a [BaseType (typeof (NSObject))] type for something that is a protocol - and not a class - natively (the reported Drastic.FFMpegKit 'Session' type is one example). The existing __attribute__((weak_import)) on the generated @interface isn't enough: ld still errors on a weak-external undefined symbol that isn't defined in any linked library. Fix: only emit a direct native reference for classes we can prove exist (classes that belong to a known platform/SDK framework). For any other class (a third-party binding class, or a class with no managed type) we: * pass '-U _OBJC_CLASS_$_X' to the native linker so a missing class doesn't break the link (this is a no-op for classes that do exist), and * fall back to objc_getClass at runtime if the direct reference is null. This mirrors the weak + dlsym fallback we already do for inlined dlfcn symbols, and restores the net10 behavior (a missing class handle resolves to null) for these classes while keeping the fast direct reference + native dead-strip protection for SDK classes. Fixes #26268 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07efe22e-a3b1-444f-8ae9-08325ffcaf1c
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
…ttribute.
The new InlineClassGetHandle optimization rewrites `Class.GetHandle ("X")`
into a direct native reference to `_OBJC_CLASS_$_X`. This breaks third-party
bindings that declare a `[BaseType (typeof (NSObject))]` type for something
that is a protocol - not a class - natively (e.g. Drastic.FFMpegKit's
`Session`). bgen generates a `[Protocol]` attribute on the concrete class it
emits for these, and the static registrar never registers such classes, so
there's no native Objective-C class to reference. Inlining the call then turns
into a hard link error (`Undefined symbols: _OBJC_CLASS_$_X`), even when the
class is never used at runtime.
Fix this by skipping the inlining when the target class has a `[Protocol]`
attribute (`ObjCType.IsFakeProtocol`). The call falls back to a runtime lookup
instead, which returns null for a missing native class - the same behavior
these bindings had before Class.GetHandle inlining existed.
This replaces the previous, more complex approach (emitting linker `-U` flags
plus an `objc_getClass` fallback), which is reverted here.
The `ReferenceNativeSymbol ... Ignore` workarounds for the P2 and
ProtocolWithBlockProperties test bindings are now redundant (they're fake
protocols, handled automatically) and are removed; TestProtocolRegister keeps
its workaround since it's a plain `[Register]` class, not a `[Protocol]`.
Fixes #26268
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 07efe22e-a3b1-444f-8ae9-08325ffcaf1c
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Pull request overview
This PR adjusts the InlineClassGetHandle linker optimization to avoid generating hard native link dependencies (_OBJC_CLASS_$_X) for managed “fake protocol” types (classes with a [Protocol] attribute) where no native Objective-C class symbol exists, restoring the previous runtime-lookup behavior and preventing publish-time link failures (fixes #26268).
Changes:
- Skip
Class.GetHandleinlining when the resolved targetObjCTypeisIsFakeProtocol. - Remove now-redundant
ReferenceNativeSymbol ... Ignoreentries for fake-protocol test bindings in linker test projects. - Document the fake-protocol scenario and the new inlining exception in
docs/code/class-handles.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tools/dotnet-linker/Steps/InlineClassGetHandleStep.cs | Avoids inlining Class.GetHandle for fake-protocol types to prevent undefined ObjC class symbol link errors. |
| tests/linker/trimmode link/dotnet/shared.csproj | Removes redundant ReferenceNativeSymbol ignores for fake-protocol types; keeps the explicit ignore for TestProtocolRegister. |
| tests/linker/link all/dotnet/shared.csproj | Same as above for the “link all” linker test configuration. |
| docs/code/class-handles.md | Documents why inlining must be avoided when the native ObjC class does not exist (fake protocols). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Address review feedback: Class.GetHandle returns a NativeHandle (a value type), so for a missing native class it returns a zero handle (IntPtr.Zero), not null. Fix the wording in the code comment and the documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07efe22e-a3b1-444f-8ae9-08325ffcaf1c
✅ API diff for current PR / commitNET (empty diffs)✅ API diff vs stableNET (empty diffs)ℹ️ Generator diffGenerator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes) Pipeline on Agent |
🚀 [CI Build #75aa7ee] Test results 🚀Test results✅ All tests passed on VSTS: test results. 🎉 All 256 tests passed 🎉 Tests counts✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download macOS tests✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download Linux Build VerificationPipeline on Agent |
…classes into hard link errors when inlining Class.GetHandle. Fixes #26268 (#26347) The `Class.GetHandle` inlining feature (compatibility/strict `InlineClassGetHandle`) rewrites a surviving `Class.GetHandle ("X")` call into a direct native reference (`[X class]` -> `_OBJC_CLASS_$_X`). If the native Objective-C class doesn't actually exist in any linked library, the native link fails with `Undefined symbols: _OBJC_CLASS_$_X`, even if the class is never used at runtime. This happens for third-party bindings that declare a `[BaseType (typeof (NSObject))]` type for something that is a protocol - and not a class - natively (the reported Drastic.FFMpegKit `Session` type is one example). bgen generates a `[Protocol]` attribute on the concrete class it emits for these types, and the static registrar never registers such classes, so there's no native Objective-C class to reference. Fix: don't inline `Class.GetHandle` when the target class has a `[Protocol]` attribute (`ObjCType.IsFakeProtocol`). The call falls back to a runtime lookup instead, which returns a zero handle (`IntPtr.Zero`) for a missing native class - the same behavior these bindings had before `Class.GetHandle` inlining existed. The change is contained within `InlineClassGetHandleStep`. The `ReferenceNativeSymbol ... Ignore` workarounds for the `P2` and `ProtocolWithBlockProperties` test bindings are now redundant (they're fake protocols, handled automatically) and are removed; `TestProtocolRegister` keeps its workaround since it's a plain `[Register]` class, not a `[Protocol]`. Fixes #26268 🤖 Pull request created by Copilot --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Backport of #26302. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The
Class.GetHandleinlining feature (compatibility/strictInlineClassGetHandle)rewrites a surviving
Class.GetHandle ("X")call into a direct native reference(
[X class]->_OBJC_CLASS_$_X). If the native Objective-C class doesn't actuallyexist in any linked library, the native link fails with
Undefined symbols: _OBJC_CLASS_$_X, even if the class is never used at runtime.This happens for third-party bindings that declare a
[BaseType (typeof (NSObject))]type for something that is a protocol - and not a class - natively (the reported
Drastic.FFMpegKit
Sessiontype is one example). bgen generates a[Protocol]attribute on the concrete class it emits for these types, and the static registrar
never registers such classes, so there's no native Objective-C class to reference.
Fix: don't inline
Class.GetHandlewhen the target class has a[Protocol]attribute(
ObjCType.IsFakeProtocol). The call falls back to a runtime lookup instead, whichreturns a zero handle (
IntPtr.Zero) for a missing native class - the same behavior thesebindings had before
Class.GetHandleinlining existed. The change is contained withinInlineClassGetHandleStep.This replaces an earlier, more complex approach (emitting linker
-Uflags plus anobjc_getClassruntime fallback), which is reverted in this PR.The
ReferenceNativeSymbol ... Ignoreworkarounds for theP2andProtocolWithBlockPropertiestest bindings are now redundant (they're fake protocols,handled automatically) and are removed;
TestProtocolRegisterkeeps its workaroundsince it's a plain
[Register]class, not a[Protocol].Fixes #26268
🤖 Pull request created by Copilot