如何找到泛型参数的 TypeSpec
How to find TypeSpec of generic argument
一些初步信息
给定以下 C# 函数:
public static void func<T>(T t)
{
System.Console.WriteLine(t);
}
编译成如下CIL:
.method public hidebysig static void func<T>(!!T t) cil managed
{
ldarg.0
box !!T
call void [mscorlib]System.Console::WriteLine(object)
ret
}
上述方法的签名是10 01 01 01 1E 00
其中:
10 - Calling convention (IMAGE_CEE_CS_CALLCONV_GENERIC)
01 - Function generic arguments count (which is 1)
01 - Function arguments count (which is 1)
01 - Return type (ELEMENT_TYPE_VOID)
1E - First argument type (ELEMENT_TYPE_MVAR)
00 - Index of above MVAR (which is 0)
另请参阅以下指令及其实际字节码:
box !!T - 8C 1B000001
1B000001
指向 TypeSpec table 中的第一个条目,它指向 blob 02 1E 00
其中:
02 - Blob length
1E - Type type (ELEMENT_TYPE_MVAR)
00 - Index of above MVAR (which is 0)
正如我们所见,方法签名以描述性的方式包含泛型参数,其中我们有实际的类型签名。
但是,当使用需要 TypeDef/Ref/Spec 的 OpCode 时,会提供 TypeSpec,并且 TypeSpec 指向带有类型信息的签名。
所以我的问题是:
我正在编写一个执行一些 IL 重写的探查器,并且给定函数签名,我想将一些 OpCodes 添加到将使用参数运行的函数体中。
使用 IMetaDataImport2
接口,如何获取给定通用参数所需的 TypeSpec 令牌?
我可以看到 2 个选项:
- 迭代 EnumTypeSpecs 直到找到匹配的签名
- 使用
IMetaDataEmit
接口创建新的TypeSpec
但是,出于明显的原因,我想避免这 2 个选项并选择一个更明智的选择。
所以我最终采用了我的第一个建议。
我想这并不理想,但确实有效。
代码如下,有兴趣的朋友(省略错误处理):
HCORENUM typeSpecEnum = NULL;
mdTypeSpec typeSpec = mdTypeSpecNil;
ULONG outNum = -1;
// Loop through enum
while (true)
{
// Get next enum
HRESULT hr = pMetadataImport->EnumTypeSpecs(&typeSpecEnum, &typeSpec, 1, &outNum);
if (hr == S_FALSE && outNum == 0) // According to doc, this means no more. End loop
break;
// Get the signature of this typespec
PCCOR_SIGNATURE curSpecSig = NULL;
ULONG curSpecSigLen = -1;
pMetadataImport->GetTypeSpecFromToken(typeSpec, &curSpecSig, &curSpecSigLen);
if (curSpecSigLen == <my_len> && memcmp(curSpecSig, <my_sig>, <my_len>) == 0)
; // Token found
else
typeSpec = mdTypeSpecNil; // Reset and goto next token
}
pMetadataImport->CloseEnum(typeSpecEnum); // Don't forget to close enum
一些初步信息
给定以下 C# 函数:
public static void func<T>(T t)
{
System.Console.WriteLine(t);
}
编译成如下CIL:
.method public hidebysig static void func<T>(!!T t) cil managed
{
ldarg.0
box !!T
call void [mscorlib]System.Console::WriteLine(object)
ret
}
上述方法的签名是10 01 01 01 1E 00
其中:
10 - Calling convention (IMAGE_CEE_CS_CALLCONV_GENERIC) 01 - Function generic arguments count (which is 1) 01 - Function arguments count (which is 1) 01 - Return type (ELEMENT_TYPE_VOID) 1E - First argument type (ELEMENT_TYPE_MVAR) 00 - Index of above MVAR (which is 0)
另请参阅以下指令及其实际字节码:
box !!T - 8C 1B000001
1B000001
指向 TypeSpec table 中的第一个条目,它指向 blob 02 1E 00
其中:
02 - Blob length 1E - Type type (ELEMENT_TYPE_MVAR) 00 - Index of above MVAR (which is 0)
正如我们所见,方法签名以描述性的方式包含泛型参数,其中我们有实际的类型签名。
但是,当使用需要 TypeDef/Ref/Spec 的 OpCode 时,会提供 TypeSpec,并且 TypeSpec 指向带有类型信息的签名。
所以我的问题是:
我正在编写一个执行一些 IL 重写的探查器,并且给定函数签名,我想将一些 OpCodes 添加到将使用参数运行的函数体中。
使用 IMetaDataImport2
接口,如何获取给定通用参数所需的 TypeSpec 令牌?
我可以看到 2 个选项:
- 迭代 EnumTypeSpecs 直到找到匹配的签名
- 使用
IMetaDataEmit
接口创建新的TypeSpec
但是,出于明显的原因,我想避免这 2 个选项并选择一个更明智的选择。
所以我最终采用了我的第一个建议。
我想这并不理想,但确实有效。
代码如下,有兴趣的朋友(省略错误处理):
HCORENUM typeSpecEnum = NULL;
mdTypeSpec typeSpec = mdTypeSpecNil;
ULONG outNum = -1;
// Loop through enum
while (true)
{
// Get next enum
HRESULT hr = pMetadataImport->EnumTypeSpecs(&typeSpecEnum, &typeSpec, 1, &outNum);
if (hr == S_FALSE && outNum == 0) // According to doc, this means no more. End loop
break;
// Get the signature of this typespec
PCCOR_SIGNATURE curSpecSig = NULL;
ULONG curSpecSigLen = -1;
pMetadataImport->GetTypeSpecFromToken(typeSpec, &curSpecSig, &curSpecSigLen);
if (curSpecSigLen == <my_len> && memcmp(curSpecSig, <my_sig>, <my_len>) == 0)
; // Token found
else
typeSpec = mdTypeSpecNil; // Reset and goto next token
}
pMetadataImport->CloseEnum(typeSpecEnum); // Don't forget to close enum