// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // Common runtime code shared between emscripten and wasi. // Use #ifdef HOST_BROWSER/HOST_WASI for target specific code. // #ifdef __EMSCRIPTEN__ #define HOST_BROWSER 1 #else #define HOST_WASI 1 #endif #ifdef HOST_BROWSER #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wasm-config.h" #include "pinvoke.h" #ifdef GEN_PINVOKE #include "wasm_m2n_invoke.g.h" #endif #include "gc-common.h" #include "runtime.h" #ifdef DRIVER_GEN #include "driver-gen.c" #endif #ifdef HOST_WASI #define EMSCRIPTEN_KEEPALIVE #endif int mono_wasm_enable_gc = 1; /* Missing from public headers */ char *mono_fixup_symbol_name (const char *prefix, const char *key, const char *suffix); void mono_icall_table_init (void); void mono_wasm_enable_debugging (int); void mono_ee_interp_init (const char *opts); void mono_marshal_ilgen_init (void); void mono_method_builder_ilgen_init (void); void mono_sgen_mono_ilgen_init (void); char *monoeg_g_getenv(const char *variable); int monoeg_g_setenv(const char *variable, const char *value, int overwrite); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); void mono_trace_init (void); MonoMethod *mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoGCHandle target_handle, MonoError *error); /* Not part of public headers */ #define MONO_ICALL_TABLE_CALLBACKS_VERSION 3 typedef struct { int version; void* (*lookup) (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *flags); const char* (*lookup_icall_symbol) (void* func); } MonoIcallTableCallbacks; void mono_install_icall_table_callbacks (const MonoIcallTableCallbacks *cb); #define g_new(type, size) ((type *) malloc (sizeof (type) * (size))) #define g_new0(type, size) ((type *) calloc (sizeof (type), (size))) #if !defined(ENABLE_AOT) || defined(EE_MODE_LLVMONLY_INTERP) #ifndef LINK_ICALLS // FIXME: llvm+interp mode needs this to call icalls #define NEED_NORMAL_ICALL_TABLES 1 #endif #endif #ifdef LINK_ICALLS #include "icall-table.h" static int compare_int (const void *k1, const void *k2) { return *(int*)k1 - *(int*)k2; } static void* icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *out_flags) { uint32_t token = mono_method_get_token (method); assert (token); assert ((token & MONO_TOKEN_METHOD_DEF) == MONO_TOKEN_METHOD_DEF); uint32_t token_idx = token - MONO_TOKEN_METHOD_DEF; int *indexes = NULL; int indexes_size = 0; uint8_t *flags = NULL; void **funcs = NULL; *out_flags = 0; const char *image_name = mono_image_get_name (mono_class_get_image (mono_method_get_class (method))); #if defined(ICALL_TABLE_corlib) if (!strcmp (image_name, "System.Private.CoreLib")) { indexes = corlib_icall_indexes; indexes_size = sizeof (corlib_icall_indexes) / 4; flags = corlib_icall_flags; funcs = corlib_icall_funcs; assert (sizeof (corlib_icall_indexes [0]) == 4); } #endif #ifdef ICALL_TABLE_System if (!strcmp (image_name, "System")) { indexes = System_icall_indexes; indexes_size = sizeof (System_icall_indexes) / 4; flags = System_icall_flags; funcs = System_icall_funcs; } #endif assert (indexes); void *p = bsearch (&token_idx, indexes, indexes_size, 4, compare_int); if (!p) { return NULL; printf ("wasm: Unable to lookup icall: %s\n", mono_method_get_name (method)); exit (1); } uint32_t idx = (int*)p - indexes; *out_flags = flags [idx]; //printf ("ICALL: %s %x %d %d\n", methodname, token, idx, (int)(funcs [idx])); return funcs [idx]; } static const char* icall_table_lookup_symbol (void *func) { assert (0); return NULL; } #endif static void init_icall_table (void) { #ifdef LINK_ICALLS /* Link in our own linked icall table */ static const MonoIcallTableCallbacks mono_icall_table_callbacks = { MONO_ICALL_TABLE_CALLBACKS_VERSION, icall_table_lookup, icall_table_lookup_symbol }; mono_install_icall_table_callbacks (&mono_icall_table_callbacks); #endif #ifdef NEED_NORMAL_ICALL_TABLES mono_icall_table_init (); #endif } /* * get_native_to_interp: * * Return a pointer to a wasm function which can be used to enter the interpreter to * execute METHOD from native code. * EXTRA_ARG is the argument passed to the interp entry functions in the runtime. */ static void* get_native_to_interp (MonoMethod *method, void *extra_arg) { void *addr; MONO_ENTER_GC_UNSAFE; MonoClass *klass = mono_method_get_class (method); MonoImage *image = mono_class_get_image (klass); MonoAssembly *assembly = mono_image_get_assembly (image); MonoAssemblyName *aname = mono_assembly_get_name (assembly); const char *name = mono_assembly_name_get_name (aname); const char *class_name = mono_class_get_name (klass); const char *method_name = mono_method_get_name (method); char key [128]; int len; assert (strlen (name) < 100); snprintf (key, sizeof(key), "%s_%s_%s", name, class_name, method_name); char *fixedName = mono_fixup_symbol_name ("", key, ""); addr = wasm_dl_get_native_to_interp (fixedName, extra_arg); free (fixedName); MONO_EXIT_GC_UNSAFE; return addr; } static void *sysglobal_native_handle; static void* wasm_dl_load (const char *name, int flags, char **err, void *user_data) { #if WASM_SUPPORTS_DLOPEN if (!name) return dlopen(NULL, flags); #else if (!name) return NULL; #endif void* handle = wasm_dl_lookup_pinvoke_table (name); if (handle) return handle; if (!strcmp (name, "System.Globalization.Native")) return sysglobal_native_handle; #if WASM_SUPPORTS_DLOPEN return dlopen(name, flags); #endif return NULL; } static void* wasm_dl_symbol (void *handle, const char *name, char **err, void *user_data) { if (handle == sysglobal_native_handle) assert (0); #if WASM_SUPPORTS_DLOPEN if (!wasm_dl_is_pinvoke_tables (handle)) { return dlsym (handle, name); } #endif PinvokeImport *table = (PinvokeImport*)handle; for (int i = 0; table [i].name; ++i) { if (!strcmp (table [i].name, name)) return table [i].func; } return NULL; } MonoDomain * mono_wasm_load_runtime_common (int debug_level, MonoLogCallback log_callback, const char *interp_opts) { MonoDomain *domain; mini_parse_debug_option ("top-runtime-invoke-unhandled"); mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL); mono_wasm_install_get_native_to_interp_tramp (get_native_to_interp); #ifdef GEN_PINVOKE mono_wasm_install_interp_to_native_callback (mono_wasm_interp_to_native_callback); #endif #ifdef ENABLE_AOT monoeg_g_setenv ("MONO_AOT_MODE", "aot", 1); // Defined in driver-gen.c register_aot_modules (); #ifdef EE_MODE_LLVMONLY_INTERP mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY_INTERP); #else mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY); #endif #else mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_ONLY); /* * debug_level > 0 enables debugging and sets the debug log level to debug_level * debug_level == 0 disables debugging and enables interpreter optimizations * debug_level < 0 enabled debugging and disables debug logging. * * Note: when debugging is enabled interpreter optimizations are disabled. */ if (debug_level) { // Disable optimizations which interfere with debugging interp_opts = "-all"; mono_wasm_enable_debugging (debug_level); } #endif init_icall_table (); mono_ee_interp_init (interp_opts); mono_marshal_ilgen_init(); mono_method_builder_ilgen_init (); mono_sgen_mono_ilgen_init (); mono_trace_init (); mono_trace_set_log_handler (log_callback, NULL); domain = mono_jit_init_version ("mono", NULL); mono_thread_set_main (mono_thread_current ()); return domain; } // TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoAssembly* mono_wasm_assembly_load (const char *name) { MonoAssembly *res; assert (name); MonoImageOpenStatus status; MONO_ENTER_GC_UNSAFE; MonoAssemblyName* aname = mono_assembly_name_new (name); res = mono_assembly_load (aname, NULL, &status); mono_assembly_name_free (aname); MONO_EXIT_GC_UNSAFE; return res; } // TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoClass* mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name) { assert (assembly); MonoClass *result; MONO_ENTER_GC_UNSAFE; result = mono_class_from_name (mono_assembly_get_image (assembly), namespace, name); MONO_EXIT_GC_UNSAFE; return result; } // TODO https://github.com/dotnet/runtime/issues/98366 EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments) { assert (klass); MonoMethod* result; MONO_ENTER_GC_UNSAFE; result = mono_class_get_method_from_name (klass, name, arguments); MONO_EXIT_GC_UNSAFE; return result; } /* * mono_wasm_marshal_get_managed_wrapper: * Creates a wrapper for a function pointer to a method marked with * UnamangedCallersOnlyAttribute. * This wrapper ensures that the interpreter initializes the pointers. */ void mono_wasm_marshal_get_managed_wrapper (const char* assemblyName, const char* namespaceName, const char* typeName, const char* methodName, int num_params) { MonoError error; mono_error_init (&error); MonoAssembly* assembly = mono_wasm_assembly_load (assemblyName); assert (assembly); MonoClass* class = mono_wasm_assembly_find_class (assembly, namespaceName, typeName); assert (class); MonoMethod* method = mono_wasm_assembly_find_method (class, methodName, num_params); assert (method); MonoMethod *managedWrapper = mono_marshal_get_managed_wrapper (method, NULL, 0, &error); assert (managedWrapper); mono_compile_method (managedWrapper); }