dotnet_runtime/tools-local/tasks/mobile.tasks/AppleAppBuilder/Templates/runtime.m

271 lines
8.1 KiB
Objective-C

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#import <Foundation/Foundation.h>
#include <mono/utils/mono-publib.h>
#include <mono/utils/mono-logger.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/exception.h>
#include <mono/jit/jit.h>
#include <mono/jit/mono-private-unstable.h>
#include <TargetConditionals.h>
#import <os/log.h>
#include <sys/stat.h>
#include <sys/mman.h>
static char *bundle_path;
// no-op for iOS and tvOS.
// watchOS is not supported yet.
#define MONO_ENTER_GC_UNSAFE
#define MONO_EXIT_GC_UNSAFE
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)
{
//%DllMap%
}
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
void mono_jit_set_aot_mode (MonoAotMode mode);
void mono_ios_register_modules (void);
#endif
void
mono_ios_runtime_init (void)
{
// for now, only Invariant Mode is supported (FIXME: integrate ICU)
setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", TRUE);
// uncomment for debug output:
//
// setenv ("MONO_LOG_LEVEL", "debug", TRUE);
// setenv ("MONO_LOG_MASK", "all", TRUE);
id args_array = [[NSProcessInfo processInfo] arguments];
assert ([args_array count] <= 128);
const char *managed_argv [128];
int argi;
for (argi = 0; argi < [args_array count]; argi++) {
NSString* arg = [args_array objectAtIndex: argi];
managed_argv[argi] = [arg UTF8String];
}
bool wait_for_debugger = FALSE;
const char* bundle = get_bundle_path ();
chdir (bundle);
// TODO: set TRUSTED_PLATFORM_ASSEMBLIES, APP_PATHS and NATIVE_DLL_SEARCH_DIRECTORIES
monovm_initialize(0, NULL, NULL);
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
register_dllmap ();
// register modules
mono_ios_register_modules ();
mono_jit_set_aot_mode (MONO_AOT_MODE_FULL);
#endif
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_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");
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
// device runtimes are configured to use lazy gc thread creation
MONO_ENTER_GC_UNSAFE;
mono_gc_init_finalizer_thread ();
MONO_EXIT_GC_UNSAFE;
#endif
const char* executable = "%EntryPointLibName%";
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, argi, managed_argv);
// Print this so apps parsing logs can detect when we exited
os_log_info (OS_LOG_DEFAULT, "Exit code: %d.", res);
exit (res);
}