Skip to content

Commit

Permalink
Merge pull request #46 from js6pak/fix-return-buffer
Browse files Browse the repository at this point in the history
Fix return buffer in HarmonySupport
  • Loading branch information
ghorsington authored Sep 30, 2022
2 parents 5c89ea7 + aedeb42 commit d32dc73
Showing 1 changed file with 49 additions and 12 deletions.
61 changes: 49 additions & 12 deletions Il2CppInterop.HarmonySupport/Il2CppDetourMethodPatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ private static readonly MethodInfo ObjectBaseToPtrMethodInfo
= AccessTools.Method(typeof(IL2CPP),
nameof(IL2CPP.Il2CppObjectBaseToPtr));

private static readonly MethodInfo ObjectBaseToPtrNotNullMethodInfo
= AccessTools.Method(typeof(IL2CPP),
nameof(IL2CPP.Il2CppObjectBaseToPtrNotNull));

private static readonly MethodInfo ReportExceptionMethodInfo
= AccessTools.Method(typeof(Il2CppDetourMethodPatcher), nameof(ReportException));

Expand Down Expand Up @@ -183,25 +187,55 @@ public override DynamicMethodDefinition CopyOriginal()
return dmd;
}

// Tries to guess whether a function needs a return buffer for the return struct, in all cases except win64 it's undefined behaviour
private static bool IsReturnBufferNeeded(int size)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values
return size != 1 && size != 4 && size != 8;
}

if (Environment.Is64BitProcess)
{
// x64 gcc and clang seem to use a return buffer for everything above 16 bytes
return size > 16;
}

// Looks like on x32 gcc and clang return buffer is always used
return true;
}

private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo targetManagedMethodInfo)
{
// managedParams are the interop types used on the managed side
// unmanagedParams are IntPtr references that are used by IL2CPP compiled assembly
var paramStartIndex = 0;

var managedReturnType = AccessTools.GetReturnedType(Original);
var hasReturnBuffer = managedReturnType.IsSubclassOf(typeof(ValueType)) && Environment.Is64BitProcess;
var unmanagedReturnType = managedReturnType.NativeType();

var returnSize = IntPtr.Size;

var isReturnValueType = managedReturnType.IsSubclassOf(typeof(ValueType));
if (isReturnValueType)
{
uint align = 0;
returnSize = IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(managedReturnType), ref align);
}

var hasReturnBuffer = isReturnValueType && IsReturnBufferNeeded(returnSize);
if (hasReturnBuffer)
// C compilers seem to return values larger than 64 bits by allocating a return buffer on caller's side and passing it as the first parameter
// C compilers seem to return large structs by allocating a return buffer on caller's side and passing it as the first parameter
// TODO: Handle ARM
// TODO: Check if this applies to values other than structs
// TODO: Check if we can use the dummy struct generated by GetFixedSizeStructType() so that mono's marshaller can handle this
{
paramStartIndex++;
}

if (!Original.IsStatic)
{
unmanagedReturnType = typeof(IntPtr);
paramStartIndex++;
}

Expand All @@ -225,8 +259,6 @@ private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo tar
Array.Copy(managedParams.Select(TrampolineHelpers.NativeType).ToArray(), 0,
unmanagedParams, paramStartIndex, managedParams.Length);

var unmanagedReturnType = managedReturnType.NativeType();

var dmd = new DynamicMethodDefinition("(il2cpp -> managed) " + Original.Name,
unmanagedReturnType,
unmanagedParams
Expand Down Expand Up @@ -285,15 +317,11 @@ private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo tar
{
if (hasReturnBuffer)
{
uint align = 0;
var size =
IL2CPP.il2cpp_class_value_size(Il2CppClassPointerStore.GetNativeClassPointer(managedReturnType),
ref align);

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldloc, managedReturnVariable);
il.Emit(OpCodes.Call, ObjectBaseToPtrMethodInfo);
il.Emit(OpCodes.Ldc_I4, size);
il.Emit(OpCodes.Call, ObjectBaseToPtrNotNullMethodInfo);
EmitUnbox(il);
il.Emit(OpCodes.Ldc_I4, returnSize);
il.Emit(OpCodes.Cpblk);

// Return the same pointer to the return buffer
Expand All @@ -311,6 +339,15 @@ private DynamicMethodDefinition GenerateNativeToManagedTrampoline(MethodInfo tar
return dmd;
}

private static void EmitUnbox(ILGenerator il)
{
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Conv_I);
il.Emit(OpCodes.Sizeof, typeof(void*));
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Add);
}

private static void ReportException(Exception ex) =>
Logger.Instance.LogError(ex, "During invoking native->managed trampoline");

Expand Down

0 comments on commit d32dc73

Please sign in to comment.