When we write the UnwindInfo for stack space allocation, depending on the size, we write an extra WORD/DWORD to the UnwindInfo stream as follow:
|
void Compiler::unwindAllocStackWindows(unsigned size) |
|
{ |
|
assert(compGeneratingProlog); |
|
|
|
FuncInfoDsc* func = funCurrentFunc(); |
|
|
|
assert(func->unwindHeader.Version == 1); // Can't call this before unwindBegProlog |
|
assert(func->unwindHeader.CountOfUnwindCodes == 0); // Can't call this after unwindReserve |
|
assert(size % 8 == 0); // Stack size is *always* 8 byte aligned |
|
UNWIND_CODE* code; |
|
if (size <= 128) |
|
{ |
|
assert(func->unwindCodeSlot > sizeof(UNWIND_CODE)); |
|
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
|
code->UnwindOp = UWOP_ALLOC_SMALL; |
|
code->OpInfo = (size - 8) / 8; |
|
} |
|
else if (size <= 0x7FFF8) |
|
{ |
|
assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(USHORT))); |
|
USHORT* codedSize = (USHORT*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(USHORT)]; |
|
*codedSize = (USHORT)(size / 8); |
|
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
|
code->UnwindOp = UWOP_ALLOC_LARGE; |
|
code->OpInfo = 0; |
|
} |
|
else |
|
{ |
|
assert(func->unwindCodeSlot > (sizeof(UNWIND_CODE) + sizeof(ULONG))); |
|
ULONG* codedSize = (ULONG*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(ULONG)]; |
|
*codedSize = size; |
|
code = (UNWIND_CODE*)&func->unwindCodes[func->unwindCodeSlot -= sizeof(UNWIND_CODE)]; |
|
code->UnwindOp = UWOP_ALLOC_LARGE; |
|
code->OpInfo = 1; |
|
} |
|
unsigned int cbProlog = unwindGetCurrentOffset(func); |
|
noway_assert((BYTE)cbProlog == cbProlog); |
|
code->CodeOffset = (BYTE)cbProlog; |
|
} |
Notice how line 298 and line 307 moves the unwindCodeSlot further than just the usual sizeof(UNWIND_CODE).
However, the similar logic is missing from the decoder, here is the parsing code:
|
public UnwindCode(byte[] image, int index, ref int offset) |
|
{ |
|
Index = index; |
|
|
|
int off = offset; |
|
CodeOffset = NativeReader.ReadByte(image, ref off); |
|
byte op = NativeReader.ReadByte(image, ref off); |
|
UnwindOp = (UnwindOpCodes)(op & 15); |
|
OpInfo = (byte)(op >> 4); |
|
|
|
OffsetLow = CodeOffset; |
|
OffsetHigh = OpInfo; |
|
|
|
FrameOffset = NativeReader.ReadUInt16(image, ref offset); |
|
NextFrameOffset = -1; |
|
|
|
IsOpInfo = false; |
The size of the extra word/dword happens to be a multiple of 2, therefore these word/dword are misinterpreted as extra unwind opcode. This is just wrong. This explains why we saw more than one unwind opcode having the same code offset in R2RDump.
When we write the UnwindInfo for stack space allocation, depending on the size, we write an extra WORD/DWORD to the UnwindInfo stream as follow:
runtime/src/coreclr/src/jit/unwindamd64.cpp
Lines 278 to 316 in 0b16471
Notice how line 298 and line 307 moves the
unwindCodeSlotfurther than just the usualsizeof(UNWIND_CODE).However, the similar logic is missing from the decoder, here is the parsing code:
runtime/src/coreclr/src/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/UnwindInfo.cs
Lines 63 to 79 in 0b16471
The size of the extra word/dword happens to be a multiple of 2, therefore these word/dword are misinterpreted as extra unwind opcode. This is just wrong. This explains why we saw more than one unwind opcode having the same code offset in
R2RDump.