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:
nattthebear 2017-05-20 09:46:45 -04:00
parent 3f8a89d171
commit 5c8efb51ba
6 changed files with 289 additions and 63 deletions

View File

@ -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" />

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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);
}
}
}
}