internal FnPointer(Delegate d, CallingConvention convention) { var def = d.Method.GetBaseDefinition(); var pars = def.GetParameters(); if (Constants.X64) { var dt = DelegateCreator.NewDelegateType(def.ReturnType, pars.Select(p => p.ParameterType).ToArray()); var fd = Delegate.CreateDelegate(dt, d.Target, d.Method); var ptr = Marshal.GetFunctionPointerForDelegate(fd); Int64 = ptr.ToInt64(); } else { // var ptr = Marshal.GetFunctionPointerForDelegate(d); // convention = CallingConvention.X86CDecl; // Int64 = ptr.ToInt64(); // TODO: Need fixing!!! throw new NotImplementedException("Managed method cannot be called from machine code in X86 mode by now."); } var args = pars.Select(p => p.ParameterType.GetVariableType()).ToArray(); var ret = def.ReturnType.GetVariableType(); FunctionDeclaration = new FunctionDeclaration(convention, args, ret); }
private bool Win32FuncWith <T, TDelegate>(IntPtr addressOrgFuncAddr, IntPtr orgFuncAddr, IntPtr proxyFuncAddr, Type returnType, out TDelegate saveOrgFunc) where T : Delegate where TDelegate : Delegate { saveOrgFunc = null; TDelegate orgFuncDelegate; Type genericType = typeof(T); if (genericType.IsGenericType == true) { Type specificType = DelegateCreator.NewDelegateType(returnType, genericType.GetGenericArguments()); object obj = Marshal.GetDelegateForFunctionPointer(orgFuncAddr, specificType); orgFuncDelegate = obj as TDelegate; } else { orgFuncDelegate = Marshal.GetDelegateForFunctionPointer(orgFuncAddr, typeof(T)) as TDelegate; } if (WriteProtectedAddress(addressOrgFuncAddr, proxyFuncAddr) == false) { return(false); } _addressOrgFuncAddr = addressOrgFuncAddr; _orgFuncAddr = orgFuncAddr; saveOrgFunc = orgFuncDelegate as TDelegate; return(true); }
public static CodeContext <T> CreateContext <T>() { var t = typeof(T); var args = new Type[0]; Type delType; if (t == typeof(Action)) { delType = DelegateCreator.NewDelegateType(args); } else if (Utils.Actions.Contains(t.GetGenericTypeDefinition())) { var gargs = t.GetGenericArguments(); args = new Type[gargs.Length].InitializeWith(i => gargs[i]); delType = DelegateCreator.NewDelegateType(args); } else if (Utils.Funcs.Contains(t.GetGenericTypeDefinition())) { var gargs = t.GetGenericArguments(); args = new Type[gargs.Length - 1].InitializeWith(i => gargs[i]); var ret = gargs.Last(); delType = DelegateCreator.NewDelegateType(ret, args); } else { throw new ArgumentException(); } var asm = new Assembler(); var ctx = new CodeContext <T>(asm, delType); asm._codeContext = ctx; return(ctx); }
public static CodeContext <T> CreateContext <T>(CallingConvention convention = CallingConvention.Default) { var t = typeof(T); var args = new Type[0]; Type ret = null; Type delType; if (t == typeof(Action)) { delType = DelegateCreator.NewDelegateType(args); } else if (Utils.Actions.Contains(t.GetGenericTypeDefinition())) { var gargs = t.GetGenericArguments(); args = new Type[gargs.Length].InitializeWith(i => gargs[i]); delType = DelegateCreator.NewDelegateType(args); } else if (Utils.Funcs.Contains(t.GetGenericTypeDefinition())) { var gargs = t.GetGenericArguments(); args = new Type[gargs.Length - 1].InitializeWith(i => gargs[i]); ret = gargs.Last(); delType = DelegateCreator.NewDelegateType(ret, args); } else { throw new ArgumentException(); } var compiler = new Compiler(); var ctx = new CodeContext <T>(compiler, delType); compiler._codeContext = ctx; compiler._codeProcessor = new CodeProcessor(compiler._assembler, compiler, compiler._codeContext); compiler.BeginFunction(args.Select(a => a.GetVariableType()).ToArray(), ret.GetVariableType(), convention); return(ctx); }
public static object InstantiatePatch(Type proxyType, List <object> baseCascade) { var(baseTypes, baseMethodSets) = BuildBaseCascadeInfo(baseCascade); var patchTargets = new Dictionary <MethodInfo, MethodInfo>(); foreach (var method in proxyType.GetMethods(InstanceBinding)) { var patchInfo = method.GetCustomAttribute <LogicProxyAttribute>(); if (patchInfo == null) { continue; } var patchTarget = GetPatchTarget(); patchTargets.Add(patchTarget, method); MethodInfo GetPatchTarget() { var t = method.FindMatchingMethod(baseMethodSets, true, (real, general) => { if (real == general) { return(true); } if (real.IsClass && general == typeof(object)) { return(true); } if (real.IsByRef && (general == typeof(IntPtr) || general == typeof(void *))) { return(true); } return(false); }); //Patch real implementation, not V-table stub return(t.GetDeclaredMember()); } } var(fullInterface, proxyServiceBaseType) = GetProxyServiceInfo(proxyType); var proxyServiceInstanceProperty = proxyServiceBaseType.BaseType.GetProperty(nameof(ProxyService <object, object> .Instance)); var redirectedMethods = new Dictionary <MethodInfo, MethodInfo>(); foreach (var(target, redirectionTarget) in patchTargets) { var copy = CreateDuplicate(target); redirectedMethods.Add(target, copy); var args = target.GetEffectiveParameterTypes(); var redirectionStub = new DynamicMethod(target.Name + "_" + proxyType.Name + "_DetourStub", target.ReturnType, args); var IL = redirectionStub.GetILGenerator(); if (redirectionTarget.IsStatic == false) { IL.Emit(OpCodes.Call, proxyServiceInstanceProperty.GetMethod); } var offset = target.IsStatic ? 0 : 1; for (int i = 0; i < args.Length - offset; i++) { IL.Emit(OpCodes.Ldarg, i + offset); } IL.Emit(OpCodes.Call, redirectionTarget); IL.Emit(OpCodes.Ret); RedirectMethod(target, redirectionStub); } object interfaceInstance; if (redirectedMethods.Count == 0 && baseTypes[0].Implements(fullInterface)) { interfaceInstance = baseCascade[0]; } else { var implementationTargets = new List <(MethodInfo Interface, int BaseIndex, MemberInfo ImplementationTarget)>(); foreach (var method in fullInterface.GetAllInterfaceMethods()) { for (var i = 0; i < baseTypes.Length; i++) { var baseType = baseTypes[i]; var implementation = baseType.TryFindImplementation(method, out var isInterfaceImplementation); if (implementation != null) { if (redirectedMethods.TryGetValue(implementation, out var redirection)) { //Direct call of duplicated method implementation = redirection; } else if (implementation.IsAssemblyPublic()) { //Direct call of implementing method is best choice, if accessible //=> No op } else if (isInterfaceImplementation && method.IsAssemblyPublic()) { //Direct interface call is second best choice implementation = method; } else { //Fall-back to delegate proxy call to expose internal method //=> No op } implementationTargets.Add((method, i, implementation)); goto matchFound; } } if (method.IsProperty()) { string dataTargetName = method.Name.Substring("get_".Length); for (var i = 0; i < baseTypes.Length; i++) { var baseType = baseTypes[i]; var dataTarget = TryFindDataTarget(baseType, method, dataTargetName); if (dataTarget != null) { implementationTargets.Add((method, i, dataTarget)); goto matchFound; } } } throw new Exception("Could not find implementation or data target for " + method); matchFound :; } var proxy = DynamicTypeBuilder.Create($"{fullInterface.Name}_" + proxyType.Name + proxyType.GetHashCode() + "_Proxy"); proxy.Type.AddInterfaceImplementation(fullInterface); var baseIndexToField = new Dictionary <int, FieldBuilder>(); foreach (var(_, baseIndex, _) in implementationTargets) { if (baseIndexToField.ContainsKey(baseIndex)) { continue; } var field = proxy.DeclareField(baseCascade[baseIndex].GetType()); baseIndexToField.Add(baseIndex, field); } var proxyDelegates = new List <(Delegate Instance, FieldBuilder Field)>(); foreach (var(interfaceTarget, baseIndex, implementationTarget) in implementationTargets) { var method = proxy.Type.DefineMethod(interfaceTarget.Name, MethodAttributes.Public | MethodAttributes.Virtual); var IL = method.GetILGenerator(); var argTypes = interfaceTarget.GetParameters().Select(x => x.ParameterType).ToArray(); method.SetParameters(argTypes); var returnType = interfaceTarget.ReturnType; method.SetReturnType(returnType); proxy.Type.DefineMethodOverride(method, interfaceTarget); if (implementationTarget is MethodInfo methodTarget) { EmitCallTarget(methodTarget); } else if (implementationTarget is FieldInfo fieldTarget) { var il = IL; var isInstanceField = fieldTarget.IsStatic == false; DynamicMethod proxyStub = null; if (fieldTarget.IsAssemblyPublic()) { if (isInstanceField) { LoadBaseInstance(); } } else { //Note: Nasty hack follows! //Private field, dynamic type visibility rules, hacky solution, read below var declaringType = fieldTarget.DeclaringType; var args = argTypes.Prepend(new[] { declaringType }).ToArray(); var stubName = fieldTarget.Name + '_' + returnType.Name + "_ProxyAccessor"; proxyStub = new DynamicMethod(stubName, returnType, args, declaringType, true); il = proxyStub.GetILGenerator(); if (isInstanceField) { il.Emit(OpCodes.Ldarg_0); } } if (returnType == typeof(void)) { var op = isInstanceField ? OpCodes.Stfld : OpCodes.Stsfld; il.Emit(OpCodes.Ldarg_1); il.Emit(op, fieldTarget); } else { var op = isInstanceField ? OpCodes.Ldfld : OpCodes.Ldsfld; il.Emit(op, fieldTarget); } if (proxyStub != null) { il.Emit(OpCodes.Ret); EmitCallTarget(proxyStub); } } else { throw new Exception("Unexpected implementation target " + implementationTarget.GetType()); } IL.Emit(OpCodes.Ret); void LoadBaseInstance() { IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldfld, baseIndexToField[baseIndex]); } void PassArgs() { for (int i = 1; i <= argTypes.Length; i++) { IL.Emit(OpCodes.Ldarg, i); } } void EmitCallTarget(MethodInfo callTarget) { //TODO: For some reason Mono is super unhappy when calling IL.Emit(Call, DynamicMethod) // => Proxy via delegate which is apparently fine bool forceDelegateProxy = callTarget is DynamicMethod; if (forceDelegateProxy || callTarget.IsAssemblyPublic() == false) { //Note: Nasty hack follows! //Dynamic type has to follow all visibility rules normal assembly would have to so it can't call private methods directly //Runtime delegates can reflect, call and expose private members, but can't instantiate interface //=> Call proxy delegate inside proxy type to expose private method via public interface. We need to go deeper :) var delegateType = DelegateCreator.NewDelegateType(callTarget, unboundInstanceCall: true); //var proxyDelegate = Delegate.CreateDelegate(delegateType, callTarget); var proxyDelegate = callTarget.CreateDelegate(delegateType); var delegateField = proxy.DeclareField(delegateType); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldfld, delegateField); proxyDelegates.Add((proxyDelegate, delegateField)); callTarget = delegateType.GetMethod("Invoke"); } LoadBaseInstance(); PassArgs(); var isInterfaceCall = callTarget.DeclaringType?.IsInterface ?? false; IL.Emit(isInterfaceCall ? OpCodes.Callvirt : OpCodes.Call, callTarget); } } proxy.DeclareCtorAndInitFields ( proxyDelegates.Select(x => x.Field).Concat( baseIndexToField.Select(x => x.Value)) ); interfaceInstance = proxy.Instantiate ( proxyDelegates.Select(x => x.Instance).Concat( baseIndexToField.Select(x => baseCascade[x.Key])) .ToArray() ); } var proxyInstance = Activator.CreateInstance(proxyType); proxyType.GetProperty(nameof(ProxyService <object, object> .Vanilla)).SetValue(proxyInstance, interfaceInstance); return(proxyInstance); }
public static void Install(Harmony harmony, object vanilla, Type vanillaInterfaceType) { Instance = Activator.CreateInstance <TInstance>(); var vanillaImplType = vanilla.GetType(); var fullInterfaceType = typeof(TService); Log.Debug.PrintLine($"Installing {Instance.GetType()} -> {fullInterfaceType} -> {vanillaImplType} -> {vanillaInterfaceType}"); var stubs = new List <MethodBase>(); var patches = new Dictionary <MethodBase, ServicePatch>(); foreach (var method in typeof(TInstance).GetMethods(InstanceBinding)) { var replace = method.GetCustomAttribute <HarmonyReplaceAttribute>(); if (replace != null) { var patch = GetPatch(); int stubId = stubs.Count; var interfaceMethod = patch.GetImplementedInterface(vanillaInterfaceType); if (interfaceMethod != null) { stubs.Add(interfaceMethod); } else { //This is not method from vanilla interface var extendedInterfaceMethod = patch.Target.FindMatchingMethod(fullInterfaceType.GetAllInterfaceMethods(), false); if (extendedInterfaceMethod != null) { //We can proxy it via extended interface stubs.Add(extendedInterfaceMethod); } else { //Or don't proxy it at all if there is no match in extended interface either stubId = -1; } } patch.VanillaReplace(method, stubId); continue; } //TODO: Fix for `Vanilla` call var prefix = method.GetCustomAttribute <HarmonyPrefix>(); if (prefix != null) { GetPatch().Patch.AddPrefix(new HarmonyMethod(method)); continue; } var postfix = method.GetCustomAttribute <HarmonyPostfix>(); if (postfix != null) { GetPatch().Patch.AddPostfix(new HarmonyMethod(method)); continue; } var finalizer = method.GetCustomAttribute <HarmonyFinalizer>(); if (finalizer != null) { GetPatch().Patch.AddFinalizer(new HarmonyMethod(method)); continue; } ServicePatch GetPatch() { var target = method.FindMatchingMethod(vanillaImplType.GetMethods(InstanceBinding), true, IsTypeMatching); //Path real implementation, not V-table stub target = target.GetDeclaredMember(); if (patches.TryGetValue(target, out var patch) == false) { patch = new ServicePatch(target, harmony.CreateProcessor(target)); patches.Add(target, patch); } return(patch); bool IsTypeMatching(Type real, Type general) { if (real == general) { return(true); } if (real.IsClass && general == typeof(object)) { return(true); } if (real.IsByRef && (general == typeof(IntPtr) || general == typeof(void *))) { return(true); } return(false); } } } foreach (var(_, patch) in patches) { patch.Patch.Patch(); } if (stubs.Count > 0 || vanillaInterfaceType != fullInterfaceType) { StubIndirections = new bool[stubs.Count]; var proxy = PatchHelpers.CreateDynamicType($"{fullInterfaceType.Name}_Proxy"); proxy.SetParent(typeof(object)); proxy.AddInterfaceImplementation(fullInterfaceType); var stubsField = AccessTools.Field(typeof(CreativeService <TInstance, TService>), "StubIndirections"); var vanillaInstance = proxy.DefineField("__Vanilla", vanillaImplType, FieldAttributes.Public | FieldAttributes.InitOnly); var proxyDelegates = new List <(Delegate Delegate, FieldInfo Field)>(); var vanillaMethods = vanillaInterfaceType.GetAllInterfaceMethods().ToHashSet(); var declaredVanillaMethods = vanillaImplType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (var method in fullInterfaceType.GetAllInterfaceMethods()) { var args = method.GetParameters(); var proxyMethod = proxy.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual); proxyMethod.SetReturnType(method.ReturnType); proxyMethod.SetParameters(args.Select(x => x.ParameterType).ToArray()); proxy.DefineMethodOverride(proxyMethod, method); var IL = proxyMethod.GetILGenerator(); var stubId = stubs.IndexOf(method); if (stubId >= 0) { //Set stub mark IL.Emit(OpCodes.Ldsfld, stubsField); IL.Emit(OpCodes.Ldc_I4, stubId); IL.Emit(OpCodes.Ldc_I4_1); IL.Emit(OpCodes.Stelem_I1); } MethodInfo directCallTarget = null; var isInterfaceCall = vanillaMethods.Contains(method); if (isInterfaceCall == false) { directCallTarget = method.FindMatchingMethod(declaredVanillaMethods, true); if (directCallTarget.IsAssemblyPublic() == false) { //Note: Nasty hack follows! //Dynamic type has to follow all visibility rules normal assembly would so it can't call private methods directly //Runtime delegates can reflect, call and expose private members, but can't instantiate interface //=> Call proxy delegate inside proxy type to expose private method via public interface. We need to go deeper :) var delegateType = DelegateCreator.NewDelegateType(directCallTarget, unboundInstanceCall: true); var proxyDelegate = Delegate.CreateDelegate(delegateType, directCallTarget); var fieldName = $"__ProxyDelegate_{directCallTarget.Name}_{proxyDelegates.Count}"; var delegateField = proxy.DefineField(fieldName, delegateType, FieldAttributes.Private | FieldAttributes.InitOnly); IL.Emit(OpCodes.Ldarg_0); //This IL.Emit(OpCodes.Ldfld, delegateField); proxyDelegates.Add((proxyDelegate, delegateField)); directCallTarget = delegateType.GetMethod("Invoke"); } } if (directCallTarget == null || directCallTarget.IsStatic == false) { IL.Emit(OpCodes.Ldarg_0); //This IL.Emit(OpCodes.Ldfld, vanillaInstance); } var argCount = args.Length; for (int i = 1; i <= argCount; i++) { IL.Emit(OpCodes.Ldarg, i); } if (isInterfaceCall) { IL.Emit(OpCodes.Callvirt, method); } else { IL.Emit(OpCodes.Call, directCallTarget); } IL.Emit(OpCodes.Ret); } var ctorArgTypes = new[] { vanillaImplType }.Concat(proxyDelegates.Select(x => x.Field.FieldType)); var ctor = proxy.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, ctorArgTypes.ToArray()); var cIL = ctor.GetILGenerator(); cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Call, proxy.BaseType.GetConstructor(Type.EmptyTypes)); cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Ldarg_1); cIL.Emit(OpCodes.Stfld, vanillaInstance); for (int i = 0; i < proxyDelegates.Count; i++) { cIL.Emit(OpCodes.Ldarg_0); cIL.Emit(OpCodes.Ldarg, i + 2); cIL.Emit(OpCodes.Stfld, proxyDelegates[i].Field); } cIL.Emit(OpCodes.Ret); var ctorArgs = new object[] { vanilla }.Concat(proxyDelegates.Select(x => x.Delegate)); Instance.Vanilla = (TService)Activator.CreateInstance(proxy.CreateType(), ctorArgs.ToArray()); } else { Instance.Vanilla = (TService)vanilla; } foreach (var property in typeof(TInstance).GetProperties(InstanceBinding | BindingFlags.Static)) { if (property.HasAttribute <HarmonyPropertyAttribute>()) { property.InjectVanillaData(vanillaImplType, harmony); } } }