-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
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
| 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:
runtime/src/coreclr/src/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/UnwindInfo.cs
Lines 63 to 79 in 0b16471
| 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.