/* * Fix for detour jump overriding the method that's right next in memory * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77 * * It happens for any very small method, not just virtual, but it just so happens that * virtual methods are usually the only empty ones. The problem is with the detour code * overriding the method that's right next in memory. * * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes * as required. * * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get * overriden by the detour, but trampolines don't follow this rule and a trampoline generated * for the dynamic method from the patch is placed right after the method being detoured. * * The detour code then overrides the beggining of the trampoline and that leads to a * segfault on execution. * * There's also the fact that Linux seems to allocate the detour code far away in memory * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit * smaller jumps suffice. * * The fix changes the order in which methods are JITted so that no trampoline is placed * after the detoured method (or at least the trampoline that causes this specific crash) */ internal static void PadShortMethods(MethodBase method) { if (isWindows) { return; } var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0; if (count == 0) { return; } // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes // and high enough to not generate too many fix methods if (count >= 16) { return; } var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]); methodDef.GetILGenerator().Emit(OpCodes.Ret); _ = GetMethodStart(methodDef.Generate(), out var _); // trigger allocation/generation of jitted assembler }
/// <summary>A low level way to read the body of a method. Used for quick searching in methods</summary> /// <param name="method">The original method</param> /// <returns>All instructions as opcode/operand pairs</returns> /// public static IEnumerable <KeyValuePair <OpCode, object> > ReadMethodBody(MethodBase method) { var dummyMethod = new DynamicMethodDefinition($"{method.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]); return(MethodBodyReader.GetInstructions(dummyMethod.GetILGenerator(), method) .Select(instr => new KeyValuePair <OpCode, object>(instr.opcode, instr.operand))); }
/// <summary>Creates a field reference</summary> /// <typeparam name="T">The class the field is defined in or "object" if type cannot be accessed at compile time</typeparam> /// <typeparam name="U">The type of the field</typeparam> /// <param name="fieldInfo">FieldInfo for the field</param> /// <returns>A read and writable field reference</returns> /// public static FieldRef <T, U> FieldRefAccess <T, U>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException(nameof(fieldInfo)); } if (!typeof(U).IsAssignableFrom(fieldInfo.FieldType)) { throw new ArgumentException("FieldInfo type does not match FieldRefAccess return type."); } if (typeof(T) != typeof(object)) { if (fieldInfo.DeclaringType == null || !fieldInfo.DeclaringType.IsAssignableFrom(typeof(T))) { throw new MissingFieldException(typeof(T).Name, fieldInfo.Name); } } var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_fi_{fieldInfo.Name}", typeof(U).MakeByRefType(), new[] { typeof(T) }); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <T, U>)dm.Generate().CreateDelegate <FieldRef <T, U> >()); }
private DynamicMethodDefinition GenerateManagedOriginal() { // Here we generate the "managed" version of the native method // It simply calls the trampoline generated by MonoMod // As a result, we can pass the managed original to HarmonyManipulator like a normal method var orig = Original; var dmd = new DynamicMethodDefinition($"NativeDetour<{orig.GetID(simple: true)}>", _returnType, _argTypes); dmd.Definition.Name += $"?{dmd.GetHashCode()}"; var def = dmd.Definition; for (var i = 0; i < _argTypeNames.Length; i++) { def.Parameters[i].Name = _argTypeNames[i]; } var il = dmd.GetILGenerator(); il.Emit(OpCodes.Ldc_I4, dmd.GetHashCode()); il.Emit(OpCodes.Call, GetTrampolineMethod); for (var i = 0; i < _argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, _invokeTrampolineMethod); il.Emit(OpCodes.Ret); return(dmd); }
internal static FieldRef <T, F> FieldRefAccess <T, F>(FieldInfo fieldInfo, bool needCastclass) { ValidateFieldType <F>(fieldInfo); var delegateInstanceType = typeof(T); var declaringType = fieldInfo.DeclaringType; var dm = new DynamicMethodDefinition($"__refget_{delegateInstanceType.Name}_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new[] { delegateInstanceType }); var il = dm.GetILGenerator(); // Backwards compatibility: This supports static fields, even those defined in structs. if (fieldInfo.IsStatic) { // ldarg.0 + ldflda actually works for static fields, but the potential castclass (and InvalidCastException) below must be avoided // so might as well use the singular ldsflda for static fields. il.Emit(OpCodes.Ldsflda, fieldInfo); } else { il.Emit(OpCodes.Ldarg_0); // The castclass is needed when T is a parent class or interface of declaring type (e.g. if T is object), // since there's no guarantee the instance passed to the delegate is actually of the declaring type. // In such a situation, the castclass will throw an InvalidCastException and thus prevent undefined behavior. if (needCastclass) { il.Emit(OpCodes.Castclass, declaringType); } il.Emit(OpCodes.Ldflda, fieldInfo); } il.Emit(OpCodes.Ret); return((FieldRef <T, F>)dm.Generate().CreateDelegate(typeof(FieldRef <T, F>))); }
internal MethodPatcher(MethodBase original, MethodBase source, string harmonyInstanceID, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers) { if (original == null) { throw new ArgumentNullException(nameof(original)); } this.original = original; this.source = source; this.harmonyInstanceID = harmonyInstanceID; this.prefixes = prefixes; this.postfixes = postfixes; this.transpilers = transpilers; this.finalizers = finalizers; Memory.MarkForNoInlining(original); if (Harmony.DEBUG) { FileLog.LogBuffered($"### Patch {original.FullDescription()}"); FileLog.FlushBuffer(); } idx = prefixes.Count() + postfixes.Count() + finalizers.Count(); firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original); returnType = AccessTools.GetReturnedType(original); patch = CreateDynamicMethod(original, $"_Patch{idx}"); if (patch == null) { throw new Exception("Could not create dynamic method"); } il = patch.GetILGenerator(); emitter = new Emitter(il); }
public static MethodInfo Prefix(MethodBase method) { var dynamicMethod = new DynamicMethodDefinition(method.Name + "_Class11Patch_Prefix", typeof(bool), new[] { typeof(string).MakeByRefType(), typeof(int) }); dynamicMethod.Definition.Parameters[0].Name = "__result"; dynamicMethod.Definition.Parameters[1].Name = "dummy"; var il = dynamicMethod.GetILGenerator(); //load "patched" into __result il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, "patched"); il.Emit(OpCodes.Stind_Ref); //set prefixed to true il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Stfld, typeof(Class11Patch).GetField(nameof(prefixed))); //return false il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ret); return(dynamicMethod.Generate()); }
/* * Fix for detour jump overriding the method that's right next in memory * See https://github.com/MonoMod/MonoMod.Common/blob/58702d64645aba613ad16275c0b78278ff0d2055/RuntimeDetour/Platforms/Native/DetourNativeX86Platform.cs#L77 * * It happens for any very small method, not just virtual, but it just so happens that * virtual methods are usually the only empty ones. The problem is with the detour code * overriding the method that's right next in memory. * * The 64bit absolute jump detour requires 14 bytes but on Linux an empty method is just * 10 bytes. On Windows, due to prologue differences, an empty method is exactly 14 bytes * as required. * * Now, the small code size wouldn't be a problem if it wasn't for the way Mono compiles * trampolines. Usually methods on x64 are 16 bytes aligned, so no actual code could get * overriden by the detour, but trampolines don't follow this rule and a trampoline generated * for the dynamic method from the patch is placed right after the method being detoured. * * The detour code then overrides the beggining of the trampoline and that leads to a * segfault on execution. * * There's also the fact that Linux seems to allocate the detour code far away in memory * so it uses the 64 bit jump in the first place. On Windows with Mono usually the 32 bit * smaller jumps suffice. * * The fix changes the order in which methods are JITted so that no trampoline is placed * after the detoured method (or at least the trampoline that causes this specific crash) */ internal static void PadShortMethods(MethodBase method) { if (isWindows) { return; } var count = method.GetMethodBody()?.GetILAsByteArray()?.Length ?? 0; if (count == 0) { return; } // the 16 here is arbitrary but high enough to prevent the jitted code from getting under 14 bytes // and high enough to not generate too many fix methods if (count >= 16) { return; } var methodDef = new DynamicMethodDefinition($"PadMethod-{Guid.NewGuid()}", typeof(void), new Type[0]); methodDef.GetILGenerator().Emit(OpCodes.Ret); // invoke the method so that it generates a trampoline that will later get overridden by the detour code _ = methodDef.Generate().Invoke(null, null); }
/// <summary> /// Returns an instruction to call the specified delegate. /// </summary> /// <typeparam name="T">The delegate type to emit.</typeparam> /// <param name="action">The delegate to emit.</param> /// <returns>The instruction to </returns> public static CodeInstruction EmitDelegate <T>(T action) where T : Delegate { if (action.Method.IsStatic && action.Target == null) { return(new CodeInstruction(OpCodes.Call, action.Method)); } var paramTypes = action.Method.GetParameters().Select(x => x.ParameterType).ToArray(); var dynamicMethod = new DynamicMethodDefinition(action.Method.Name, action.Method.ReturnType, paramTypes); var il = dynamicMethod.GetILGenerator(); var targetType = action.Target.GetType(); var preserveContext = action.Target != null && targetType.GetFields().Any(x => !x.IsStatic); if (preserveContext) { var currentDelegateCounter = _delegateCounter++; _delegateCache[currentDelegateCounter] = action; var cacheField = AccessTools.Field(typeof(Transpilers), nameof(_delegateCache)); var getMethod = AccessTools.Method(typeof(Dictionary <int, Delegate>), "get_Item"); il.Emit(OpCodes.Ldsfld, cacheField); il.Emit(OpCodes.Ldc_I4, currentDelegateCounter); il.Emit(OpCodes.Callvirt, getMethod); } else { if (action.Target == null) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.GetParameters().Length == 0 && !x.IsStatic)); } il.Emit(OpCodes.Ldftn, action.Method); il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) })); } for (var i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldarg_S, (short)i); } il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("Invoke")); il.Emit(OpCodes.Ret); return(new CodeInstruction(OpCodes.Call, dynamicMethod.Generate())); }
/// <summary>Returns the methods unmodified list of code instructions</summary> /// <param name="original">The original method/constructor</param> /// <param name="generator">A new generator that now contains all local variables and labels contained in the result</param> /// <returns>A list containing all the original <see cref="CodeInstruction"/></returns> /// public static List <CodeInstruction> GetOriginalInstructions(MethodBase original, out ILGenerator generator) { var method = new DynamicMethodDefinition($"{original.Name}_Dummy{Guid.NewGuid()}", typeof(void), new Type[0]); generator = method.GetILGenerator(); var reader = MethodBodyReader.GetInstructions(generator, original); return(reader.Select(ins => ins.GetCodeInstruction()).ToList()); }
static ILHookExtensions() { var detourGetter = new DynamicMethodDefinition("GetDetour", typeof(Detour), new[] { typeof(ILHook) }); var il = detourGetter.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, AccessTools.PropertyGetter(typeof(ILHook), "_Ctx")); il.Emit(OpCodes.Ldfld, AccessTools.Field(AccessTools.Inner(typeof(ILHook), "Context"), "Detour")); il.Emit(OpCodes.Ret); GetAppliedDetour = detourGetter.Generate().CreateDelegate <Func <ILHook, Detour> >() as Func <ILHook, Detour>; }
/// <summary>Creates a new <see cref="ILGenerator">generator</see> matching the method/constructor to use when reading method bodies</summary> /// <param name="original">The original method/constructor to copy method information from</param> /// <returns>A new <see cref="ILGenerator"/></returns> /// public static ILGenerator CreateILGenerator(MethodBase original) { var returnType = original is MethodInfo m ? m.ReturnType : typeof(void); var parameterTypes = original.GetParameters().Select(pi => pi.ParameterType).ToList(); if (original.IsStatic is false) { parameterTypes.Insert(0, original.DeclaringType); } var method = new DynamicMethodDefinition($"ILGenerator_{original.Name}", returnType, parameterTypes.ToArray()); return(method.GetILGenerator()); }
internal static StructFieldRef <T, F> StructFieldRefAccess <T, F>(FieldInfo fieldInfo) where T : struct { ValidateFieldType <F>(fieldInfo); var dm = new DynamicMethodDefinition($"__refget_{typeof(T).Name}_struct_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new[] { typeof(T).MakeByRefType() }); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, fieldInfo); il.Emit(OpCodes.Ret); return((StructFieldRef <T, F>)dm.Generate().CreateDelegate(typeof(StructFieldRef <T, F>))); }
public static InstantiationHandler CreateInstantiationHandler(Type type) { var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); if (constructorInfo is null) { throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type)); } var dynamicMethod = new DynamicMethodDefinition($"InstantiateObject_{type.Name}", type, null); var generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Newobj, constructorInfo); generator.Emit(OpCodes.Ret); return((InstantiationHandler)dynamicMethod.Generate().CreateDelegate(typeof(InstantiationHandler))); }
/// <summary>Returns an instruction to call the specified closure</summary> /// <typeparam name="T">The delegate type to emit</typeparam> /// <param name="closure">The closure that defines the method to call</param> /// <returns>A <see cref="CodeInstruction"/> that calls the closure as a method</returns> /// public static CodeInstruction CallClosure <T>(T closure) where T : Delegate { if (closure.Method.IsStatic && closure.Target == null) { return(new CodeInstruction(OpCodes.Call, closure.Method)); } var parameters = closure.Method.GetParameters().Select(x => x.ParameterType).ToArray(); var closureMethod = new DynamicMethodDefinition(closure.Method.Name, closure.Method.ReturnType, parameters); var il = closureMethod.GetILGenerator(); var targetType = closure.Target.GetType(); var preserveContext = closure.Target != null && targetType.GetFields().Any(x => !x.IsStatic); if (preserveContext) { var n = State.closureCache.Count; State.closureCache[n] = closure; il.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(Transpilers), nameof(State.closureCache))); il.Emit(OpCodes.Ldc_I4, n); il.Emit(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Dictionary <int, Delegate>), "Item")); } else { if (closure.Target == null) { il.Emit(OpCodes.Ldnull); } else { il.Emit(OpCodes.Newobj, AccessTools.FirstConstructor(targetType, x => x.IsStatic == false && x.GetParameters().Length == 0)); } il.Emit(OpCodes.Ldftn, closure.Method); il.Emit(OpCodes.Newobj, AccessTools.Constructor(typeof(T), new[] { typeof(object), typeof(IntPtr) })); } for (var i = 0; i < parameters.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(T), nameof(Action.Invoke))); il.Emit(OpCodes.Ret); return(new CodeInstruction(OpCodes.Call, closureMethod.Generate())); }
private static Delegate CreateGetStaticFieldDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__FieldAccess" + fi.DeclaringType?.Name + fi.Name, typeof(TField), new Type[0] ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldsfld, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(Func <TField>))); }
private static Delegate CreateGetStaticFieldRefDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__ReturnByRef" + fi.DeclaringType?.Name + fi.Name, fi.FieldType.MakeByRefType(), new Type[0] ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldsflda, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(FuncByRef <TField>))); }
private static Delegate CreateGetFieldRefDelegate <TClass, TField>(FieldInfo info) { var dm = new DynamicMethodDefinition ( "__ReturnByRef" + info.DeclaringType?.Name + info.Name, typeof(TField).MakeByRefType(), new[] { typeof(TClass) } ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldflda, info); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(FuncByRef <TClass, TField>))); }
static int SizeOf(Type type) { if (sizes.TryGetValue(type, out var size)) { return(size); } var dm = new DynamicMethodDefinition("SizeOfType", typeof(int), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Sizeof, type); il.Emit(OpCodes.Ret); size = (int)dm.Generate().Invoke(null, null); sizes.Add(type, size); return(size); }
/// <summary>Creates a static field reference delegate</summary> /// <typeparam name="F">The type of the field</typeparam> /// <param name="fieldInfo">FieldInfo for the field</param> /// <returns>A read and writable field reference delegate</returns> /// public static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo) { if (fieldInfo == null) { throw new ArgumentNullException(nameof(fieldInfo)); } var t = fieldInfo.DeclaringType; var dm = new DynamicMethodDefinition($"__refget_{t.Name}_static_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldsflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <F>)dm.Generate().CreateDelegate <FieldRef <F> >()); }
private static Delegate CreateSetStaticFieldDelegate <TField>(FieldInfo fi) { var dm = new DynamicMethodDefinition ( "__FieldSet" + fi.DeclaringType?.Name + fi.Name, typeof(void), new[] { typeof(TField) } ); ILGenerator gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Stsfld, fi); gen.Emit(OpCodes.Ret); return(dm.Generate().CreateDelegate(typeof(Action <TField>))); }
/// <inheritdoc /> public override DynamicMethodDefinition CopyOriginal() { if (!(Original is MethodInfo mi)) { return(null); } var dmd = new DynamicMethodDefinition("OrigWrapper", returnType, argTypes); var il = dmd.GetILGenerator(); for (var i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, mi); il.Emit(OpCodes.Ret); return(dmd); }
internal static FieldRef <F> StaticFieldRefAccess <F>(FieldInfo fieldInfo) { if (fieldInfo.IsStatic is false) { throw new ArgumentException("Field must be static"); } Tools.ValidateFieldType <F>(fieldInfo); var dm = new DynamicMethodDefinition($"__refget_{fieldInfo.DeclaringType?.Name ?? "null"}_static_fi_{fieldInfo.Name}", typeof(F).MakeByRefType(), new Type[0]); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldsflda, fieldInfo); il.Emit(OpCodes.Ret); return((FieldRef <F>)dm.Generate().CreateDelegate(typeof(FieldRef <F>))); }
private static void HookSystemRuntimeTypeGetMethodBase() { var systemRuntimeTypeType = typeof(Type).Assembly.GetType("System.RuntimeType"); var getMethodBase1 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic) .Where(m => m.Name == "GetMethodBase") .Where(m => m.GetParameters().Length == 2) .FirstOrDefault(m => m.GetParameters().First().ParameterType == systemRuntimeTypeType && m.GetParameters().Last().ParameterType.Name == "IRuntimeMethodInfo"); var getMethodBase2 = systemRuntimeTypeType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic) .FirstOrDefault(m => m.Name == "GetMethodBase" && m.GetParameters().Length == 1); var myMethod = typeof(Program).GetMethod(nameof(Hook5), BindingFlags.Static | BindingFlags.Public); var replacementMethod = new DynamicMethodDefinition( getMethodBase2.Name, getMethodBase2.ReturnType, getMethodBase2.GetParameters().Select(x => x.ParameterType).ToArray() ) { OwnerType = getMethodBase1.DeclaringType }; var iLGenerator = replacementMethod.GetILGenerator(); iLGenerator.DeclareLocal(typeof(MethodBase), false); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldnull); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, getMethodBase1); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Stloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloca, 0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Call, myMethod); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ldloc_0); iLGenerator.Emit(System.Reflection.Emit.OpCodes.Ret); var replacementMethodInfo = replacementMethod.Generate(); Memory.SimpleHook(getMethodBase2, replacementMethodInfo); }
/// <summary>Creates an instantiation delegate</summary> /// <typeparam name="T">Type that constructor creates</typeparam> /// <returns>The new instantiation delegate</returns> /// public static InstantiationHandler <T> CreateInstantiationHandler <T>() { var constructorInfo = typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null); if (constructorInfo == null) { throw new ApplicationException( $"The type {typeof(T)} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public)."); } var dmd = new DynamicMethodDefinition($"InstantiateObject_{typeof(T).Name}", typeof(T), null); var il = dmd.GetILGenerator(); il.Emit(OpCodes.Newobj, constructorInfo); il.Emit(OpCodes.Ret); return((InstantiationHandler <T>)dmd.Generate().CreateDelegate <InstantiationHandler <T> >()); }
internal MethodPatcher(MethodBase original, MethodBase source, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers, bool debug) { if (original is null) { throw new ArgumentNullException(nameof(original)); } this.debug = debug; this.original = original; this.source = source; this.prefixes = prefixes; this.postfixes = postfixes; this.transpilers = transpilers; this.finalizers = finalizers; Memory.MarkForNoInlining(original); if (debug) { FileLog.LogBuffered($"### Patch: {original.FullDescription()}"); FileLog.FlushBuffer(); } idx = prefixes.Count() + postfixes.Count() + finalizers.Count(); useStructReturnBuffer = StructReturnBuffer.NeedsFix(original); if (debug && useStructReturnBuffer) { FileLog.Log($"### Note: A buffer for the returned struct is used. That requires an extra IntPtr argument before the first real argument"); } returnType = AccessTools.GetReturnedType(original); patch = CreateDynamicMethod(original, $"_Patch{idx}", debug); if (patch is null) { throw new Exception("Could not create replacement method"); } il = patch.GetILGenerator(); emitter = new Emitter(il, debug); }
internal MethodPatcher(MethodBase original, MethodBase source, List <MethodInfo> prefixes, List <MethodInfo> postfixes, List <MethodInfo> transpilers, List <MethodInfo> finalizers, bool debug) { if (original == null) { throw new ArgumentNullException(nameof(original)); } this.debug = debug; this.original = original; this.source = source; this.prefixes = prefixes; this.postfixes = postfixes; this.transpilers = transpilers; this.finalizers = finalizers; Memory.MarkForNoInlining(original); if (debug) { FileLog.LogBuffered($"### Patch {original.FullDescription()}"); FileLog.FlushBuffer(); } idx = prefixes.Count() + postfixes.Count() + finalizers.Count(); firstArgIsReturnBuffer = NativeThisPointer.NeedsNativeThisPointerFix(original); if (debug && firstArgIsReturnBuffer) { FileLog.Log($"### Special case: extra argument after 'this' is pointer to valuetype (simulate return value)"); } returnType = AccessTools.GetReturnedType(original); patch = CreateDynamicMethod(original, $"_Patch{idx}", debug); if (patch == null) { throw new Exception("Could not create replacement method"); } il = patch.GetILGenerator(); emitter = new Emitter(il, debug); }
public static MethodInfo WrapInterop(MethodInfo transpiler) { lock (Wrappers) { if (Wrappers.TryGetValue(transpiler, out var wrapped)) { return(wrapped); } } using (var dmd = new DynamicMethodDefinition($"TranspilerWrapper<{transpiler.GetID(simple: true)}>", typeof(IEnumerable <CodeInstruction>), new[] { typeof(IEnumerable <CodeInstruction>), typeof(ILGenerator), typeof(MethodBase) })) { var il = dmd.GetILGenerator(); il.Emit(OpCodes.Ldtoken, transpiler); il.Emit(OpCodes.Call, ResolveToken); il.Emit(OpCodes.Castclass, typeof(MethodInfo)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, ApplyTranspilerMethod); il.Emit(OpCodes.Ret); var generatedWrapper = dmd.GenerateWith <DMDCecilGenerator>(); lock (Wrappers) { Wrappers[transpiler] = generatedWrapper; } return(generatedWrapper); } }
private static Func <Scene, Vector2, Color> GetHueIL() { string methodName = "ColorHelper._getHue"; DynamicMethodDefinition method = new DynamicMethodDefinition(methodName, typeof(Color), new[] { typeof(Scene), typeof(Vector2) }); var gen = method.GetILGenerator(); FieldInfo crystalSpinner = typeof(ColorHelper).GetField(nameof(ColorHelper.crystalSpinner), BindingFlags.NonPublic | BindingFlags.Static); // ColorHelper.crystalSpinner.Scene = scene; gen.Emit(OpCodes.Ldsfld, crystalSpinner); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Call, typeof(Entity).GetProperty("Scene").GetSetMethod(true)); // return ColorHelper.crystalSpinner.GetHue(position); gen.Emit(OpCodes.Ldsfld, crystalSpinner); gen.Emit(OpCodes.Ldarg_1); gen.Emit(OpCodes.Call, typeof(CrystalStaticSpinner).GetMethod("GetHue", BindingFlags.NonPublic | BindingFlags.Instance)); gen.Emit(OpCodes.Ret); return((Func <Scene, Vector2, Color>)method.Generate().CreateDelegate(typeof(Func <Scene, Vector2, Color>))); }
private DynamicMethodDefinition GenerateManagedOriginal() { // Here we generate the "managed" version of the native method // It simply calls the trampoline generated by MonoMod // As a result, we can pass the managed original to HarmonyManipulator like a normal method var orig = Original; lock (CounterLock) { newOriginal = counter; counter++; } var dmd = new DynamicMethodDefinition($"NativeDetour_Wrapper<{orig.GetID(simple: true)}>?{newOriginal}", returnType, argTypes); var def = dmd.Definition; for (var i = 0; i < argTypeNames.Length; i++) { def.Parameters[i].Name = argTypeNames[i]; } var il = dmd.GetILGenerator(); il.Emit(OpCodes.Ldc_I4, newOriginal); il.Emit(OpCodes.Call, GetTrampolineMethod); for (var i = 0; i < argTypes.Length; i++) { il.Emit(OpCodes.Ldarg, i); } il.Emit(OpCodes.Call, invokeTrampolineMethod); il.Emit(OpCodes.Ret); return(dmd); }