3

I am using Mono.Cecil to inject some instructions in property setter and i have weird bug when injecting Brfalse_s instruction. Here is code, it's short and simple.

private void InjectNullProtection(PropertyDefinition property)
{
    IList<Instruction> instructions = property.SetMethod.Body.Instructions;

        Instruction first = instructions.First();

        if (!property.SetMethod.Body.Variables.Any(v => v.VariableType.FullName == GetTypeReference(typeof(bool)).FullName))
        {
            property.SetMethod.Body.Variables.Add(new VariableDefinition(GetTypeReference(typeof(bool))));
        }

        ILProcessor processor = property.SetMethod.Body.GetILProcessor();
        Instruction ret = instructions.Single(i => i.OpCode.Code == Code.Ret);

        processor.InsertBefore(first, processor.Create(OpCodes.Nop));
        processor.InsertBefore(first, processor.Create(OpCodes.Ldarg_1));
        processor.InsertBefore(first, processor.Create(OpCodes.Ldnull));
        processor.InsertBefore(first, processor.Create(OpCodes.Cgt_Un));
        processor.InsertBefore(first, processor.Create(OpCodes.Stloc_0));
        processor.InsertBefore(first, processor.Create(OpCodes.Ldloc_0));

        processor.InsertBefore(first, processor.Create(OpCodes.Brfalse_S, ret));
}

Everything works fine if target setter doesn't have any try/catch blocks. But if there are try/catch blocks in setter, target label for for Brfalse_s instruction is wrong, instead of Ret instruction label, it has label of instruction from one of try blocks. Here is Example:

IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldnull
IL_0003: cgt.un
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: brfalse.s IL_0037

IL_0009: nop
IL_000a: ldarg.0
IL_000b: ldstr "Name"
IL_0010: callvirt instance void WpfApplication2.Observable1::OnPropertyChanging(string)
IL_0015: nop
IL_0016: ldarg.0
IL_0017: ldarg.1
IL_0018: stfld string WpfApplication2.Observable1::'<Name>k__BackingField'
IL_001d: ldarg.0
IL_001e: ldstr "Name"
IL_0023: callvirt instance void WpfApplication2.Observable1::OnPropertyChanged(string)
IL_0028: nop
IL_0029: nop
IL_002a: ldarg.0
IL_002b: ldstr "FullName"
IL_0030: callvirt instance void WpfApplication2.Observable1::OnPropertyChanged(string)
IL_0035: nop
IL_0036: nop

IL_0037: ret

Here you can see that Brfalse_s has IL_0037 target label, which is correct. But in next example, something goes wrong.

IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldnull
IL_0003: cgt.un
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: brfalse.s IL_0011

IL_0009: nop
.try
{
    IL_000a: nop
    IL_000b: ldarg.0
    IL_000c: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()

    IL_0011: ldarg.0
    IL_0012: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0018: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_001d: callvirt instance void WpfApplication2.Observable2::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_0022: nop
    IL_0023: ldarg.0
    IL_0024: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0029: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_002e: ldarg.0
    IL_002f: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0035: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_003a: callvirt instance void WpfApplication2.Observable3::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_003f: nop
    IL_0040: ldarg.0
    IL_0041: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0046: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_004b: callvirt instance class WpfApplication2.Observable4 WpfApplication2.Observable3::get_Observable4()
    IL_0050: ldarg.0
    IL_0051: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3Observable4PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0057: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_005c: callvirt instance void WpfApplication2.Observable4::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_0061: nop
    IL_0062: ldarg.0
    IL_0063: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0068: ldarg.0
    IL_0069: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_006f: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_0074: callvirt instance void WpfApplication2.Observable2::remove_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_0079: nop
    IL_007a: ldarg.0
    IL_007b: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0080: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_0085: ldarg.0
    IL_0086: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_008c: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_0091: callvirt instance void WpfApplication2.Observable3::remove_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_0096: nop
    IL_0097: leave.s IL_009e
} // end .try
catch [mscorlib]System.NullReferenceException
{
    IL_0099: pop
    IL_009a: nop
    IL_009b: nop
    IL_009c: leave.s IL_009e
} // end handler

