From 45bb35eb86dad9204b2b67a6b8b3e42ac28ff816 Mon Sep 17 00:00:00 2001
From: Smaug123 <3138005+Smaug123@users.noreply.github.com>
Date: Thu, 20 Feb 2025 10:22:56 +0000
Subject: [PATCH] Progress
---
.github/workflows/dotnet.yaml | 98 ++++++++++++++++++++++++++++
Directory.Build.props | 19 ++++++
WoofWare.PawPrint/AbstractMachine.fs | 42 +++++++++++-
WoofWare.PawPrint/Assembly.fs | 25 +++++++
WoofWare.PawPrint/BitTwiddling.fs | 6 ++
WoofWare.PawPrint/IlOp.fs | 8 ++-
WoofWare.PawPrint/TypeInfo.fs | 22 +++----
nix/deps.json | 10 +++
8 files changed, 216 insertions(+), 14 deletions(-)
create mode 100644 .github/workflows/dotnet.yaml
create mode 100644 Directory.Build.props
diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml
new file mode 100644
index 0000000..ec4ac6a
--- /dev/null
+++ b/.github/workflows/dotnet.yaml
@@ -0,0 +1,98 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
+name: .NET
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+env:
+ DOTNET_NOLOGO: true
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ NUGET_XMLDOC_MODE: ''
+ DOTNET_MULTILEVEL_LOOKUP: 0
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
+ - name: Install Nix
+ uses: cachix/install-nix-action@v30
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Restore dependencies
+ run: nix develop --command dotnet restore
+ - name: Build
+ run: nix develop --command dotnet build --no-restore --configuration Release
+ - name: Test
+ run: nix develop --command dotnet test --no-build --verbosity normal --configuration Release
+
+ build-nix:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install Nix
+ uses: cachix/install-nix-action@v30
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Build
+ run: nix build
+ - name: Reproducibility check
+ run: nix build --rebuild
+
+ check-dotnet-format:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install Nix
+ uses: cachix/install-nix-action@v30
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Run Fantomas
+ run: nix run .#fantomas -- --check .
+
+ check-nix-format:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Install Nix
+ uses: cachix/install-nix-action@v30
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Run Alejandra
+ run: nix develop --command alejandra --check .
+
+ flake-check:
+ name: Check flake
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - name: Install Nix
+ uses: cachix/install-nix-action@v30
+ with:
+ extra_nix_config: |
+ access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
+ - name: Flake check
+ run: nix flake check
+
+ all-required-checks-complete:
+ if: ${{ always() }}
+ needs: [check-dotnet-format, check-nix-format, build, build-nix, flake-check]
+ runs-on: ubuntu-latest
+ steps:
+ - uses: G-Research/common-actions/check-required-lite@2b7dc49cb14f3344fbe6019c14a31165e258c059
+ with:
+ needs-context: ${{ toJSON(needs) }}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..19a68f4
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,19 @@
+
+
+ embedded
+ true
+ [UNDEFINED]
+ true
+ true
+ true
+ embedded
+ FS3388,FS3559
+
+
+
+
+
+
+ true
+
+
diff --git a/WoofWare.PawPrint/AbstractMachine.fs b/WoofWare.PawPrint/AbstractMachine.fs
index 9327064..037b187 100644
--- a/WoofWare.PawPrint/AbstractMachine.fs
+++ b/WoofWare.PawPrint/AbstractMachine.fs
@@ -1,6 +1,8 @@
namespace WoofWare.PawPrint
+open System
open System.Collections.Immutable
+open System.Reflection.Metadata
open Microsoft.FSharp.Core
type ThreadId = | ThreadId of int
@@ -376,11 +378,49 @@ module AbstractMachine =
| Rethrow -> failwith "todo"
| Throw -> failwith "todo"
+ let private executeUnaryMetadata
+ (op : UnaryMetadataTokenIlOp)
+ (metadataToken : MetadataToken)
+ (state : IlMachineState)
+ (dumped : DumpedAssembly)
+ (thread : ThreadId)
+ : IlMachineState
+ =
+ match op with
+ | Call ->
+ let handle =
+ match metadataToken.Kind with
+ | HandleKind.MethodSpecification -> MethodSpecificationHandle.op_Explicit metadataToken
+ | k -> failwith $"Unrecognised kind: %O{k}"
+
+ let method =
+ dumped.Methods.[MethodDefinitionHandle.op_Explicit dumped.MethodSpecs.[handle].Method]
+
+ failwith "TODO: now do this!"
+ state
+ | Callvirt -> failwith "todo"
+ | Castclass -> failwith "todo"
+ | Newobj -> failwith "todo"
+ | Newarr -> failwith "todo"
+ | Box -> failwith "todo"
+ | Ldelema -> failwith "todo"
+ | Isinst -> failwith "todo"
+ | Stfld -> failwith "todo"
+ | Stsfld -> failwith "todo"
+ | Ldfld -> failwith "todo"
+ | Ldflda -> failwith "todo"
+ | Ldsfld -> failwith "todo"
+ | Unbox_Any -> failwith "todo"
+ | Stelem -> failwith "todo"
+ | Ldelem -> failwith "todo"
+
let executeOneStep (state : IlMachineState) (dumped : DumpedAssembly) (thread : ThreadId) : IlMachineState =
let instruction = state.ThreadState.[thread].MethodState
match instruction.ExecutingMethod.Locations.[instruction.IlOpIndex] with
| IlOp.Nullary op -> executeNullary state thread dumped op
| UnaryConst unaryConstIlOp -> failwith "todo"
- | UnaryMetadataToken (unaryMetadataTokenIlOp, bytes) -> failwith "todo"
+ | UnaryMetadataToken (unaryMetadataTokenIlOp, bytes) ->
+ executeUnaryMetadata unaryMetadataTokenIlOp bytes state dumped thread
| Switch immutableArray -> failwith "todo"
+ | UnaryStringToken (unaryStringTokenIlOp, stringHandle) -> failwith "todo"
diff --git a/WoofWare.PawPrint/Assembly.fs b/WoofWare.PawPrint/Assembly.fs
index d7013b3..73dc9b5 100644
--- a/WoofWare.PawPrint/Assembly.fs
+++ b/WoofWare.PawPrint/Assembly.fs
@@ -14,6 +14,9 @@ type DumpedAssembly =
Types : TypeInfo list
Methods : IReadOnlyDictionary
MainMethod : MethodDefinitionHandle
+ /// Map of four-byte int token to metadata
+ MethodDefinitions : Map
+ MethodSpecs : ImmutableDictionary
}
[]
@@ -38,10 +41,32 @@ module Assembly =
|> List.collect (fun ty -> ty.Methods |> List.map (fun mi -> KeyValuePair (mi.Handle, mi)))
|> ImmutableDictionary.CreateRange
+ let methodDefnMetadata =
+ metadataReader.MethodDefinitions
+ |> Seq.map (fun mh ->
+ let def = metadataReader.GetMethodDefinition mh
+ let eh : EntityHandle = MethodDefinitionHandle.op_Implicit mh
+ let token = MetadataTokens.GetToken eh
+ token, def
+ )
+ |> Map.ofSeq
+
+ let methodSpecs =
+ Seq.init
+ (metadataReader.GetTableRowCount TableIndex.MethodSpec)
+ (fun i ->
+ let i = i + 1
+ let handle = MetadataTokens.MethodSpecificationHandle i
+ KeyValuePair (handle, metadataReader.GetMethodSpecification handle)
+ )
+ |> ImmutableDictionary.CreateRange
+
{
Types = result
MainMethod = entryPointMethod
Methods = methods
+ MethodDefinitions = methodDefnMetadata
+ MethodSpecs = methodSpecs
}
let print (main : MethodDefinitionHandle) (dumped : DumpedAssembly) : unit =
diff --git a/WoofWare.PawPrint/BitTwiddling.fs b/WoofWare.PawPrint/BitTwiddling.fs
index 4594815..dcbb2c6 100644
--- a/WoofWare.PawPrint/BitTwiddling.fs
+++ b/WoofWare.PawPrint/BitTwiddling.fs
@@ -17,3 +17,9 @@ module internal BitTwiddling =
let inline toUint64 (bytes : ReadOnlySpan) : uint64 =
uint64 (toUint32 (bytes.Slice (0, 4)))
+ 0x10000UL * uint64 (toUint32 (bytes.Slice (4, 4)))
+
+ let inline toInt32 (bytes : ReadOnlySpan) : int32 =
+ int32 bytes.[0]
+ + int32 bytes.[1] * 256
+ + int32 bytes.[2] * 256 * 256
+ + int32 bytes.[3] * 256 * 256 * 256
diff --git a/WoofWare.PawPrint/IlOp.fs b/WoofWare.PawPrint/IlOp.fs
index b8766f4..8bb053e 100644
--- a/WoofWare.PawPrint/IlOp.fs
+++ b/WoofWare.PawPrint/IlOp.fs
@@ -1,6 +1,7 @@
namespace WoofWare.PawPrint
open System.Collections.Immutable
+open System.Reflection.Metadata
type NullaryIlOp =
| Nop
@@ -121,18 +122,21 @@ type UnaryMetadataTokenIlOp =
| Ldfld
| Ldflda
| Ldsfld
- | Ldstr
| Unbox_Any
| Stelem
| Ldelem
+type UnaryStringTokenIlOp = | Ldstr
+
/// A four-byte metadata token.
-type MetadataToken = byte[]
+type MetadataToken = EntityHandle
+type StringToken = StringHandle
type IlOp =
| Nullary of NullaryIlOp
| UnaryConst of UnaryConstIlOp
| UnaryMetadataToken of UnaryMetadataTokenIlOp * MetadataToken
+ | UnaryStringToken of UnaryStringTokenIlOp * StringToken
| Switch of int32 ImmutableArray
static member Format (opCode : IlOp) (offset : int) : string = $" IL_%04X{offset}: %-20O{opCode}"
diff --git a/WoofWare.PawPrint/TypeInfo.fs b/WoofWare.PawPrint/TypeInfo.fs
index 3f7cd52..707cd30 100644
--- a/WoofWare.PawPrint/TypeInfo.fs
+++ b/WoofWare.PawPrint/TypeInfo.fs
@@ -131,18 +131,16 @@ module TypeInfo =
LanguagePrimitives.EnumOfValue (uint16 op)
let private readMetadataToken (reader : byref) : MetadataToken =
- [|
- reader.ReadByte ()
- reader.ReadByte ()
- reader.ReadByte ()
- reader.ReadByte ()
- |]
+ reader.ReadUInt32 () |> int |> MetadataTokens.EntityHandle
+
+ let private readStringToken (reader : byref) : StringToken =
+ reader.ReadUInt32 () |> int |> MetadataTokens.StringHandle
let private readMethodBody (peReader : PEReader) (methodDef : MethodDefinition) : (IlOp * int) list =
if methodDef.RelativeVirtualAddress = 0 then
[]
else
- let methodBody = peReader.GetMethodBody (methodDef.RelativeVirtualAddress)
+ let methodBody = peReader.GetMethodBody methodDef.RelativeVirtualAddress
let ilBytes = methodBody.GetILBytes ()
use bytes = fixed ilBytes
let mutable reader : BlobReader = BlobReader (bytes, ilBytes.Length)
@@ -283,8 +281,7 @@ module TypeInfo =
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Callvirt, readMetadataToken &reader)
| ILOpCode.Cpobj -> failwith "todo"
| ILOpCode.Ldobj -> failwith "todo"
- | ILOpCode.Ldstr ->
- IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Ldstr, readMetadataToken &reader)
+ | ILOpCode.Ldstr -> IlOp.UnaryStringToken (UnaryStringTokenIlOp.Ldstr, readStringToken &reader)
| ILOpCode.Newobj ->
IlOp.UnaryMetadataToken (UnaryMetadataTokenIlOp.Newobj, readMetadataToken &reader)
| ILOpCode.Castclass ->
@@ -463,14 +460,17 @@ module TypeInfo =
(reader : MetadataReader, handle : TypeDefinitionHandle, rawTypeKind : byte)
: TypeDefn
=
+ let handle : EntityHandle = TypeDefinitionHandle.op_Implicit handle
let typeKind = reader.ResolveSignatureTypeKind (handle, rawTypeKind)
+
TypeDefn.FromDefinition typeKind
member this.GetTypeFromReference
- (reader : MetadataReader, foo : TypeReferenceHandle, rawTypeKind : byte)
+ (reader : MetadataReader, handle : TypeReferenceHandle, rawTypeKind : byte)
: TypeDefn
=
- let typeKind = reader.ResolveSignatureTypeKind (foo, rawTypeKind)
+ let handle : EntityHandle = TypeReferenceHandle.op_Implicit handle
+ let typeKind = reader.ResolveSignatureTypeKind (handle, rawTypeKind)
TypeDefn.FromReference typeKind
member this.GetPointerType (typeCode : TypeDefn) : TypeDefn = TypeDefn.Pointer typeCode
diff --git a/nix/deps.json b/nix/deps.json
index 97254dc..f752ab5 100644
--- a/nix/deps.json
+++ b/nix/deps.json
@@ -14,6 +14,11 @@
"version": "5.0.2",
"hash": "sha256-YOoosLEiszPsOOaNAkWhFGU04JJKDOFVoA/ggrZMN10="
},
+ {
+ "pname": "FSharp.Core",
+ "version": "9.0.101",
+ "hash": "sha256-bR4PHanvKrzD43qFQxmOmmhhpz+ZmKZMPlgGnlRNcp4="
+ },
{
"pname": "FsUnit",
"version": "7.0.1",
@@ -144,6 +149,11 @@
"version": "17.13.0",
"hash": "sha256-L/CJzou7dhmShUgXq3aXL3CaLTJll17Q+JY2DBdUUpo="
},
+ {
+ "pname": "Nerdbank.GitVersioning",
+ "version": "3.7.115",
+ "hash": "sha256-sqn+i7vvBgBUtm7j82mH+SpApgI2hsmL5DYfLm1Z7gw="
+ },
{
"pname": "Newtonsoft.Json",
"version": "13.0.1",