Skip to content

Commit

Permalink
Merge pull request #371 from Washi1337/development
Browse files Browse the repository at this point in the history
5.0.0-beta.2
  • Loading branch information
Washi1337 authored Oct 26, 2022
2 parents 16362a8 + 9c23525 commit 76a5ed2
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 155 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<RepositoryUrl>https://github.com/Washi1337/AsmResolver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<LangVersion>10</LangVersion>
<Version>5.0.0-beta.1</Version>
<Version>5.0.0-beta.2</Version>
</PropertyGroup>

</Project>
91 changes: 56 additions & 35 deletions src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class DynamicMethodDefinition : MethodDefinition
public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj) :
base(new MetadataToken(TableIndex.Method, 0))
{
dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
var methodBase = FieldReader.ReadField<MethodBase>(dynamicMethodObj, "m_method");
object resolver = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
var methodBase = FieldReader.ReadField<MethodBase>(resolver, "m_method");
if (methodBase is null)
{
throw new ArgumentException(
Expand All @@ -37,9 +37,17 @@ public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj)
Name = methodBase.Name;
Attributes = (MethodAttributes)methodBase.Attributes;
Signature = module.DefaultImporter.ImportMethodSignature(ResolveSig(methodBase, module));
CilMethodBody = CreateDynamicMethodBody(this, dynamicMethodObj);
CilMethodBody = CreateDynamicMethodBody(this, resolver);
}

/// <summary>
/// Determines whether dynamic method reading is fully supported in the current host's .NET environment.
/// </summary>
public static bool IsSupported => DynamicTypeSignatureResolver.IsSupported;

/// <inheritdoc />
public override ModuleDefinition Module { get; }

private MethodSignature ResolveSig(MethodBase methodBase, ModuleDefinition module)
{
var importer = module.DefaultImporter;
Expand All @@ -58,9 +66,6 @@ private MethodSignature ResolveSig(MethodBase methodBase, ModuleDefinition modul
returnType, parameterTypes);
}

/// <inheritdoc />
public override ModuleDefinition Module { get; }

/// <summary>
/// Creates a CIL method body from a dynamic method.
/// </summary>
Expand All @@ -73,42 +78,58 @@ private static CilMethodBody CreateDynamicMethodBody(MethodDefinition method, ob
throw new ArgumentException("Method body should reference a serialized module.");

var result = new CilMethodBody(method);
dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);

// Attempt to get the code field.
byte[]? code = FieldReader.ReadField<byte[]>(dynamicMethodObj, "m_code");

// If it is still null, it might still be set using DynamicILInfo::SetCode.
// Find the code stored in the DynamicILInfo if available.
if (code is null
&& FieldReader.TryReadField<MethodBase>(dynamicMethodObj, "m_method", out var methodBase)
&& methodBase is not null
&& FieldReader.TryReadField(methodBase, "m_DynamicILInfo", out object? dynamicILInfo)
&& dynamicILInfo is not null)
object resolver = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);

// We prefer to extract the information from DynamicILInfo if it is there, as it has more accurate info
// if the DynamicMethod code is not flushed yet into the resolver (e.g., it hasn't been invoked yet).
object? dynamicILInfo = null;
if (FieldReader.TryReadField<MethodBase>(resolver, "m_method", out var m) && m is not null)
FieldReader.TryReadField(m, "m_DynamicILInfo", out dynamicILInfo);

// Extract all required information to construct the body.
byte[]? code;
object scope;
List<object?> tokenList;
byte[]? localSig;
byte[]? ehHeader;
IList<object>? ehInfos;

if (resolver.GetType().FullName != "System.Reflection.Emit.DynamicILInfo" && dynamicILInfo is not null)
{
code = FieldReader.ReadField<byte[]>(dynamicILInfo, "m_code");
}
scope = FieldReader.ReadField<object>(dynamicILInfo, "m_scope")!;
tokenList = FieldReader.ReadField<List<object?>>(scope, "m_tokens")!;
localSig = FieldReader.ReadField<byte[]>(dynamicILInfo, "m_localSignature");
ehHeader = FieldReader.ReadField<byte[]>(dynamicILInfo, "m_exceptions");

if (code is null)
throw new InvalidOperationException("Dynamic method does not have a CIL code stream.");

// Get remaining fields.
object scope = FieldReader.ReadField<object>(dynamicMethodObj, "m_scope")!;
var tokenList = FieldReader.ReadField<List<object?>>(scope, "m_tokens")!;
byte[] localSig = FieldReader.ReadField<byte[]>(dynamicMethodObj, "m_localSignature")!;
byte[] ehHeader = FieldReader.ReadField<byte[]>(dynamicMethodObj, "m_exceptionHeader")!;
var ehInfos = FieldReader.ReadField<IList<object>>(dynamicMethodObj, "m_exceptions")!;
// DynamicILInfo does not have EH info. Try recover it from the resolver.
ehInfos = FieldReader.ReadField<IList<object>>(resolver, "m_exceptions");
}
else
{
code = FieldReader.ReadField<byte[]>(resolver, "m_code");
scope = FieldReader.ReadField<object>(resolver, "m_scope")!;
tokenList = FieldReader.ReadField<List<object?>>(scope, "m_tokens")!;
localSig = FieldReader.ReadField<byte[]>(resolver, "m_localSignature");
ehHeader = FieldReader.ReadField<byte[]>(resolver, "m_exceptionHeader");
ehInfos = FieldReader.ReadField<IList<object>>(resolver, "m_exceptions");
}

//Local Variables
DynamicMethodHelper.ReadLocalVariables(result, method, localSig);
// Interpret local variables signatures.
if (localSig is not null)
DynamicMethodHelper.ReadLocalVariables(result, method, localSig);

// Read raw instructions.
var reader = new BinaryStreamReader(code);
var disassembler = new CilDisassembler(reader, new DynamicCilOperandResolver(module, result, tokenList));
result.Instructions.AddRange(disassembler.ReadInstructions());
if (code is not null)
{
var reader = new BinaryStreamReader(code);
var operandResolver = new DynamicCilOperandResolver(module, result, tokenList);
var disassembler = new CilDisassembler(reader, operandResolver);
result.Instructions.AddRange(disassembler.ReadInstructions());
}

//Exception Handlers
DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehInfos, ehHeader, module.DefaultImporter);
// Interpret exception handler information or header.
DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehHeader, ehInfos, module.DefaultImporter);

return result;
}
Expand Down
79 changes: 13 additions & 66 deletions src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,22 @@
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Serialized;
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;
using AsmResolver.IO;
using AsmResolver.PE.DotNet.Cil;
using AsmResolver.PE.DotNet.Metadata.Tables.Rows;

namespace AsmResolver.DotNet.Dynamic
{
internal static class DynamicMethodHelper
{
private static readonly MethodInfo GetTypeFromHandleUnsafeMethod;

static DynamicMethodHelper()
{
// We need to use reflection for this to stay compatible with .netstandard 2.0.
GetTypeFromHandleUnsafeMethod = typeof(Type)
.GetMethod("GetTypeFromHandleUnsafe",
(BindingFlags) (-1),
null,
new[] {typeof(IntPtr)},
null)!;
}

public static void ReadLocalVariables(CilMethodBody methodBody, MethodDefinition method, byte[] localSig)
{
if (method.Module is not SerializedModuleDefinition module)
throw new ArgumentException("Method body should reference a serialized module.");

var reader = new BinaryStreamReader(localSig);
if (ReadLocalVariableSignature(new BlobReadContext(module.ReaderContext), ref reader)
is not { } localsSignature)
var context = new BlobReadContext(module.ReaderContext, DynamicTypeSignatureResolver.Instance);
if (CallingConventionSignature.FromReader(context, ref reader)
is not LocalVariablesSignature localsSignature)
{
throw new ArgumentException("Invalid local variables signature.");
}
Expand All @@ -44,64 +30,25 @@ public static void ReadLocalVariables(CilMethodBody methodBody, MethodDefinition
methodBody.LocalVariables.Add(new CilLocalVariable(localsSignature.VariableTypes[i]));
}

private static TypeSignature ReadTypeSignature(in BlobReadContext context, ref BinaryStreamReader reader)
{
return (ElementType) reader.PeekByte() == ElementType.Internal
? ReadInternalTypeSignature(context, ref reader)
: TypeSignature.FromReader(in context, ref reader);
}

private static TypeSignature ReadInternalTypeSignature(in BlobReadContext context, ref BinaryStreamReader reader)
{
var address = IntPtr.Size switch
{
4 => new IntPtr(reader.ReadInt32()),
_ => new IntPtr(reader.ReadInt64())
};

// Let the runtime translate the address to a type and import it.
var clrType = (Type?) GetTypeFromHandleUnsafeMethod.Invoke(null, new object[] { address });

var type = clrType is not null
? new ReferenceImporter(context.ReaderContext.ParentModule).ImportType(clrType)
: InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec);

return new TypeDefOrRefSignature(type);
}

private static LocalVariablesSignature ReadLocalVariableSignature(
in BlobReadContext context,
ref BinaryStreamReader reader)
public static void ReadReflectionExceptionHandlers(CilMethodBody methodBody,
byte[]? ehHeader, IList<object>? ehInfos, ReferenceImporter importer)
{
var result = new LocalVariablesSignature();
result.Attributes = (CallingConventionAttributes) reader.ReadByte();

if (!reader.TryReadCompressedUInt32(out uint count))
if (ehHeader is {Length: > 4})
{
context.ReaderContext.BadImage("Invalid number of local variables in local variable signature.");
return result;
InterpretEHSection(methodBody, importer, ehHeader);
}

for (int i = 0; i < count; i++)
result.VariableTypes.Add(ReadTypeSignature(context, ref reader));

return result;
}

public static void ReadReflectionExceptionHandlers(CilMethodBody methodBody,
IList<object>? ehInfos, byte[] ehHeader, ReferenceImporter importer)
{
//Sample needed!
if (ehHeader is { Length: > 4 })
throw new NotImplementedException("Exception handlers from ehHeader not supported yet.");

if (ehInfos is { Count: > 0 })
else if (ehInfos is { Count: > 0 })
{
foreach (var ehInfo in ehInfos)
InterpretEHInfo(methodBody, importer, ehInfo);
}
}

private static void InterpretEHSection(CilMethodBody methodBody, ReferenceImporter importer, byte[] ehHeader)
{
throw new NotImplementedException("Raw exception data is not supported yet.");
}

private static void InterpretEHInfo(CilMethodBody methodBody, ReferenceImporter importer, object ehInfo)
{
for (int i = 0; i < FieldReader.ReadField<int>(ehInfo, "m_currentCatch"); i++)
Expand Down
59 changes: 59 additions & 0 deletions src/AsmResolver.DotNet.Dynamic/DynamicTypeSignatureReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using AsmResolver.DotNet.Signatures;
using AsmResolver.DotNet.Signatures.Types;

namespace AsmResolver.DotNet.Dynamic
{
/// <summary>
/// Provides an implementation for the <see cref="ITypeSignatureResolver"/> that resolves metadata tokens from
/// the underlying module's tables stream, and is able to transform addresses referencing method tables in the
/// current process to type signatures.
/// </summary>
public class DynamicTypeSignatureResolver : PhysicalTypeSignatureResolver
{
private static readonly MethodInfo? GetTypeFromHandleUnsafeMethod;

static DynamicTypeSignatureResolver()
{
// We need to use reflection for this to stay compatible with .netstandard 2.0.
GetTypeFromHandleUnsafeMethod = typeof(Type)
.GetMethod("GetTypeFromHandleUnsafe",
(BindingFlags) (-1),
null,
new[] {typeof(IntPtr)},
null);
}

/// <summary>
/// Gets the singleton instance of the <see cref="DynamicTypeSignatureResolver"/> class.
/// </summary>
public new static DynamicTypeSignatureResolver Instance
{
get;
} = new();

/// <summary>
/// Gets a value indicating whether dynamic resolution of method tables is supported.
/// </summary>
[MemberNotNullWhen(true, nameof(GetTypeFromHandleUnsafeMethod))]
public static bool IsSupported => GetTypeFromHandleUnsafeMethod is not null;

/// <inheritdoc />
public override TypeSignature ResolveRuntimeType(in BlobReadContext context, nint address)
{
if (!IsSupported)
throw new PlatformNotSupportedException("The current platform does not support the translation of raw type handles to System.Type instances.");

// Let the runtime translate the address to a type and import it.
var clrType = (Type?) GetTypeFromHandleUnsafeMethod.Invoke(null, new object[] { address });

var type = clrType is not null
? new ReferenceImporter(context.ReaderContext.ParentModule).ImportType(clrType)
: InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec);

return new TypeDefOrRefSignature(type);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public OriginalMetadataTokenProvider(ModuleDefinition? module)

private MetadataToken GetToken(IMetadataMember member)
{
if (_module is not null && member is IModuleProvider provider && provider.Module == _module)
if (_module is not null && member is IModuleProvider provider && provider.Module != _module)
throw new MemberNotImportedException(member);

return member.MetadataToken;
Expand Down
26 changes: 21 additions & 5 deletions src/AsmResolver.DotNet/ReferenceImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,14 +506,26 @@ public virtual IMethodDescriptor ImportMethod(MethodBase method)
if (method is null)
throw new ArgumentNullException(nameof(method));

// We need to create a method spec if this method is a generic instantiation.
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
return ImportGenericMethod((MethodInfo) method);

// Test whether we have a declaring type.
var originalDeclaringType = method.DeclaringType;
if (originalDeclaringType is null)
throw new ArgumentException("Method's declaring type is null.");

// System.Reflection substitutes all type parameters in the MethodInfo instance with their concrete
// arguments if the declaring type is a generic instantiation. However in metadata, we need the original
// parameter references. Thus, resolve the original method info first if required.
if (originalDeclaringType.IsGenericType && !originalDeclaringType.IsGenericTypeDefinition)
method = method.Module.ResolveMethod(method.MetadataToken)!;

var returnType = method is MethodInfo info
? ImportTypeSignature(info.ReturnType)
: TargetModule.CorLibTypeFactory.Void;

var parameters = method.DeclaringType is { IsConstructedGenericType: true }
var parameters = originalDeclaringType is { IsConstructedGenericType: true }
? method.Module.ResolveMethod(method.MetadataToken)!.GetParameters()
: method.GetParameters();

Expand All @@ -523,12 +535,16 @@ public virtual IMethodDescriptor ImportMethod(MethodBase method)

var result = new MethodSignature(
method.IsStatic ? 0 : CallingConventionAttributes.HasThis,
returnType, parameterTypes);
returnType,
parameterTypes);

if (method.DeclaringType == null)
throw new ArgumentException("Method's declaring type is null.");
if (method.IsGenericMethodDefinition)
{
result.IsGeneric = true;
result.GenericParameterCount = method.GetGenericArguments().Length;
}

return new MemberReference(ImportType(method.DeclaringType), method.Name, result);
return new MemberReference(ImportType(originalDeclaringType), method.Name, result);
}

[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls AsmResolver.DotNet.ReferenceImporter.ImportMethod(System.Reflection.MethodBase)")]
Expand Down
Loading

0 comments on commit 76a5ed2

Please sign in to comment.