Address more PR feedback.
[platform/upstream/coreclr.git] / src / gcdump / i386 / gcdumpx86.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /*****************************************************************************
5  *                               GCDumpX86.cpp
6  */
7
8 /*****************************************************************************/
9 #ifdef _TARGET_X86_
10 /*****************************************************************************/
11
12 #include "utilcode.h"           // For _ASSERTE()
13 #include "gcdump.h"
14
15
16 /*****************************************************************************/
17
18 #define castto(var,typ) (*(typ *)&var)
19
20 #define sizeto(typ,mem) (offsetof(typ, mem) + sizeof(((typ*)0)->mem))
21
22 #define CALLEE_SAVED_REG_MAXSZ  (4*sizeof(int)) // EBX,ESI,EDI,EBP
23
24 /*****************************************************************************/
25
26 const char *        RegName(unsigned reg)
27 {
28     static const char * const regNames[] =
29     {
30         "EAX",
31         "ECX",
32         "EDX",
33         "EBX",
34
35         "ESP",
36         "EBP",
37         "ESI",
38         "EDI"
39     };
40
41     _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
42
43     return regNames[reg];
44 }
45
46 const char *        CalleeSavedRegName(unsigned reg)
47 {
48     static const char * const regNames[] =
49     {
50         "EDI",
51         "ESI",
52         "EBX",
53         "EBP"
54     };
55
56     _ASSERTE(reg < (sizeof(regNames)/sizeof(regNames[0])));
57
58     return regNames[reg];
59 }
60
61 /*****************************************************************************/
62
63 unsigned            GCDump::DumpInfoHdr (PTR_CBYTE      table,
64                                          InfoHdr*       header,
65                                          unsigned *     methodSize,
66                                          bool           verifyGCTables)
67 {
68     unsigned        count;
69     PTR_CBYTE       tableStart  = table;
70     PTR_CBYTE       bp          = table;
71
72     if (verifyGCTables)
73         _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
74
75     /* Get the method size */
76
77     table += decodeUnsigned(table, methodSize);
78
79     table = decodeHeader(table, header);
80
81     BOOL hasArgTabOffset = FALSE;
82     if (header->untrackedCnt == HAS_UNTRACKED)
83     {
84         hasArgTabOffset = TRUE;
85         table += decodeUnsigned(table, &count);
86         header->untrackedCnt = count;
87     }
88
89     if (header->varPtrTableSize == HAS_VARPTR)
90     {
91         hasArgTabOffset = TRUE;
92         table += decodeUnsigned(table, &count);
93         header->varPtrTableSize = count;
94     }
95
96     if (header->gsCookieOffset == HAS_GS_COOKIE_OFFSET)
97     {
98         table += decodeUnsigned(table, &count);
99         header->gsCookieOffset = count;
100     }
101
102     if (header->syncStartOffset == HAS_SYNC_OFFSET)
103     {
104         table += decodeUnsigned(table, &count);
105         header->syncStartOffset = count;
106         table += decodeUnsigned(table, &count);
107         header->syncEndOffset = count;
108     }
109
110     //
111     // First print out all the basic information
112     //
113     
114     gcPrintf("    method      size   = %04X\n", *methodSize);
115     gcPrintf("    prolog      size   = %2u \n", header->prologSize);
116     gcPrintf("    epilog      size   = %2u \n", header->epilogSize);
117     gcPrintf("    epilog     count   = %2u \n", header->epilogCount);
118     gcPrintf("    epilog      end    = %s  \n", header->epilogAtEnd   ? "yes" : "no");
119     
120     gcPrintf("    callee-saved regs  = ");
121     if (header->ediSaved) gcPrintf("EDI ");
122     if (header->esiSaved) gcPrintf("ESI ");
123     if (header->ebxSaved) gcPrintf("EBX ");
124     if (header->ebpSaved) gcPrintf("EBP ");
125     gcPrintf("\n");
126         
127     gcPrintf("    ebp frame          = %s  \n", header->ebpFrame      ? "yes" : "no");
128     gcPrintf("    fully interruptible= %s  \n", header->interruptible ? "yes" : "no");
129     gcPrintf("    double align       = %s  \n", header->doubleAlign   ? "yes" : "no");
130     gcPrintf("    arguments size     = %2u DWORDs\n", header->argCount);
131     gcPrintf("    stack frame size   = %2u DWORDs\n", header->frameSize);
132     gcPrintf("    untracked count    = %2u \n", header->untrackedCnt);
133     gcPrintf("    var ptr tab count  = %2u \n", header->varPtrTableSize);
134
135     //
136     // Now display optional information
137     //
138     
139     if (header->security)       gcPrintf("    security check obj = yes\n");
140     if (header->handlers)       gcPrintf("    exception handlers = yes\n");
141     if (header->localloc)       gcPrintf("    localloc           = yes\n");
142     if (header->editNcontinue)  gcPrintf("    edit & continue    = yes\n");
143     if (header->profCallbacks)  gcPrintf("    profiler callbacks = yes\n");
144     if (header->varargs)        gcPrintf("    varargs            = yes\n");
145     if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET) 
146                                 gcPrintf("    GuardStack cookie  = [%s%u]\n",
147                                           header->ebpFrame ? "EBP-" : "ESP+", header->gsCookieOffset);
148     if (header->syncStartOffset != INVALID_SYNC_OFFSET) 
149                                 gcPrintf("    Sync region = [%u,%u]\n",
150                                           header->syncStartOffset, header->syncEndOffset);
151
152     if  (header->epilogCount > 1 || (header->epilogCount != 0 &&
153                                      header->epilogAtEnd == 0))
154     {
155         if (verifyGCTables)
156             _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
157
158         unsigned offs = 0;
159
160         for (unsigned i = 0; i < header->epilogCount; i++)
161         {
162             table += decodeUDelta(table, &offs, offs);
163             gcPrintf("    epilog #%2u    at   %04X\n", i, offs);
164         }
165     }
166     else
167     {
168         if  (header->epilogCount)
169             gcPrintf("    epilog        at   %04X\n", (*methodSize - header->epilogSize));
170     }
171
172     if (hasArgTabOffset)
173     {
174         unsigned argTabOffset;
175         table += decodeUnsigned(table, &argTabOffset);
176         gcPrintf("    argTabOffset = %x  \n", argTabOffset);
177     }
178
179     {
180         unsigned cur  = 0;
181         unsigned last = table-bp;
182         while (cur < last)
183         {
184             unsigned amount = last - cur;
185             if (amount>5)
186                 amount = 5;
187
188             DumpEncoding(bp+cur, amount);
189         gcPrintf("\n");
190
191             cur += amount;
192         }
193     }
194
195     return  (table - tableStart);
196 }
197
198 /*****************************************************************************/
199
200 #ifdef _PREFAST_
201 #pragma warning(push)
202 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
203 #endif
204 size_t              GCDump::DumpGCTable(PTR_CBYTE      table,
205                                         const InfoHdr& header,
206                                         unsigned       methodSize,
207                                         bool           verifyGCTables)
208 {
209     int             sz;
210     PTR_CBYTE       tableStart = table;
211     PTR_CBYTE       bp;
212
213     unsigned        count;
214     unsigned        curOffs;
215
216 //#if!TGT_x86
217 //    _ASSERTE(!"NYI");
218 //#endif
219
220     if (verifyGCTables)
221         _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEF);
222
223     unsigned        calleeSavedRegs = 0;
224     if (header.doubleAlign)
225     {
226         calleeSavedRegs = 0;
227         if (header.ediSaved) calleeSavedRegs++;
228         if (header.esiSaved) calleeSavedRegs++;
229         if (header.ebxSaved) calleeSavedRegs++;
230     }
231
232     /* Dump the untracked frame variable table */
233
234     count = header.untrackedCnt;
235
236     int lastStkOffs = 0;
237     while (count-- > 0)
238     {
239         int       stkOffsDelta;
240         unsigned  lowBits;
241
242         char      reg = header.ebpFrame ? 'B' : 'S';
243
244         sz    = (unsigned int)decodeSigned(table, &stkOffsDelta);
245         int stkOffs = lastStkOffs - stkOffsDelta;
246         lastStkOffs = stkOffs;
247
248         table = DumpEncoding(table, sz);
249
250         _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
251
252         lowBits  =   OFFSET_MASK & stkOffs;
253         stkOffs &=  ~OFFSET_MASK;
254
255         assert(!header.doubleAlign || stkOffs >= 0);
256
257         if  (header.doubleAlign &&
258              unsigned(stkOffs) >= sizeof(int)*(header.frameSize+calleeSavedRegs))
259         {
260             reg = 'B';
261             stkOffs -= sizeof(int)*(header.frameSize+calleeSavedRegs);
262             _ASSERTE(stkOffs >= (int) (2*sizeof(int)));
263         }
264
265         if  (stkOffs < 0)
266             gcPrintf("            [E%cP-%02XH] ", reg, -stkOffs);
267         else
268             gcPrintf("            [E%cP+%02XH] ", reg, +stkOffs);
269
270         gcPrintf("an untracked %s%s local\n",
271                     (lowBits & pinned_OFFSET_FLAG)  ? "pinned " : "",
272                     (lowBits & byref_OFFSET_FLAG)   ? "byref"   : ""
273            );
274     }
275
276     if (verifyGCTables)
277         _ASSERTE(*castto(table, unsigned short *)++ == 0xCAFE);
278
279     /* Dump the frame variable lifetime table */
280
281     count   = header.varPtrTableSize;
282     curOffs = 0;
283
284     while (count-- > 0)
285     {
286         unsigned varOffs;
287         unsigned begOffs;
288         unsigned endOffs;
289         unsigned lowBits;
290
291         bp = table;
292
293         table += decodeUnsigned(table, &varOffs);
294         table += decodeUDelta  (table, &begOffs, curOffs);
295         table += decodeUDelta  (table, &endOffs, begOffs);
296
297         DumpEncoding(bp, table-bp);
298
299         _ASSERTE(0 == ~OFFSET_MASK % sizeof(void*));
300
301         lowBits  = varOffs & 0x3;
302         varOffs &= ~OFFSET_MASK;
303
304         // [EBP+0] is the return address - cant be a var
305         _ASSERTE(!header.ebpFrame || varOffs);
306
307         curOffs = begOffs;
308
309         DumpOffset(begOffs);
310         gcPrintf("..");
311         DumpOffset(endOffs);
312         gcPrintf("  [E%s%02XH] a ", header.ebpFrame ? "BP-" : "SP+",
313                                   varOffs);
314
315         gcPrintf("%s%s pointer\n",
316                     (lowBits & byref_OFFSET_FLAG) ? "byref " : "",
317                     (lowBits & this_OFFSET_FLAG)  ? "this"   : ""
318            );
319
320         _ASSERTE(endOffs <= methodSize);
321     }
322
323     if (verifyGCTables)
324         _ASSERTE(*castto(table, unsigned short *)++ == 0xBABE);
325
326     /* Dump the pointer table */
327
328     curOffs = 0;
329     bp      = table;
330
331     if  (header.interruptible)
332     {
333         //
334         // Dump the Fully Interruptible pointer table
335         //
336         unsigned argCnt = 0;
337         bool     isThis = false;
338         bool     iptr   = false;
339
340         for (;;)
341         {
342             unsigned    isPop;
343             unsigned    argOffs;
344             unsigned    val = *table++;
345
346             _ASSERTE(curOffs <= methodSize);
347
348             if  (!(val & 0x80))
349             {
350                 /* A small 'regPtr' encoding */
351
352                 curOffs += val & 0x7;
353
354                 DumpEncoding(bp, table-bp); bp = table;
355                 DumpOffsetEx(curOffs);
356                 gcPrintf("        reg %s becoming %s", RegName(((val >> 3) & 7)),
357                                                      (val & 0x40) ? "live"
358                                                                   : "dead");
359                 if (isThis)
360                     gcPrintf(" 'this'");
361                 if (iptr)
362                     gcPrintf(" (iptr)");
363                 gcPrintf("\n");
364
365                 isThis = false;
366                 iptr   = false;
367                 continue;
368             }
369
370             /* This is probably an argument push/pop */
371
372             argOffs = (val & 0x38) >> 3;
373
374             /* 6 [110] and 7 [111] are reserved for other encodings */
375
376             if  (argOffs < 6)
377             {
378                 /* A small argument encoding */
379
380                 curOffs += (val & 0x07);
381                 isPop    = (val & 0x40);
382
383             ARG:
384
385                 if  (isPop)
386                 {
387                     // A Pop of 0, means little-delta
388
389                     if (argOffs != 0)
390                     {
391                         _ASSERTE(header.ebpFrame || argOffs <= argCnt);
392
393                         DumpEncoding(bp, table-bp); bp = table;
394                         DumpOffsetEx(curOffs);
395
396                         gcPrintf("        pop %2d ", argOffs);
397                         if  (!header.ebpFrame)
398                         {
399                             argCnt -= argOffs;
400                             gcPrintf("args (%d)", argCnt);
401                         }
402                         else
403                             gcPrintf("ptrs");
404
405                         gcPrintf("\n");
406                     }
407                 }
408                 else
409                 {
410                     _ASSERTE(header.ebpFrame || argOffs >= argCnt);
411
412                     DumpEncoding(bp, table-bp); bp = table;
413                     DumpOffsetEx(curOffs);
414
415                     gcPrintf("        push ptr %2d", argOffs);
416                     if  (!header.ebpFrame)
417                     {
418                         argCnt = argOffs+1;
419                         gcPrintf("  (%d)", argCnt);
420                     }
421                     if (isThis)
422                         gcPrintf(" 'this'");
423                     if (iptr)
424                         gcPrintf(" (iptr)");
425                     gcPrintf("\n");
426
427                     isThis = false;
428                     iptr   = false;
429                 }
430
431                 continue;
432             }
433             else if (argOffs == 6)
434             {
435                 if (val & 0x40)
436                 {
437                     /* Bigger delta  000=8,001=16,010=24,...,111=64 */
438
439                     curOffs += (((val & 0x07) + 1) << 3);
440                 }
441                 else
442                 {
443                     /* non-ptr arg push */
444
445                     curOffs += (val & 0x07);
446                     _ASSERTE(!header.ebpFrame);
447                     argCnt++;
448
449                     DumpEncoding(bp, table-bp); bp = table;
450                     DumpOffsetEx(curOffs);
451
452                     gcPrintf("        push non-ptr (%d)\n", argCnt);
453                 }
454
455                 continue;
456             }
457
458             /* argOffs was 7 [111] which is reserved for the larger encodings */
459
460             _ASSERTE(argOffs==7);
461
462             switch (val)
463             {
464             case 0xFF:
465                 goto DONE_REGTAB;
466
467             case 0xBC:
468                 isThis = true;
469                 break;
470
471             case 0xBF:
472                 iptr = true;
473                 break;
474
475             case 0xB8:
476                 table   += decodeUnsigned(table, &val);
477                 curOffs += val;
478                 break;
479
480             case 0xF8:
481             case 0xFC:
482                 isPop  = val & 0x04;
483                 table += decodeUnsigned(table, &argOffs);
484                 goto ARG;
485
486             case 0xFD:
487                 table += decodeUnsigned(table, &argOffs);
488                 assert(argOffs);
489
490                 DumpEncoding(bp, table-bp); bp = table;
491                 DumpOffsetEx(curOffs);
492
493                 gcPrintf("        kill args %2d\n", argOffs);
494                 break;
495
496             case 0xF9:
497                 table  += decodeUnsigned(table, &argOffs);
498                 argCnt += argOffs;
499                 break;
500
501             default:
502                 gcPrintf("Unexpected special code %04X\n", val);
503                 _ASSERTE(!"");
504             }
505         }
506     }
507     else if (header.ebpFrame)        // interruptible is false
508     {
509         //
510         // Dump the Partially Interruptible, EBP-frame method, pointer table
511         //
512
513         for (;;)
514         {
515             unsigned        argMask = 0, byrefArgMask = 0;
516             unsigned        regMask, byrefRegMask = 0;
517
518             unsigned        argCnt = 0;
519             PTR_CBYTE       argTab = NULL;
520             unsigned        argTabSize;
521
522             unsigned        val, nxt;
523
524             /* Get the next byte and check for a 'special' entry */
525
526             unsigned        encType = *table++;
527
528             _ASSERTE(curOffs <= methodSize);
529
530             switch (encType)
531             {
532
533             default:
534
535                 /* A tiny or small call entry */
536
537                 val = encType;
538
539                 if ((val & 0x80) == 0x00)
540                 {
541                     if (val & 0x0F)
542                     {
543                         /* A tiny call entry */
544
545                         curOffs += (val & 0x0F);
546                         regMask  = (val & 0x70) >> 4;
547                         argMask  = 0;
548                     }
549                     else
550                     {
551                         DumpEncoding(bp, table-bp); bp = table;
552
553                         gcPrintf("            thisptr in ");
554                         if (val & 0x10)
555                             gcPrintf("EDI\n");
556                         else if (val & 0x20)
557                             gcPrintf("ESI\n");
558                         else if (val & 0x40)
559                             gcPrintf("EBX\n");
560                         else
561                             _ASSERTE(!"Reserved GC encoding");
562
563                         continue;
564                     }
565                 }
566                 else
567                 {
568                     /* A small call entry */
569
570                     curOffs += (val & 0x7F);
571                     val      = *table++;
572                     regMask  = val >> 5;
573                     argMask  = val & 0x1F;
574                 }
575                 break;
576
577             case 0xFD:  // medium encoding
578
579                 argMask  = *table++;
580                 val      = *table++;
581                 argMask |= (val & 0xF0) << 4;
582                 nxt      = *table++;
583                 curOffs += (val & 0x0F) + ((nxt & 0x1F) << 4);
584                 regMask  = nxt >> 5;                   // EBX,ESI,EDI
585
586                 break;
587
588             case 0xF9:  // medium encoding with byrefs
589
590                 curOffs += *table++;
591                 val      = *table++;
592                 argMask  = val & 0x1F;
593                 regMask  = val >> 5;
594                 val      = *table++;
595                 byrefArgMask    = val & 0x1F;
596                 byrefRegMask    = val >> 5;
597
598                 break;
599
600             case 0xFE:  // large encoding
601             case 0xFA:  // large encoding with byrefs
602
603                 val         = *table++;
604                 regMask     = val & 0x7;
605                 byrefRegMask= val >> 4;
606                 curOffs    += *PTR_DWORD(table); table += sizeof(DWORD);
607                 argMask     = *PTR_DWORD(table); table += sizeof(DWORD);
608                 if (encType == 0xFA) // read byrefArgMask
609                     {byrefArgMask = *PTR_DWORD(table); table += sizeof(DWORD);}
610
611                 break;
612
613             case 0xFB:  // huge encoding
614
615                 val         = *table++;
616                 regMask     = val & 0x7;
617                 byrefRegMask= val >> 4;
618                 curOffs     = *PTR_DWORD(table); table += sizeof(DWORD);
619                 argCnt      = *PTR_DWORD(table); table += sizeof(DWORD);
620                 argTabSize  = *PTR_DWORD(table); table += sizeof(DWORD);
621                 argTab      = table; table += argTabSize;
622
623                 break;
624
625             case 0xFF:
626                 goto DONE_REGTAB;
627             }
628
629             /*
630                 Here we have the following values:
631
632                 curOffs      ...    the code offset of the call
633                 regMask      ...    mask of live pointer register variables
634                 argMask      ...    bitmask of pushed pointer arguments
635                 byrefRegMask ...    byref qualifier for regMask
636                 byrefArgMask ...    byrer qualifier for argMask
637              */
638
639             _ASSERTE((byrefArgMask & argMask) == byrefArgMask);
640             _ASSERTE((byrefRegMask & regMask) == byrefRegMask);
641
642             DumpEncoding(bp, table-bp); bp = table;
643             DumpOffsetEx(curOffs);
644
645             gcPrintf("        call [ ");
646
647             if (regMask & 1)
648                 gcPrintf("EDI%c", (byrefRegMask & 1) ? '\'' : ' ');
649             if (regMask & 2)
650                 gcPrintf("ESI%c", (byrefRegMask & 2) ? '\'' : ' ');
651             if (regMask & 4)
652                 gcPrintf("EBX%c", (byrefRegMask & 4) ? '\'' : ' ');
653
654             if (!header.ebpFrame)
655             {
656                 if (regMask & 8)
657                     gcPrintf("EBP ");
658             }
659
660             if  (argCnt)
661             {
662                 gcPrintf("] ptrArgs=[");
663
664                 do
665                 {
666                     argTab += decodeUnsigned(argTab, &val);
667
668                     assert((val & this_OFFSET_FLAG) == 0);
669                     unsigned  stkOffs = val & ~byref_OFFSET_FLAG;
670                     unsigned  lowBit  = val &  byref_OFFSET_FLAG;
671
672                     gcPrintf("%u%s", stkOffs, lowBit ? "i" : "");
673                     if  (argCnt > 1)
674                         gcPrintf(" ");
675                 }
676                 while (--argCnt);
677                 assert(argTab == table);
678
679                 gcPrintf("]");
680             }
681             else
682             {
683                 gcPrintf("] argMask=%02X", argMask);
684
685                 if (byrefArgMask) gcPrintf(" (iargs=%02X)", byrefArgMask);
686             }
687
688             gcPrintf("\n");
689         }
690     }
691     else // interruptible is false, ebpFrame is false
692     {
693         //
694         // Dump the Partially Interruptible, EBP-less method, pointer table
695         //
696         unsigned lastSkip = 0;
697         unsigned imask    = 0;
698
699         for (;;)
700         {
701             unsigned    val = *table++;
702
703             _ASSERTE(curOffs <= methodSize);
704
705             if  (!(val & 0x80))
706             {
707                 if (!(val & 0x40))
708                 {
709                     if (!(val & 0x20))
710                     {
711                         //
712                         // push    000DDDDD          push one item, 5-bit delta
713                         //
714
715                         curOffs += val & 0x1F;
716
717                         DumpEncoding(bp, table-bp); bp = table;
718                         DumpOffsetEx(curOffs);
719
720                         gcPrintf("        push\n");
721                     }
722                     else
723                     {
724                         //
725                         // push    00100000 [pushCount]     ESP push multiple items
726                         //
727
728                         unsigned pushCount;
729
730                         assert(val == 0x20);
731                         table    += decodeUnsigned(table, &pushCount);
732
733                         DumpEncoding(bp, table-bp); bp = table;
734                         DumpOffsetEx(curOffs);
735
736                         gcPrintf("       push %d\n", pushCount);
737                     }
738                 }
739                 else
740                 {
741                     unsigned    popSize;
742                     unsigned    skip;
743
744                     if ((val & 0x3f) == 0)
745                     {
746                         //
747                         // skip    01000000 [Delta]  Skip arbitrary sized delta
748                         //
749
750                         table   += decodeUnsigned(table, &skip);
751                         curOffs += skip;
752                         lastSkip = skip;
753                     }
754                     else
755                     {
756                         //
757                         //  pop     01CCDDDD         pop CC items, 4-bit delta
758                         //
759
760                         popSize = (val & 0x30) >> 4;
761                         skip    =  val & 0x0f;
762                         curOffs += skip;
763
764                         if (popSize > 0)
765                         {
766                             DumpEncoding(bp, table-bp); bp = table;
767                             DumpOffsetEx(curOffs);
768
769                             gcPrintf("        pop %d\n", popSize);
770                         }
771                         else
772                             lastSkip = skip;
773                     }
774                 }
775             }
776             else
777             {
778                 unsigned    callArgCnt;
779                 unsigned    callRegMask;
780                 bool        callPndTab = false;
781                 unsigned    callPndMask = 0;
782                 unsigned    callPndTabCnt = 0, callPndTabSize = 0;
783
784                 switch ((val & 0x70) >> 4)
785                 {
786                 default:
787                     //
788                     // call    1PPPPPPP          Call Pattern, P=[0..79]
789                     //
790                     decodeCallPattern((val & 0x7f), &callArgCnt,  &callRegMask,
791                                                     &callPndMask, &lastSkip);
792                     curOffs += lastSkip;
793
794                 PRINT_CALL:
795
796                     DumpEncoding(bp, table-bp); bp = table;
797                     DumpOffsetEx(curOffs);
798
799                     gcPrintf("        call %d [ ", callArgCnt);
800
801                     unsigned    iregMask, iargMask;
802
803                     iregMask = imask & 0xF;
804                     iargMask = imask >> 4;
805
806                     assert((callRegMask & 0x0F) == callRegMask);
807                     if (callRegMask & 1)
808                         gcPrintf("EDI%c", (iregMask & 1) ? '\'' : ' ');
809                     if (callRegMask & 2)
810                         gcPrintf("ESI%c", (iregMask & 2) ? '\'' : ' ');
811                     if (callRegMask & 4)
812                         gcPrintf("EBX%c", (iregMask & 4) ? '\'' : ' ');
813                     if (callRegMask & 8)
814                         gcPrintf("EBP%c", (iregMask & 8) ? '\'' : ' ');
815                     gcPrintf("]");
816
817                     if (callPndTab)
818                     {
819 #if defined(_DEBUG) && !defined(STRIKE)
820                 // note: _ASSERTE is a no-op for strike
821                         PTR_CBYTE offsStart = table;
822 #endif
823                         gcPrintf(" argOffs(%d) =", callPndTabCnt);
824                         for (unsigned i=0; i < callPndTabCnt; i++)
825                         {
826                             unsigned pndOffs;
827                             table += decodeUnsigned(table, &pndOffs);
828                             gcPrintf(" %4X", pndOffs);
829                         }
830                         _ASSERTE(offsStart + callPndTabSize == table);
831                         bp = table;
832                     }
833                     else
834                     {
835                         if (callPndMask)
836                             gcPrintf(" argMask=%02X", callPndMask);
837                         if (iargMask)
838                             gcPrintf(" (iargs=%02X)", iargMask);
839                     }
840                     gcPrintf("\n");
841
842                     imask = lastSkip = 0;
843                     break;
844
845                   case 5:
846                     //
847                     // call    1101RRRR DDCCCMMM  Call RegMask=RRRR,ArgCnt=CCC,
848                     //                        ArgMask=MMM Delta=commonDelta[DD]
849                     //
850                     callRegMask     = val & 0xf;    // EBP,EBX,ESI,EDI
851                     val             = *table++;
852                     callPndMask     = (val & 0x7);
853                     callArgCnt      = (val >> 3) & 0x7;
854                     lastSkip        = callCommonDelta[val>>6];
855                     curOffs        += lastSkip;
856
857                     goto PRINT_CALL;
858
859                   case 6:
860                     //
861                     // call    1110RRRR [ArgCnt] [ArgMask]
862                     //                          Call ArgCnt,RegMask=RRR,ArgMask
863                     //
864                     callRegMask = val & 0xf;    // EBP,EBX,ESI,EDI
865                     table += decodeUnsigned(table, &callArgCnt);
866                     table += decodeUnsigned(table, &callPndMask);
867                     goto PRINT_CALL;
868
869                   case 7:
870                     switch (val & 0x0C)
871                     {
872                     case 0x00:
873                         assert(val == 0xF0);
874                         /*  iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask */
875                         table += decodeUnsigned(table, &imask);
876                         DumpEncoding(bp, table-bp); bp = table;
877                         gcPrintf("            iptrMask = %02X\n", imask);
878                         break;
879
880                     case 0x04:
881                         DumpEncoding(bp, table-bp); bp = table;
882                         gcPrintf("            thisptr in %s\n", CalleeSavedRegName(val&0x3));
883                         break;
884
885                     case 0x08:
886                         val             = *table++;
887                         callRegMask     = val & 0xF;
888                         imask           = val >> 4;
889                         lastSkip        = *PTR_DWORD(table); table += sizeof(DWORD);
890                         curOffs        += lastSkip;
891                         callArgCnt      = *PTR_DWORD(table); table += sizeof(DWORD);
892                         callPndTabCnt   = *PTR_DWORD(table); table += sizeof(DWORD);
893                         callPndTabSize  = *PTR_DWORD(table); table += sizeof(DWORD);
894                         callPndTab      = true;
895                         goto PRINT_CALL;
896
897                     case 0x0C:
898                         assert(val==0xff);
899                         goto DONE_REGTAB;
900                         break;
901
902                     default:
903                         _ASSERTE(!"reserved GC encoding");
904                         break;
905                     }
906                     break;
907                 }
908             }
909         }
910     }
911
912 DONE_REGTAB:
913
914     _ASSERTE(curOffs <= methodSize);
915
916     if (verifyGCTables)
917         _ASSERTE(*castto(table, unsigned short *)++ == 0xBEEB);
918
919     _ASSERTE(table > bp);
920
921     DumpEncoding(bp, table-bp);
922 //  gcPrintf("     ");
923     gcPrintf("\n");
924
925     return  (table - tableStart);
926 }
927 #ifdef _PREFAST_
928 #pragma warning(pop)
929 #endif
930
931
932 /*****************************************************************************/
933
934 void                GCDump::DumpPtrsInFrame(PTR_CBYTE   infoBlock,
935                                             PTR_CBYTE   codeBlock,
936                                             unsigned    offs,
937                                             bool        verifyGCTables)
938 {
939     PTR_CBYTE       table = infoBlock;
940
941     size_t          methodSize;
942     size_t          stackSize;
943     size_t          prologSize;
944     size_t          epilogSize;
945     unsigned        epilogCnt;
946     BOOL            epilogEnd;
947     size_t          argSize;
948     BOOL            secCheck;
949     BOOL            dblAlign;
950
951     if (verifyGCTables)
952         _ASSERTE(*castto(table, unsigned short *)++ == 0xFEEF);
953
954     /* Get hold of the method size */
955
956     unsigned int methodSizeTemp;
957     table += decodeUnsigned(table, &methodSizeTemp);
958     methodSize = methodSizeTemp;
959
960     //
961     // New style InfoBlk Header 
962     //
963     // Typically only uses one-byte to store everything.
964     //
965     InfoHdr header;
966     table = decodeHeader(table, &header);
967     
968     if (header.untrackedCnt == HAS_UNTRACKED)
969     {
970         unsigned count;
971         table += decodeUnsigned(table, &count);
972         header.untrackedCnt = count;
973     }
974     if (header.varPtrTableSize == HAS_VARPTR)
975     {
976         unsigned count;
977         table += decodeUnsigned(table, &count);
978         header.varPtrTableSize = count;
979     }
980     if (header.gsCookieOffset == HAS_GS_COOKIE_OFFSET)
981     {
982         unsigned offset;
983         table += decodeUnsigned(table, &offset);
984         header.gsCookieOffset = offset;
985         _ASSERTE(offset != INVALID_GS_COOKIE_OFFSET);
986     }
987     if (header.syncStartOffset == HAS_SYNC_OFFSET)
988     {
989         unsigned offset;
990         table += decodeUnsigned(table, &offset);
991         header.syncStartOffset = offset;
992         _ASSERTE(offset != INVALID_SYNC_OFFSET);
993         table += decodeUnsigned(table, &offset);
994         header.syncEndOffset = offset;
995         _ASSERTE(offset != INVALID_SYNC_OFFSET);
996     }
997
998     prologSize = header.prologSize;
999     epilogSize = header.epilogSize;
1000     epilogCnt  = header.epilogCount;
1001     epilogEnd  = header.epilogAtEnd;
1002     secCheck   = header.security;
1003     dblAlign   = header.doubleAlign;
1004     argSize    = header.argCount * 4;
1005     stackSize  = header.frameSize;
1006
1007 #ifdef DEBUG
1008     if  (offs == 0)
1009     {
1010         gcPrintf("    method      size = %04X\n", methodSize);
1011         gcPrintf("    stack frame size = %3u \n",  stackSize);
1012         gcPrintf("    prolog      size = %3u \n", prologSize);
1013         gcPrintf("    epilog      size = %3u \n", epilogSize);
1014         gcPrintf("    epilog      end  = %s  \n", epilogEnd ? "yes" : "no");
1015         gcPrintf("    epilog     count = %3u \n", epilogCnt );
1016         gcPrintf("    security         = %s  \n", secCheck  ? "yes" : "no");
1017         gcPrintf("    dblAlign         = %s  \n", dblAlign  ? "yes" : "no");
1018         gcPrintf("    untracked count  = %3u \n", header.untrackedCnt);
1019         gcPrintf("    var ptr tab count= %3u \n", header.varPtrTableSize);
1020         gcPrintf("\n");
1021     }
1022 #endif
1023
1024     /* Are we within the prolog of the method? */
1025
1026     if  (offs < prologSize)
1027     {
1028         gcPrintf("    Offset %04X is within the method's prolog\n", offs);
1029         return;
1030     }
1031
1032     /* Are we within an epilog of the method? */
1033
1034     if  (epilogCnt)
1035     {
1036         unsigned    eps;
1037
1038         if  (epilogCnt > 1 || !epilogEnd)
1039         {
1040             if (verifyGCTables)
1041                 _ASSERTE(*castto(table, unsigned short *)++ == 0xFACE);
1042
1043             unsigned prevEps = 0;
1044             for (unsigned i = 0; i < epilogCnt; i++)
1045             {
1046                 table += decodeUDelta(table, &eps, prevEps);
1047
1048                 if ((offs >= eps) && (offs <  eps + epilogSize))
1049                         goto EPILOG_MSG;
1050             }
1051         }
1052         else
1053         {
1054             eps = (int)(methodSize - epilogSize);
1055             if ((offs >= eps) && (offs <  eps + epilogSize))
1056             {
1057 EPILOG_MSG:     gcPrintf("    Offset %04X is within the method's epilog"
1058                        " (%02X bytes into it)\n", offs, offs - eps);
1059                 return;
1060             }
1061         }
1062     }
1063     gcPrintf("    Offset %04X is within the method's body\n", offs);
1064 }
1065
1066 /*****************************************************************************/
1067 #endif // _TARGET_X86_
1068 /*****************************************************************************/