ล็อกแล้ว How to use IMetaDataEmit::SetFieldRVA

  • 6 กุมภาพันธ์ 2555 16:13
     
      มีโค้ด

    Hi,

    I want to add a field via IMetaDataEmit, which has a FieldRVA. Such fields are created by the C# compiler when static fields with primitive arrays are used:

    From a statement like this:

     

    private static int[] staticReadonlyIntArray = new int[] {1, 2, 3, 4, 5, 6};

     

    the C# compiler creates IL-code like this:

     

      .field private static int32[] staticReadonlyIntArray
      .method private hidebysig specialname rtspecialname static 
              void  .cctor() cil managed
      {
        // Code size       25 (0x19)
        .maxstack  8
        IL_0000:  ldc.i4.6
        IL_0001:  newarr     [mscorlib]System.Int32
        IL_0006:  dup
        IL_0007:  ldtoken    field valuetype '<PrivateImplementationDetails>{53713D6B-BBC5-4482-852B-4D43DB1E6C7C}'/'__StaticArrayInitTypeSize=24' '<PrivateImplementationDetails>{53713D6B-BBC5-4482-852B-4D43DB1E6C7C}'::'$$method0x6000032-1'
        IL_000c:  call       void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array,
                                                                                                            valuetype [mscorlib]System.RuntimeFieldHandle)
        IL_0011:  stsfld     int32[] MyStaticConstructor::staticReadonlyIntArray
        IL_0016:  nop
        IL_0017:  nop
        IL_0018:  ret
      } // end of method MyStaticConstructor::.cctor

     

    and a new weird type like this:

     

    .class private auto ansi '<PrivateImplementationDetails>{53713D6B-BBC5-4482-852B-4D43DB1E6C7C}'
           extends [mscorlib]System.Object
    {
      .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
      .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=24'
             extends [mscorlib]System.ValueType
      {
        .pack 1
        .size 24
      } // end of class '__StaticArrayInitTypeSize=24'
    
      .field static assembly valuetype '<PrivateImplementationDetails>{53713D6B-BBC5-4482-852B-4D43DB1E6C7C}'/'__StaticArrayInitTypeSize=24' '$$method0x6000032-1' at I_000028D0
    } // end of class '<PrivateImplementationDetails>{53713D6B-BBC5-4482-852B-4D43DB1E6C7C}'

     

    which references a data field like this:

     

    .data cil I_000028D0 = bytearray (
                     01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
                     05 00 00 00 06 00 00 00) 

     

    From what I understand, the ".field static assembly valuetype 'SomeField' at I_000028D0" statement means that the field has the HasFieldRVA flag and there is an entry in the FieldRVA metatable which references the field-token and contains an RVA-value. Here's the definition from ECMA-335:

     

    22.18  F i e l d RVA  :   0 x 1 D 
    The FieldRVA table has the following columns: 
     *  RVA (a 4-byte constant) 
     *  Field (an index into Field table) 
    Conceptually, each row in the FieldRVA table is an extension to exactly one row in the Field table, and records 
    the RVA (Relative Virtual Address) within the image file at which this field‘s initial value is stored. 
    A row in the FieldRVA table is created for each static parent field that has specified the optional data 
    label §16).  The RVA column is the relative virtual address of the data in the PE file (§16.3).

     

    So far so good.

    Now, to get this done I inject the field first with "IMetaDataEmit::DefineField". Then I guess I would need to call "IMetaDataEmit::SetFieldRVA" with that fields token as first parameter. But I cannot figure out how to compute the RVA-value for the second parameter. It should be the address to the ".data" entry, right? How do I write that ".data"-entry? Is this even possible with IMetaDataEmit?

    Thanks,

    Christoph

     

     

     


