Skip to content

Commit 7e1a7ce

Browse files
Fix generic inst in locals signature
1 parent 001f2eb commit 7e1a7ce

File tree

5 files changed

+172
-15
lines changed

5 files changed

+172
-15
lines changed

.vscode/launch.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@
88
"name": ".NET Core Attach",
99
"type": "coreclr",
1010
"request": "attach",
11-
"processId": "${command:pickProcess}"
12-
}
11+
"processId": "${command:pickProcess}",
12+
},
13+
{
14+
"name": ".NET Core Attach - non-JMC",
15+
"type": "coreclr",
16+
"request": "attach",
17+
"processId": "${command:pickProcess}",
18+
"justMyCode": false,
19+
},
1320
]
1421
}

ILAssembler.build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ task Clean {
3939
}
4040

4141
task AssertDotNet -If { -not $global:dotnet } {
42-
$requirements = Import-PowerShellDataFile "$PSScriptRoot/requirements"
42+
$requirements = Import-PowerShellDataFile "$PSScriptRoot/requirements.psd1"
4343
$global:dotnet = & "$PSScriptRoot/tools/GetDotNet.ps1" -Version $requirements['DotnetSdk::release'] -Unix:$IsUnix
4444
}
4545

examples/InvokeInlineAssembly.ps1

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using namespace System.ComponentModel
2+
using namespace System.Diagnostics
3+
using namespace System.Runtime.InteropServices
4+
5+
# Windows x64
6+
# int(int* args, int count)
7+
# {
8+
# int total = 0;
9+
# for (int i = 0; i < count; i++)
10+
# {
11+
# total += args[i];
12+
# }
13+
#
14+
# return total;
15+
# }
16+
$assembly = (
17+
0x85, 0xD2, # test edx,edx
18+
0x7E, 0x14, # jle 18 <LBB0_1>
19+
0x41, 0x89, 0xD0, # mov r8d,edx
20+
0x31, 0xD2, # xor edx,edx
21+
0x31, 0xC0, # xor eax,eax
22+
# LBB0_4:
23+
0x03, 0x04, 0x91, # add eax,DWORD PTR [rcx+rdx*4]
24+
0x48, 0x83, 0xC2, 0x01, # add rdx,0x1
25+
0x49, 0x39, 0xD0, # cmp r8,rdx
26+
0x75, 0xF4, # jne b <LBB0_4>
27+
0xC3, # ret
28+
# LBB0_1
29+
0x31, 0xC0, # xor eax,eax
30+
0xC3 # ret
31+
)
32+
33+
$delegate = il { [int]([byte[]] $asm, [int[]] $args) } {
34+
.locals init {
35+
[void+] $virtualProtectEx,
36+
37+
[void+] $getLastError,
38+
39+
[uint] $flOldProtect,
40+
41+
[pinned] [byte[]] $pinnedAsm,
42+
43+
[pinned] [int[]] $pinnedArgs
44+
}
45+
46+
# [DllImport("kernel32")]
47+
# static extern int VirtualProtectEx(void*, void*, int, int, int*)
48+
ldstr 'kernel32'
49+
call { [IntPtr] [NativeLibrary]::Load([string]) }
50+
dup
51+
ldstr 'VirtualProtectEx'
52+
call { [IntPtr] [NativeLibrary]::GetExport([IntPtr], [string]) }
53+
stloc.auto $virtualProtectEx
54+
55+
# [DllImport("kernel32")]
56+
# static extern int GetLastError()
57+
ldstr 'GetLastError'
58+
call { [IntPtr] [NativeLibrary]::GetExport([IntPtr], [string]) }
59+
stloc.auto $getLastError
60+
61+
# pinnedAsm = asm
62+
ldarg.0
63+
stloc.auto $pinnedAsm
64+
65+
# Process.GetCurrentProcess().Handle
66+
call { [Process] [Process]::GetCurrentProcess() }
67+
callvirt { [IntPtr] [Process].get_Handle() }
68+
# &pinnedAsm[0]
69+
ldloc.auto $pinnedAsm
70+
ldc.i4.0
71+
ldelema { [byte] }
72+
# pinnedAsm.Length
73+
ldloc.auto $pinnedAsm
74+
ldlen
75+
# 0x40
76+
ldc.i4.s 0x40
77+
# &flOldProtect
78+
ldloca.auto $flOldProtect
79+
80+
# VirtualProtectEx(Process.GetCurrentProcess().Handle, &pinnedAsm[0], pinnedAsm.Length, 0x40, &flOldProtect)
81+
ldloc.auto $virtualProtectEx
82+
calli unmanaged stdcall { [int]([void+], [void+], [int], [int], [int+]) }
83+
84+
# if (vpResult != 0) goto success
85+
brtrue.s success
86+
87+
# throw new Win32Exception(GetLastError())
88+
ldloc.auto $getLastError
89+
calli unmanaged stdcall { [int]@() }
90+
newobj { [void] [Win32Exception].new([int]) }
91+
throw
92+
93+
success:
94+
95+
# pinnedArgs = args
96+
ldarg.1
97+
stloc.auto $pinnedArgs
98+
99+
# &pinnedArgs[0]
100+
ldloc.auto $pinnedArgs
101+
ldc.i4.0
102+
ldelema { [int] }
103+
# pinnedArgs.Length
104+
ldloc.auto $pinnedArgs
105+
ldlen
106+
# &pinnedAsm[0]
107+
ldloc.auto $pinnedAsm
108+
ldc.i4.0
109+
ldelema { [byte] }
110+
111+
# return ((delegate* unmanaged[Stdcall]<int*, int, int>)&pinnedAsm[0])(&pinnedArgs[0], pinnedArgs.Length)
112+
calli unmanaged stdcall { [int]([int+], [int]) }
113+
ret
114+
}
115+
116+
$delegate.Invoke($assembly, @(20, 60, 324))
117+
# returns: 404

src/ILAssembler/CilAssemblage.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public static Delegate CreateDelegate(Type delegateType, ScriptBlockAst body)
5555

5656
public static Delegate CreateDelegate(Type delegateType, ScriptBlockAst body, bool skipCache)
5757
{
58-
return CreateCilMethod(delegateType, body, skipCache)
59-
.CreateDelegate(delegateType);
58+
DynamicMethod method = CreateCilMethod(delegateType, body, skipCache);
59+
return method.CreateDelegate(delegateType);
6060
}
6161

6262
private static DynamicMethod CreateCilMethod(Type delegateType, ScriptBlockAst body, bool skipCache = false)

src/ILAssembler/EncoderExtensions.cs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ internal static class EncoderExtensions
1010
{
1111
public static EntityHandle GetHandleFor(this DynamicILInfo ilInfo, RuntimeTypeHandle typeHandle)
1212
{
13-
return MetadataTokens.EntityHandle(ilInfo.GetTokenFor(typeHandle));
13+
int token = ilInfo.GetTokenFor(typeHandle);
14+
EntityHandle handle = MetadataTokens.EntityHandle(token);
15+
return handle;
1416
}
1517

1618
public static void Type(this SignatureTypeEncoder encoder, Type type, DynamicILInfo ilInfo)
@@ -30,20 +32,51 @@ public static void Type(this SignatureTypeEncoder encoder, Type type, DynamicILI
3032
return;
3133
}
3234

35+
if (type.IsGenericParameter)
36+
{
37+
if (type.DeclaringMethod is not null)
38+
{
39+
encoder.GenericMethodTypeParameter(type.GenericParameterPosition);
40+
return;
41+
}
42+
43+
encoder.GenericTypeParameter(type.GenericParameterPosition);
44+
}
45+
3346
if (type.IsGenericType)
3447
{
35-
Type genericDefinition = type.GetGenericTypeDefinition();
36-
Type[] genericArguments = type.GetGenericArguments();
37-
GenericTypeArgumentsEncoder argumentsEncoder = encoder.GenericInstantiation(
38-
ilInfo.GetHandleFor(genericDefinition.TypeHandle),
39-
genericArguments.Length,
40-
genericDefinition.IsValueType);
41-
42-
foreach (Type genericArgument in genericArguments)
48+
// The supported way of declaring a generic instantiation isn't
49+
// working correctly in a locals sig. Unsure if it's something
50+
// we're doing wrong or if it's a problem with the DynamicILInfo
51+
// API. As a workaround, we're depending on an implementation
52+
// detail used by the runtime to embed a type handle directly
53+
// as a pointer.
54+
//
55+
// The proper implementation is commented below this.
56+
//
57+
// https://github.com/SeeminglyScience/ILAssembler/issues/42
58+
const byte ELEMENT_TYPE_INTERNAL = 0x21;
59+
encoder.Builder.WriteByte(ELEMENT_TYPE_INTERNAL);
60+
IntPtr rawHandle = type.TypeHandle.Value;
61+
unsafe
4362
{
44-
Type(argumentsEncoder.AddArgument(), genericArgument, ilInfo);
63+
encoder.Builder.WriteBytes(
64+
(byte*)&rawHandle,
65+
sizeof(IntPtr));
4566
}
4667

68+
// Type genericDefinition = type.GetGenericTypeDefinition();
69+
// Type[] genericArguments = type.GetGenericArguments();
70+
// GenericTypeArgumentsEncoder argumentsEncoder = encoder.GenericInstantiation(
71+
// ilInfo.GetHandleFor(genericDefinition.TypeHandle),
72+
// genericArguments.Length,
73+
// genericDefinition.IsValueType);
74+
75+
// foreach (Type genericArgument in genericArguments)
76+
// {
77+
// Type(argumentsEncoder.AddArgument(), genericArgument, ilInfo);
78+
// }
79+
4780
return;
4881
}
4982

0 commit comments

Comments
 (0)