Skip to content

Commit

Permalink
Merge pull request #262 from Washi1337/development
Browse files Browse the repository at this point in the history
4.9.0
  • Loading branch information
Washi1337 committed Mar 11, 2022
2 parents 84cb170 + c5a61f6 commit 4ac45c3
Show file tree
Hide file tree
Showing 168 changed files with 2,213 additions and 767 deletions.
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "nuget" # See documentation for possible values
target-branch: "development"
directory: "/" # Location of package manifests
schedule:
interval: "daily"

- package-ecosystem: "github-actions"
target-branch: "development"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "daily"
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project>

<PropertyGroup>
<Copyright>Copyright © Washi 2016-2021</Copyright>
<Copyright>Copyright © Washi 2016-2022</Copyright>
<PackageProjectUrl>https://github.com/Washi1337/AsmResolver</PackageProjectUrl>
<PackageLicense>https://github.com/Washi1337/AsmResolver/LICENSE.md</PackageLicense>
<RepositoryUrl>https://github.com/Washi1337/AsmResolver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<LangVersion>9</LangVersion>
<Version>4.8.0</Version>
<LangVersion>10</LangVersion>
<Version>4.9.0</Version>
</PropertyGroup>

</Project>
8 changes: 4 additions & 4 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
only:
- master

image: Visual Studio 2019
version: 4.8.0-master-build.{build}
image: Visual Studio 2022
version: 4.9.0-master-build.{build}
configuration: Release

skip_commits:
Expand Down Expand Up @@ -32,8 +32,8 @@
only:
- development

image: Visual Studio 2019
version: 4.8.0-dev-build.{build}
image: Visual Studio 2022
version: 4.9.0-dev-build.{build}
configuration: Release

skip_commits:
Expand Down
71 changes: 54 additions & 17 deletions docs/dotnet/managed-method-bodies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,22 @@ By default, depending on the value of ``OpCode.OperandType``, ``Operand`` contai
+----------------------------------------+----------------------------------------------+

.. warning::

Providing an incorrect operand type will result in the CIL assembler to fail assembling the method body upon writing the module to the disk.

Creating a new instruction can be done using one of the constructors, together with the ``CilOpCodes`` static class:

