views:

91

answers:

1

Hi. I was looking at the IL code of a valid method with Reflector and I've run into this:

L_00a5: leave.s L_0103

Instructions with the suffix .s are supposed to take an int8 operand, and sure enough this should be the case with Leave_S as well. However, 0x0103 is 259, which exceeds the capacity of an int8. The method somehow works, but when I read the instructions with method Mono.Reflection.Disassembler.GetInstructions it retrieves

L_00a5: leave.s L_0003

that is, 3 instead of 259, because it's supposed to be an int8. So, my question: how is the original instruction (leave.s L_0103) possible? I have looked at the ECMA documentation for that (Partition III: CIL Instruction Set) and I can't find anything that explains it.

Any ideas? Thanks.


EDIT #1: Ok, I'm an idiot. In the case of branch instructions the offset must be counted from the beginning of the instruction following the current instruction. I swear I read the documentation, but somehow I managed to skip that. In my defence, I'm pretty sick today. Sigh.

Thank you. (And thanks for not calling me an idiot, even though this was pretty idiotic :P)


EDIT #2: By the way, in case anyone is interested, when Mono.Reflection.Disassembler.GetInstructions disassembles the instructions it changes the meaning of the operand in branch instructions. In particular, as it has been pointed out, the operand of a branch instruction represents the offset from the beginning of the next instruction, not from 0. However, Mono.Reflection gives back the offset starting at 0 (which may be why I was confused; although it doesn't explain how I managed to skip part of the documentation).

An extract of MethodBodyReader.ReadOperand(Instruction instruction):

switch (instruction.OpCode.OperandType) {
...
case OperandType.ShortInlineBrTarget:
    instruction.Operand = (sbyte) (il.ReadByte () + il.position);
    break;
...
}

As you can see it adds il.position, which is the offset (starting at 0) of the next instruction. Also, it casts to sbyte, which is the reason I'm getting 3 instead of 259. This appears to be a bug (the offset starting from 0 may be larger than an sbyte). I'll ask Jb Evain (the author) and report back.


EDIT #3: He hasn't answered yet but I've changed it to:

switch (instruction.OpCode.OperandType) {
...
case OperandType.ShortInlineBrTarget:
    instruction.Operand = ((sbyte) il.ReadByte ()) + il.position;
    break;
...
}

and it seems to have solved my problem. I cast to sbyte to get the sign right, in case it's a backwards jump (negative offset), and then when I add il.position (which is an int) the result is an int.

I'll let you know what he says anyway.


EDIT #4: I forgot to report back. The author confirms this was a bug.

+3  A: 

target instruction, represented as a 1-byte signed offset from the beginning of the instruction following the current instruction

0xA5 is within 127 bytes of 0x103. There's no way for a leave.s to reach from 0xA5 to 0x03 however.

Ben Voigt