/// <summary> /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a> /// </summary> public GcInfo(byte[] image, int offset, Machine machine, ushort majorVersion) { Offset = offset; CodeLength = (int)NativeReader.DecodeUnsignedGc(image, ref offset); Header = InfoHdrDecoder.DecodeHeader(image, ref offset, CodeLength); SlotTable = new GcSlotTable(image, Header, ref offset); Transitions = new Dictionary <int, List <BaseGcTransition> >(); if (Header.Interruptible) { GetTransitionsFullyInterruptible(image, ref offset); } else if (Header.EbpFrame) { GetTransitionsEbpFrame(image, ref offset); } else { GetTransitionsNoEbp(image, ref offset); } Size = offset - Offset; }
public UnwindInfo(byte[] image, int offset) { int startOffset = offset; FunctionLength = NativeReader.DecodeUnsignedGc(image, ref offset); Size = offset - startOffset; }
/// <summary> /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a> /// </summary> private void DecodeFrameVariableLifetimeTable(byte[] image, InfoHdrSmall header, ref int offset) { uint count = header.VarPtrTableSize; uint curOffs = 0; while (count-- > 0) { uint varOffs = NativeReader.DecodeUnsignedGc(image, ref offset); uint begOffs = NativeReader.DecodeUDelta(image, ref offset, curOffs); uint endOffs = NativeReader.DecodeUDelta(image, ref offset, begOffs); uint lowBits = varOffs & 0x3; varOffs &= ~OFFSET_MASK; curOffs = begOffs; string reg = header.EbpFrame ? "BP" : "SP"; GcSlots.Add(new GcSlot(GcSlots.Count, reg, (int)begOffs, (int)endOffs, (int)varOffs, (int)lowBits, GcSlotFlags.GC_SLOT_BASE)); } }
/// <summary> /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a> /// </summary> private void SaveCallTransition(byte[] image, ref int offset, uint val, uint curOffs, uint callRegMask, bool callPndTab, uint callPndTabCnt, uint callPndMask, uint lastSkip, ref uint imask) { uint iregMask, iargMask; iregMask = imask & 0xF; iargMask = imask >> 4; GcTransitionCall transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, callRegMask, iregMask); AddNewTransition(transition); if (callPndTab) { for (int i = 0; i < callPndTabCnt; i++) { uint pndOffs = NativeReader.DecodeUnsignedGc(image, ref offset); uint stkOffs = val & ~byref_OFFSET_FLAG; uint lowBit = val & byref_OFFSET_FLAG; transition.PtrArgs.Add(new GcTransitionCall.PtrArg(pndOffs, 0)); } } else { if (callPndMask != 0) { transition.ArgMask = callPndMask; } if (iargMask != 0) { transition.IArgs = iargMask; } } imask = lastSkip = 0; }
public UnwindInfo(byte[] image, int offset) { FunctionLength = NativeReader.DecodeUnsignedGc(image, ref offset); Size = sizeof(int); }
private void GetTransitionsNoEbp(byte[] image, ref int offset) { uint curOffs = 0; uint lastSkip = 0; uint imask = 0; for (; ;) { uint val = image[offset++]; if ((val & 0x80) == 0) { if ((val & 0x40) == 0) { if ((val & 0x20) == 0) { // push 000DDDDD push one item, 5-bit delta curOffs += val & 0x1F; AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.PUSH)); } else { // push 00100000 [pushCount] ESP push multiple items uint pushCount = NativeReader.DecodeUnsignedGc(image, ref offset); AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.PUSH, false, false, (int)pushCount)); } } else { uint popSize; uint skip; if ((val & 0x3f) == 0) { // // skip 01000000 [Delta] Skip arbitrary sized delta // skip = NativeReader.DecodeUnsignedGc(image, ref offset); curOffs += skip; lastSkip = skip; } else { // pop 01CCDDDD pop CC items, 4-bit delta popSize = (val & 0x30) >> 4; skip = val & 0x0f; curOffs += skip; if (popSize > 0) { AddNewTransition(new GcTransitionRegister((int)curOffs, Registers.ESP, Action.POP, false, false, (int)popSize)); } else { lastSkip = skip; } } } } else { uint callArgCnt; uint callRegMask; bool callPndTab = false; uint callPndMask = 0; uint callPndTabCnt = 0, callPndTabSize = 0; switch ((val & 0x70) >> 4) { default: // // call 1PPPPPPP Call Pattern, P=[0..79] // CallPattern.DecodeCallPattern((val & 0x7f), out callArgCnt, out callRegMask, out callPndMask, out lastSkip); curOffs += lastSkip; SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); break; case 5: // // call 1101RRRR DDCCCMMM Call RegMask=RRRR,ArgCnt=CCC, // ArgMask=MMM Delta=commonDelta[DD] // callRegMask = val & 0xf; // EBP,EBX,ESI,EDI val = image[offset++]; callPndMask = (val & 0x7); callArgCnt = (val >> 3) & 0x7; lastSkip = CallPattern.callCommonDelta[val >> 6]; curOffs += lastSkip; SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); break; case 6: // // call 1110RRRR [ArgCnt] [ArgMask] // Call ArgCnt,RegMask=RRR,ArgMask // callRegMask = val & 0xf; // EBP,EBX,ESI,EDI callArgCnt = NativeReader.DecodeUnsignedGc(image, ref offset); callPndMask = NativeReader.DecodeUnsignedGc(image, ref offset); SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); break; case 7: switch (val & 0x0C) { case 0x00: // iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask imask = NativeReader.DecodeUnsignedGc(image, ref offset); AddNewTransition(new IPtrMask((int)curOffs, imask)); break; case 0x04: AddNewTransition(new CalleeSavedRegister((int)curOffs, (CalleeSavedRegisters)(val & 0x3))); break; case 0x08: val = image[offset++]; callRegMask = val & 0xF; imask = val >> 4; lastSkip = NativeReader.ReadUInt32(image, ref offset); curOffs += lastSkip; callArgCnt = NativeReader.ReadUInt32(image, ref offset); callPndTabCnt = NativeReader.ReadUInt32(image, ref offset); callPndTabSize = NativeReader.ReadUInt32(image, ref offset); callPndTab = true; SaveCallTransition(image, ref offset, val, curOffs, callRegMask, callPndTab, callPndTabCnt, callPndMask, lastSkip, ref imask); break; case 0x0C: return; default: throw new BadImageFormatException("Invalid GC encoding"); } break; } } } }
/// <summary> /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a> /// </summary> private void GetTransitionsEbpFrame(byte[] image, ref int offset) { while (true) { uint argMask = 0, byrefArgMask = 0; uint regMask, byrefRegMask = 0; uint argCnt = 0; int argOffset = offset; uint argTabSize; uint val, nxt; uint curOffs = 0; // Get the next byte and check for a 'special' entry uint encType = image[offset++]; GcTransitionCall transition = null; switch (encType) { default: // A tiny or small call entry val = encType; if ((val & 0x80) == 0x00) { if ((val & 0x0F) != 0) { // A tiny call entry curOffs += (val & 0x0F); regMask = (val & 0x70) >> 4; argMask = 0; } else { Registers reg; if ((val & 0x10) != 0) { reg = Registers.EDI; } else if ((val & 0x20) != 0) { reg = Registers.ESI; } else if ((val & 0x40) != 0) { reg = Registers.EBX; } else { throw new BadImageFormatException("Invalid register"); } transition = new GcTransitionCall((int)curOffs); transition.CallRegisters.Add(new GcTransitionCall.CallRegister(reg, false)); AddNewTransition(transition); continue; } } else { // A small call entry curOffs += (val & 0x7F); val = image[offset++]; regMask = val >> 5; argMask = val & 0x1F; } break; case 0xFD: // medium encoding argMask = image[offset++]; val = image[offset++]; argMask |= (val & 0xF0) << 4; nxt = image[offset++]; curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4); regMask = nxt >> 5; // EBX,ESI,EDI break; case 0xF9: // medium encoding with byrefs curOffs += image[offset++]; val = image[offset++]; argMask = val & 0x1F; regMask = val >> 5; val = image[offset++]; byrefArgMask = val & 0x1F; byrefRegMask = val >> 5; break; case 0xFE: // large encoding case 0xFA: // large encoding with byrefs val = image[offset++]; regMask = val & 0x7; byrefRegMask = val >> 4; curOffs += NativeReader.ReadUInt32(image, ref offset); argMask = NativeReader.ReadUInt32(image, ref offset); if (encType == 0xFA) // read byrefArgMask { byrefArgMask = NativeReader.ReadUInt32(image, ref offset); } break; case 0xFB: // huge encoding val = image[offset++]; regMask = val & 0x7; byrefRegMask = val >> 4; curOffs = NativeReader.ReadUInt32(image, ref offset); argCnt = NativeReader.ReadUInt32(image, ref offset); argTabSize = NativeReader.ReadUInt32(image, ref offset); argOffset = offset; offset += (int)argTabSize; break; case 0xFF: return; } /* * Here we have the following values: * * curOffs ... the code offset of the call * regMask ... mask of live pointer register variables * argMask ... bitmask of pushed pointer arguments * byrefRegMask ... byref qualifier for regMask * byrefArgMask ... byrer qualifier for argMask */ transition = new GcTransitionCall((int)curOffs, Header.EbpFrame, regMask, byrefRegMask); AddNewTransition(transition); if (argCnt != 0) { do { val = NativeReader.DecodeUnsignedGc(image, ref argOffset); uint stkOffs = val & ~byref_OFFSET_FLAG; uint lowBit = val & byref_OFFSET_FLAG; transition.PtrArgs.Add(new GcTransitionCall.PtrArg(stkOffs, lowBit)); }while (--argCnt > 0); } else { transition.ArgMask = argMask; if (byrefArgMask != 0) { transition.IArgs = byrefArgMask; } } } }
/// <summary> /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/gcdump/i386/gcdumpx86.cpp">GCDump::DumpGCTable</a> /// </summary> private void GetTransitionsFullyInterruptible(byte[] image, ref int offset) { uint argCnt = 0; bool isThis = false; bool iptr = false; uint curOffs = 0; while (true) { uint isPop; uint argOffs; uint val = image[offset++]; if ((val & 0x80) == 0) { /* A small 'regPtr' encoding */ curOffs += val & 0x7; Action isLive = Action.LIVE; if ((val & 0x40) == 0) { isLive = Action.DEAD; } AddNewTransition(new GcTransitionRegister((int)curOffs, (Registers)((val >> 3) & 7), isLive, isThis, iptr)); isThis = false; iptr = false; continue; } /* This is probably an argument push/pop */ argOffs = (val & 0x38) >> 3; /* 6 [110] and 7 [111] are reserved for other encodings */ if (argOffs < 6) { /* A small argument encoding */ curOffs += (val & 0x07); isPop = (val & 0x40); ArgEncoding(image, ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); continue; } else if (argOffs == 6) { if ((val & 0x40) != 0) { curOffs += (((val & 0x07) + 1) << 3); } else { // non-ptr arg push curOffs += (val & 0x07); argCnt++; AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.PUSH, Header.EbpFrame, false, false, false)); } continue; } // argOffs was 7 [111] which is reserved for the larger encodings switch (val) { case 0xFF: return; case 0xBC: isThis = true; break; case 0xBF: iptr = true; break; case 0xB8: val = NativeReader.DecodeUnsignedGc(image, ref offset); curOffs += val; break; case 0xF8: case 0xFC: isPop = val & 0x04; argOffs = NativeReader.DecodeUnsignedGc(image, ref offset); ArgEncoding(image, ref isPop, ref argOffs, ref argCnt, ref curOffs, ref isThis, ref iptr); break; case 0xFD: argOffs = NativeReader.DecodeUnsignedGc(image, ref offset); AddNewTransition(new GcTransitionPointer((int)curOffs, argOffs, argCnt, Action.KILL, Header.EbpFrame)); break; case 0xF9: argOffs = NativeReader.DecodeUnsignedGc(image, ref offset); argCnt += argOffs; break; default: throw new BadImageFormatException($"Unexpected special code {val}"); } } }
/// <summary> /// Initialize the GcInfo header /// based on <a href="https://github.com/dotnet/runtime/blob/main/src/coreclr/inc/gcdecoder.cpp">src\inc\gcdecoder.cpp</a> DecodeHeader and <a href="https://github.com/dotnet/runtime/blob/main/src/coreclr/gcdump/i386/gcdumpx86.cpp">GCDump::DumpInfoHdr</a> /// </summary> public static InfoHdrSmall DecodeHeader(byte[] image, ref int offset, int codeLength) { byte nextByte = image[offset++]; byte encoding = (byte)(nextByte & 0x7f); InfoHdrSmall header = GetInfoHdr(encoding); while ((nextByte & (uint)InfoHdrAdjustConstants.MORE_BYTES_TO_FOLLOW) != 0) { nextByte = image[offset++]; encoding = (byte)(nextByte & (uint)InfoHdrAdjustConstants.ADJ_ENCODING_MAX); if (encoding < (uint)InfoHdrAdjust.NEXT_FOUR_START) { if (encoding < (uint)InfoHdrAdjust.SET_ARGCOUNT) { header.FrameSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_FRAMESIZE); } else if (encoding < (uint)InfoHdrAdjust.SET_PROLOGSIZE) { header.ArgCount = (byte)(encoding - (uint)InfoHdrAdjust.SET_ARGCOUNT); } else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGSIZE) { header.PrologSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_PROLOGSIZE); } else if (encoding < (uint)InfoHdrAdjust.SET_EPILOGCNT) { header.EpilogSize = (byte)(encoding - (uint)InfoHdrAdjust.SET_EPILOGSIZE); } else if (encoding < (uint)InfoHdrAdjust.SET_UNTRACKED) { header.EpilogCount = (byte)((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) / 2); header.EpilogAtEnd = ((encoding - (uint)InfoHdrAdjust.SET_EPILOGCNT) & 1) == 1 ? true : false; } else if (encoding < (uint)InfoHdrAdjust.FIRST_FLIP) { header.UntrackedCnt = (byte)(encoding - (uint)InfoHdrAdjust.SET_UNTRACKED); } else { switch (encoding) { case (byte)InfoHdrAdjust.FLIP_EDI_SAVED: header.EdiSaved = !header.EdiSaved; break; case (byte)InfoHdrAdjust.FLIP_ESI_SAVED: header.EsiSaved = !header.EsiSaved; break; case (byte)InfoHdrAdjust.FLIP_EBX_SAVED: header.EbxSaved = !header.EbxSaved; break; case (byte)InfoHdrAdjust.FLIP_EBP_SAVED: header.EbpSaved = !header.EbpSaved; break; case (byte)InfoHdrAdjust.FLIP_EBP_FRAME: header.EbpFrame = !header.EbpFrame; break; case (byte)InfoHdrAdjust.FLIP_INTERRUPTIBLE: header.Interruptible = !header.Interruptible; break; case (byte)InfoHdrAdjust.FLIP_DOUBLE_ALIGN: header.DoubleAlign = !header.DoubleAlign; break; case (byte)InfoHdrAdjust.FLIP_SECURITY: header.Security = !header.Security; break; case (byte)InfoHdrAdjust.FLIP_HANDLERS: header.Handlers = !header.Handlers; break; case (byte)InfoHdrAdjust.FLIP_LOCALLOC: header.Localloc = !header.Localloc; break; case (byte)InfoHdrAdjust.FLIP_EDITnCONTINUE: header.EditNcontinue = !header.EditNcontinue; break; case (byte)InfoHdrAdjust.FLIP_VAR_PTR_TABLE_SZ: header.VarPtrTableSize ^= HAS_VARPTR; break; case (byte)InfoHdrAdjust.FFFF_UNTRACKED_CNT: header.UntrackedCnt = HAS_UNTRACKED; break; case (byte)InfoHdrAdjust.FLIP_VARARGS: header.Varargs = !header.Varargs; break; case (byte)InfoHdrAdjust.FLIP_PROF_CALLBACKS: header.ProfCallbacks = !header.ProfCallbacks; break; case (byte)InfoHdrAdjust.FLIP_HAS_GENERICS_CONTEXT: header.GenericsContext ^= 1; break; case (byte)InfoHdrAdjust.FLIP_GENERICS_CONTEXT_IS_METHODDESC: header.GenericsContextIsMethodDesc ^= 1; break; case (byte)InfoHdrAdjust.FLIP_HAS_GS_COOKIE: header.GsCookieOffset ^= HAS_GS_COOKIE_OFFSET; break; case (byte)InfoHdrAdjust.FLIP_SYNC: header.SyncStartOffset ^= HAS_SYNC_OFFSET; break; case (byte)InfoHdrAdjust.FLIP_REV_PINVOKE_FRAME: header.RevPInvokeOffset ^= HAS_REV_PINVOKE_FRAME_OFFSET; break; case (byte)InfoHdrAdjust.NEXT_OPCODE: nextByte = image[offset++]; encoding = (byte)(nextByte & (int)InfoHdrAdjustConstants.ADJ_ENCODING_MAX); // encoding here always corresponds to codes in InfoHdrAdjust2 set if (encoding < (int)InfoHdrAdjustConstants.SET_RET_KIND_MAX) { header.ReturnKind = (ReturnKinds)encoding; } else { throw new BadImageFormatException("Unexpected gcinfo header encoding"); } break; default: throw new BadImageFormatException("Unexpected gcinfo header encoding"); } } } else { byte lowBits; switch (encoding >> 4) { case 5: lowBits = (byte)(encoding & 0xf); header.FrameSize <<= 4; header.FrameSize += lowBits; break; case 6: lowBits = (byte)(encoding & 0xf); header.ArgCount <<= 4; header.ArgCount += lowBits; break; case 7: if ((encoding & 0x8) == 0) { lowBits = (byte)(encoding & 0x7); header.PrologSize <<= 3; header.PrologSize += lowBits; } else { lowBits = (byte)(encoding & 0x7); header.EpilogSize <<= 3; header.EpilogSize += lowBits; } break; default: throw new BadImageFormatException("Unexpected gcinfo header encoding"); } } } if (header.UntrackedCnt == HAS_UNTRACKED) { header.HasArgTabOffset = true; header.UntrackedCnt = NativeReader.DecodeUnsignedGc(image, ref offset); } if (header.VarPtrTableSize == HAS_VARPTR) { header.HasArgTabOffset = true; header.VarPtrTableSize = NativeReader.DecodeUnsignedGc(image, ref offset); } if (header.GsCookieOffset == HAS_GS_COOKIE_OFFSET) { header.GsCookieOffset = NativeReader.DecodeUnsignedGc(image, ref offset); } if (header.SyncStartOffset == HAS_SYNC_OFFSET) { header.SyncStartOffset = NativeReader.DecodeUnsignedGc(image, ref offset); header.SyncEndOffset = NativeReader.DecodeUnsignedGc(image, ref offset); } if (header.RevPInvokeOffset == HAS_REV_PINVOKE_FRAME_OFFSET) { header.RevPInvokeOffset = NativeReader.DecodeUnsignedGc(image, ref offset); } header.Epilogs = new List <int>(); if (header.EpilogCount > 1 || (header.EpilogCount != 0 && !header.EpilogAtEnd)) { uint offs = 0; for (int i = 0; i < header.EpilogCount; i++) { offs = NativeReader.DecodeUDelta(image, ref offset, offs); header.Epilogs.Add((int)offs); } } else { if (header.EpilogCount != 0) { header.Epilogs.Add(codeLength - (int)header.EpilogSize); } } if (header.HasArgTabOffset) { header.ArgTabOffset = NativeReader.DecodeUnsignedGc(image, ref offset); } return(header); }