When exception marshalling was originally implemented, for backwards
compatibility concerns it was only turned on by default for platforms that
really needed it (watchOS).
However, exception marshalling is by far the safest option, so in .NET we're
enabling it by default for all platforms (it's still possible to disable it
for those who wants to).
Ref: https://docs.microsoft.com/en-us/xamarin/ios/platform/exception-marshaling
* Make Runtime.Arch a readonly field.
* Tell the AOT compiler Runtime.Arch is a constant value.
* Tell the linker to stub out the method we use to fetch the current
architecture from native code (it turned out a bit complicated to set the
Arch field when it's readonly - the solution I came up with was to call a
P/Invoke).
Test case (size of the main executable): link all (debug)
* Before: 33.522.704 bytes
* After: 33.506.112 bytes
* Difference: -16.592 bytes (-0.05 %)
There were no size differences in release mode, nor were there any size
differences in the "don't link" test, neither for debug nor release mode.
Fixes https://github.com/xamarin/xamarin-macios/issues/5518.
This makes diagnosing what happens much easier in some cases.
Exhibit A, pre fix:
*** Terminating app due to uncaught exception 'ObjCRuntime.RuntimeException', reason: 'Failed to lookup the required marshalling information.
Additional information:
Selector: conformsToProtocol:
Type: ViewController
Exhibit B, post fix:
*** Terminating app due to uncaught exception 'ObjCRuntime.RuntimeException', reason: 'Failed to lookup the required marshalling information.
Additional information:
Selector: conformsToProtocol:
Type: ViewController
(ObjCRuntime.RuntimeException)
Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException)
at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle)
Failed to marshal the Objective-C object 0x7f813fd2f470 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException)
at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution)
at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution)
at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution)
at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue)
at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
It does exactly what you'd think it does: nothing helpful at all.
Make sure to use a GCHandle that's alive instead.
Output before fix:
Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2):
Failed to print exception: System.NullReferenceException: Object reference not set to an instance of an object.
at ObjCRuntime.Runtime.PrintException(Exception exc, Boolean isInnerException, StringBuilder sb)
at ObjCRuntime.Runtime.PrintAllExceptions(IntPtr exception_gchandle)
Output after fix:
Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2):
Failed to lookup the required marshalling information.
Additional information:
Selector: conformsToProtocol:
Type: ViewController
(ObjCRuntime.RuntimeException)
Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException)
at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle)
Failed to marshal the Objective-C object 0x7f89e6c2a2a0 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException)
at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution)
at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution)
at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution)
at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue)
at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
The deadlock goes like this:
1. Thread A holds the framework_peer_release_lock lock, and tries to lock the
refcount_mutex lock.
2. Thread B holds the refcount_mutex, and is waiting for the GC to complete
3. Thread C is trying to lock the framework_peer_release_lock while running
the GC.
The fix is in thread A, by not doing anything at all with the
framework_peer_release_lock lock locked.
The code contains extensive comments explaining the situation and the solution.
Fixes https://github.com/xamarin/xamarin-macios/issues/13066.
Co-authored-by: Chris Hamons <chris.hamons@xamarin.com>
P/Invokes may point to a dylib, while the actual library linked into the .app
might be a static library, so make sure to compare without the extension.
This fixes an issue when linking with the static version of the runtime libraries.
Setting the XAMARIN_LOG_MARSHALLED_EXCEPTIONS environment variable will now
make us print all exceptions that go through exception marshalling, and how
we'll handle them.
Fixes https://github.com/xamarin/xamarin-macios/issues/12343.
* Add support for Mono Components.
* Modify how we look up symbols from native libraries shipped with Mono: we keep
track of which native libraries we linked with, and depending on how we linked
to those assemblies, we look the symbols up at runtime in either the current executable
(if linking statically), or the actual library (where the P/Invoke says they're
supposed to be).
* This means that we have to propagate how libmono is linked from the MSBuild code
to the Application class so that our existing logic is able to correctly determine
which native mono lib to use.
* Modify how we list the P/Invokes we need to preserve by taking into account the
list of native libraries from Mono we have to link with (for .NET). For legacy
Xamarin, I've reverted the logic to how it was before we started adding .NET support.
Fixes https://github.com/xamarin/xamarin-macios/issues/10950.
Fixes https://github.com/xamarin/xamarin-macios/issues/11145.
Fixes https://github.com/xamarin/xamarin-macios/issues/12100.
List all the assemblies in the app bundle and pass them to MonoVM/CoreCLR's in
the TRUSTED_PLATFORM_ASSEMBLIES initialization property.
This way CoreCLR knows where to find System.Private.CoreLib.dll for fat apps
(it's in the runtimeidentifier-specific subdirectory, and by default CoreCLR
will only look next to libcoreclr.dylib).
Fixes https://github.com/xamarin/xamarin-macios/issues/12265.
This also meant propagating how libmono is linked from the MSBuild code to the Application
class so that our existing logic is able to correctly determine which native mono
lib to use.
* Use the Apple-provided TARGET_OS_SIMULATOR define to determine if we're
running in a simulator, instead of checking the current architecture. This
way we properly detect ARM64-based simulators (and it'll work correctly in
the future).
* Always set Runtime.Arch = SIMULATOR for Mac Catalyst. The final value for
Runtime.Arch for Mac Catalyst is tracked in #10312, but this is a stop-gap
measure to make sure we have the same value between X64 and ARM64 on Mac
Catalyst, and until now we've had Runtime.Arch = SIMULATOR for X64, so just
go with that for now.
Return early when we're not going to try resolving anything, which means that
if we didn't find something by the end, we know that it's because we failed
(and not because we weren't supposed to try), and we log that.
This makes it easier to diagnose a few failure conditions.
To have consistent behavior in .NET, set the current directory to the root of
the app bundle for all platforms.
This is a breaking change for legacy Xamarin.Mac, which used to set the
current directory to the Contents/Resources subdirectory, but there's a simple
workaround for customers that depend on the old behavior (change it in Main
themselves), and I believe the consistent experience across platforms warrants
this change.
Note that we already had a breaking change here for macOS/.NET: we were
(unintentionally) setting the current directory to the Contents/MonoBundle
directory, which neither matched mobile platforms, nor the legacy Xamarin.Mac
behavior.
This solves the problem of what to do for Mac Catalyst apps, because there's
no need to choose between the macOS or the mobile behavior, since they're the
same.
This required changing the launch of macOS apps using CoreCLR to pass the full
path to the entry assembly, since the entry assembly isn't in the current
directory anymore.
While not strictly necessary to not leak (because the process is exiting
anyway), it makes it easier to read leak reports, because these dictionaries
won't show up as leaked memory anymore.
Before:
There were 258096 MonoObjects created, 258015 MonoObjects freed, so 81 were not freed. (dynamic registrar)
There were 205834 MonoObjects created, 205833 MonoObjects freed, so 1 were not freed. (static registrar)
After:
There were 258104 MonoObjects created, 258025 MonoObjects freed, so 79 were not freed. (dynamic registrar)
There were 205834 MonoObjects created, 205834 MonoObjects freed, so no leaked MonoObjects. (static registrar)
* [runtime] Add support for exception marshalling to CoreCLR.
* [runtime] Add an empty implementation of the toggle ref machinery.
We need this to use the unhandled exception handler support in CoreCLR,
because the ObjectiveCMarshal.Initialize call to initialize unhandled
exception support requires passing toggle ref callbacks as well.
* [tests] The TestConstrainedGenericType test can now be re-enabled, after a few updates.
Before:
There were 258046 MonoObjects created, 235142 MonoObjects freed, so 22904 were not freed. (dynamic registrar)
There were 205804 MonoObjects created, 204193 MonoObjects freed, so 1611 were not freed. (static registrar)
After:
There were 258054 MonoObjects created, 235172 MonoObjects freed, so 22882 were not freed. (dynamic registrar)
There were 205804 MonoObjects created, 205190 MonoObjects freed, so 614 were not freed. (static registrar)
* [runtime] Mark numerous Mono Embedding API as used only by the MonoVM bridge.
* [runtime] Exclude more unused code from the CoreCLR build.
* There's no tracing in CoreCLR, so no need to process the MONO_TRACE environment
variable (and set the trace options).
* There's no sdb debuggger, so that code can be skipped.
* The profiler support is very different, so skip that code too.
* We don't support AOT, nor aot data files, so skip that.
* [runtime] Stop generating dummy implementations of the Mono Embedding API for CoreCLR.
All of the Mono Embedding API now falls in either of these two categories:
* Only used by the MonoVM bridge.
* Has a CoreCLR implementation.
Which means that we don't need the code to generate dummy implementations for
methods that aren't in any of these two categories anymore.
* Make 'throw Objective-C exception' the default for managed exception marshalling.
* Make 'throw managed exception' the default for Objective-C exception marshalling.
* Disallow the 'unwind through native frames' option: CoreCLR won't do it.
* Disallow the 'unwind through managed frames' option: it's the safeset
option by far, and also matches the reverse case.
* Disallow the 'disable' option: this is also not safe, let's try to go the
safe route with CoreCLR.
* Change the default in native code too.
Partial fix for #10940.
Passing a 'MonoObject*' to a function that expects a GCHandle doesn't quite
work, so make sure to get a GCHandle for the exception we want to print
information about.
* Implement our xamarin_dyn_objc_msgSend[Super] overrides for ARM64.
* Modify mmp to use those overrides.
* Fix an issue with the existing xamarin_arm64_common_trampoline that caused
exceptions to not unwind correctly.
* Add an ARM64 variation of xammac tests in xharness.
* Various test fixes.
When using the MonoVM, we compare MonoClass instances by pointer. This turns
out a bit complicated for CoreCLR, because our MonoClass instances are not
unique (there can be multiple MonoClass instances that refer to the same
type), so instead implement helper methods that do the comparison. This also
has the benefit of not requiring any memory allocations on CoreCLR.
There's no general way to set a pending managed exception in CoreCLR (the
current plan is to support setting a pending managed exception for the
objc_msgSend family of functions). This means that the way we've implemented
custom wrappers that can handle Objective-C exceptions won't work, because
those wrappers currently tries to set a pending managed exception (which Mono
throws upon returning from the corresponding native wrapper function).
So rewrite this a bit: these custom wrappers now return a GCHandle with the
managed exception as an out parameter, and the calling managed code throws
that exception instead.
This also required adjusting a few API definitions to match how their wrapper
functions are defined.
* [runtime] Call into managed code to handle runtime exceptions.
This makes things easier for CoreCLR.
There should be no significant performance hits; this code path is
exceptional, and exceptions are already very heavy-weight anyways.
* Update to use xamarin_free instead of mono_free as per review.
* Port more to managed code.