Dumitru Chirutac
Dumitru Chirutac

Reputation: 637

How to get a correct method reference when performing a JMP instruction to a method that resides in a different assembly

I have the following code that basically creates a DynamicAssembly with 2 types with one public method each.

        var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly
                (new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run);
        var module = assembly.DefineDynamicModule("Main");
        var type1 = module.DefineType("type1");


        var method1 = type1.DefineMethod
                      (
                        "Method1", MethodAttributes.Public, typeof(void), null
                      );

        var gen = method1.GetILGenerator();
        gen.Emit(OpCodes.Ret);

        var t1 = type1.CreateType();

        var createdMethod1 = t1.GetMethod("Method1");

        var type2 = module.DefineType("type2");

        var method2 = type2.DefineMethod
                      (
                        "Method2", MethodAttributes.Public, typeof(void), null
                      );


        byte[] ilCodes = new byte[5];
        ilCodes[0] = (byte)OpCodes.Jmp.Value;
        ilCodes[1] = (byte)(createdMethod1.MetadataToken & 0xFF);
        ilCodes[2] = (byte)(createdMethod1.MetadataToken >> 8 & 0xFF);
        ilCodes[3] = (byte)(createdMethod1.MetadataToken >> 16 & 0xFF);
        ilCodes[4] = (byte)(createdMethod1.MetadataToken >> 24 & 0xFF);

        method2.SetMethodBody(ilCodes, ilCodes.Length, null, null, null);


        var obj = Activator.CreateInstance(type2.CreateType());

        obj.GetType().GetMethod("Method2").Invoke(obj, null);

Whenever I make a call to type2.method2() I have a JMP instruction to type1.method1().

This works like a charm, both types reside in the same assembly.

Now if I want to redirect to a type that is in another assembly how can I get the correct assembly/module reference in order the JMP instruction to succeed. If I just do like this:

        byte[] ilCodes = new byte[5];
        ilCodes[0] = (byte)OpCodes.Jmp.Value;
        ilCodes[1] = (byte)(methodFromOtherAssembly.MetadataToken & 0xFF);
        ilCodes[2] = (byte)(methodFromOtherAssembly.MetadataToken >> 8 & 0xFF);
        ilCodes[3] = (byte)(methodFromOtherAssembly.MetadataToken >> 16 & 0xFF);
        ilCodes[4] = (byte)(methodFromOtherAssembly.MetadataToken >> 24 & 0xFF);

It keeps failing with IndexNotFound exception.

I want to do this with Raw IL byte instructions.

Upvotes: 3

Views: 278

Answers (1)

Mike Zboray
Mike Zboray

Reputation: 40818

Metadata tokens are not globally unique. They are resolved in the context of the calling method's module. You need to use GetMetadataToken on the module to get the proper token. Your first example works because the methods share the same module.

This will get the proper token from the dynamic module:

byte[] ilCodes = new byte[5];
int token = module.GetMetadataToken(methodFromOtherAssembly).Token;
ilCodes[0] = (byte)OpCodes.Jmp.Value;
ilCodes[1] = (byte)(token & 0xFF);
ilCodes[2] = (byte)(token >> 8 & 0xFF);
ilCodes[3] = (byte)(token >> 16 & 0xFF);
ilCodes[4] = (byte)(token >> 24 & 0xFF);

Upvotes: 6

Related Questions