IL_009e: nop
IL_009f: nop
.try
{
    IL_00a0: nop
    IL_00a1: ldarg.0
    IL_00a2: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_00a7: ldarg.0
    IL_00a8: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_00ae: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_00b3: callvirt instance void WpfApplication2.Observable2::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_00b8: nop
    IL_00b9: ldarg.0
    IL_00ba: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_00bf: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_00c4: ldarg.0
    IL_00c5: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2Observable3PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_00cb: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_00d0: callvirt instance void WpfApplication2.Observable3::remove_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_00d5: nop
    IL_00d6: ldarg.0
    IL_00d7: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_00dc: ldarg.0
    IL_00dd: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_00e3: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_00e8: callvirt instance void WpfApplication2.Observable2::remove_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_00ed: nop
    IL_00ee: leave.s IL_00f5
} // end .try
catch [mscorlib]System.NullReferenceException
{
    IL_00f0: pop
    IL_00f1: nop
    IL_00f2: nop
    IL_00f3: leave.s IL_00f5
} // end handler

IL_00f5: nop
IL_00f6: nop
IL_00f7: ldarg.0
IL_00f8: ldstr "Observable2"
IL_00fd: callvirt instance void WpfApplication2.Observable1::OnPropertyChanging(string)
IL_0102: nop
IL_0103: ldarg.0
IL_0104: ldarg.1
IL_0105: stfld class WpfApplication2.Observable2 WpfApplication2.Observable1::'<Observable2>k__BackingField'
IL_010a: nop
.try
{
    IL_010b: nop
    IL_010c: ldarg.0
    IL_010d: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0112: ldarg.0
    IL_0113: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0119: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_011e: callvirt instance void WpfApplication2.Observable2::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_0123: nop
    IL_0124: ldarg.0
    IL_0125: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_012a: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_012f: ldarg.0
    IL_0130: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0136: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_013b: callvirt instance void WpfApplication2.Observable3::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_0140: nop
    IL_0141: ldarg.0
    IL_0142: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0147: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_014c: callvirt instance class WpfApplication2.Observable4 WpfApplication2.Observable3::get_Observable4()
    IL_0151: ldarg.0
    IL_0152: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3Observable4PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0158: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_015d: callvirt instance void WpfApplication2.Observable4::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_0162: nop
    IL_0163: ldarg.0
    IL_0164: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0169: ldarg.0
    IL_016a: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_0170: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_0175: callvirt instance void WpfApplication2.Observable2::add_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_017a: nop
    IL_017b: ldarg.0
    IL_017c: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_0181: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_0186: ldarg.0
    IL_0187: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Observable4Name_OnObservable2Observable3PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_018d: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_0192: callvirt instance void WpfApplication2.Observable3::add_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_0197: nop
    IL_0198: leave.s IL_019f
} // end .try
catch [mscorlib]System.NullReferenceException
{
    IL_019a: pop
    IL_019b: nop
    IL_019c: nop
    IL_019d: leave.s IL_019f
} // end handler

