none
How to build a CIL method signature in a CLR profiler? RRS feed

  • Question

  • I have a couple classes that looks like this:

    class MyClass
    {
        public static MyOtherClass GetOtherClass(String a)
        {
            return new MyOtherClass();
        }
     
        public void Bar()
        {
        }
    }
     
    class MyOtherClass
    {
    }

    Using the CLR profiler I want to be able to inject a call to GetOtherClass into Bar at JIT compilation time.  In order to inject a call I need to know the signature of the method so I can create a token for it with IMetaDataEmit::DefineMemberRef.  The signature will look something like 0x00 0x01 0x12 0x?? 0x?? 0x0E.  The problem is that the 4th and possibly 5th bytes (depending on the size of my assembly) will be a reference to MyOtherObject.  I know the name of the class I want (MyOtherObject) and I believe I can look that up using IMetaDataImport but once I find it I don't know how to get that 1 or 2 byte reference expected in the signature.

    Is it the TypeRef provided by IMetaDataEmit::FindTypeRef with the first byte and any leading 0x00 bytes removed?  If so, how do I get a ModuleRef for the module I am currently JIT compiling?  I can get a ModuleID (is that the same as a ModuleRef?), AssemblyID, metadataToken, etc. but I can't figure out how to turn those into a ModuleRef.

    The reason I need this is that the rest of the signature will not change as long as I keep the signature of my method the same (static, same return type, same parameters) but as I add/remove from my assembly it is my understanding that the reference to MyOtherClass could move around.  I would like to build something that works reliably even as my assembly is changed.



    • Edited by Micah Zoltu Monday, January 14, 2013 6:18 PM
    Saturday, January 12, 2013 3:59 AM

Answers

  • Reference ECMA-335.  There is a section for "Signature Formats" with the rail diagrams that you'll need.  Specifically, "23.2 Blobs and signatures" explains the compressed integer algorithm.  You'll call the CorSigCompressData api using ELEMENT_TYPE_CLASS followed by a CorSigCompressToken call for what you've mentioned in your initial post.

    Use Broman's sigparser as a reference (http://blogs.msdn.com/b/davbr/archive/2005/10/13/sample-a-signature-blob-parser-for-your-profiler.aspx)

    Other answers: JitCompilationStarted callback will provide you with a FunctionID that will give access to the MethodDef token and ModuleID for that function.  These will give access to an IMetaDataImport*.  A ModuleID is NOT a ModuleRef.  A ModuleID can be used to identify the ModuleDef.  If the methods you want to call are in the same module as the method you're injecting into then you won't need any *Ref tokens.  If the methods are in a different module, you may need to call DefineAssemblyRef, DefineTypeRef, or DefineMemberRef to acquire the MemberRef token you want.

    Give this page a read too: http://blogs.msdn.com/b/davbr/archive/2011/10/17/metadata-tokens-run-time-ids-and-type-loading.aspx

    Upvote if this helps.  Hope it does.

    Cheers

    • Marked as answer by Micah Zoltu Tuesday, January 15, 2013 12:25 AM
    Monday, January 14, 2013 6:39 PM
  • For any future internet searchers, I ended up writing a method to build the signature that looks like this:
    // Make sure outSignature has enough space allocated for it.  Currently that is 6 bytes, but that could change so allocate extra.
    void GenerateGetOtherClassSignature(IMetaDataImport* metadataImport, BYTE* outSignature, size_t& outSignatureSize)
    {
    	mdToken otherClassToken;
    	metadataImport->FindTypeDefByName(L"MyOtherClass", NULL, &otherClassToken);
    	BYTE* signatureStart = outSignature;
    	// default calling convention
    	*(outSignature++) = 0x00;
    	// 1 parameter
    	*(outSignature++) = 0x01;
    	// return type class
    	outSignature += CorSigCompressData(ELEMENT_TYPE_CLASS, outSignature);
    	// return type class token
    	outSignature += CorSigCompressToken(otherClassToken, outSignature);
    	// parameter 1
    	outSignature += CorSigCompressData(ELEMENT_TYPE_STRING, outSignature);
    	outSignatureSize = outSignature - signatureStart;
    }
    

    • Marked as answer by Micah Zoltu Thursday, January 24, 2013 10:02 PM
    Tuesday, January 15, 2013 12:34 AM

All replies

  • Hi Micah,

    Welcome to the MSDN Forum.

    I am trying to involve some other one into this case, wait it patiently, please. Thank you.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, January 14, 2013 7:45 AM
    Moderator
  • Reference ECMA-335.  There is a section for "Signature Formats" with the rail diagrams that you'll need.  Specifically, "23.2 Blobs and signatures" explains the compressed integer algorithm.  You'll call the CorSigCompressData api using ELEMENT_TYPE_CLASS followed by a CorSigCompressToken call for what you've mentioned in your initial post.

    Use Broman's sigparser as a reference (http://blogs.msdn.com/b/davbr/archive/2005/10/13/sample-a-signature-blob-parser-for-your-profiler.aspx)

    Other answers: JitCompilationStarted callback will provide you with a FunctionID that will give access to the MethodDef token and ModuleID for that function.  These will give access to an IMetaDataImport*.  A ModuleID is NOT a ModuleRef.  A ModuleID can be used to identify the ModuleDef.  If the methods you want to call are in the same module as the method you're injecting into then you won't need any *Ref tokens.  If the methods are in a different module, you may need to call DefineAssemblyRef, DefineTypeRef, or DefineMemberRef to acquire the MemberRef token you want.

    Give this page a read too: http://blogs.msdn.com/b/davbr/archive/2011/10/17/metadata-tokens-run-time-ids-and-type-loading.aspx

    Upvote if this helps.  Hope it does.

    Cheers

    • Marked as answer by Micah Zoltu Tuesday, January 15, 2013 12:25 AM
    Monday, January 14, 2013 6:39 PM
  • For any future internet searchers, I ended up writing a method to build the signature that looks like this:
    // Make sure outSignature has enough space allocated for it.  Currently that is 6 bytes, but that could change so allocate extra.
    void GenerateGetOtherClassSignature(IMetaDataImport* metadataImport, BYTE* outSignature, size_t& outSignatureSize)
    {
    	mdToken otherClassToken;
    	metadataImport->FindTypeDefByName(L"MyOtherClass", NULL, &otherClassToken);
    	BYTE* signatureStart = outSignature;
    	// default calling convention
    	*(outSignature++) = 0x00;
    	// 1 parameter
    	*(outSignature++) = 0x01;
    	// return type class
    	outSignature += CorSigCompressData(ELEMENT_TYPE_CLASS, outSignature);
    	// return type class token
    	outSignature += CorSigCompressToken(otherClassToken, outSignature);
    	// parameter 1
    	outSignature += CorSigCompressData(ELEMENT_TYPE_STRING, outSignature);
    	outSignatureSize = outSignature - signatureStart;
    }
    

    • Marked as answer by Micah Zoltu Thursday, January 24, 2013 10:02 PM
    Tuesday, January 15, 2013 12:34 AM
  • I should note that IMetaDataImport::FindTypeDefByName doesn't work in all scenarios.  Someone else may be able to provide better details on what situations it works in and what it doesn't.  It seems to be related to whether there is already a token for that type available or not.  If not then you have to create a new token for the type (see below).

    As far as I can tell, the following works in all cases in place of that line:

    metaDataEmit->DefineTypeRefByName(moduleToken, className, &otherClassToken)

    However, this requires an IMetaDataEmit and a module token for the module the class is in.  Assuming you have this information available (or can get it) then it should work in all cases.

    Thursday, January 24, 2013 10:02 PM