#if RUNTIME_MONO #include #include #include "mono-api.h" #include "../libmono/icalls/mscorlib/System.Diagnostics/StackFrame.h" #include "../libmono/icalls/mscorlib/System.Diagnostics/StackTrace.h" #include "../libmono/icalls/mscorlib/System.Runtime.InteropServices/Marshal.h" #include "../libmono/vm/StackTrace.h" #include "../libmono/vm/MetadataCache.h" #include "il2cpp-callbacks.h" #include "il2cpp-mono-support.h" #include "il2cpp-class-internals.h" #include "il2cpp-object-internals.h" #include #include "os/Image.h" #include "os/Thread.h" #include "os/c-api/Allocator.h" #include "utils/PathUtils.h" #include "utils/StringUtils.h" #include "utils/Environment.h" #include "utils/utf8-cpp/source/utf8.h" #include "vm-utils/Debugger.h" #include "vm-utils/NativeSymbol.h" static void* il2cpp_get_vtable_trampoline(MonoVTable *vtable, int slot_index) { return 0; } static void* il2cpp_get_imt_trampoline(MonoVTable *vtable, int imt_slot_index) { return 0; } static void mono_thread_start_cb(intptr_t tid, void* stack_start, void* func) { mono::vm::StackTrace::InitializeStackTracesForCurrentThread(); } static void mono_thread_attach_cb(intptr_t tid, void* stack_start) { mono::vm::StackTrace::InitializeStackTracesForCurrentThread(); } static void mono_thread_cleanup_cb(MonoNativeThreadId tid) { //assert(tid == il2cpp::os::Thread::CurrentThreadId()); //mono::vm::StackTrace::CleanupStackTracesForCurrentThread(); } static void il2cpp_throw_exception(MonoException* ex) { if (mono_unity_exception_get_trace_ips(ex) == NULL) { // Only write the stack trace if there is not one already in the exception. // When we exit managed try/finally and try/catch blocks with an exception, this method is // called with the original exception which already has the proper stack trace. // Getting the stack trace again here will lose the frames between the original throw // and the finally or catch block. const mono::vm::StackFrames& frames = *mono::vm::StackTrace::GetStackFrames(); size_t i = frames.size() - 1; MonoArray* ips = MonoArrayNew(mono_unity_defaults_get_int_class(), frames.size()); for (mono::vm::StackFrames::const_iterator iter = frames.begin(); iter != frames.end(); ++iter, --i) { mono_array_set(ips, const MonoMethod*, i, (*iter).method); } mono_unity_exception_set_trace_ips(ex, ips); } throw Il2CppExceptionWrapper(ex); } static void il2cpp_walk_stack_with_ctx(MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data) { mono::vm::StackTrace::WalkFrameStack(func, ctx, user_data); } static int32_t il2cpp_exception_walk_trace(MonoException *ex, MonoInternalExceptionFrameWalk func, void* user_data) { MonoArray *ta = mono_unity_exception_get_trace_ips(ex); int len, i; if (ta == NULL) return 0; len = (int)mono_array_length(ta) >> 1; for (i = 0; i < len; i++) { void* ip = mono_array_get(ta, void*, i * 2 + 0); void* generic_info = mono_array_get(ta, void*, i * 2 + 1); const MonoMethod* method = il2cpp::utils::NativeSymbol::GetMethodFromNativeSymbol((Il2CppMethodPointer)ip); if (method == NULL) { if (func(NULL, ip, 0, 0, user_data)) return 1; } else { if (func(const_cast(method), ip, 0, 1, user_data)) return 1; } } return len > 0; } static int32_t il2cpp_current_thread_has_handle_block_guard() { return 0; } static gboolean il2cpp_install_handler_block_guard(MonoThreadUnwindState *ctx) { return false; } static MonoObject* il2cpp_mono_fake_box(void *ptr) { return ((MonoObject*)ptr) - 1; } MonoObject* il2cpp_mono_determine_target_object(MonoMethod *method, MonoObject *obj) { if (!obj) return NULL; if (mono_class_is_valuetype(mono_unity_method_get_class(method))) return obj; MonoClass *klass = mono_unity_object_get_class(obj); if (mono_unity_class_is_delegate(klass) && (strcmp(mono_unity_method_get_name(method), "Invoke") != 0)) return mono_unity_delegate_get_target((MonoDelegate*)obj); return obj; } MonoObject* il2cpp_mono_runtime_invoke(MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error) { il2cpp_mono_method_initialize_function_pointers(method, error); if (!il2cpp_mono_error_ok(error)) return NULL; if (exc) *exc = NULL; MonoObject *target = il2cpp_mono_determine_target_object(method, (MonoObject*)obj); void **newParams = params; std::vector newParamsVec; std::vector byRefParams; if (params && mono_method_signature(method)) { int numParams = mono_unity_signature_num_parameters(mono_method_signature(method)); if (numParams > 0) { newParamsVec.resize(numParams); for (int i = 0; i < numParams; ++i) { MonoClass *paramSigClass = mono_unity_signature_get_class_for_param(mono_method_signature(method), i); if (mono_class_is_nullable(paramSigClass)) { if (mono_unity_signature_param_is_byref(mono_method_signature(method), i)) byRefParams.push_back(i); MonoObject *paramObj = (MonoObject*)params[i]; void *nullable = alloca(mono_unity_class_get_instance_size(paramSigClass)); mono_nullable_init((uint8_t*)nullable, paramObj, paramSigClass); newParamsVec[i] = nullable; } else { newParamsVec[i] = params[i]; } } newParams = &newParamsVec[0]; } } if (mono_class_is_nullable(mono_unity_method_get_class(method))) target = mono_value_box(g_MonoDomain, mono_unity_class_get_castclass(mono_unity_method_get_class(method)), target); else if (target != NULL && mono_class_is_valuetype(mono_unity_method_get_class(method))) target = il2cpp_mono_fake_box(target); try { MonoClass *klass = mono_method_get_class(method); if (strcmp(mono_unity_method_get_name(method), ".ctor") == 0 || (mono_unity_method_is_static(method) && mono_unity_class_has_cctor(klass))) mono_runtime_class_init_full(il2cpp_mono_class_vtable(g_MonoDomain, klass), error); void *retVal = ((InvokerMethod)mono_unity_method_get_invoke_pointer(method))((Il2CppMethodPointer)mono_unity_method_get_method_pointer(method), method, target, newParams); std::vector::const_iterator end = byRefParams.end(); for (std::vector::const_iterator it = byRefParams.begin(); it != end; ++it) { MonoClass *paramSigClass = mono_unity_signature_get_class_for_param(mono_method_signature(method), *it); params[*it] = mono_value_box(g_MonoDomain, paramSigClass, newParamsVec[*it]); } return mono_unity_method_convert_return_type_if_needed(method, retVal); } catch (Il2CppExceptionWrapper& ex) { if (exc) *exc = (MonoObject*)ex.ex; else mono_set_pending_exception((MonoException*)ex.ex); return NULL; } } MonoObject* il2cpp_mono_finalize_runtime_invoke(MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method) { MonoMethod* finalizer = mono_class_get_finalizer(mono_object_get_class(this_obj)); if (finalizer == NULL) { assert(0 && "Why did we get a NULL finalizer? Something may not be correct."); return NULL; } MonoError unused; return il2cpp_mono_runtime_invoke(finalizer, this_obj, params, exc, &unused); } void il2cpp_mono_set_cast_details(MonoClass *from, MonoClass *to) { } MonoObject* il2cpp_mono_capture_context_runtime_invoke(MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method) { return NULL; } void il2cpp_mono_dummy_callback() { } void* il2cpp_mono_delegate_trampoline(MonoDomain *domain, MonoClass *klass) { int vtableSize = mono_class_get_vtable_size(klass); MonoMethod *method = NULL; for (int i = 0; i < vtableSize; ++i) { MonoMethod *vtableMethod = mono_class_get_vtable_entry(klass, i); const char *vtableMethodName = mono_unity_method_get_name(vtableMethod); const char *substr = strstr(vtableMethodName, "Invoke"); if (substr == vtableMethodName) //if the pointers are equal, vtableMethodName starts with "Invoke" { method = vtableMethod; break; } } if (!method) return NULL; MonoError unused; il2cpp_mono_method_initialize_function_pointers(method, &unused); return mono_unity_method_get_method_pointer(method); } void* il2cpp_mono_create_jump_trampoline(MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper, MonoError *error) { il2cpp_mono_method_initialize_function_pointers(method, error); return mono_unity_method_get_method_pointer(method); } void* il2cpp_mono_create_ftnptr(MonoDomain *domain, void* addr) { return addr; } char* il2cpp_mono_get_runtime_build_info() { return mono_unity_get_runtime_build_info(__DATE__, __TIME__); } #if IL2CPP_MONO_DEBUGGER void il2cpp_debugger_save_thread_context(Il2CppThreadUnwindState* context, int frameCountAdjust) { il2cpp::utils::Debugger::SaveThreadContext(context, frameCountAdjust); } #endif void il2cpp_install_callbacks() { MonoRuntimeCallbacks callbacks; g_MonoDomain = mono_domain_get(); memset(&callbacks, 0, sizeof(callbacks)); callbacks.get_vtable_trampoline = il2cpp_get_vtable_trampoline; callbacks.get_imt_trampoline = il2cpp_get_imt_trampoline; callbacks.runtime_invoke = il2cpp_mono_runtime_invoke; callbacks.set_cast_details = il2cpp_mono_set_cast_details; callbacks.create_jump_trampoline = il2cpp_mono_create_jump_trampoline; callbacks.create_ftnptr = il2cpp_mono_create_ftnptr; callbacks.get_runtime_build_info = il2cpp_mono_get_runtime_build_info; callbacks.create_delegate_trampoline = il2cpp_mono_delegate_trampoline; #if IL2CPP_MONO_DEBUGGER // These don't exist in the Mono code used by default. We might need to add them (or something similar) // later. I'll comment them out to get the build working for now though. //callbacks.il2cpp_debugger_save_thread_context = il2cpp_debugger_save_thread_context; //callbacks.get_global_breakpoint_state_pointer =il2cpp::utils::Debugger::GetGlobalBreakpointPointer; #endif mono_install_callbacks(&callbacks); MonoRuntimeExceptionHandlingCallbacks cbs; memset(&cbs, 0, sizeof(cbs)); cbs.mono_raise_exception = il2cpp_throw_exception; cbs.mono_walk_stack_with_ctx = il2cpp_walk_stack_with_ctx; cbs.mono_install_handler_block_guard = il2cpp_install_handler_block_guard; cbs.mono_exception_walk_trace = il2cpp_exception_walk_trace; cbs.mono_current_thread_has_handle_block_guard = il2cpp_current_thread_has_handle_block_guard; mono_install_eh_callbacks(&cbs); mono_unity_domain_install_finalize_runtime_invoke(g_MonoDomain, il2cpp_mono_finalize_runtime_invoke); mono_unity_domain_install_capture_context_runtime_invoke(g_MonoDomain, il2cpp_mono_capture_context_runtime_invoke); mono_unity_domain_install_capture_context_method(g_MonoDomain, (void*)il2cpp_mono_dummy_callback); //mono_install_delegate_trampoline(il2cpp_mono_delegate_trampoline); MonoThreadInfoRuntimeCallbacks ticallbacks; memset(&ticallbacks, 0, sizeof(ticallbacks)); ticallbacks.thread_state_init_from_handle = mono_unity_thread_state_init_from_handle; mono_thread_info_runtime_init(&ticallbacks); } void il2cpp_mono_runtime_init() { register_allocator(mono_unity_alloc); mono_runtime_init(g_MonoDomain, mono_thread_start_cb, mono_thread_attach_cb); mono_threads_install_cleanup(mono_thread_cleanup_cb); mono_thread_attach(g_MonoDomain); mono_class_set_allow_gc_aware_layout(false); mono_add_internal_call("System.Diagnostics.StackFrame::get_frame_info", (void*)mono::icalls::mscorlib::System::Diagnostics::StackFrame::get_frame_info); mono_add_internal_call("System.Diagnostics.StackTrace::get_trace", (void*)mono::icalls::mscorlib::System::Diagnostics::StackTrace::get_trace); mono_add_internal_call("System.Runtime.InteropServices.Marshal::GetFunctionPointerForDelegateInternal", (void*)mono::icalls::mscorlib::System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegateInternal); mono_add_internal_call("System.Runtime.InteropServices.Marshal::GetDelegateForFunctionPointerInternal", (void*)mono::icalls::mscorlib::System::Runtime::InteropServices::Marshal::GetDelegateForFunctionPointerInternal); RegisterAllManagedMethods(); initialize_interop_data_map(); il2cpp::os::Image::Initialize(); mono_profiler_started(); } static void MonoSetConfigStr(const std::string& executablePath) { std::string appBase = il2cpp::utils::PathUtils::DirectoryName(executablePath); std::string configFileName = il2cpp::utils::PathUtils::Basename(executablePath); configFileName.append(".config"); mono_domain_set_config(g_MonoDomain, appBase.c_str(), configFileName.c_str()); } void il2cpp_mono_set_config_utf16(const Il2CppChar* executablePath) { IL2CPP_ASSERT(executablePath); std::string exePathUtf8 = il2cpp::utils::StringUtils::Utf16ToUtf8(executablePath, -1); MonoSetConfigStr(exePathUtf8); } void il2cpp_mono_set_config(const char* executablePath) { IL2CPP_ASSERT(executablePath); std::string executablePathStr(executablePath); MonoSetConfigStr(executablePathStr); } void il2cpp_mono_set_commandline_arguments_utf16(int argc, const Il2CppChar* const* argv) { std::vector args(argc); for (int i = 0; i < argc; ++i) args[i] = il2cpp::utils::StringUtils::Utf16ToUtf8(argv[i], -1); std::vector cargs(argc); for (int i = 0; i < argc; ++i) cargs[i] = args[i].c_str(); mono_runtime_set_main_args(argc, const_cast(&cargs[0])); for (int i = 0; i < argc; ++i) { /* TODO: uncomment after mono debugger changes merged if (strncmp(args[i].c_str(), "--debugger-agent=", 17) == 0) { mono_debugger_set_il2cpp_breakpoints(g_Il2CppSequencePointCount, (Il2CppSequencePoint**)g_Il2CppSequencePoints); mono_debugger_agent_parse_options((char*)(args[i].c_str() + 17)); //opt->mdb_optimizations = TRUE; //enable_debugging = TRUE; il2cpp::utils::Debugger::RegisterCallbacks(breakpoint_callback); } */ } il2cpp::utils::Environment::SetMainArgs(argv, argc); } void il2cpp_mono_set_commandline_arguments(int argc, const char* const* argv) { mono_runtime_set_main_args(argc, const_cast(argv)); il2cpp::utils::Environment::SetMainArgs(argv, argc); } void il2cpp_mono_initialize_metadata() { mono::vm::MetadataCache::Initialize(); } #endif