From 67d1cb6362a0f55eb66c3115add8eacd92ff1114 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Wed, 18 Mar 2020 23:51:47 +0300 Subject: [PATCH] [mono] Add iOS sample (AOT, arm64) (#33633) * Add iOS sample --- src/mono/netcore/sample/iOS/.gitignore | 4 + src/mono/netcore/sample/iOS/CMakeLists.txt | 47 +++++ src/mono/netcore/sample/iOS/Makefile | 65 +++++++ src/mono/netcore/sample/iOS/Plist.in | 41 +++++ src/mono/netcore/sample/iOS/Program.cs | 50 +++++ src/mono/netcore/sample/iOS/Program.csproj | 9 + src/mono/netcore/sample/iOS/main.m | 77 ++++++++ src/mono/netcore/sample/iOS/runtime.h | 6 + src/mono/netcore/sample/iOS/runtime.m | 287 +++++++++++++++++++++++++++++ 9 files changed, 586 insertions(+) create mode 100644 src/mono/netcore/sample/iOS/.gitignore create mode 100644 src/mono/netcore/sample/iOS/CMakeLists.txt create mode 100644 src/mono/netcore/sample/iOS/Makefile create mode 100644 src/mono/netcore/sample/iOS/Plist.in create mode 100644 src/mono/netcore/sample/iOS/Program.cs create mode 100644 src/mono/netcore/sample/iOS/Program.csproj create mode 100644 src/mono/netcore/sample/iOS/main.m create mode 100644 src/mono/netcore/sample/iOS/runtime.h create mode 100644 src/mono/netcore/sample/iOS/runtime.m diff --git a/src/mono/netcore/sample/iOS/.gitignore b/src/mono/netcore/sample/iOS/.gitignore new file mode 100644 index 0000000..8a7ad91 --- /dev/null +++ b/src/mono/netcore/sample/iOS/.gitignore @@ -0,0 +1,4 @@ +bin/ +xcode/ +*.o +*.dll \ No newline at end of file diff --git a/src/mono/netcore/sample/iOS/CMakeLists.txt b/src/mono/netcore/sample/iOS/CMakeLists.txt new file mode 100644 index 0000000..5a6aaef --- /dev/null +++ b/src/mono/netcore/sample/iOS/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.14.5) + +project(HelloiOS) + +# make sure "make all" is executed first +file(GLOB DLLS *.dll) +file(GLOB DLLS_AOT *.dll.o) + +set(APP_RESOURCES + ${DLLS} +) + +# add the executable +add_executable( + HelloiOS + main.m + runtime.h + runtime.m + ${APP_RESOURCES} +) + +include_directories("../../../../../artifacts/bin/mono/iOS.${MONO_ARCH}.${MONO_CONFIG}/include/mono-2.0") + +set_target_properties(HelloiOS PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Plist.in + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING "NO" + RESOURCE "${APP_RESOURCES}" +) + +# FIXME: `XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING` should not be NO +# investigate why `PALEXPORT` doesn't preserve symbols + +target_link_libraries( + HelloiOS + "-framework Foundation" + "-framework UIKit" + "-framework GSS" + "-lz" + "-liconv" + "-force_load ../../../../../artifacts/bin/mono/iOS.${MONO_ARCH}.${MONO_CONFIG}/libmono.a" + "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.IO.Compression.Native.a" + "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.Native.a" + "-force_load ../../../../../artifacts/bin/native/netcoreapp5.0-iOS-${MONO_CONFIG}-${MONO_ARCH}/libSystem.Security.Cryptography.Native.Apple.a" + ${DLLS_AOT} +) \ No newline at end of file diff --git a/src/mono/netcore/sample/iOS/Makefile b/src/mono/netcore/sample/iOS/Makefile new file mode 100644 index 0000000..4113101 --- /dev/null +++ b/src/mono/netcore/sample/iOS/Makefile @@ -0,0 +1,65 @@ +MONO_CONFIG=Debug +ARTIFACTS_BIN=../../../../../artifacts/bin/ +ARTIFACTS_BCL=$(ARTIFACTS_BIN)runtime/netcoreapp5.0-iOS-$(MONO_CONFIG)-arm64 +ARTIFACTS_MONO=$(ARTIFACTS_BIN)/mono/iOS.arm64.$(MONO_CONFIG) + +DOTNET := $(shell cd ../../ && bash init-tools.sh | tail -1) +SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path) + +# once a new library is added here (e.g. System.Console.dll) it should also be +# added in mono_ios_register_modules() (runtime.m) and both lib.dll and lib.dll.o +# should be added to xcodeproj +all: prepare + make aot-lib LIB=$(ARTIFACTS_MONO)/System.Private.CoreLib.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Runtime.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Runtime.Extensions.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Collections.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Core.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Threading.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Threading.Tasks.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Linq.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Memory.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Runtime.InteropServices.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Text.Encoding.Extensions.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/Microsoft.Win32.Primitives.dll + make aot-lib LIB=$(ARTIFACTS_BCL)/System.Console.dll + make Program.dll.o + +# recompile Program.cs AOT +Program.dll.o: bin/Program.dll Makefile + make aot-lib LIB=bin/Program.dll + +# we need to copy some BCL libs to ARTIFACTS_MONO +# to be able to aot other bcl libs +prepare: + cp $(ARTIFACTS_BCL)/System.Memory.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Collections.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Threading.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Threading.Thread.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Runtime.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Runtime.InteropServices.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Text.Encoding.Extensions.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/Microsoft.Win32.Primitives.dll $(ARTIFACTS_MONO) + cp $(ARTIFACTS_BCL)/System.Console.dll $(ARTIFACTS_MONO) + +bin/Program.dll: Program.cs + $(DOTNET) build -c Debug Program.csproj + +aot-lib: + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 MONO_PATH=$(ARTIFACTS_MONO) \ + $(ARTIFACTS_MONO)/cross/./mono-aot-cross -O=gsharedvt,float32 --nollvm --debug \ + --aot=mtriple=arm64-ios,static,asmonly,direct-icalls,no-direct-calls,dwarfdebug,full $(LIB) && \ + clang -isysroot $(SYSROOT) -miphoneos-version-min=10.1 -arch arm64 -c $(LIB).s + cp $(LIB) $(notdir $(LIB)) + +# generate an xcode project +xcode: all + cmake -S. -BXcode -GXcode \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_ARCHITECTURES=arm64 \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.1 \ + -DCMAKE_INSTALL_PREFIX=`pwd`/_install \ + -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=YES \ + -DCMAKE_IOS_INSTALL_COMBINED=YES \ + -DMONO_CONFIG=$(MONO_CONFIG) \ + -DMONO_ARCH=arm64 \ No newline at end of file diff --git a/src/mono/netcore/sample/iOS/Plist.in b/src/mono/netcore/sample/iOS/Plist.in new file mode 100644 index 0000000..8934e53 --- /dev/null +++ b/src/mono/netcore/sample/iOS/Plist.in @@ -0,0 +1,41 @@ + + + + + CFBundleDevelopmentRegion + en-US + CFBundleExecutable + HelloiOS + CFBundleIdentifier + net.dot.HelloiOS + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + HelloiOS + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + + + diff --git a/src/mono/netcore/sample/iOS/Program.cs b/src/mono/netcore/sample/iOS/Program.cs new file mode 100644 index 0000000..6e3571d --- /dev/null +++ b/src/mono/netcore/sample/iOS/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +// it's not part of the BCL but runtime needs it for native-to-managed callbacks in AOT +// To be replaced with NativeCallableAttribute +public class MonoPInvokeCallbackAttribute : Attribute +{ + public MonoPInvokeCallbackAttribute(Type delegateType) { } +} + +public static class Program +{ + // Defined in main.m + [DllImport("__Internal")] + private extern static void ios_set_text(string value); + + [DllImport("__Internal")] + private extern static void ios_register_button_click(Action action); + + private static Action buttonClickHandler = null; + + private static int counter = 0; + + // Called by native code, see main.m + [MonoPInvokeCallback(typeof(Action))] + private static async void OnButtonClick() + { + ios_set_text("OnButtonClick! #" + counter++); + } + + public static async Task Main(string[] args) + { + // Register a managed callback (will be called by UIButton, see main.m) + // Also, keep the handler alive so GC won't collect it. + ios_register_button_click(buttonClickHandler = OnButtonClick); + + const string msg = "Hello World!\n.NET 5.0"; + for (int i = 0; i < msg.Length; i++) + { + // a kind of an animation + ios_set_text(msg.Substring(0, i + 1)); + await Task.Delay(100); + } + + // TODO: https://github.com/dotnet/runtime/issues/33667 + Console.WriteLine("Done!"); + } +} \ No newline at end of file diff --git a/src/mono/netcore/sample/iOS/Program.csproj b/src/mono/netcore/sample/iOS/Program.csproj new file mode 100644 index 0000000..0b65c42 --- /dev/null +++ b/src/mono/netcore/sample/iOS/Program.csproj @@ -0,0 +1,9 @@ + + + + Exe + bin + $(NetCoreAppCurrent) + + + diff --git a/src/mono/netcore/sample/iOS/main.m b/src/mono/netcore/sample/iOS/main.m new file mode 100644 index 0000000..d3c6881 --- /dev/null +++ b/src/mono/netcore/sample/iOS/main.m @@ -0,0 +1,77 @@ +#import +#import "runtime.h" + +@interface ViewController : UIViewController +@end + +@interface AppDelegate : UIResponder +@property (strong, nonatomic) UIWindow *window; +@property (strong, nonatomic) ViewController *controller; +@end + +@implementation AppDelegate +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.controller = [[ViewController alloc] initWithNibName:nil bundle:nil]; + self.window.rootViewController = self.controller; + [self.window makeKeyAndVisible]; + return YES; +} +@end + +UILabel *label; +void (*clickHandlerPtr)(void); + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + label = [[UILabel alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + label.textColor = [UIColor greenColor]; + label.font = [UIFont boldSystemFontOfSize: 30]; + label.numberOfLines = 2; + label.textAlignment = NSTextAlignmentCenter; + [self.view addSubview:label]; + + UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark]; + [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside]; + [button setFrame:CGRectMake(50, 300, 200, 50)]; + [button setTitle:@"Click me" forState:UIControlStateNormal]; + [button setExclusiveTouch:YES]; + [self.view addSubview:button]; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + mono_ios_runtime_init (); + }); +} +-(void) buttonClicked:(UIButton*)sender +{ + if (clickHandlerPtr) + clickHandlerPtr(); +} + +@end + +// called from C# sample +void +ios_register_button_click (void* ptr) +{ + clickHandlerPtr = ptr; +} + +// called from C# sample +void +ios_set_text (const char* value) +{ + NSString* nsstr = [NSString stringWithUTF8String:strdup(value)]; + dispatch_async(dispatch_get_main_queue(), ^{ + label.text = nsstr; + }); +} + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/src/mono/netcore/sample/iOS/runtime.h b/src/mono/netcore/sample/iOS/runtime.h new file mode 100644 index 0000000..ddddd96 --- /dev/null +++ b/src/mono/netcore/sample/iOS/runtime.h @@ -0,0 +1,6 @@ +#ifndef runtime_h +#define runtime_h + +void mono_ios_runtime_init (void); + +#endif /* runtime_h */ diff --git a/src/mono/netcore/sample/iOS/runtime.m b/src/mono/netcore/sample/iOS/runtime.m new file mode 100644 index 0000000..79fbd4f --- /dev/null +++ b/src/mono/netcore/sample/iOS/runtime.m @@ -0,0 +1,287 @@ +#import +#include +#include +#include +#include +#include +#include + +#import +#include +#include + +static os_log_t stdout_log; + +/* These are not in public headers */ +typedef unsigned char* (*MonoLoadAotDataFunc) (MonoAssembly *assembly, int size, void *user_data, void **out_handle); +typedef void (*MonoFreeAotDataFunc) (MonoAssembly *assembly, int size, void *user_data, void *handle); +void mono_install_load_aot_data_hook (MonoLoadAotDataFunc load_func, MonoFreeAotDataFunc free_func, void *user_data); +void mono_trace_init (void); +void mono_gc_init_finalizer_thread (void); + +static char *bundle_path; + +const char * +get_bundle_path (void) +{ + if (bundle_path) + return bundle_path; + + NSBundle* main_bundle = [NSBundle mainBundle]; + NSString* path = [main_bundle bundlePath]; + bundle_path = strdup ([path UTF8String]); + + return bundle_path; +} + +static unsigned char * +load_aot_data (MonoAssembly *assembly, int size, void *user_data, void **out_handle) +{ + *out_handle = NULL; + + char path [1024]; + int res; + + MonoAssemblyName *assembly_name = mono_assembly_get_name (assembly); + const char *aname = mono_assembly_name_get_name (assembly_name); + const char *bundle = get_bundle_path (); + + os_log_info (OS_LOG_DEFAULT, "Looking for aot data for assembly '%s'.", aname); + res = snprintf (path, sizeof (path) - 1, "%s/%s.aotdata", bundle, aname); + assert (res > 0); + + int fd = open (path, O_RDONLY); + if (fd < 0) { + os_log_info (OS_LOG_DEFAULT, "Could not load the aot data for %s from %s: %s\n", aname, path, strerror (errno)); + return NULL; + } + + void *ptr = mmap (NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if (ptr == MAP_FAILED) { + os_log_info (OS_LOG_DEFAULT, "Could not map the aot file for %s: %s\n", aname, strerror (errno)); + close (fd); + return NULL; + } + + close (fd); + os_log_info (OS_LOG_DEFAULT, "Loaded aot data for %s.\n", aname); + *out_handle = ptr; + return (unsigned char *) ptr; +} + +static void +free_aot_data (MonoAssembly *assembly, int size, void *user_data, void *handle) +{ + munmap (handle, size); +} + +static MonoAssembly* +load_assembly (const char *name, const char *culture) +{ + const char *bundle = get_bundle_path (); + char filename [1024]; + char path [1024]; + int res; + + os_log_info (OS_LOG_DEFAULT, "assembly_preload_hook: %{public}s %{public}s %{public}s\n", name, culture, bundle); + + int len = strlen (name); + int has_extension = len > 3 && name [len - 4] == '.' && (!strcmp ("exe", name + (len - 3)) || !strcmp ("dll", name + (len - 3))); + + // add extensions if required. + strlcpy (filename, name, sizeof (filename)); + if (!has_extension) { + strlcat (filename, ".dll", sizeof (filename)); + } + + if (culture && strcmp (culture, "")) + res = snprintf (path, sizeof (path) - 1, "%s/%s/%s", bundle, culture, filename); + else + res = snprintf (path, sizeof (path) - 1, "%s/%s", bundle, filename); + assert (res > 0); + + struct stat buffer; + if (stat (path, &buffer) == 0) { + MonoAssembly *assembly = mono_assembly_open (path, NULL); + assert (assembly); + return assembly; + } + return NULL; +} + +static MonoAssembly* +assembly_preload_hook (MonoAssemblyName *aname, char **assemblies_path, void* user_data) +{ + const char *name = mono_assembly_name_get_name (aname); + const char *culture = mono_assembly_name_get_culture (aname); + return load_assembly (name, culture); +} + +char * +strdup_printf (const char *msg, ...) +{ + va_list args; + char *formatted = NULL; + va_start (args, msg); + vasprintf (&formatted, msg, args); + va_end (args); + return formatted; +} + +static MonoObject * +fetch_exception_property (MonoObject *obj, const char *name, bool is_virtual) +{ + MonoMethod *get = NULL; + MonoMethod *get_virt = NULL; + MonoObject *exc = NULL; + + get = mono_class_get_method_from_name (mono_get_exception_class (), name, 0); + if (get) { + if (is_virtual) { + get_virt = mono_object_get_virtual_method (obj, get); + if (get_virt) + get = get_virt; + } + + return (MonoObject *) mono_runtime_invoke (get, obj, NULL, &exc); + } else { + printf ("Could not find the property System.Exception.%s", name); + } + + return NULL; +} + +static char * +fetch_exception_property_string (MonoObject *obj, const char *name, bool is_virtual) +{ + MonoString *str = (MonoString *) fetch_exception_property (obj, name, is_virtual); + return str ? mono_string_to_utf8 (str) : NULL; +} + +void +unhandled_exception_handler (MonoObject *exc, void *user_data) +{ + NSMutableString *msg = [[NSMutableString alloc] init]; + + MonoClass *type = mono_object_get_class (exc); + char *type_name = strdup_printf ("%s.%s", mono_class_get_namespace (type), mono_class_get_name (type)); + char *trace = fetch_exception_property_string (exc, "get_StackTrace", true); + char *message = fetch_exception_property_string (exc, "get_Message", true); + + [msg appendString:@"Unhandled managed exceptions:\n"]; + [msg appendFormat: @"%s (%s)\n%s\n", message, type_name, trace ? trace : ""]; + + free (trace); + free (message); + free (type_name); + + os_log_info (OS_LOG_DEFAULT, "%@", msg); + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", 1); + exit (1); +} + +void +log_callback (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) +{ + os_log_info (OS_LOG_DEFAULT, "(%s %s) %s", log_domain, log_level, message); + if (fatal) { + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", 1); + exit (1); + } +} + +static void +register_dllmap (void) +{ + mono_dllmap_insert (NULL, "libSystem.Native", NULL, "__Internal", NULL); + mono_dllmap_insert (NULL, "libSystem.IO.Compression.Native", NULL, "__Internal", NULL); + mono_dllmap_insert (NULL, "libSystem.Security.Cryptography.Native.Apple", NULL, "__Internal", NULL); +} + +void mono_jit_set_aot_mode (MonoAotMode mode); + +extern void *mono_aot_module_Program_info; +extern void *mono_aot_module_System_Private_CoreLib_info; +extern void *mono_aot_module_System_Runtime_info; +extern void *mono_aot_module_System_Runtime_Extensions_info; +extern void *mono_aot_module_System_Collections_info; +extern void *mono_aot_module_System_Core_info; +extern void *mono_aot_module_System_Threading_info; +extern void *mono_aot_module_System_Threading_Tasks_info; +extern void *mono_aot_module_System_Linq_info; +extern void *mono_aot_module_System_Memory_info; +extern void *mono_aot_module_System_Runtime_InteropServices_info; +extern void *mono_aot_module_System_Text_Encoding_Extensions_info; +extern void *mono_aot_module_Microsoft_Win32_Primitives_info; +extern void *mono_aot_module_System_Console_info; +extern void *mono_aot_module_Program_info; + +void mono_ios_register_modules (void) +{ + mono_aot_register_module (mono_aot_module_Program_info); + mono_aot_register_module (mono_aot_module_System_Private_CoreLib_info); + mono_aot_register_module (mono_aot_module_System_Runtime_info); + mono_aot_register_module (mono_aot_module_System_Runtime_Extensions_info); + mono_aot_register_module (mono_aot_module_System_Collections_info); + mono_aot_register_module (mono_aot_module_System_Core_info); + mono_aot_register_module (mono_aot_module_System_Threading_info); + mono_aot_register_module (mono_aot_module_System_Threading_Tasks_info); + mono_aot_register_module (mono_aot_module_System_Linq_info); + mono_aot_register_module (mono_aot_module_System_Memory_info); + mono_aot_register_module (mono_aot_module_System_Runtime_InteropServices_info); + mono_aot_register_module (mono_aot_module_System_Text_Encoding_Extensions_info); + mono_aot_register_module (mono_aot_module_Microsoft_Win32_Primitives_info); + mono_aot_register_module (mono_aot_module_System_Console_info); + mono_aot_register_module (mono_aot_module_Program_info); +} + +void mono_ios_setup_execution_mode (void) +{ + mono_jit_set_aot_mode (MONO_AOT_MODE_FULL); +} + +void +mono_ios_runtime_init (void) +{ + // for now, only Invariant Mode is supported (FIXME: integrate ICU) + setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE); + + stdout_log = os_log_create ("net.dot.mono", "stdout"); + + bool wait_for_debugger = FALSE; + char* executable = "Program.dll"; + + const char* bundle = get_bundle_path (); + chdir (bundle); + + register_dllmap (); + + // register modules + mono_ios_register_modules (); + mono_ios_setup_execution_mode (); + + mono_debug_init (MONO_DEBUG_FORMAT_MONO); + mono_install_assembly_preload_hook (assembly_preload_hook, NULL); + mono_install_load_aot_data_hook (load_aot_data, free_aot_data, NULL); + mono_install_unhandled_exception_hook (unhandled_exception_handler, NULL); + mono_trace_init (); + mono_trace_set_log_handler (log_callback, NULL); + mono_set_signal_chaining (TRUE); + mono_set_crash_chaining (TRUE); + + if (wait_for_debugger) { + char* options[] = { "--debugger-agent=transport=dt_socket,server=y,address=0.0.0.0:55555" }; + mono_jit_parse_options (1, options); + } + mono_jit_init_version ("dotnet.ios", "mobile"); + mono_gc_init_finalizer_thread (); + + MonoAssembly *assembly = load_assembly (executable, NULL); + assert (assembly); + os_log_info (OS_LOG_DEFAULT, "Executable: %{public}s", executable); + + int res = mono_jit_exec (mono_domain_get (), assembly, 1, &executable); + // Print this so apps parsing logs can detect when we exited + os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", res); +} + -- 2.7.4