In compiled IL, there are no labels. Instead, jump instructions use relative offsets from the start of the following instruction.
For example, consider this trivial C# function:
public static bool IsZero(int n)
{
if (n == 0)
return true;
return false;
}
In IL, you might write it like this:
.method public hidebysig static bool IsZero(int32 n) cil managed
{
ldarg.0
brtrue.s label
ldc.i4.1
ret
label:
ldc.i4.0
ret
}
If you compile that using ilasm and then decompile it back using ildasm, with "Show bytes" enabled, you get:
.method public hidebysig static bool IsZero(int32 n) cil managed
// SIG: 00 01 02 08
{
// Method begins at RVA 0x2052
// Code size 7 (0x7)
.maxstack 8
IL_0000: /* 02 | */ ldarg.0
IL_0001: /* 2D | 02 */ brtrue.s IL_0005
IL_0003: /* 17 | */ ldc.i4.1
IL_0004: /* 2A | */ ret
IL_0005: /* 16 | */ ldc.i4.0
IL_0006: /* 2A | */ ret
} // end of method Program::IsZero
Notice how the labels are not in any way represented in the bytes (shown in comments). And that brtrue.s label
from the original IL is here shown as brtrue.s IL_0005
, where the bytes are 2D 02
. 2D
is the compiled form of brtrue.s
, and 02
is a relative offset. Since the following instruction starts at absolute offset 3, the target is at absolute offset 3 + 2 = 5.