Search code examples
c#.netreflectioncilmono.cecil

How to make a dynamic method which uses static variables from the same assembly?


I created a dynamic method, but when I try to access outside resources, it gives me an exception

TargetInvocationException: Exception has been thrown by the target of an invocation.

Basically I want to write a method to byte array, then load it as a dynamic method. I know a simple byte array isn't enough to reconstruct the metadata links, but how make a dynamic method which uses a variable from the same assembly?

I tried to convert that code:

public static int z = 10;
public static int sum(int x, int y) {
    return x + y + z;
}

Which gives me the IL:

0        L_0000:      ldarg.0      
1        L_0001:      ldarg.1      
2        L_0002:      add          
3        L_0003:      ldsfld       System.Int32 CodeGen.Program::z
4        L_0008:      add          
5        L_0009:      ret  

Which is the bytes:

02 03 58 7E 06 00 00 04 58 2A

The I tested it like that:

public static int z = 10;

static void Main(string[] args) {

    DynamicMethod sum = new DynamicMethod("sum", typeof(int), new Type[] { typeof(int), typeof(int) });
    var info = sum.GetDynamicILInfo();
    var bytes = new byte[] { 0x02, 0x03, 0x58, 0x7E, 0x06, 0x00, 0x00, 0x04, 0x58, 0x2A }; // { 0x02, 0x17, 0x58, 0x2A }; // this is a code for int sum(int x, int y) { return x + y; }

    info.SetCode(bytes, 8);

    var sig = SignatureHelper.GetMethodSigHelper(CallingConventions.Standard, typeof(int));
    byte[] bsig = sig.GetSignature();
    bsig[0] = 0x7;

    info.SetLocalSignature(bsig);

    int x = (int) sum.Invoke(null, new object[] { 10, 20 });
    Console.WriteLine(x.ToString());
    Console.ReadLine();
}

TL;DR I want to fix the references of a byte array which represents a dynamic method's IL. How to do that? Also, I don't want to use the ILGenerator(), I want a byte array.


Solution

  • The metadata token can't be reused as is; instead you'll need to use DynamicILInfo.GetTokenFor to get a new token corresponding to the field which you can then use. In your case this would look something like this:

    var tok = info.GetTokenFor(typeof(...).GetField("z").FieldHandle);
    bytes[4] = tok;    
    bytes[5] = tok >> 8;
    bytes[6] = tok >> 16;
    bytes[7] = tok >> 24;
    

    However, you'll probably also need to create your DynamicMethod so that JIT visibility checks are disabled, or your method won't be able to access the private field, anyway.

    On an unrelated note, it's not clear to me what you're doing with signature info stuff - it looks like you're setting the local variable signature to a modified method signature for some reason... I think you should be doing something more like this instead:

    info.SetLocalSignature(SignatureHelper.GetLocalVarSigHelper().GetSignature())
    

    Of course, you'll need to vary this based on what locals your method actually uses.