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