none
how to use DefineMethodSpec to define a generic method instance for called in IL RRS feed

  • Question

  • Hi,

    i need to instrument generic method. Be more clearly i want to write IL to call the instrumented generic method when jit compile start. but now i don't know how to define a generic method that can be called in the IL. i know it need to use DefineMethodSpec to define the methodspec. but how to use DefineMethodSpec? how to setup the parameter pvSigBlob for DefineMethodSpec?

    Monday, October 29, 2018 8:42 AM

All replies

  • First, create the generic method definition using IMetaDataEmit::DefineMethod Method (the format of the signature is defined in ECMA-335 Partition II Section 23.2.1).

    Next, use DefineMethodSpec to create a metadata token that combines the method you just defined with the generic type arguments you want (the format of the signature is defined in ECMA-335 Partition II Section 23.2.15).

    The RVA passed into DefineMethod is allocated via IMethodMalloc.Alloc and the format is defined in ECMA-335 Partition II Sections 25.4.2 and 25.4.3.

    Monday, October 29, 2018 9:07 PM
  • Hi Brian,

    i have another question.

    should i define all the instance for all the possible type arguments? then judge which generic method instance need to be called when Jit compile start?

    Tuesday, October 30, 2018 10:43 AM
  • If there is only a small number of possible types used as generic type arguments, then you can certainly do that. But if the set of possible types is large/unbounded, then you may have to give up on the generic method and just pass the value in as an object instead.
    Tuesday, October 30, 2018 8:43 PM
  • Hi Brian,

    i can not give up generic method because the source code is not writed by me.

    is there any method to get the type argument when Jit compile start. i know GetMethodProps can get the parameter signature. i want to know if there is similar way to get type argument signature.

    Wednesday, October 31, 2018 9:09 AM
  • You can get the type arguments from the FunctionID using ICorProfilerInfo2::GetFunctionInfo2. This will give the type arguments as ClassIDs, which can be further broken down using ICorProfilerInfo2::GetClassIDInfo2.
    Wednesday, October 31, 2018 10:14 AM
  • thanks again,
    but how to fetch the type argument signature by GetClassIDInfo2? GetClassIDInfo2 only return the token value which is in pTypeDefToken. how to convert it to type argument signature?
    Wednesday, October 31, 2018 11:54 AM
  • Hi Brian,

    What i want to build a method B that use IL rewriting of method C when method C start jit compile.

    the method B will call method C in this way:

    void B<T>()

    {

      C<T>();

    }

    so i need to define the instance of method C which can be called by B. so when method C start jit compile i need to fetch its type argument signature.

    Wednesday, October 31, 2018 12:13 PM
  • Well that simplifies things a whole heap. You only need one MethodSpec for that and you don't need to extract the type arguments at jit time. You just need to reference the `first method type argument` in the MethodSpec signature using ELEMENT_TYPE_MVAR followed by the index as a compressed unsigned int.

    See ECMA-335 Partition II sections 23.1.16, 23.2, 23.2.12

    Wednesday, October 31, 2018 8:35 PM
  • Hi Brian,

    very thanks for your answers before. but still have problem.

    i define the MethodSpec as you said. and then i commit the IL programme using SetILFunctionBody. the programme throw an exception: 

    BadImageFormatException:An attempt was made to load a program with an incorrect format (Exception from HRESULT: 0x8007000B)

    i have modified every kind of IL progrmme and trying the simplest way it still have this exception.

    the IL programme is running well for the normal method, is there anything difference in the IL programme or metadata from normal method and generic method?

    Friday, November 2, 2018 9:41 AM
  • Its hard to tell from here. Did you try decompiling the method header & body you passed to SetILFunctionBody to ensure it's what you expect? Did you verify that evaluation stack would be consistent at each step through the IL? If you have any exception handler records, did you verify that they line up with the correct instructions? ... did you provide the RVA relative to the module load address?
    Friday, November 2, 2018 11:34 AM
  • Hi Brian,

    how to decompile the method submitted by SetILFunctionBody ?

    Monday, November 5, 2018 2:44 AM
  • The format is described in ECMA-335 Partitions II & III, you should have encountered everything you need to decode the method body in generating it.

    I used to decode it by hand, then a few years ago I wrote a simple tool to automate most of it (which I put on github earlier this year.)

    Monday, November 5, 2018 8:12 AM
  • Hi Brain,

    i think the problem maybe the MethodSpec defined by DefineMethodSpec. because when i replace MethodSpec with another normal method token and called in the generic method IL body it run well. so i think the metadata for the generic method that is submited is correct.

    the pvSigBlob parameter of DefineMethodSpec is 4 byte(assume there is only one type argument):

    IMAGE_CEE_CS_CALLCONV_GENERICINST,1,ELEMENT_TYPE_MVAR,0

    is this correct signature?

    Monday, November 5, 2018 10:05 AM
  • seems ok, but a couple of things to keep in mind:

    * If it's a generic method on a generic type, then you will need to specify the generic type arguments of both.

    * If the generic method you are calling has type arg constraints, then the caller must have type arg constraints that allow it to comply.

    Monday, November 5, 2018 8:38 PM
  • Hi Brian,

    how to use your method check to decompile the method ?

    Wednesday, November 7, 2018 10:19 AM
  • https://github.com/brian-reichle/MethodCheck/wiki#how-to-use

    You will want the 'Body' option rather than the 'IL' option.
    Wednesday, November 7, 2018 11:53 AM
  • Hi Brian,

    the decompile result is:

    .maxstack 9
    .locals 1100004B
    // code size: 76
    IL_0000
      nop
      ldstr           70001E08
      ldstr           70001E2E
      ldstr           70001E4E
      ldstr           70001EAE
      ldstr           70001EC0
      ldarg.0
      ldarg.1
      ldarg.2
      box             1B00000D
      call            0A0000F1
      call            0A0000F2
      pop
      ldnull
      stloc.0
      nop
      ldarg.0
      ldarg.1
      ldarg.2
      call            2B00000C
      stloc.0
      nop
      leave.s         IL_0049 // +13
    IL_003C
      stloc.1
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      ldloc.1
      throw
    IL_0049
      nop
      ldloc.0
      ret

    .try IL_0000 to IL_003C catch 0100002F IL_003C to IL_0049

    if i remove call            2B00000C then will not have excpetion. so i think the problem should be the token 2B00000C, there is something wrong when i define this generic method token.

    Thursday, November 8, 2018 7:06 AM
  •         string Execute3<T>(string a, T r)
            {
                return r.ToString() + a;
            }

    the generic method is like this one and don't have generic type.

    Thursday, November 8, 2018 7:39 AM
  • How is the method referenced by 2B00000C defined? ie. method signature, number of type arguments of the method and containing class(es) etc. If you change it to call a non-generic method in the same assembly, do you still get the exception?
    Thursday, November 8, 2018 7:50 AM
  • Hi Brian,

    i use DefineMethod to define method then use DefineMethodSpec define 2B00000C.

    the parameter of DefineMethod is major get from GetMethodProps include method signature and dwImplFlags and dwMethodFlags.

    the signature of DefineMethodSpec is IMAGE_CEE_CS_CALLCONV_GENERICINST,1,ELEMENT_TYPE_MVAR,0

    if i replace 2B00000C with another normal method there is no exception. the usage of DefineMethod for generic method and normal method is the same. the only difference is generic method have extra step that use DefineMethodSpec

    Thursday, November 8, 2018 9:06 AM
  • I just wanted to confirm that the type defining this method is not itself generic, because if it is then you also need to specify the type arguments for the class.

    eg.

    public static class Foo<A>
    {
        public static class Bar<B>
        {
            public static void Baz<C>()
            {
            }
        }
    }
    Calling:
    Foo<X>.Bar<Y>.Baz<Z>();

    Is conceptually like calling:

    Foo`1.Bar`1.Baz<X, Y, Z>();

    While we are at it:

    * is the method visible to the instrumented code?

    * You show the method as an instance method, did you mean static? if it really is an instance method, the issue might be that you didn't provide an instance.



    Thursday, November 8, 2018 9:46 AM
  • No, it is class is not generic. only this method is generic.
    Thursday, November 8, 2018 9:53 AM
  • Hi Brian,

    is the method visible to the instrumented code? how to check this? why should a method not visible to the instrumentd code?

    * You show the method as an instance method, did you mean static? if it really is an instance method, the issue might be that you didn't provide an instance. i don't quite understand this. the method is exactly this:

        public class restController : Controller
        {
            string Execute3<T>(string a, T r)
            {
                return r.ToString() + a;
            }


    Thursday, November 8, 2018 10:10 AM
  • 1. eg. if the method is private and defined on a different class to the one being instrumented.

    2. instance methods have an implicit 'this' argument that needs to be provided by the call site.

    Would I be correct in assuming that the Execute3 method is on the same class that defines the method being instrumented? This would be consistent with what I see in the decompiled method.

      ldarg.0
      ldarg.1
      ldarg.2
      call            2B00000C
      stloc.0

    Would I be also correct in assuming the instrumented method has the same signature as Execute3?

    Thursday, November 8, 2018 11:17 AM
  • Hi Brian,

    1. it is in the same class.

    2. we have load the this arg for the method.

    i want to make it clearer. we instrument Execute3 method and make a token for the orginal Execute3 method body by defineMethod and defineMethodSpec. in the intrumented Execute3 method we call the original Execute3 method. 2B00000C is the token for original Execute3 body.

    Friday, November 9, 2018 2:53 AM
  • Hi Brian,

    i may find out the problem. how to get the method's RVA for providing to Definemethod?

    Wednesday, November 28, 2018 11:53 AM
  • RVA == Relative Virtual Address, it's the address relative to the module base load address (the location where the module was loaded).

    ICorProfilerInfo::GetModuleInfo will return the baseLoadAddress in ppBaseLoadAddress.

    Wednesday, November 28, 2018 8:39 PM
  • then use GetILFunctionBody to get the method address and minus the base address of the module?

    i did it this way. but in some dotnet core version GetModuleInfo return the wrong address. how to check if the address returned by GetModuleInfo is valid?

    Thursday, November 29, 2018 3:24 AM
  • If you pass the address from GetILFunctionBody into SetILFunctionBody and they don't correspond to the same module, then it's unlikely to work. Partly because the metadata tokens encoded into the method body are module specific, and partly because the RVA needs to be positive and fit within 32 bits.

    SetILFunctionBody takes a pointer, so just pass in the absolute address without translation. IMetaDataEmit::DefineMethod, IMetaDataEmit::SetMethodProps and IMetaDataEmit::SetRVA, on the other hand take an RVA and so need the translation.

    I'm not familiar with .NET Core. I would expect it to behave mostly the same as .NET Framework (with the obvious exception of remoting/appdomain related callbacks), but if not then you should probably raise it with the .NET Core guys.

    Thursday, November 29, 2018 7:58 AM
  • Hi Brian,

    if possible use GetRVAStaticAddress to get rva of a method during ModuleAttachedToAssembly or JitCompileStart?

    Monday, December 3, 2018 10:50 AM
  • I don't know, I have never tried; but why would you want to? if it's because you want to access the field in the jitted method, then you are better off just emitting the load/store instructions with the metadata token. If it's because you want to use the value of the field to influence the jitting, then that smells of nondeterminism.
    Monday, December 3, 2018 8:46 PM
  • Hi Brian,

    i have fixed my problem. very thanks for your kindly help.

    Wednesday, December 12, 2018 7:27 AM
  • hi , i has some problem , generic method DefineMethodSpec ok and SetILFunctionBody error, how do you handle it?
    Tuesday, January 8, 2019 11:52 AM