diff --git a/Harmony/Internal/Memory.cs b/Harmony/Internal/Memory.cs
index fa7e42cc..1052958b 100644
--- a/Harmony/Internal/Memory.cs
+++ b/Harmony/Internal/Memory.cs
@@ -1,15 +1,19 @@
using MonoMod.RuntimeDetour;
+using MonoMod.Utils;
using System;
using System.Reflection;
+using System.Reflection.Emit;
using System.Runtime.CompilerServices;
namespace HarmonyLib
{
/// A low level memory helper
+ ///
public static class Memory
{
/// Mark method for no inlining (currently only works on Mono)
/// The method/constructor to change
+ ///
unsafe public static void MarkForNoInlining(MethodBase method)
{
// TODO for now, this only works on mono
@@ -30,6 +34,9 @@ public static string DetourMethod(MethodBase original, MethodBase replacement)
var originalCodeStart = GetMethodStart(original, out var exception);
if (originalCodeStart == 0)
return exception.Message;
+
+ FixVirtualMethodTrampoline(original);
+
var patchCodeStart = GetMethodStart(replacement, out exception);
if (patchCodeStart == 0)
return exception.Message;
@@ -45,6 +52,18 @@ internal static void DetourMethodAndPersist(MethodBase original, MethodBase repl
PatchTools.RememberObject(original, replacement);
}
+ internal static void FixVirtualMethodTrampoline(MethodBase method)
+ {
+ if (!method.IsVirtual || method.IsAbstract || method.IsFinal) return;
+ var bytes = method.GetMethodBody()?.GetILAsByteArray();
+ if (bytes == null || bytes.Length == 0) return;
+ if (bytes.Length == 1 && bytes[0] == 0x2A) return;
+
+ var methodDef = new DynamicMethodDefinition($"VirtualFix-{Guid.NewGuid()}", typeof(void), new Type[0]);
+ methodDef.GetILGenerator().Emit(OpCodes.Ret);
+ _ = GetMethodStart(methodDef.Generate(), out var _); // trigger allocation/generation of jitted assembler
+ }
+
/// Writes a jump to memory
/// The memory address
/// Jump destination