Track addresses of arguments (#53)

This commit is contained in:
Patrick Stevens
2025-06-15 21:34:30 +01:00
committed by GitHub
parent 3b1f916743
commit 2c249edfc7
7 changed files with 71 additions and 7 deletions

View File

@@ -389,8 +389,10 @@ type UnaryConstIlOp =
| Blt_un of int32 | Blt_un of int32
| Ldloc_s of uint8 | Ldloc_s of uint8
| Ldloca_s of uint8 | Ldloca_s of uint8
/// Load the address of an argument onto the stack.
| Ldarga of uint16 | Ldarga of uint16
| Ldarg_s of uint8 | Ldarg_s of uint8
/// Load the address of an argument onto the stack.
| Ldarga_s of uint8 | Ldarga_s of uint8
/// Unconditionally transfer control to this offset from the next instruction; /// Unconditionally transfer control to this offset from the next instruction;
/// like Br but can leave a try/filter/catch block too, and ensures surrounding `finally` blocks execute. /// like Br but can leave a try/filter/catch block too, and ensures surrounding `finally` blocks execute.

View File

@@ -72,7 +72,9 @@ type CliValueType =
| Float64 of float | Float64 of float
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
type CliRuntimePointerSource = | LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 type CliRuntimePointerSource =
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
type CliRuntimePointer = type CliRuntimePointer =
| Unmanaged of unit | Unmanaged of unit

View File

@@ -4,6 +4,7 @@ open Microsoft.FSharp.Core
type ManagedPointerSource = type ManagedPointerSource =
| LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16 | LocalVariable of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Argument of sourceThread : ThreadId * methodFrame : int * whichVar : uint16
| Heap of ManagedHeapAddress | Heap of ManagedHeapAddress
| Null | Null
@@ -11,7 +12,10 @@ type ManagedPointerSource =
match this with match this with
| ManagedPointerSource.Null -> "<null pointer>" | ManagedPointerSource.Null -> "<null pointer>"
| ManagedPointerSource.Heap addr -> $"%O{addr}" | ManagedPointerSource.Heap addr -> $"%O{addr}"
| LocalVariable (source, method, var) -> $"<variable %i{var} in method frame %i{method} of thread %O{source}>" | ManagedPointerSource.LocalVariable (source, method, var) ->
$"<variable %i{var} in method frame %i{method} of thread %O{source}>"
| ManagedPointerSource.Argument (source, method, var) ->
$"<argument %i{var} in method frame %i{method} of thread %O{source}>"
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
type NativeIntSource = type NativeIntSource =
@@ -65,7 +69,9 @@ type EvalStackValue =
| EvalStackValue.Float f -> $"Float(%f{f})" | EvalStackValue.Float f -> $"Float(%f{f})"
| EvalStackValue.ManagedPointer managedPointerSource -> $"Pointer(%O{managedPointerSource})" | EvalStackValue.ManagedPointer managedPointerSource -> $"Pointer(%O{managedPointerSource})"
| EvalStackValue.ObjectRef managedHeapAddress -> $"ObjectRef(%O{managedHeapAddress})" | EvalStackValue.ObjectRef managedHeapAddress -> $"ObjectRef(%O{managedHeapAddress})"
| EvalStackValue.UserDefinedValueType evalStackValues -> failwith "todo" | EvalStackValue.UserDefinedValueType evalStackValues ->
let desc = evalStackValues |> List.map string<EvalStackValue> |> String.concat " | "
$"Struct(%s{desc})"
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module EvalStackValue = module EvalStackValue =
@@ -168,6 +174,10 @@ module EvalStackValue =
CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar) CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, whichVar)
|> CliRuntimePointer.Managed |> CliRuntimePointer.Managed
|> CliType.RuntimePointer |> CliType.RuntimePointer
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
CliRuntimePointerSource.Argument (sourceThread, methodFrame, whichVar)
|> CliRuntimePointer.Managed
|> CliType.RuntimePointer
| ManagedPointerSource.Heap managedHeapAddress -> CliType.ObjectRef (Some managedHeapAddress) | ManagedPointerSource.Heap managedHeapAddress -> CliType.ObjectRef (Some managedHeapAddress)
| ManagedPointerSource.Null -> CliType.ObjectRef None | ManagedPointerSource.Null -> CliType.ObjectRef None
| EvalStackValue.NativeInt nativeIntSource -> | EvalStackValue.NativeInt nativeIntSource ->
@@ -175,7 +185,11 @@ module EvalStackValue =
| NativeIntSource.Verbatim 0L -> CliType.ObjectRef None | NativeIntSource.Verbatim 0L -> CliType.ObjectRef None
| NativeIntSource.Verbatim i -> failwith $"refusing to interpret verbatim native int {i} as a pointer" | NativeIntSource.Verbatim i -> failwith $"refusing to interpret verbatim native int {i} as a pointer"
| NativeIntSource.FunctionPointer _ -> failwith "TODO" | NativeIntSource.FunctionPointer _ -> failwith "TODO"
| i -> failwith $"TODO: %O{i}" | EvalStackValue.UserDefinedValueType fields ->
match fields with
| [ esv ] -> toCliTypeCoerced target esv
| fields -> failwith $"TODO: don't know how to coerce struct of {fields} to a pointer"
| _ -> failwith $"TODO: {popped}"
| CliType.Bool _ -> | CliType.Bool _ ->
match popped with match popped with
| EvalStackValue.Int32 i -> | EvalStackValue.Int32 i ->
@@ -194,6 +208,10 @@ module EvalStackValue =
CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var)
|> CliRuntimePointer.Managed |> CliRuntimePointer.Managed
|> CliType.RuntimePointer |> CliType.RuntimePointer
| ManagedPointerSource.Argument (sourceThread, methodFrame, var) ->
CliRuntimePointerSource.Argument (sourceThread, methodFrame, var)
|> CliRuntimePointer.Managed
|> CliType.RuntimePointer
| _ -> failwith $"TODO: %O{popped}" | _ -> failwith $"TODO: %O{popped}"
| CliType.Char _ -> | CliType.Char _ ->
match popped with match popped with
@@ -241,6 +259,9 @@ module EvalStackValue =
| CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) -> | CliRuntimePointerSource.LocalVariable (sourceThread, methodFrame, var) ->
ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var) ManagedPointerSource.LocalVariable (sourceThread, methodFrame, var)
|> EvalStackValue.ManagedPointer |> EvalStackValue.ManagedPointer
| CliRuntimePointerSource.Argument (sourceThread, methodFrame, var) ->
ManagedPointerSource.Argument (sourceThread, methodFrame, var)
|> EvalStackValue.ManagedPointer
| CliType.ValueType fields -> fields |> List.map ofCliType |> EvalStackValue.UserDefinedValueType | CliType.ValueType fields -> fields |> List.map ofCliType |> EvalStackValue.UserDefinedValueType
type EvalStack = type EvalStack =

