-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Direct Call Invoke #172
Comments
Native exceptions on Windows can be caught without cost. It works, I've tried throwing my Exception in C++ code this way, intercepting like this and throwing my Managed Exception wrapper. static partial class ExceptionHandler
{
[MethodImpl(MethodImplOptions.NoInlining)]
private static unsafe void InitializeWinExceptionHandler()
{
Win32.AddVectoredExceptionHandler(1, &Win32.Win32VehHandler);
}
static unsafe class Win32
{
internal static uint Win32VehHandler(ref ExceptionPointers exceptionPointers)
{
var exceptionInformation = exceptionPointers.ExceptionRecord->ExceptionInformation;
if (exceptionInformation.Length >= 3 && exceptionInformation[0] == 0x19930520) // win c++ magic number
{
// exceptionInformation[1] ExceptionObject. Should be Il2CppSystem.Il2CppException
// exceptionInformation[2] ThrowInfo. Contains ptr to ExceptionObject RTTI, attributes, etc.
// exceptionInformation[3] on x64 (maybe): ModuleHandle. Used for rva_to_va conversion
var exception = exceptionInformation[1];
if (exception == 0)
return 0; // continue search
try
{ // somehow check is exception is Il2CppObject or random c++ exception
var result = IL2CPP.il2cpp_object_get_class(exception);
if (result == IntPtr.Zero) // it`s not a Il2CppException
return 0; // continue search
// it`s a Il2CppException, so...
throw new Il2CppInterop.Runtime.Il2CppException(exception);
// will quickly pass control to catch(Il2CppException) in managed code.
}
// it`s not a Il2CppException
catch
{
return 0; // continue search
}
}
return 0; // continue search
}
[DllImport("Kernel32.dll", SetLastError = true)]
internal static extern int AddVectoredExceptionHandler(uint first, delegate* <ref ExceptionPointers, uint> address);
[StructLayout(LayoutKind.Sequential)]
internal struct ExceptionPointers
{
public ExceptionRecord* ExceptionRecord;
public byte* ContextRecord; // Registers
}
[StructLayout(LayoutKind.Sequential)]
internal struct ExceptionRecord
{
public int ExceptionCode;
public int ExceptionFlags;
public ExceptionRecord* InnerExceptionRecord;
public void* ExceptionAddress;
public nint NumberParameters;
private nint _exceptionInformation;
public Span<nint> ExceptionInformation
{
get
{
if (NumberParameters == 0)
return Array.Empty<nint>();
return new Span<nint>(Unsafe.AsPointer(ref _exceptionInformation), (int)NumberParameters);
}
}
}
}
} So we can now call Il2cpp methods directly via calli and handle exceptions via c# try catch. |
@BadRyuner I've already had a working windows POC for a while: internal static partial class Il2CppExceptionHelper
{
[SupportedOSPlatform("windows")]
public static unsafe partial class Windows
{
private static ReadOnlySpan<byte> ExceptionTypeName => ".?AUIl2CppExceptionWrapper@@"u8;
public static bool Filter(Exception exception)
{
if (exception is not SEHException) return false;
var exceptionRecord = GetExceptionRecord();
if (exceptionRecord->ExceptionCode != EXCEPTION_MSVC || exceptionRecord->NumberParameters < 3)
{
return false;
}
// Based on wine's _is_exception_typeof, licensed under LGPL v2.1+
var @base = exceptionRecord->NumberParameters >= 4 ? exceptionRecord->ExceptionInformation[3] : 0;
var et = (cxx_exception_type*)exceptionRecord->ExceptionInformation[2];
var tit = (cxx_type_info_table*)(@base + et->type_info_table);
for (var i = 0; i < tit->count; i++)
{
var cti = (cxx_type_info*)(@base + tit->info[i]);
var tti = (type_info*)(@base + cti->type_info);
var mangled = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(&tti->mangled);
if (mangled.SequenceEqual(ExceptionTypeName))
{
return true;
}
}
return false;
}
[DoesNotReturn]
public static void ReThrowWrapped()
{
var exceptionRecord = GetExceptionRecord();
Debug.Assert(exceptionRecord->ExceptionCode == EXCEPTION_MSVC);
Debug.Assert(exceptionRecord->NumberParameters >= 3);
var exception = (Il2CppExceptionWrapper*)exceptionRecord->ExceptionInformation[1];
var il2CppException = exception->Exception;
throw new WrappedIl2CppException(il2CppException);
}
private static EXCEPTION_RECORD* GetExceptionRecord()
{
var exceptionPointers = (EXCEPTION_POINTERS*)Marshal.GetExceptionPointers();
return exceptionPointers->ExceptionRecord;
}
}
} try
{
var thunk = (delegate * unmanaged<T1, T2, T3, TReturn>)ptr;
return thunk(a, b, c);
}
catch (SEHException exception) when (Il2CppExceptionHelper.Windows.Filter(exception))
{
Il2CppExceptionHelper.Windows.ReThrowWrapped();
return default;
} My approach is somewhat different in that I used C#'s builtin ability to catch SEH exceptions. I also have a somewhat working implementation for linux/osx using MonoMod's NativeExceptionHelper |
Directly invoking method calls would enable use to avoid the performance overhead of using
il2cpp_runtime_invoke
. However, this comes at the cost of having to catch native exceptions ourselves.The text was updated successfully, but these errors were encountered: