Add some Import related stuff to PeRunner. Build BizExvoker which will be used to import managed libraries as dependencies to PEs
This commit is contained in:
parent
3f8a89d171
commit
5c8efb51ba
|
@ -90,7 +90,9 @@
|
|||
<Compile Include="BinaryQuickSerializer.cs" />
|
||||
<Compile Include="Bit.cs" />
|
||||
<Compile Include="BitReverse.cs" />
|
||||
<Compile Include="BizInvoke\BizExvoker.cs" />
|
||||
<Compile Include="BizInvoke\BizInvoker.cs" />
|
||||
<Compile Include="BizInvoke\BizInvokeUtilities.cs" />
|
||||
<Compile Include="BizInvoke\DynamicLibraryImportResolver.cs" />
|
||||
<Compile Include="Buffer.cs" />
|
||||
<Compile Include="Colors.cs" />
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public static class BizExvoker
|
||||
{
|
||||
/// <summary>
|
||||
/// the assembly that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly AssemblyBuilder ImplAssemblyBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// the module that all delegate types are placed in
|
||||
/// </summary>
|
||||
private static readonly ModuleBuilder ImplModuleBuilder;
|
||||
|
||||
static BizExvoker()
|
||||
{
|
||||
var aname = new AssemblyName("BizExvokeProxyAssembly");
|
||||
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
|
||||
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizExvokerModule");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// holds the delegate types for a type
|
||||
/// </summary>
|
||||
private class DelegateStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// the type that this storage was made for
|
||||
/// </summary>
|
||||
public Type OriginalType { get; }
|
||||
/// <summary>
|
||||
/// the type that the delegate types reside in
|
||||
/// </summary>
|
||||
public Type StorageType { get; }
|
||||
|
||||
public List<StoredDelegateInfo> DelegateTypes { get; } = new List<StoredDelegateInfo>();
|
||||
|
||||
public class StoredDelegateInfo
|
||||
{
|
||||
public MethodInfo Method { get; }
|
||||
public Type DelegateType { get; }
|
||||
public string EntryPointName { get; }
|
||||
public StoredDelegateInfo(MethodInfo method, Type delegateType, string entryPointName)
|
||||
{
|
||||
Method = method;
|
||||
DelegateType = delegateType;
|
||||
EntryPointName = entryPointName;
|
||||
}
|
||||
}
|
||||
|
||||
public DelegateStorage(Type type)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Select(m => new
|
||||
{
|
||||
Info = m,
|
||||
Attr = m.GetCustomAttributes(true).OfType<BizExportAttribute>().FirstOrDefault()
|
||||
})
|
||||
.Where(a => a.Attr != null)
|
||||
.ToList();
|
||||
|
||||
var typeBuilder = ImplModuleBuilder.DefineType(
|
||||
"Bizhawk.BizExvokeHolder" + type.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed);
|
||||
|
||||
foreach (var a in methods)
|
||||
{
|
||||
MethodBuilder unused;
|
||||
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr.CallingConvention, typeBuilder, out unused).CreateType();
|
||||
DelegateTypes.Add(new StoredDelegateInfo(a.Info, delegateType, a.Attr.EntryPoint ?? a.Info.Name));
|
||||
}
|
||||
StorageType = typeBuilder.CreateType();
|
||||
OriginalType = type;
|
||||
}
|
||||
}
|
||||
|
||||
private class ExvokerImpl : IImportResolver
|
||||
{
|
||||
private readonly Dictionary<string, IntPtr> EntryPoints = new Dictionary<string, IntPtr>();
|
||||
|
||||
private readonly List<Delegate> Delegates = new List<Delegate>();
|
||||
|
||||
public ExvokerImpl(object o, DelegateStorage d)
|
||||
{
|
||||
foreach (var sdt in d.DelegateTypes)
|
||||
{
|
||||
var del = Delegate.CreateDelegate(sdt.DelegateType, o, sdt.Method);
|
||||
Delegates.Add(del); // prevent garbage collection of the delegate, which would invalidate the pointer
|
||||
EntryPoints.Add(sdt.EntryPointName, Marshal.GetFunctionPointerForDelegate(del));
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
IntPtr ret;
|
||||
EntryPoints.TryGetValue(entryPoint, out ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly Dictionary<Type, DelegateStorage> Impls = new Dictionary<Type, DelegateStorage>();
|
||||
|
||||
|
||||
public static IImportResolver GetExvoker(object o)
|
||||
{
|
||||
DelegateStorage ds;
|
||||
lock (Impls)
|
||||
{
|
||||
var type = o.GetType();
|
||||
if (!Impls.TryGetValue(type, out ds))
|
||||
{
|
||||
ds = new DelegateStorage(type);
|
||||
Impls.Add(type, ds);
|
||||
}
|
||||
}
|
||||
|
||||
return new ExvokerImpl(o, ds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// mark an instance method to be exported by BizExvoker
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class BizExportAttribute : Attribute
|
||||
{
|
||||
public CallingConvention CallingConvention { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of entry point; if not given, the method's name is used
|
||||
/// </summary>
|
||||
public string EntryPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BizImportAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="c">unmanaged calling convention</param>
|
||||
public BizExportAttribute(CallingConvention c)
|
||||
{
|
||||
CallingConvention = c;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Common.BizInvoke
|
||||
{
|
||||
public static class BizInvokeUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// create a delegate type to match a method type
|
||||
/// </summary>
|
||||
/// <param name="method">the method to "clone"</param>
|
||||
/// <param name="nativeCall">native calling convention to use</param>
|
||||
/// <param name="enclosingType">the type to define this delegate type as a nested type on</param>
|
||||
/// <param name="invokeMethod">the methodBuilder for the magic Invoke method on the resulting type</param>
|
||||
/// <returns>the resulting typeBuilder</returns>
|
||||
public static TypeBuilder CreateDelegateType(MethodInfo method, CallingConvention nativeCall, TypeBuilder enclosingType,
|
||||
out MethodBuilder invokeMethod)
|
||||
{
|
||||
var paramInfos = method.GetParameters();
|
||||
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
|
||||
var returnType = method.ReturnType;
|
||||
|
||||
// create the delegate type
|
||||
var delegateType = enclosingType.DefineNestedType(
|
||||
"DelegateType" + method.Name,
|
||||
TypeAttributes.Class | TypeAttributes.NestedPrivate | TypeAttributes.Sealed,
|
||||
typeof(MulticastDelegate));
|
||||
|
||||
var delegateCtor = delegateType.DefineConstructor(
|
||||
MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
new[] { typeof(object), typeof(IntPtr) });
|
||||
|
||||
delegateCtor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
|
||||
|
||||
var delegateInvoke = delegateType.DefineMethod(
|
||||
"Invoke",
|
||||
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
|
||||
returnType,
|
||||
paramTypes);
|
||||
|
||||
// we have to project all of the attributes from the baseMethod to the delegateInvoke
|
||||
// so for something like [Out], the interop engine will see it and use it
|
||||
for (int i = 0; i < paramInfos.Length; i++)
|
||||
{
|
||||
var p = delegateInvoke.DefineParameter(i + 1, ParameterAttributes.None, paramInfos[i].Name);
|
||||
foreach (var a in paramInfos[i].GetCustomAttributes(false))
|
||||
{
|
||||
p.SetCustomAttribute(GetAttributeBuilder(a));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var p = delegateInvoke.DefineParameter(0, ParameterAttributes.Retval, method.ReturnParameter.Name);
|
||||
foreach (var a in method.ReturnParameter.GetCustomAttributes(false))
|
||||
{
|
||||
p.SetCustomAttribute(GetAttributeBuilder(a));
|
||||
}
|
||||
}
|
||||
|
||||
delegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
|
||||
|
||||
// add the [UnmanagedFunctionPointer] to the delegate so interop will know how to call it
|
||||
var attr = new CustomAttributeBuilder(typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) }), new object[] { nativeCall });
|
||||
delegateType.SetCustomAttribute(attr);
|
||||
|
||||
invokeMethod = delegateInvoke;
|
||||
return delegateType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get an attribute builder to clone an attribute to a delegate type
|
||||
/// </summary>
|
||||
private static CustomAttributeBuilder GetAttributeBuilder(object o)
|
||||
{
|
||||
// anything more clever we can do here?
|
||||
var t = o.GetType();
|
||||
if (t == typeof(OutAttribute) || t == typeof(InAttribute))
|
||||
{
|
||||
return new CustomAttributeBuilder(t.GetConstructor(Type.EmptyTypes), new object[0]);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unknown parameter attribute " + t.Name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,13 +46,13 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// <summary>
|
||||
/// the module that all proxies are placed in
|
||||
/// </summary>
|
||||
private static readonly ModuleBuilder ImplModuleBilder;
|
||||
private static readonly ModuleBuilder ImplModuleBuilder;
|
||||
|
||||
static BizInvoker()
|
||||
{
|
||||
var aname = new AssemblyName("BizInvokeProxyAssembly");
|
||||
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
|
||||
ImplModuleBilder = ImplAssemblyBuilder.DefineDynamicModule("BizInvokerModule");
|
||||
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizInvokerModule");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -155,7 +155,7 @@ namespace BizHawk.Common.BizInvoke
|
|||
// hooks that will be run on the created proxy object
|
||||
var postCreateHooks = new List<Action<object, IImportResolver>>();
|
||||
|
||||
var type = ImplModuleBilder.DefineType("Bizhawk.BizInvokeProxy" + baseType.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, baseType);
|
||||
var type = ImplModuleBuilder.DefineType("Bizhawk.BizInvokeProxy" + baseType.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed, baseType);
|
||||
|
||||
var monitorField = monitor ? type.DefineField("MonitorField", typeof(IMonitor), FieldAttributes.Public) : null;
|
||||
|
||||
|
@ -188,54 +188,14 @@ namespace BizHawk.Common.BizInvoke
|
|||
/// </summary>
|
||||
private static Action<object, IImportResolver> ImplementMethodDelegate(TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo monitorField)
|
||||
{
|
||||
// create the delegate type
|
||||
MethodBuilder delegateInvoke;
|
||||
var delegateType = BizInvokeUtilities.CreateDelegateType(baseMethod, nativeCall, type, out delegateInvoke);
|
||||
|
||||
var paramInfos = baseMethod.GetParameters();
|
||||
var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray();
|
||||
var returnType = baseMethod.ReturnType;
|
||||
|
||||
// create the delegate type
|
||||
var delegateType = type.DefineNestedType(
|
||||
"DelegateType" + baseMethod.Name,
|
||||
TypeAttributes.Class | TypeAttributes.NestedPrivate | TypeAttributes.Sealed,
|
||||
typeof(MulticastDelegate));
|
||||
|
||||
var delegateCtor = delegateType.DefineConstructor(
|
||||
MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
|
||||
CallingConventions.Standard,
|
||||
new[] { typeof(object), typeof(IntPtr) });
|
||||
|
||||
delegateCtor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
|
||||
|
||||
var delegateInvoke = delegateType.DefineMethod(
|
||||
"Invoke",
|
||||
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
|
||||
returnType,
|
||||
paramTypes);
|
||||
|
||||
// we have to project all of the attributes from the baseMethod to the delegateInvoke
|
||||
// so for something like [Out], the interop engine will see it and use it
|
||||
for (int i = 0; i < paramInfos.Length; i++)
|
||||
{
|
||||
var p = delegateInvoke.DefineParameter(i + 1, ParameterAttributes.None, paramInfos[i].Name);
|
||||
foreach (var a in paramInfos[i].GetCustomAttributes(false))
|
||||
{
|
||||
p.SetCustomAttribute(GetAttributeBuilder(a));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var p = delegateInvoke.DefineParameter(0, ParameterAttributes.Retval, baseMethod.ReturnParameter.Name);
|
||||
foreach (var a in baseMethod.ReturnParameter.GetCustomAttributes(false))
|
||||
{
|
||||
p.SetCustomAttribute(GetAttributeBuilder(a));
|
||||
}
|
||||
}
|
||||
|
||||
delegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);
|
||||
|
||||
// add the [UnmanagedFunctionPointer] to the delegate so interop will know how to call it
|
||||
var attr = new CustomAttributeBuilder(typeof(UnmanagedFunctionPointerAttribute).GetConstructor(new[] { typeof(CallingConvention) }), new object[] { nativeCall });
|
||||
delegateType.SetCustomAttribute(attr);
|
||||
|
||||
// define a field on the class to hold the delegate
|
||||
var field = type.DefineField(
|
||||
"DelegateField" + baseMethod.Name,
|
||||
|
@ -522,18 +482,6 @@ namespace BizHawk.Common.BizInvoke
|
|||
|
||||
throw new InvalidOperationException("Unrecognized parameter type!");
|
||||
}
|
||||
|
||||
private static CustomAttributeBuilder GetAttributeBuilder(object o)
|
||||
{
|
||||
// anything more clever we can do here?
|
||||
var t = o.GetType();
|
||||
if (t == typeof(OutAttribute) || t == typeof(InAttribute))
|
||||
{
|
||||
return new CustomAttributeBuilder(t.GetConstructor(Type.EmptyTypes), new object[0]);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unknown parameter attribute " + t.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -12,6 +12,12 @@ namespace BizHawk.Common
|
|||
|
||||
public static class ImportResolverExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolve an entry point and throw an exception if that resolution is NULL
|
||||
/// </summary>
|
||||
/// <param name="dll"></param>
|
||||
/// <param name="entryPoint"></param>
|
||||
/// <returns></returns>
|
||||
public static IntPtr SafeResolve(this IImportResolver dll, string entryPoint)
|
||||
{
|
||||
var ret = dll.Resolve(entryPoint);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using PeNet;
|
||||
using BizHawk.Common;
|
||||
using PeNet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
@ -11,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
{
|
||||
private static readonly ulong CanonicalStart = 0x0000036f00000000;
|
||||
|
||||
public class PeWrapper
|
||||
public class PeWrapper : IImportResolver
|
||||
{
|
||||
public Dictionary<int, IntPtr> ExportsByOrdinal { get; } = new Dictionary<int, IntPtr>();
|
||||
/// <summary>
|
||||
|
@ -19,6 +20,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// </summary>
|
||||
public Dictionary<string, IntPtr> ExportsByName { get; } = new Dictionary<string, IntPtr>();
|
||||
|
||||
public Dictionary<string, Dictionary<string, IntPtr>> ImportsByModule { get; } = new Dictionary<string, Dictionary<string, IntPtr>>();
|
||||
|
||||
public string ModuleName { get; }
|
||||
|
||||
private readonly byte[] _fileData;
|
||||
|
@ -146,10 +149,36 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
}
|
||||
|
||||
// collect information about imports
|
||||
// NB: Hints are not the same as Ordinals. Off by 1??
|
||||
// NB: Hints are not the same as Ordinals
|
||||
foreach (var import in _pe.ImportedFunctions)
|
||||
{
|
||||
|
||||
Dictionary<string, IntPtr> module;
|
||||
if (!ImportsByModule.TryGetValue(import.DLL, out module))
|
||||
{
|
||||
module = new Dictionary<string, IntPtr>();
|
||||
ImportsByModule.Add(import.DLL, module);
|
||||
}
|
||||
module.Add(import.Name, Z.US(import.Thunk));
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Resolve(string entryPoint)
|
||||
{
|
||||
IntPtr ret;
|
||||
ExportsByName.TryGetValue(entryPoint, out ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void ConnectImports(string moduleName, IImportResolver module)
|
||||
{
|
||||
Dictionary<string, IntPtr> imports;
|
||||
if (ImportsByModule.TryGetValue(moduleName, out imports))
|
||||
{
|
||||
foreach (var kvp in imports)
|
||||
{
|
||||
var valueArray = new IntPtr[] { module.SafeResolve(kvp.Key) };
|
||||
Marshal.Copy(valueArray, 0, kvp.Value, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue