diff --git a/AsmResolver.sln b/AsmResolver.sln index 1fac52b35..0cf5601e0 100644 --- a/AsmResolver.sln +++ b/AsmResolver.sln @@ -90,6 +90,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.Symbols.Pdb", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.Symbols.Pdb.Tests", "test\AsmResolver.Symbols.Pdb.Tests\AsmResolver.Symbols.Pdb.Tests.csproj", "{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.DotNet.Dynamic.Tests", "test\AsmResolver.DotNet.Dynamic.Tests\AsmResolver.DotNet.Dynamic.Tests.csproj", "{C089D0AB-B428-4136-89CC-7974CB590513}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsmResolver.DotNet.Dynamic", "src\AsmResolver.DotNet.Dynamic\AsmResolver.DotNet.Dynamic.csproj", "{62420213-67AD-40FC-B451-BD05C2437CE3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -452,6 +456,30 @@ Global {AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x64.Build.0 = Release|Any CPU {AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x86.ActiveCfg = Release|Any CPU {AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x86.Build.0 = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x64.ActiveCfg = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x64.Build.0 = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x86.ActiveCfg = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x86.Build.0 = Debug|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|Any CPU.Build.0 = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|x64.ActiveCfg = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|x64.Build.0 = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|x86.ActiveCfg = Release|Any CPU + {C089D0AB-B428-4136-89CC-7974CB590513}.Release|x86.Build.0 = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x64.ActiveCfg = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x64.Build.0 = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x86.ActiveCfg = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x86.Build.0 = Debug|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|Any CPU.Build.0 = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x64.ActiveCfg = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x64.Build.0 = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x86.ActiveCfg = Release|Any CPU + {62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -491,6 +519,8 @@ Global {2D1DF5DA-7367-4490-B3F0-B996348E150B} = {B3AF102B-ABE1-41B2-AE48-C40702F45AB0} {9E311832-D0F2-42CA-84DD-9A91B88F0287} = {34A95168-A162-4F6A-803B-B6F221FE9EA6} {AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE} = {786C1732-8C96-45DD-97BB-639C9AA7F45B} + {C089D0AB-B428-4136-89CC-7974CB590513} = {786C1732-8C96-45DD-97BB-639C9AA7F45B} + {62420213-67AD-40FC-B451-BD05C2437CE3} = {34A95168-A162-4F6A-803B-B6F221FE9EA6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3302AC79-6D23-4E7D-8C5F-C0C7261044D0} diff --git a/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj b/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj new file mode 100644 index 000000000..1f4ff4172 --- /dev/null +++ b/src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj @@ -0,0 +1,32 @@ + + + + AsmResolver.DotNet.Dynamic + Dynamic method support for the AsmResolver executable file inspection toolsuite. + exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly dynamic + true + 1701;1702;NU5105 + net6.0;netcoreapp3.1;netstandard2.0 + enable + + + + bin\Debug\AsmResolver.DotNet.xml + + + + bin\Release\AsmResolver.DotNet.xml + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/src/AsmResolver.DotNet/Code/Cil/DynamicCilOperandResolver.cs b/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs similarity index 92% rename from src/AsmResolver.DotNet/Code/Cil/DynamicCilOperandResolver.cs rename to src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs index d3244f8e2..6cfa15b76 100644 --- a/src/AsmResolver.DotNet/Code/Cil/DynamicCilOperandResolver.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicCilOperandResolver.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Reflection; +using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Serialized; using AsmResolver.DotNet.Signatures; using AsmResolver.IO; using AsmResolver.PE.DotNet.Cil; using AsmResolver.PE.DotNet.Metadata.Tables; -namespace AsmResolver.DotNet.Code.Cil +namespace AsmResolver.DotNet.Dynamic { /// /// Provides an implementation of that resolves operands based on @@ -34,13 +35,13 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe switch (token.Table) { case TableIndex.TypeDef: - object? type = _tokens[(int) token.Rid]; + object? type = _tokens[(int)token.Rid]; if (type is RuntimeTypeHandle runtimeTypeHandle) return _importer.ImportType(Type.GetTypeFromHandle(runtimeTypeHandle)); break; case TableIndex.Field: - object? field = _tokens[(int) token.Rid]; + object? field = _tokens[(int)token.Rid]; if (field is null) return null; @@ -61,7 +62,7 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe case TableIndex.Method: case TableIndex.MemberRef: - object? obj = _tokens[(int) token.Rid]; + object? obj = _tokens[(int)token.Rid]; if (obj is null) return null; @@ -94,7 +95,7 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe break; case TableIndex.StandAloneSig: - var reader = ByteArrayDataSource.CreateReader((byte[]) _tokens[(int) token.Rid]!); + var reader = ByteArrayDataSource.CreateReader((byte[])_tokens[(int)token.Rid]!); return CallingConventionSignature.FromReader(new BlobReadContext(_readerContext), ref reader); } @@ -104,7 +105,7 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe /// public override object? ResolveString(MetadataToken token) { - return _tokens[(int) token.Rid] as string; + return _tokens[(int)token.Rid] as string; } } } diff --git a/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs b/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs new file mode 100644 index 000000000..4349d9006 --- /dev/null +++ b/src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +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; +using MethodAttributes = AsmResolver.PE.DotNet.Metadata.Tables.Rows.MethodAttributes; + +namespace AsmResolver.DotNet.Dynamic +{ + /// + /// Represents a single method in a type definition of a .NET module. + /// + public class DynamicMethodDefinition : MethodDefinition + { + /// + /// Create a Dynamic Method Definition + /// + /// Target Module + /// Dynamic Method / Delegate / DynamicResolver + public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj) : + base(new MetadataToken(TableIndex.Method, 0)) + { + dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj); + var methodBase = FieldReader.ReadField(dynamicMethodObj, "m_method"); + if (methodBase is null) + { + throw new ArgumentException( + "Could not get the underlying method base in the provided dynamic method object."); + } + + Module = module; + Name = methodBase.Name; + Attributes = (MethodAttributes)methodBase.Attributes; + Signature = new ReferenceImporter(module).ImportMethodSignature(ResolveSig(methodBase, module)); + CilMethodBody = CreateDynamicMethodBody(this, dynamicMethodObj); + } + + private MethodSignature ResolveSig(MethodBase methodBase, ModuleDefinition module) + { + var imp = new ReferenceImporter(module); + var returnType = methodBase is MethodInfo info + ? imp.ImportTypeSignature(info.ReturnType) + : module.CorLibTypeFactory.Void; + + var parameters = methodBase.GetParameters(); + + var parameterTypes = new TypeSignature[parameters.Length]; + for (int i = 0; i < parameterTypes.Length; i++) + parameterTypes[i] = imp.ImportTypeSignature(parameters[i].ParameterType); + + return new MethodSignature( + methodBase.IsStatic ? 0 : CallingConventionAttributes.HasThis, + returnType, parameterTypes); + } + + /// + public override ModuleDefinition Module { get; } + + /// + /// Creates a CIL method body from a dynamic method. + /// + /// The method that owns the method body. + /// The Dynamic Method/Delegate/DynamicResolver. + /// The method body. + private static CilMethodBody CreateDynamicMethodBody(MethodDefinition method, object dynamicMethodObj) + { + if (!(method.Module is SerializedModuleDefinition module)) + throw new ArgumentException("Method body should reference a serialized module."); + + var result = new CilMethodBody(method); + dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj); + + //Get Runtime Fields + byte[] code = FieldReader.ReadField(dynamicMethodObj, "m_code")!; + object scope = FieldReader.ReadField(dynamicMethodObj, "m_scope")!; + var tokenList = FieldReader.ReadField>(scope, "m_tokens")!; + byte[] localSig = FieldReader.ReadField(dynamicMethodObj, "m_localSignature")!; + byte[] ehHeader = FieldReader.ReadField(dynamicMethodObj, "m_exceptionHeader")!; + var ehInfos = FieldReader.ReadField>(dynamicMethodObj, "m_exceptions")!; + + //Local Variables + DynamicMethodHelper.ReadLocalVariables(result, method, localSig); + + // Read raw instructions. + var reader = ByteArrayDataSource.CreateReader(code); + var disassembler = new CilDisassembler(reader, new DynamicCilOperandResolver(module, result, tokenList)); + result.Instructions.AddRange(disassembler.ReadInstructions()); + + //Exception Handlers + DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehInfos, ehHeader, new ReferenceImporter(module)); + + return result; + } + } +} diff --git a/src/AsmResolver.DotNet/DynamicMethodHelper.cs b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs similarity index 63% rename from src/AsmResolver.DotNet/DynamicMethodHelper.cs rename to src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs index f3cece5db..118fd49ac 100644 --- a/src/AsmResolver.DotNet/DynamicMethodHelper.cs +++ b/src/AsmResolver.DotNet.Dynamic/DynamicMethodHelper.cs @@ -6,22 +6,37 @@ 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 +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 SerializedModuleDefinition module)) throw new ArgumentException("Method body should reference a serialized module."); var reader = ByteArrayDataSource.CreateReader(localSig); - if (CallingConventionSignature.FromReader( - new BlobReadContext(module.ReaderContext), - ref reader) is not LocalVariablesSignature localsSignature) + if (ReadLocalVariableSignature( + new BlobReadContext(module.ReaderContext), + ref reader) is not LocalVariablesSignature localsSignature) { throw new ArgumentException("Invalid local variables signature."); } @@ -30,14 +45,58 @@ 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) + { + var result = new LocalVariablesSignature(); + result.Attributes = (CallingConventionAttributes) reader.ReadByte(); + + if (!reader.TryReadCompressedUInt32(out uint count)) + { + context.ReaderContext.BadImage("Invalid number of local variables in local variable signature."); + return result; + } + + for (int i = 0; i < count; i++) + result.VariableTypes.Add(ReadTypeSignature(context, ref reader)); + + return result; + } + public static void ReadReflectionExceptionHandlers(CilMethodBody methodBody, IList? ehInfos, byte[] ehHeader, ReferenceImporter importer) { //Sample needed! - if (ehHeader is {Length: > 4}) + if (ehHeader is { Length: > 4 }) throw new NotImplementedException("Exception handlers from ehHeader not supported yet."); - if (ehInfos is {Count: > 0}) + if (ehInfos is { Count: > 0 }) { foreach (var ehInfo in ehInfos) InterpretEHInfo(methodBody, importer, ehInfo); @@ -61,7 +120,7 @@ private static void InterpretEHInfo(CilMethodBody methodBody, ReferenceImporter int handlerStart = FieldReader.ReadField(ehInfo, "m_catchAddr")![i]; int handlerEnd = FieldReader.ReadField(ehInfo, "m_catchEndAddr")![i]; var exceptionType = FieldReader.ReadField(ehInfo, "m_catchClass")![i]; - var handlerType = (CilExceptionHandlerType) FieldReader.ReadField(ehInfo, "m_type")![i]; + var handlerType = (CilExceptionHandlerType)FieldReader.ReadField(ehInfo, "m_type")![i]; var endTryLabel = instructions.GetByOffset(tryEnd)?.CreateLabel() ?? new CilOffsetLabel(tryEnd); @@ -81,7 +140,6 @@ private static void InterpretEHInfo(CilMethodBody methodBody, ReferenceImporter } } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls GetTypes")] public static object ResolveDynamicResolver(object dynamicMethodObj) { //Convert dynamicMethodObj to DynamicResolver @@ -111,7 +169,7 @@ public static object ResolveDynamicResolver(object dynamicMethodObj) .Invoke(dynamicMethodObj, null); //Create instance of dynamicResolver - dynamicMethodObj = Activator.CreateInstance(dynamicResolver, (BindingFlags) (-1), null, new[] + dynamicMethodObj = Activator.CreateInstance(dynamicResolver, (BindingFlags)(-1), null, new[] { ilGenerator }, null)!; diff --git a/src/AsmResolver.DotNet/FieldReader.cs b/src/AsmResolver.DotNet.Dynamic/FieldReader.cs similarity index 96% rename from src/AsmResolver.DotNet/FieldReader.cs rename to src/AsmResolver.DotNet.Dynamic/FieldReader.cs index 8133f1e63..40e4a4450 100644 --- a/src/AsmResolver.DotNet/FieldReader.cs +++ b/src/AsmResolver.DotNet.Dynamic/FieldReader.cs @@ -1,6 +1,6 @@ -using System.Reflection; +using System.Reflection; -namespace AsmResolver.DotNet +namespace AsmResolver.DotNet.Dynamic { internal static class FieldReader { diff --git a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj index 7b27573f6..8b9a38a50 100644 --- a/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj +++ b/src/AsmResolver.DotNet/AsmResolver.DotNet.csproj @@ -8,6 +8,7 @@ 1701;1702;NU5105 enable net6.0;netcoreapp3.1;netstandard2.0 + true diff --git a/src/AsmResolver.DotNet/Code/Cil/CilMethodBody.cs b/src/AsmResolver.DotNet/Code/Cil/CilMethodBody.cs index 180e474f8..e1915c5e6 100644 --- a/src/AsmResolver.DotNet/Code/Cil/CilMethodBody.cs +++ b/src/AsmResolver.DotNet/Code/Cil/CilMethodBody.cs @@ -130,43 +130,6 @@ public bool VerifyLabelsOnBuild | (value ? CilMethodBodyBuildFlags.VerifyLabels : 0); } - /// - /// Creates a CIL method body from a dynamic method. - /// - /// The method that owns the method body. - /// The Dynamic Method/Delegate/DynamicResolver. - /// The method body. - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls ResolveDynamicResolver")] - public static CilMethodBody FromDynamicMethod(MethodDefinition method, object dynamicMethodObj) - { - if (!(method.Module is SerializedModuleDefinition module)) - throw new ArgumentException("Method body should reference a serialized module."); - - var result = new CilMethodBody(method); - dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj); - - //Get Runtime Fields - byte[] code = FieldReader.ReadField(dynamicMethodObj, "m_code")!; - object scope = FieldReader.ReadField(dynamicMethodObj, "m_scope")!; - var tokenList = FieldReader.ReadField>(scope, "m_tokens")!; - byte[] localSig = FieldReader.ReadField(dynamicMethodObj, "m_localSignature")!; - byte[] ehHeader = FieldReader.ReadField(dynamicMethodObj, "m_exceptionHeader")!; - var ehInfos = FieldReader.ReadField>(dynamicMethodObj, "m_exceptions")!; - - //Local Variables - DynamicMethodHelper.ReadLocalVariables(result, method, localSig); - - // Read raw instructions. - var reader = ByteArrayDataSource.CreateReader(code); - var disassembler = new CilDisassembler(reader, new DynamicCilOperandResolver(module, result, tokenList)); - result.Instructions.AddRange(disassembler.ReadInstructions()); - - //Exception Handlers - DynamicMethodHelper.ReadReflectionExceptionHandlers(result, ehInfos, ehHeader, new ReferenceImporter(module)); - - return result; - } - /// /// Creates a CIL method body from a raw CIL method body. /// diff --git a/src/AsmResolver.DotNet/DynamicMethodDefinition.cs b/src/AsmResolver.DotNet/DynamicMethodDefinition.cs deleted file mode 100644 index b95d8f003..000000000 --- a/src/AsmResolver.DotNet/DynamicMethodDefinition.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Reflection; -using AsmResolver.DotNet.Code.Cil; -using AsmResolver.DotNet.Signatures; -using AsmResolver.DotNet.Signatures.Types; -using AsmResolver.PE.DotNet.Metadata.Tables; -using MethodAttributes = AsmResolver.PE.DotNet.Metadata.Tables.Rows.MethodAttributes; - -namespace AsmResolver.DotNet -{ - /// - /// Represents a single method in a type definition of a .NET module. - /// - public class DynamicMethodDefinition : MethodDefinition - { - /// - /// Create a Dynamic Method Definition - /// - /// Target Module - /// Dynamic Method / Delegate / DynamicResolver - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls ResolveDynamicResolver and FromDynamicMethod")] - public DynamicMethodDefinition(ModuleDefinition module,object dynamicMethodObj) : - base(new MetadataToken(TableIndex.Method, 0)) - { - dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj); - var methodBase = FieldReader.ReadField(dynamicMethodObj, "m_method"); - if (methodBase is null) - { - throw new ArgumentException( - "Could not get the underlying method base in the provided dynamic method object."); - } - - Module = module; - Name = methodBase.Name; - Attributes = (MethodAttributes)methodBase.Attributes; - Signature = new ReferenceImporter(module).ImportMethodSignature(ResolveSig(methodBase,module)); - CilMethodBody = CilMethodBody.FromDynamicMethod(this,dynamicMethodObj); - } - - private MethodSignature ResolveSig(MethodBase methodBase,ModuleDefinition module) - { - var imp = new ReferenceImporter(module); - var returnType = methodBase is MethodInfo info - ? imp.ImportTypeSignature(info.ReturnType) - : module.CorLibTypeFactory.Void; - - var parameters = methodBase.GetParameters(); - - var parameterTypes = new TypeSignature[parameters.Length]; - for (int i = 0; i < parameterTypes.Length; i++) - parameterTypes[i] = imp.ImportTypeSignature(parameters[i].ParameterType); - - return new MethodSignature( - methodBase.IsStatic ? 0 : CallingConventionAttributes.HasThis, - returnType, parameterTypes); - } - - /// - public override ModuleDefinition Module { get; } - - } -} diff --git a/src/AsmResolver.DotNet/ReferenceImporter.cs b/src/AsmResolver.DotNet/ReferenceImporter.cs index 33d904554..65f1061cd 100644 --- a/src/AsmResolver.DotNet/ReferenceImporter.cs +++ b/src/AsmResolver.DotNet/ReferenceImporter.cs @@ -492,6 +492,7 @@ public virtual MethodSpecification ImportMethod(MethodSpecification method) /// /// The method to import. /// The imported method. + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls System.Reflection.Module.ResolveMethod(int)")] public virtual IMethodDescriptor ImportMethod(MethodBase method) { if (method is null) @@ -522,6 +523,7 @@ public virtual IMethodDescriptor ImportMethod(MethodBase method) return new MemberReference(ImportType(method.DeclaringType), method.Name, result); } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls AsmResolver.DotNet.ReferenceImporter.ImportMethod(System.Reflection.MethodBase)")] private IMethodDescriptor ImportGenericMethod(MethodInfo method) { var memberRef = (IMethodDefOrRef) ImportMethod(method.GetGenericMethodDefinition()); @@ -576,6 +578,7 @@ public FieldSignature ImportFieldSignature(FieldSignature signature) /// The field to import. /// The imported field. /// Occurs when a field is not added to a type. + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Calls System.Reflection.Module.ResolveField(int)")] public MemberReference ImportField(FieldInfo field) { if (field is null) diff --git a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs index 389411e30..7eb81bfbd 100644 --- a/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs +++ b/src/AsmResolver.DotNet/Signatures/Types/TypeSignature.cs @@ -14,18 +14,6 @@ public abstract class TypeSignature : ExtendableBlobSignature, ITypeDescriptor { internal const string NullTypeToString = "<>"; - private static readonly MethodInfo GetTypeFromHandleUnsafeMethod; - - static TypeSignature() - { - GetTypeFromHandleUnsafeMethod = typeof(Type) - .GetMethod("GetTypeFromHandleUnsafe", - (BindingFlags) (-1), - null, - new[] {typeof(IntPtr)}, - null)!; - } - /// public abstract string? Name { @@ -156,19 +144,9 @@ public static TypeSignature FromReader(in BlobReadContext context, ref BinaryStr return new BoxedTypeSignature(FromReader(context, ref reader)); case ElementType.Internal: - 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 asmResType = clrType is not null - ? new ReferenceImporter(context.ReaderContext.ParentModule).ImportType(clrType) - : InvalidTypeDefOrRef.Get(InvalidTypeSignatureError.IllegalTypeSpec); - - return new TypeDefOrRefSignature(asmResType); + throw new NotSupportedException( + "Encountered an COR_ELEMENT_TYPE_INTERNAL type signature which is not supported by this " + + " type signature reader. Use the AsmResolver.DotNet.Dynamic extension package instead."); default: throw new ArgumentOutOfRangeException($"Invalid or unsupported element type {elementType}."); diff --git a/src/AsmResolver/IO/BinaryStreamReader.cs b/src/AsmResolver/IO/BinaryStreamReader.cs index 9de2fd993..d0a101c3d 100644 --- a/src/AsmResolver/IO/BinaryStreamReader.cs +++ b/src/AsmResolver/IO/BinaryStreamReader.cs @@ -134,6 +134,10 @@ private void AssertCanRead(uint count) throw new EndOfStreamException(); } + public int PeekByte() => CanRead(1) + ? DataSource[Offset] + : -1; + /// /// Reads a single byte from the input stream, and advances the current offset by one. /// diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj new file mode 100644 index 000000000..9a814bd0f --- /dev/null +++ b/test/AsmResolver.DotNet.Dynamic.Tests/AsmResolver.DotNet.Dynamic.Tests.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + disable + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs b/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs new file mode 100644 index 000000000..7d3f91e09 --- /dev/null +++ b/test/AsmResolver.DotNet.Dynamic.Tests/DynamicMethodDefinitionTest.cs @@ -0,0 +1,76 @@ +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using AsmResolver.DotNet.Signatures.Types; +using AsmResolver.DotNet.TestCases.Methods; +using AsmResolver.PE.DotNet.Cil; +using Xunit; + +namespace AsmResolver.DotNet.Dynamic.Tests +{ + public class DynamicMethodDefinitionTest + { + [Fact] + public void ReadDynamicMethod() + { + var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); + var generatedDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); + var dynamicMethodDef = new DynamicMethodDefinition(module, generatedDynamicMethod); + + Assert.NotNull(dynamicMethodDef); + Assert.NotEmpty(dynamicMethodDef.CilMethodBody!.Instructions); + Assert.Equal(new[] + { + CilCode.Ldarg_0, + CilCode.Stloc_0, + CilCode.Ldloc_0, + CilCode.Call, + CilCode.Ldarg_1, + CilCode.Ret + }, dynamicMethodDef.CilMethodBody.Instructions.Select(q => q.OpCode.Code)); + Assert.Equal(new TypeSignature[] + { + module.CorLibTypeFactory.String, + module.CorLibTypeFactory.Int32 + }, dynamicMethodDef.Parameters.Select(q => q.ParameterType)); + Assert.Equal(new TypeSignature[] + { + module.CorLibTypeFactory.String, + }, dynamicMethodDef.CilMethodBody.LocalVariables.Select(v => v.VariableType)); + } + + [Fact] + public void RtDynamicMethod() + { + var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); + + var generatedDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); + object rtDynamicMethod = generatedDynamicMethod + .GetType() + .GetField("m_dynMethod", (BindingFlags) (-1))? + .GetValue(generatedDynamicMethod); + var dynamicMethod = new DynamicMethodDefinition(module, rtDynamicMethod!); + + Assert.NotNull(dynamicMethod); + Assert.NotEmpty(dynamicMethod.CilMethodBody!.Instructions); + Assert.Equal(new[] + { + CilCode.Ldarg_0, + CilCode.Stloc_0, + CilCode.Ldloc_0, + CilCode.Call, + CilCode.Ldarg_1, + CilCode.Ret + }, dynamicMethod.CilMethodBody.Instructions.Select(q => q.OpCode.Code)); + Assert.Equal(new TypeSignature[] + { + module.CorLibTypeFactory.String, + module.CorLibTypeFactory.Int32 + }, dynamicMethod.Parameters.Select(q => q.ParameterType)); + Assert.Equal(new TypeSignature[] + { + module.CorLibTypeFactory.String, + }, dynamicMethod.CilMethodBody.LocalVariables.Select(v => v.VariableType)); + } + } +} diff --git a/test/AsmResolver.DotNet.Tests/Code/Cil/CilMethodBodyTest.cs b/test/AsmResolver.DotNet.Tests/Code/Cil/CilMethodBodyTest.cs index 8dda5be9e..f13198d60 100644 --- a/test/AsmResolver.DotNet.Tests/Code/Cil/CilMethodBodyTest.cs +++ b/test/AsmResolver.DotNet.Tests/Code/Cil/CilMethodBodyTest.cs @@ -124,33 +124,6 @@ public void ReadFatMethodWithExceptionHandler() Assert.Single(body.ExceptionHandlers); } - [Fact] - public void ReadDynamicMethod() - { - var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); - - var type = module.TopLevelTypes.First(t => t.Name == nameof(TDynamicMethod)); - - var method = type.Methods.FirstOrDefault(m => m.Name == nameof(TDynamicMethod.GenerateDynamicMethod)); - - DynamicMethod generateDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); - - //Dynamic method => CilMethodBody - var body = CilMethodBody.FromDynamicMethod(method, generateDynamicMethod); - - Assert.NotNull(body); - - Assert.NotEmpty(body.Instructions); - - Assert.Equal(body.Instructions.Select(q=>q.OpCode),new CilOpCode[] - { - CilOpCodes.Ldarg_0, - CilOpCodes.Call, - CilOpCodes.Ldarg_1, - CilOpCodes.Ret - }); - } - private static CilMethodBody CreateDummyBody(bool isVoid) { var module = new ModuleDefinition("DummyModule"); diff --git a/test/AsmResolver.DotNet.Tests/DynamicMethodDefinitionTest.cs b/test/AsmResolver.DotNet.Tests/DynamicMethodDefinitionTest.cs deleted file mode 100644 index f8f203513..000000000 --- a/test/AsmResolver.DotNet.Tests/DynamicMethodDefinitionTest.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using AsmResolver.DotNet.Signatures.Types; -using AsmResolver.DotNet.TestCases.Methods; -using AsmResolver.PE.DotNet.Cil; -using Xunit; - -namespace AsmResolver.DotNet.Tests -{ - public class DynamicMethodDefinitionTest - { - [Fact] - public void ReadDynamicMethod() - { - var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); - - DynamicMethod generateDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); - - var dynamicMethodDefinition = new DynamicMethodDefinition(module, generateDynamicMethod); - - Assert.NotNull(dynamicMethodDefinition); - - Assert.NotEmpty(dynamicMethodDefinition.CilMethodBody.Instructions); - - Assert.Equal(dynamicMethodDefinition.CilMethodBody.Instructions.Select(q=>q.OpCode),new [] - { - CilOpCodes.Ldarg_0, - CilOpCodes.Call, - CilOpCodes.Ldarg_1, - CilOpCodes.Ret - }); - Assert.Equal(dynamicMethodDefinition.Parameters.Select(q=>q.ParameterType),new TypeSignature[] - { - module.CorLibTypeFactory.String, - module.CorLibTypeFactory.Int32 - }); - } - - [Fact] - public void RtDynamicMethod() - { - var module = ModuleDefinition.FromFile(typeof(TDynamicMethod).Assembly.Location); - - DynamicMethod generateDynamicMethod = TDynamicMethod.GenerateDynamicMethod(); - - var rtDynamicMethod = generateDynamicMethod.GetType().GetField("m_dynMethod", (BindingFlags) (-1))?.GetValue(generateDynamicMethod); - - var dynamicMethodDefinition = new DynamicMethodDefinition(module, rtDynamicMethod); - - Assert.NotNull(dynamicMethodDefinition); - - Assert.NotEmpty(dynamicMethodDefinition.CilMethodBody.Instructions); - - Assert.Equal(dynamicMethodDefinition.CilMethodBody.Instructions.Select(q=>q.OpCode),new [] - { - CilOpCodes.Ldarg_0, - CilOpCodes.Call, - CilOpCodes.Ldarg_1, - CilOpCodes.Ret - }); - Assert.Equal(dynamicMethodDefinition.Parameters.Select(q=>q.ParameterType),new TypeSignature[] - { - module.CorLibTypeFactory.String, - module.CorLibTypeFactory.Int32 - }); - } - } -} \ No newline at end of file diff --git a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Methods/TDynamicMethod.cs b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Methods/TDynamicMethod.cs index 3cd849478..9890d227b 100644 --- a/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Methods/TDynamicMethod.cs +++ b/test/TestBinaries/DotNet/AsmResolver.DotNet.TestCases.Methods/TDynamicMethod.cs @@ -14,7 +14,7 @@ public static DynamicMethod GenerateDynamicMethod() // of Integer, and two parameters whose types are specified by // the array helloArgs. Create the method in the module that // defines the String class. - DynamicMethod hello = new DynamicMethod("Hello", + var hello = new DynamicMethod("Hello", typeof(int), helloArgs, typeof(string).Module); @@ -24,15 +24,18 @@ public static DynamicMethod GenerateDynamicMethod() Type[] writeStringArgs = {typeof(string)}; // Get the overload of Console.WriteLine that has one // String parameter. - MethodInfo writeString = typeof(Console).GetMethod("WriteLine", + var writeString = typeof(Console).GetMethod("WriteLine", writeStringArgs); // Get an ILGenerator and emit a body for the dynamic method, // using a stream size larger than the IL that will be // emitted. - ILGenerator il = hello.GetILGenerator(256); + var il = hello.GetILGenerator(256); + il.DeclareLocal(typeof(string)); // Load the first argument, which is a string, onto the stack. il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); // Call the overload of Console.WriteLine that prints a string. il.EmitCall(OpCodes.Call, writeString, null); // The Hello method returns the value of the second argument; @@ -50,4 +53,4 @@ public static DynamicMethod GenerateDynamicMethod() } } -} \ No newline at end of file +}