View File

@@ -83,6 +83,8 @@ module System_Threading_Monitor =
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
state state
|> IlMachineState.setLocalVariable sourceThread methodFrame whichVar (CliType.OfBool true) |> IlMachineState.setLocalVariable sourceThread methodFrame whichVar (CliType.OfBool true)
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
failwith "not really expecting to *edit* an argument..."
| ManagedPointerSource.Heap addr -> failwith "todo: managed heap" | ManagedPointerSource.Heap addr -> failwith "todo: managed heap"
(state, WhatWeDid.Executed) |> ExecutionResult.Stepped (state, WhatWeDid.Executed) |> ExecutionResult.Stepped

View File

@@ -80,6 +80,8 @@ module NullaryIlOp =
| EvalStackValue.ManagedPointer src -> | EvalStackValue.ManagedPointer src ->
match src with match src with
| ManagedPointerSource.Null -> failwith "TODO: throw NullReferenceException" | ManagedPointerSource.Null -> failwith "TODO: throw NullReferenceException"
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
failwith "unexpected - can we really write to an argument?"
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
{ state with { state with
ThreadState = ThreadState =
@@ -668,6 +670,17 @@ module NullaryIlOp =
| EvalStackValue.ManagedPointer src -> | EvalStackValue.ManagedPointer src ->
match src with match src with
| ManagedPointerSource.Null -> failwith "unexpected null pointer in Ldind_u1" | ManagedPointerSource.Null -> failwith "unexpected null pointer in Ldind_u1"
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
let methodState =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
match methodState with
| CliType.Bool b -> b
| CliType.Numeric numeric -> failwith $"tried to load a Numeric as a u8: {numeric}"
| CliType.Char _ -> failwith "tried to load a Char as a u8"
| CliType.ObjectRef _ -> failwith "tried to load an ObjectRef as a u8"
| CliType.RuntimePointer _ -> failwith "tried to load a RuntimePointer as a u8"
| CliType.ValueType cliTypes -> failwith "todo"
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) ->
let methodState = let methodState =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables

View File

@@ -409,9 +409,27 @@ module internal UnaryConstIlOp =
|> IlMachineState.advanceProgramCounter currentThread |> IlMachineState.advanceProgramCounter currentThread
state, WhatWeDid.Executed state, WhatWeDid.Executed
| Ldarga s -> failwith "TODO: Ldarga unimplemented" | Ldarga s ->
let executingMethod = state.ThreadState.[currentThread]
let ptr =
ManagedPointerSource.Argument (currentThread, executingMethod.ActiveMethodState, s)
state
|> IlMachineState.pushToEvalStack' (EvalStackValue.ManagedPointer ptr) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldarga_s b ->
let executingMethod = state.ThreadState.[currentThread]
let ptr =
ManagedPointerSource.Argument (currentThread, executingMethod.ActiveMethodState, uint16<byte> b)
state
|> IlMachineState.pushToEvalStack' (EvalStackValue.ManagedPointer ptr) currentThread
|> IlMachineState.advanceProgramCounter currentThread
|> Tuple.withRight WhatWeDid.Executed
| Ldarg_s b -> failwith "TODO: Ldarg_s unimplemented" | Ldarg_s b -> failwith "TODO: Ldarg_s unimplemented"
| Ldarga_s b -> failwith "TODO: Ldarga_s unimplemented"
| Leave i -> leave currentThread i state | Leave i -> leave currentThread i state
| Leave_s b -> leave currentThread (int<int8> b) state | Leave_s b -> leave currentThread (int<int8> b) state
| Starg_s b -> failwith "TODO: Starg_s unimplemented" | Starg_s b -> failwith "TODO: Starg_s unimplemented"

View File

@@ -408,6 +408,7 @@ module internal UnaryMetadataIlOp =
match source with match source with
| ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException" | ManagedPointerSource.Null -> failwith "TODO: raise NullReferenceException"
| ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo" | ManagedPointerSource.LocalVariable (sourceThread, methodFrame, whichVar) -> failwith "todo"
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) -> failwith "todo"
| ManagedPointerSource.Heap addr -> | ManagedPointerSource.Heap addr ->
match state.ManagedHeap.NonArrayObjects.TryGetValue addr with match state.ManagedHeap.NonArrayObjects.TryGetValue addr with
| false, _ -> failwith $"todo: array {addr}" | false, _ -> failwith $"todo: array {addr}"
@@ -575,7 +576,12 @@ module internal UnaryMetadataIlOp =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables state.ThreadState.[sourceThread].MethodStates.[methodFrame].LocalVariables
.[int<uint16> whichVar] .[int<uint16> whichVar]
failwith $"todo: local variable {currentValue} {field}" IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Argument (sourceThread, methodFrame, whichVar) ->
let currentValue =
state.ThreadState.[sourceThread].MethodStates.[methodFrame].Arguments.[int<uint16> whichVar]
IlMachineState.pushToEvalStack currentValue thread state
| ManagedPointerSource.Heap managedHeapAddress -> | ManagedPointerSource.Heap managedHeapAddress ->
match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with match state.ManagedHeap.NonArrayObjects.TryGetValue managedHeapAddress with
| false, _ -> failwith $"todo: array {managedHeapAddress}" | false, _ -> failwith $"todo: array {managedHeapAddress}"