Skip to content

Commit

Permalink
Merge pull request #304 from ds5678/TrimmableDotNetProject
Browse files Browse the repository at this point in the history
[Feature]: Trimmable DotNet Project
  • Loading branch information
Washi1337 authored Aug 25, 2022
2 parents 64d4946 + 558cff9 commit 9f67079
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 241 deletions.
30 changes: 30 additions & 0 deletions AsmResolver.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
32 changes: 32 additions & 0 deletions src/AsmResolver.DotNet.Dynamic/AsmResolver.DotNet.Dynamic.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Title>AsmResolver.DotNet.Dynamic</Title>
<Description>Dynamic method support for the AsmResolver executable file inspection toolsuite.</Description>
<PackageTags>exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly dynamic</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NoWarn>1701;1702;NU5105</NoWarn>
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\AsmResolver.DotNet.xml</DocumentationFile>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DocumentationFile>bin\Release\AsmResolver.DotNet.xml</DocumentationFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\AsmResolver.DotNet\AsmResolver.DotNet.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Provides an implementation of <see cref="ICilOperandResolver"/> that resolves operands based on
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}

Expand All @@ -104,7 +105,7 @@ public DynamicCilOperandResolver(SerializedModuleDefinition contextModule, CilMe
/// <inheritdoc />
public override object? ResolveString(MetadataToken token)
{
return _tokens[(int) token.Rid] as string;
return _tokens[(int)token.Rid] as string;
}
}
}
100 changes: 100 additions & 0 deletions src/AsmResolver.DotNet.Dynamic/DynamicMethodDefinition.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Represents a single method in a type definition of a .NET module.
/// </summary>
public class DynamicMethodDefinition : MethodDefinition
{
/// <summary>
/// Create a Dynamic Method Definition
/// </summary>
/// <param name="module">Target Module</param>
/// <param name="dynamicMethodObj">Dynamic Method / Delegate / DynamicResolver</param>
public DynamicMethodDefinition(ModuleDefinition module, object dynamicMethodObj) :
base(new MetadataToken(TableIndex.Method, 0))
{
dynamicMethodObj = DynamicMethodHelper.ResolveDynamicResolver(dynamicMethodObj);
var methodBase = FieldReader.ReadField<MethodBase>(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);
}

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

/// <summary>
/// Creates a CIL method body from a dynamic method.
/// </summary>
/// <param name="method">The method that owns the method body.</param>
/// <param name="dynamicMethodObj">The Dynamic Method/Delegate/DynamicResolver.</param>
/// <returns>The method body.</returns>
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<byte[]>(dynamicMethodObj, "m_code")!;
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")!;

//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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
Expand All @@ -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<object>? 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);
Expand All @@ -61,7 +120,7 @@ private static void InterpretEHInfo(CilMethodBody methodBody, ReferenceImporter
int handlerStart = FieldReader.ReadField<int[]>(ehInfo, "m_catchAddr")![i];
int handlerEnd = FieldReader.ReadField<int[]>(ehInfo, "m_catchEndAddr")![i];
var exceptionType = FieldReader.ReadField<Type?[]>(ehInfo, "m_catchClass")![i];
var handlerType = (CilExceptionHandlerType) FieldReader.ReadField<int[]>(ehInfo, "m_type")![i];
var handlerType = (CilExceptionHandlerType)FieldReader.ReadField<int[]>(ehInfo, "m_type")![i];

var endTryLabel = instructions.GetByOffset(tryEnd)?.CreateLabel() ?? new CilOffsetLabel(tryEnd);

Expand All @@ -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
Expand Down Expand Up @@ -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)!;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Reflection;
using System.Reflection;

namespace AsmResolver.DotNet
namespace AsmResolver.DotNet.Dynamic
{
internal static class FieldReader
{
Expand Down
1 change: 1 addition & 0 deletions src/AsmResolver.DotNet/AsmResolver.DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<NoWarn>1701;1702;NU5105</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0</TargetFrameworks>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
Expand Down
Loading

0 comments on commit 9f67079

Please sign in to comment.