ตอบทั้งหมด

  • 10 กุมภาพันธ์ 2555 9:26
     
     

    Have you tried setFileldProps:

    http://msdn.microsoft.com/en-us/library/ms230834.aspx


    Hard hard work, Day day up!

  • 13 กุมภาพันธ์ 2555 9:55
     
     

    Hi Mohammed, thanks for your reply.

    SetFieldProps allows to set a default value for a field and update its flags. The same thing can be achieved with DefineField. I think this is not the same thing as FieldRVA, also called "Mapped Fields". My problem is that I do not know how and where to allocate the memory which needs to be referenced in SetFieldRVA (http://msdn.microsoft.com/en-us/library/ms231203.aspx).

  • 13 กุมภาพันธ์ 2555 22:51
     
     
    What happens if you don't use that part of the API?
  • 14 กุมภาพันธ์ 2555 4:20
    ผู้ดูแล
     
     คำตอบที่เสนอ

    I'm not an expert on that area, but my educated guess would be:

    You need to use some raw PE writing functionality that is outside the scope of IMetaDataEmit for this. I assume the data will go into a standalone .data section of the PE file that is distinct from the part of the PE file that contains metadata. Determining the RVA of the data you write will then depend on what PE file API you use to write that data.

    HTH,

      -Noah

  • 14 กุมภาพันธ์ 2555 11:30
     
     

    I'm not an expert on that area, but my educated guess would be:

    You need to use some raw PE writing functionality that is outside the scope of IMetaDataEmit for this. I assume the data will go into a standalone .data section of the PE file that is distinct from the part of the PE file that contains metadata. Determining the RVA of the data you write will then depend on what PE file API you use to write that data.

    HTH,

      -Noah

    Noah, thanks for your answer.

    That is exactly what I was thinking too. The question remains, which API to use. I was already thinking about (ab-)using IMethodMalloc (http://msdn.microsoft.com/en-us/library/ms231581.aspx). Would that be feasible?

  • 16 กุมภาพันธ์ 2555 3:49
    ผู้ดูแล
     
     คำตอบ

    You didn't mention what your scenario was, but based on the questions so far I am going to guess you are trying to do IL re-writing / instrumention in a profiler and modify the module at load time?

    If that is the case, does your scenario require you to use an RVA based static at all? For example as an alternative you could declare a more typical AppDomain static and then use an explicit static constructor to initialize the value. C# likely uses the RVA static for small performance benefits when it is known that the data is read-only, but in your case it might be worthwhile to sacrifice any marginal perf benefits you would get in order to have a cleaner solution.

    As to using IMethodMalloc it definately seems like an abuse : ) It might work, but I also wouldn't be surprised to see it break at any point even if it does work now. Its possible that adding an RVA static using the profiling API isn't supported but I'll see what I can find out.

    Thanks,

      -Noah

  • 17 กุมภาพันธ์ 2555 1:59
    เจ้าของ
     
     

    Hi, Christoph.

    It's worth noting that, whatever issues you may be having with RVA-statics, DefineField is generally not going to work well when used by a profiler.  Adding any kind of static field typically doesn’t work at all.  Adding instance fields will cause AVs if you’re adding them to certain mscorlib types well-known to the CLR, and can also fail even for other types.  Look at these posts for more info:

    http://social.msdn.microsoft.com/forums/en-US/netfxtoolsdev/thread/5f30596b-e7b7-4b1f-b8e1-8172aa8dde31
    http://social.msdn.microsoft.com/Forums/en/netfxtoolsdev/thread/c352266f-ded3-4ee2-b2f9-fbeb41a70c27

    Basically, adding static methods to pre-existing types in mscorlib via IMetaDataEmit works.  But other kinds of type manipulation via IMetaDataEmit can be problematic, and are not tested under profiler scenarios.

    Yeah, pretty awful.  :-(


    Thanks,
    Dave

  • 17 กุมภาพันธ์ 2555 8:41
     
     

    Noah; using an explicit static constructor to initialize the value instead of a RVA static sounds like a valid workaround to me - I'll try that. You're right, I am trying to re-write an existing assembly via the Profiler APIs.

    David; thanks for your thoughts. In my scenario, I mainly want to add fields to new types (also added via profiler API). From what I've learned so far, it works well to add instance fields to new types (at least in .NET 2.0+), but there are limitations when adding static fields.

    Thanks,
    Christoph

  • 21 กุมภาพันธ์ 2555 2:25
    เจ้าของ
     
     คำตอบ

    As I recall, adding new types to mscorlib can be problematic.  I think there was an issue with adding references to those types (also via IMetaDataEmit) from other pre-existing types inside mscorlib, and having those references not get resolved properly.  (Only adding static methods to existing types in mscorlib is routinely tested.)  But I think you might have good luck adding types to other assemblies and then having other pre-existing (non-mscorlib) types referencing your new types. 

    It's also good to remember, mscorlib is never allowed to reference another assembly, and attempting to do so will definitely cause problems.  This combined with the above really constrains what metadata edits you can do from a profiler.

    Thanks,
    Dave

  • 24 กุมภาพันธ์ 2555 1:43
     
     

    FYI: David was very helpful in helping me understand how to add some static methods to a type in mscorlib that I could then use in other assemblies, I managed to eventually compose a solution that worked for .NET 2.0, 4.0 and Silverlight (see https://github.com/sawilde/opencover/blob/master/main/OpenCover.Profiler/CodeCoverage.cpp).

    I like to refer to these injected methods as "cuckoos".

    I hope you find it of use.

  • 7 มีนาคม 2555 20:22
     
     

    As I recall, adding new types to mscorlib can be problematic.  I think there was an issue with adding references to those types (also via IMetaDataEmit) from other pre-existing types inside mscorlib, and having those references not get resolved properly.  (Only adding static methods to existing types in mscorlib is routinely tested.)  But I think you might have good luck adding types to other assemblies and then having other pre-existing (non-mscorlib) types referencing your new types. 

    It is definitely possible to inject new types into mscorlib (at least in .NET 2.0+). I haven't had any (major) problems with that yet. The new types can be referenced from within mscorlib as well as from other assemblies.

    It's also good to remember, mscorlib is never allowed to reference another assembly, and attempting to do so will definitely cause problems.  This combined with the above really constrains what metadata edits you can do from a profiler.

    Yes, that is why I am trying to inject everything - so I don't need to create any references to other assemblies.

    I am currently working around this issue by avoiding static array initializations at all in my code (so I do not need SetFieldRVA). If I ever really need it, I think Noahs suggestion to just set the value differently and omit the marginal performance-optimization would be the way to go. Thanks for all your help!