From 8940e2250ae08b97f645aaf12024551a9fdff6a3 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 28 Apr 2023 10:51:16 +0200 Subject: [PATCH] [release/6.0.4xx-xcode14.3] [tests] Find a workaround for #xamarin/maccore@2668. (#18172) 1. Mono changed dyld lookup to start looking in directories in NATIVE_DLL_SEARCH_DIRECTORIES before the actual given path, even when the given path is absolute [1]. 2. This turned out to break Mac Catalyst, because when a DllImport says a P/Invoke is in "/System/Library/Frameworks/SceneKit.framework/SceneKit", Mono would try loading by prefixing the directories in NATIVE_DLL_SEARCH_DIRECTORIES. We add the Contents/MonoBundle directory to NATIVE_DLL_SEARCH_DIRECTORIES, so Mono would try to load "/path/to/my.app/Contents/MonoBundle//System/Library/Frameworks/SceneKit.framework/SceneKit", and things would go wrong. 3. We found a workaround: add "/" to NATIVE_DLL_SEARCH_DIRECTORIES. This works on Ventura, but apparently not on older macOS version, because the actual path we pass to dlopen ends up being "///System/Library/Frameworks/SceneKit.framework/SceneKit" (note the three initial slashes instead of a single slash). 4. Add a second workaround, where we add a dll import resolver to load exactly the path we want to load. [1]: https://github.com/dotnet/runtime/commit/5a1baebc09b34a58fe2f8e1b29d4e1f7ca7dabbc [2]: https://github.com/dotnet/runtime/pull/85255 Technical sidenote: Why trying to load "/path/to/my.app/Contents/MonoBundle//System/Library/Frameworks/SceneKit.framework/SceneKit" turned out so bad on Mac Catalyst is not obvious. What happens is this: * The app calls 'dlopen ("/path/to/my.app/Contents/MonoBundle//System/Library/Frameworks/SceneKit.framework/SceneKit")' * dlopen checks if this is a Mac Catalyst override of a macOS system framework, by prefixing "/System/iOSSupport" and trying to load that. So dlopen would try to load "/System/iOSSupport/path/to/my.app/Contents/MonoBundle//System/Library/Frameworks/SceneKit.framework/SceneKit", which would obviously fail. * Then dlopen would try a few more fallbacks, eventually trying "/System/Library/Frameworks/SceneKit.framework/SceneKit", and successfully loading that library. * Unfortunately "/System/Library/Frameworks/SceneKit.framework/SceneKit" is the wrong library to load for Mac Catalyst ("/System/iOSSupport/System/Library/Frameworks/SceneKit.framework/SceneKit" is the correct version). These two libraries are incompatible, and calling one when you mean to call the other will do nasty things like corrupting the stack. Backport of #18159 --- tests/monotouch-test/AppDelegate.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/monotouch-test/AppDelegate.cs b/tests/monotouch-test/AppDelegate.cs index f0aad527ecb3..cf5ff02f2534 100644 --- a/tests/monotouch-test/AppDelegate.cs +++ b/tests/monotouch-test/AppDelegate.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Runtime.InteropServices; using Foundation; using UIKit; using MonoTouch.NUnit.UI; @@ -66,12 +67,33 @@ public override bool FinishedLaunching (UIApplication app, NSDictionary options) static void Main (string[] args) { +#if __MACCATALYST__ + NativeLibrary.SetDllImportResolver (typeof (NSObject).Assembly, DllImportResolver); + NativeLibrary.SetDllImportResolver (typeof (AppDelegate).Assembly, DllImportResolver); +#endif // Make sure we have at least one reference to the bindings project so that mcs doesn't strip the reference to it. GC.KeepAlive (typeof(Bindings.Test.UltimateMachine)); UIApplication.Main (args, null, typeof (AppDelegate)); } +#if __MACCATALYST__ + // This is a workaround for a temporary issue in the .NET runtime + // See https://github.com/xamarin/maccore/issues/2668 + // The issue is present in .NET 7.0.5, and will likely be fixed in .NET 7.0.6. + static IntPtr DllImportResolver (string libraryName, global::System.Reflection.Assembly assembly, DllImportSearchPath? searchPath) + { + switch (libraryName) { + case "/System/Library/Frameworks/SceneKit.framework/SceneKit": + case "/System/Library/Frameworks/SceneKit.framework/Versions/A/SceneKit": + var rv = NativeLibrary.Load (libraryName); + Console.WriteLine ($"DllImportResolver callback loaded library \"{libraryName}\" from a P/Invoke in \"{assembly}\" => 0x{rv.ToString ("x")}"); + return rv; + default: + return IntPtr.Zero; + } + } +#endif public static void PresentModalViewController (UIViewController vc, double duration) { var bckp = window.RootViewController;