.. code-block:: csharp
.. code-block:: csharp
body.Instructions.AddRange(new[]
body.Instructions.AddRange(new[]
{
new CilInstruction(CilOpCodes.Ldstr, "Hello, World!"),
new CilInstruction(CilOpCodes.Ret),
});
However, the preferred way of adding instructions to add or insert new instructions is to use one of the ``Add`` or ``Insert`` overloads that directly take an opcode and operand. This is because it avoids an allocation of an array, and the overloads perform immediate validation on the created instruction.

.. code-block:: csharp
.. code-block:: csharp
var instructions = body.Instructions;
instructions.Add(CilOpCodes.Ldstr, "Hello, World!");
Expand All @@ -115,16 +115,16 @@ However, the preferred way of adding instructions to add or insert new instructi
Pushing 32-bit integer constants onto the stack
-----------------------------------------------

In CIL, pushing integer constants onto the stack is done using one of the ``ldc.i4`` instruction variants.
In CIL, pushing integer constants onto the stack is done using one of the ``ldc.i4`` instruction variants.

The recommended way to create such an instruction is not to use the constructor, but instead use the ``CilInstruction.CreateLdcI4(int)`` method instead. This automatically selects the smallest possible opcode possible and sets the operand accordingly:

.. code-block:: csharp
.. code-block:: csharp
CilInstruction push1 = CilInstruction.CreateLdcI4(1); // Returns "ldc.i4.1" macro
CilInstruction pushShort = CilInstruction.CreateLdcI4(123); // Returns "ldc.i4.s 123" macro
CilInstruction pushLarge = CilInstruction.CreateLdcI4(12345678); // Returns "ldc.i4 12345678"
If we want to get the pushed value, we can use the ``CilInstruction.GetLdcI4Constant()`` method. This method works on any of the ``ldc.i4`` variants, including all the macro opcodes that do not explicitly define an operand such as ``ldc.i4.1``.


Expand All @@ -133,7 +133,7 @@ Branching Instructions

Branch instructions are instructions that (might) transfer control to another part of the method body. To reference the instruction to jump to (the branch target), ``ICilLabel`` is used. The easiest way to create such a label is to use the ``CreateLabel()`` function on the instruction to reference:

.. code-block:: csharp
.. code-block:: csharp
CilInstruction targetInstruction = ...
ICilLabel label = targetInstruction.CreateLabel();
Expand All @@ -142,27 +142,27 @@ Branch instructions are instructions that (might) transfer control to another pa
Alternatively, when using the ``Add`` or ``Insert`` overloads, it is possible to use the return value of these overloads.

.. code-block:: csharp
.. code-block:: csharp
var instructions = body.Instructions;
var label = new CilInstructionLabel();
instructions.Add(CilOpCodes.Br, label);
/* ... */
label.Instruction = instruction.Add(CilOpCodes.Ret);
The ``switch`` operation uses a ``IList<ICilLabel>`` instead.

.. note::

When a branching instruction contains a ``null`` label or a label that references an instruction that is not present in the method body, AsmResolver will by default report an exception upon serializing the code stream. This can be disabled by setting ``VerifyLabelsOnBuild`` to ``false``.


Finding instructions by offset
Finding instructions by offset
------------------------------

Instructions stored in a method body are indexed not by offset, but by order of occurrence. If it is required to find an instruction by offset, it is possible to use the ``Instructions.GetByOffset(int)`` method, which performs a binary search (O(log(n))) and is faster than a linear search (O(n)) such as a for loop or using a construction like ``.First(i => i.Offset == offset)`` provided by ``System.Linq``.
Instructions stored in a method body are indexed not by offset, but by order of occurrence. If it is required to find an instruction by offset, it is possible to use the ``Instructions.GetByOffset(int)`` method, which performs a binary search (O(log(n))) and is faster than a linear search (O(n)) such as a for loop or using a construction like ``.First(i => i.Offset == offset)`` provided by ``System.Linq``.

For ``GetByOffset`` to work, it is required that all offsets in the instruction collection are up to date. Recalculating all offsets within an instruction collection can be done through ``Instructions.CalculateOffsets()``.

Expand All @@ -180,12 +180,12 @@ For ``GetByOffset`` to work, it is required that all offsets in the instruction
instruction1 = body.Instructions[index];
Referencing members
Referencing members
-------------------

As specified by the table above, operations such as a ``call`` require a member as operand.

It is important that the member referenced in the operand of such an instruction is imported in the module. This can be done using the ``ReferenceImporter`` class.
It is important that the member referenced in the operand of such an instruction is imported in the module. This can be done using the ``ReferenceImporter`` class.

Below an example on how to use the ``ReferenceImporter`` to emit a call to ``Console::WriteLine(string)`` using reflection:

Expand Down Expand Up @@ -238,7 +238,44 @@ Instructions can be formatted using e.g. an instance of the ``CilInstructionForm
Console.WriteLine(formatter.FormatInstruction(instruction));
Exception handlers
Patching CIL instructions
-------------------------
Instructions can be added or removed using the ``Add``, ``Insert``, ``Remove`` and ``RemoveAt`` methods:
.. code-block:: csharp
body.Instructions.Add(CilOpCodes.Ldstr, "Hello, world!");
body.Instructions.Insert(i, CilOpCodes.Ldc_I4, 1234);
body.Instructions.RemoveAt(i);
... or by using the indexer to replace existing instructions:
.. code-block:: csharp
body.Instructions[i] = new CilInstruction(CilOpCodes.Ret);
Removing or replacing instructions may not always be favourable. The original ``CilInstruction`` object might be used as a reference for a branch target or exception handler boundary. Removing or replacing these ``CilInstruction`` objects would therefore break these kinds of references, rendering the body invalid. Rather than updating all references manually, it may therefore be wiser to reuse the ``CilInstruction`` object and simply modify the ``OpCode`` and ``Operand`` properties instead:
.. code-block:: csharp
body.Instructions[i].OpCode = CilOpCodes.Ldc_I4;
body.Instructions[i].Operand = 1234;
AsmResolver provides a helper function ``ReplaceWith`` that shortens the code into a single line:
.. code-block:: csharp
body.Instructions[i].ReplaceWith(CilOpCodes.Ldc_I4, 1234);
Since it is very common to replace instructions with a `nop`, AsmResolver also defines a special ``ReplaceWithNop`` helper function:
.. code-block:: csharp
body.Instructions[i].ReplaceWithNop();
Exception handlers
------------------
Exception handlers are regions in the method body that are protected from exceptions. In AsmResolver, they are represented by the ``CilExceptionHandler`` class, and define the following properties:
Expand Down Expand Up @@ -267,4 +304,4 @@ The max stack can be computed by using the ``ComputeMaxStack`` method. By defaul
.. note::
If a ``StackImbalanceException`` is thrown upon writing the module to the disk, or upon calling ``ComputeMaxStack``, it means that not all execution paths in the provided CIL code push or pop the expected amount of values. It is a good indication that the provided CIL code is invalid.
If a ``StackImbalanceException`` is thrown upon writing the module to the disk, or upon calling ``ComputeMaxStack``, it means that not all execution paths in the provided CIL code push or pop the expected amount of values. It is a good indication that the provided CIL code is invalid.
34 changes: 34 additions & 0 deletions docs/dotnet/methods.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Methods
===============

Non-Generic Methods on Generic Types
------------------------------------------

This section covers referencing methods such as ``System.Collections.Generic.List`1<System.Int32>.Add``. They can be referenced with the ``MemberReference`` class.

.. code-block:: csharp
var corlibScope = moduleDefinition.CorLibTypeFactory.CorLibScope;
var listTypeReference = new TypeReference(corlibScope, "System.Collections.Generic", "List`1");
var listOfInt32 = listTypeReference.MakeGenericInstanceType(moduleDefinition.CorLibTypeFactory.Int32);
var addMethodDefinition = listTypeReference.Resolve().Methods.Single(m => m.Name == "Add" && m.Parameters.Count == 1);
var reference = new MemberReference(listOfInt32.ToTypeDefOrRef(), addMethodDefinition.Name, addMethodDefinition.Signature);
Generic Methods on Non-Generic Types
------------------------------------------

This section covers referencing methods such as ``System.Array.Empty<System.Int32>``. They can be referenced with the ``MethodSpecification`` class via the ``MakeGenericInstanceMethod`` extension method on ``IMethodDefOrRef``.

.. code-block:: csharp
var corlibScope = moduleDefinition.CorLibTypeFactory.CorLibScope;
var arrayTypeReference = new TypeReference(corlibScope, "System", "Array");
var emptyMethodDefinition = arrayTypeReference.Resolve().Methods.Single(m => m.Name == "Empty" && m.Parameters.Count == 0);
var reference = emptyMethodDefinition.MakeGenericInstanceMethod(moduleDefinition.CorLibTypeFactory.Int32);
3 changes: 2 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ Table of Contents:
dotnet/member-tree
dotnet/type-signatures
dotnet/importing
dotnet/methods
dotnet/managed-method-bodies
dotnet/unmanaged-method-bodies
dotnet/managed-resources
dotnet/cloning
dotnet/token-allocation
dotnet/type-memory-layout
dotnet/advanced-pe-image-building.rst
dotnet/advanced-pe-image-building.rst
7 changes: 3 additions & 4 deletions src/AsmResolver.DotNet/AsmResolver.DotNet.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Title>AsmResolver.DotNet</Title>
<Description>High level .NET image models for the AsmResolver executable file inspection toolsuite.</Description>
<PackageTags>exe pe directories imports exports resources dotnet cil inspection manipulation assembly disassembly</PackageTags>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NoWarn>1701;1702;NU5105</NoWarn>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
<TargetFrameworks>net6.0;netcoreapp3.1;netstandard2.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
Expand All @@ -28,7 +27,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="5.0.0" />
<PackageReference Include="System.Text.Json" Version="6.0.2" />
</ItemGroup>

</Project>
11 changes: 7 additions & 4 deletions src/AsmResolver.DotNet/AssemblyDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,14 @@ public void Write(string filePath, IPEImageBuilder imageBuilder, IPEFileBuilder
if (directory is null || !Directory.Exists(directory))
throw new DirectoryNotFoundException();

foreach (var module in Modules)
for (int i = 0; i < Modules.Count; i++)
{
string modulePath = module == ManifestModule
? filePath
: Path.Combine(directory, module.Name);
var module = Modules[i];
string modulePath;
if (module == ManifestModule)
modulePath = filePath;
else
modulePath = Path.Combine(directory, module.Name ?? $"module{i}.bin");

module.Write(modulePath, imageBuilder, fileBuilder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ private FieldDefinition AddPlaceHolderField(TypeDefinition placeHolderType, Meta
var placeHolderField = new FieldDefinition(
$"PlaceHolderField_{token.Rid.ToString()}",
FieldPlaceHolderAttributes,
FieldSignature.CreateStatic(_module.CorLibTypeFactory.Object));
_module.CorLibTypeFactory.Object);

// Add the field to the type.
placeHolderType.Fields.Add(placeHolderField);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ public void FinalizeTypes()
for (uint rid = 1; rid <= typeDefTable.Count; rid++)
{
var typeToken = new MetadataToken(TableIndex.TypeDef, rid);
var type = _tokenMapping.GetTypeByToken(typeToken);
var type = _tokenMapping.GetTypeByToken(typeToken)!;

// Update extends, field list and method list columns.
ref var typeRow = ref typeDefTable.GetRowRef(rid);
Expand Down Expand Up @@ -409,7 +409,7 @@ private void FinalizeMethods(ref bool paramPtrRequired)
for (uint rid = 1; rid <= definitionTable.Count; rid++)
{
var newToken = new MetadataToken(TableIndex.Method, rid);
var method = _tokenMapping.GetMethodByToken(newToken);
var method = _tokenMapping.GetMethodByToken(newToken)!;

// Serialize method body and update column.
ref var row = ref definitionTable.GetRowRef(rid);
Expand Down
Loading

0 comments on commit 4ac45c3

Please sign in to comment.