Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find TypeSpec of generic argument

Some preliminary info

Given the following C# function:

public static void func<T>(T t)
{
    System.Console.WriteLine(t);
}

It is compiled into the following CIL:

.method public hidebysig static void  func<T>(!!T t) cil managed
{
    ldarg.0
    box        !!T
    call       void [mscorlib]System.Console::WriteLine(object)
    ret
}

The signature of the above method is 10 01 01 01 1E 00 where:

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)

Also see the following instruction with it's actual byte code:

box !!T - 8C 1B000001

1B000001 points to the first entry in the TypeSpec table, which points to the blob 02 1E 00 where:

02 - Blob length
1E - Type type (ELEMENT_TYPE_MVAR)
00 - Index of above MVAR (which is 0)

As we can see, the method signature contains the generic argument in a descriptive way, where we have the actual type signature.
However, when using an OpCode which requires a TypeDef/Ref/Spec, a TypeSpec is supplied, and the TypeSpec points to a signature with the type information.


So my question is:
I'm writing a profiler which does some IL Rewriting, and given the function signature, I would like to add some OpCodes to the function body which will operate with the arguments.

Using the IMetaDataImport2 interface, how can I get the TypeSpec token I would need for a given generic parameter?

I can see 2 options:

  1. Iterating over EnumTypeSpecs till I find a signature that matches
  2. Using the IMetaDataEmit interface to create a new TypeSpec

However, for obvious reasons, I would like to avoid those 2 options and choose a more sensible alternative.

like image 991
Eli Finkel Avatar asked Nov 07 '22 20:11

Eli Finkel


1 Answers

So I ended up going with my first suggestion.
I guess it's not ideal, but it does work.

Here's the code, if anyone is interested (error handling omitted):

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
like image 95
Eli Finkel Avatar answered Nov 14 '22 08:11

Eli Finkel