IL_019f: nop
IL_01a0: nop
.try
{
    IL_01a1: nop
    IL_01a2: ldarg.0
    IL_01a3: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_01a8: ldarg.0
    IL_01a9: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_01af: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_01b4: callvirt instance void WpfApplication2.Observable2::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_01b9: nop
    IL_01ba: ldarg.0
    IL_01bb: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_01c0: callvirt instance class WpfApplication2.Observable3 WpfApplication2.Observable2::get_Observable3()
    IL_01c5: ldarg.0
    IL_01c6: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2Observable3PropertyChanged(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_01cc: newobj instance void [System]System.ComponentModel.PropertyChangedEventHandler::.ctor(object, native int)
    IL_01d1: callvirt instance void WpfApplication2.Observable3::add_PropertyChanged(class [System]System.ComponentModel.PropertyChangedEventHandler)
    IL_01d6: nop
    IL_01d7: ldarg.0
    IL_01d8: call instance class WpfApplication2.Observable2 WpfApplication2.Observable1::get_Observable2()
    IL_01dd: ldarg.0
    IL_01de: ldftn instance void WpfApplication2.Observable1::FullName_Observable2Observable3Name_OnObservable2PropertyChanging(object, class [System]System.ComponentModel.PropertyChangedEventArgs)
    IL_01e4: newobj instance void [System]System.ComponentModel.PropertyChangingEventHandler::.ctor(object, native int)
    IL_01e9: callvirt instance void WpfApplication2.Observable2::add_PropertyChanging(class [System]System.ComponentModel.PropertyChangingEventHandler)
    IL_01ee: nop
    IL_01ef: leave.s IL_01f6
} // end .try
catch [mscorlib]System.NullReferenceException
{
    IL_01f1: pop
    IL_01f2: nop
    IL_01f3: nop
    IL_01f4: leave.s IL_01f6
} // end handler

IL_01f6: nop
IL_01f7: ldarg.0
IL_01f8: ldstr "Observable2"
IL_01fd: callvirt instance void WpfApplication2.Observable1::OnPropertyChanged(string)
IL_0202: nop
IL_0203: nop
IL_0204: ldarg.0
IL_0205: ldstr "FullName"
IL_020a: callvirt instance void WpfApplication2.Observable1::OnPropertyChanged(string)
IL_020f: nop
IL_0210: nop
IL_0211: ret

Here, Brfalse_s has IL_0011 as target label instead of IL_0211.

  • 1
    Why the `stloc`+`ldloc`? You'd save yourself the work with adding a variable and everything if you just used the result directly. And is there some reason why you don't just use a single `brnull` instead of those comparisons? Are you just emitting code that was originally produced by the C# compiler (in debug, apparently)? – Luaan Nov 12 '15 at 16:22
  • I am not IL expert :) I just started learning it, and i write C# code, and i use `ILSpy` to see `IL` output, and they copy it :D How can i use result directly? – Vladimir Djurdjevic Nov 12 '15 at 16:25
  • If I understand your code correctly, what you're trying to do is basically `if (arg1 != null) return;`, right? That can be done with a simple `ldarg.1` followed by `brnull theretlabel`. Even if you decide to keep your original code, IL is stack-based, so when `cgt.un` returns a value, it returns it to the (virtual) stack - it can be immediately used by the `brfalse` without storing it in a variable. The initial `nop` is also unnecessary - the compiler uses it to allow code injection by the debugger, but you don't need that. – Luaan Nov 12 '15 at 16:34
  • Yeah, i understand how it works (stack machine) but i just wanted to try to inject something with `Mono.Cecil` just to see how it works. That's why i just reproduced what C# compiler generated (Debug). I know that `Nop` is not necessary and what is it's purpose. What i didn't know is this thing with offsets in branch instructions, and that `_S` means short jump. I just saw that C# compiler generates it for jumps, so i did the same :D Thank you so much for quick help, and detailed explanation :). – Vladimir Djurdjevic Nov 12 '15 at 21:00

1 Answers1

5

This should be obvious - you can't use brfalse.s for a long jump :)

This has nothing to do with the try, the problem is that your jump target is simply too far. Use brfalse instead.

Note how the target label becomes 0x0011 instead of 0x0211 - brfalse.s (and all the other .s jumps) take a single byte as the jump offset, so the rest of the offset is cut off.

Of course, brfalse is a bit bigger, but that doesn't really matter much - it only inflates the IL code which is already quite inflated anyway. The JIT compiler will use the most efficient jump regardless of what you do here.

When emitting custom IL, you have to be very careful - there is no processing whatsoever, you're just writing bytes to a stream. You're not going to get a whole lof of errors while doing the emitting, almost everything you do wrong is going to cause a runtime error.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • I can't believe it. I didn't realize that `.s` is about code length. Thank you for quick answer, i spent my whole day trying to figure out this. – Vladimir Djurdjevic Nov 12 '15 at 16:22