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*
6* File: dis.cpp
7*
8
9*
10* File Comments:
11*
12* This file handles disassembly. It is adapted from the MS linker.
13*
14***********************************************************************/
15
16#include "jitpch.h"
17#ifdef _MSC_VER
18#pragma hdrstop
19#endif
20
21/*****************************************************************************/
22#ifdef LATE_DISASM
23/*****************************************************************************/
24
25// Define DISASM_DEBUG to get verbose output of late disassembler inner workings.
26//#define DISASM_DEBUG
27#ifdef DISASM_DEBUG
28#ifdef DEBUG
29#define DISASM_DUMP(...) \
30 if (VERBOSE) \
31 printf(__VA_ARGS__)
32#else // !DEBUG
33#define DISASM_DUMP(...) printf(__VA_ARGS__)
34#endif // !DEBUG
35#else // !DISASM_DEBUG
36#define DISASM_DUMP(...)
37#endif // !DISASM_DEBUG
38
39/*****************************************************************************/
40
41#define MAX_CLASSNAME_LENGTH 1024
42
43#if defined(_AMD64_)
44
45#pragma comment(linker, \
46 "/ALTERNATENAME:__imp_?CchFormatAddr@DIS@@QEBA_K_KPEAG0@Z=__imp_?CchFormatAddr@DIS@@QEBA_K_KPEA_W0@Z")
47#pragma comment(linker, \
48 "/ALTERNATENAME:__imp_?CchFormatInstr@DIS@@QEBA_KPEAG_K@Z=__imp_?CchFormatInstr@DIS@@QEBA_KPEA_W_K@Z")
49#pragma comment( \
50 linker, \
51 "/ALTERNATENAME:__imp_?PfncchaddrSet@DIS@@QEAAP6A_KPEBV1@_KPEAG1PEA_K@ZP6A_K01213@Z@Z=__imp_?PfncchaddrSet@DIS@@QEAAP6A_KPEBV1@_KPEA_W1PEA_K@ZP6A_K01213@Z@Z")
52#pragma comment( \
53 linker, \
54 "/ALTERNATENAME:__imp_?PfncchregSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@PEAG_K@ZP6A_K0123@Z@Z=__imp_?PfncchregSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@PEA_W_K@ZP6A_K0123@Z@Z")
55#pragma comment( \
56 linker, \
57 "/ALTERNATENAME:__imp_?PfncchregrelSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@KPEAG_KPEAK@ZP6A_K01K234@Z@Z=__imp_?PfncchregrelSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@KPEA_W_KPEAK@ZP6A_K01K234@Z@Z")
58#pragma comment( \
59 linker, \
60 "/ALTERNATENAME:__imp_?PfncchfixupSet@DIS@@QEAAP6A_KPEBV1@_K1PEAG1PEA_K@ZP6A_K011213@Z@Z=__imp_?PfncchfixupSet@DIS@@QEAAP6A_KPEBV1@_K1PEA_W1PEA_K@ZP6A_K011213@Z@Z")
61
62#elif defined(_X86_)
63
64#pragma comment(linker, "/ALTERNATENAME:__imp_?CchFormatAddr@DIS@@QBEI_KPAGI@Z=__imp_?CchFormatAddr@DIS@@QBEI_KPA_WI@Z")
65#pragma comment(linker, "/ALTERNATENAME:__imp_?CchFormatInstr@DIS@@QBEIPAGI@Z=__imp_?CchFormatInstr@DIS@@QBEIPA_WI@Z")
66#pragma comment( \
67 linker, \
68 "/ALTERNATENAME:__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPAGIPA_K@ZP6GI012I3@Z@Z=__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPA_WIPA_K@ZP6GI012I3@Z@Z")
69#pragma comment( \
70 linker, \
71 "/ALTERNATENAME:__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PAGI@ZP6GI012I@Z@Z=__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PA_WI@ZP6GI012I@Z@Z")
72#pragma comment( \
73 linker, \
74 "/ALTERNATENAME:__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPAGIPAK@ZP6GI01K2I3@Z@Z=__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPA_WIPAK@ZP6GI01K2I3@Z@Z")
75#pragma comment( \
76 linker, \
77 "/ALTERNATENAME:__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPAGIPA_K@ZP6GI01I2I3@Z@Z=__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPA_WIPA_K@ZP6GI01I2I3@Z@Z")
78
79#endif
80
81/*****************************************************************************
82 * Given an absolute address from the beginning of the code
83 * find the corresponding emitter block and the relative offset
84 * of the current address in that block
85 * Was used to get to the fixup list of each block. The new emitter has
86 * no such fixups. Something needs to be added for this.
87 */
88
89// These structs were defined in emit.h. Fake them here so DisAsm.cpp can compile
90
91typedef struct codeFix
92{
93 codeFix* cfNext;
94 unsigned cfFixup;
95} * codeFixPtr;
96
97typedef struct codeBlk
98{
99 codeFix* cbFixupLst;
100} * codeBlkPtr;
101
102/*****************************************************************************
103 * The following is the callback for jump label and direct function calls fixups.
104 * "addr" represents the address of jump that has to be
105 * replaced with a label or function name.
106 *
107 * Return 1 if a name was written representing the address, 0 otherwise.
108 */
109
110/* static */
111size_t __stdcall DisAssembler::disCchAddr(
112 const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
113{
114 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
115 assert(pDisAsm);
116 return pDisAsm->disCchAddrMember(pdis, addr, wz, cchMax, pdwDisp);
117}
118
119size_t DisAssembler::disCchAddrMember(
120 const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
121{
122 /* First check the termination type of the instruction
123 * because this might be a helper or static function call
124 * check to see if we have a fixup for the current address */
125
126 size_t retval = 0; // assume we don't know
127
128#if defined(_TARGET_XARCH_)
129
130 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
131
132 DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
133
134 switch (terminationType)
135 {
136 // int disCallSize;
137
138 case DISX86::trmtaJmpShort:
139 case DISX86::trmtaJmpCcShort:
140
141 /* We have a short jump in the current code block - generate the label to which we jump */
142
143 assert(0 <= disTarget && disTarget < disTotalCodeSize);
144 swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
145 retval = 1;
146 break;
147
148 case DISX86::trmtaJmpNear:
149 case DISX86::trmtaJmpCcNear:
150
151 /* We have a near jump. Check if is in the current code block.
152 * Otherwise we have no target for it. */
153
154 if (0 <= disTarget && disTarget < disTotalCodeSize)
155 {
156 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
157 retval = 1;
158 }
159 break;
160
161 case DISX86::trmtaCallNear16:
162 case DISX86::trmtaCallNear32:
163
164 /* check for local calls (i.e. CALL label) */
165
166 if (0 <= disTarget && disTarget < disTotalCodeSize)
167 {
168 /* not a "call ds:[0000]" - go ahead */
169 /* disTarget within block boundary -> local call */
170
171 swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
172 retval = 1;
173 break;
174 }
175
176 /* this is a near call - in our case usually VM helper functions */
177
178 /* find the emitter block and the offset of the call fixup */
179 /* for the fixup offset we have to add the opcode size for the call - in the case of a near call is 1 */
180
181 // disCallSize = 1;
182
183 {
184 size_t absoluteTarget = (size_t)disGetLinearAddr(disTarget);
185 const char* name = disGetMethodFullName(absoluteTarget);
186 if (name != nullptr)
187 {
188 swprintf_s(wz, cchMax, W("%zx %S"), dspAddr(absoluteTarget), name);
189 retval = 1;
190 break;
191 }
192 }
193
194 break;
195
196#ifdef _TARGET_AMD64_
197
198 case DISX86::trmtaFallThrough:
199
200 /* memory indirect case. Could be for an LEA for the base address of a switch table, which is an arbitrary
201 * address, currently of the first block after the prolog. */
202
203 /* find the emitter block and the offset for the fixup
204 * "addr" is the address of the immediate */
205
206 break;
207
208#endif // _TARGET_AMD64_
209
210 default:
211
212 printf("Termination type is %d\n", (int)terminationType);
213 assert(!"treat this case\n");
214 break;
215 }
216
217#elif defined(_TARGET_ARM64_)
218
219 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
220
221 DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
222
223 switch (terminationType)
224 {
225 // int disCallSize;
226
227 case DISARM64::TRMTA::trmtaBra:
228 case DISARM64::TRMTA::trmtaBraCase:
229 case DISARM64::TRMTA::trmtaBraCc:
230 case DISARM64::TRMTA::trmtaBraCcCase:
231 case DISARM64::TRMTA::trmtaBraCcInd:
232 case DISARM64::TRMTA::trmtaBraInd:
233
234 /* We have a jump. Check if is in the current code block.
235 * Otherwise we have no target for it. */
236
237 if (0 <= disTarget && disTarget < disTotalCodeSize)
238 {
239 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
240 retval = 1;
241 }
242 break;
243
244 case DISARM64::trmtaCall:
245 case DISARM64::trmtaCallCc:
246 case DISARM64::trmtaCallCcInd:
247 case DISARM64::trmtaCallInd:
248
249 /* check for local calls (i.e. CALL label) */
250
251 if (0 <= disTarget && disTarget < disTotalCodeSize)
252 {
253 /* not a "call [0000]" - go ahead */
254 /* disTarget within block boundary -> local call */
255
256 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
257 retval = 1;
258 break;
259 }
260
261 /* this is a near call - in our case usually VM helper functions */
262
263 /* find the emitter block and the offset of the call fixup */
264 /* for the fixup offset we have to add the opcode size for the call - in the case of a near call is 1 */
265
266 // disCallSize = 1;
267
268 {
269 size_t absoluteTarget = (size_t)disGetLinearAddr(disTarget);
270 const char* name = disGetMethodFullName(absoluteTarget);
271 if (name != nullptr)
272 {
273 swprintf_s(wz, cchMax, W("%zx %S"), dspAddr(absoluteTarget), name);
274 retval = 1;
275 break;
276 }
277 }
278
279 break;
280
281 case DISARM64::trmtaFallThrough:
282
283 /* memory indirect case. Could be for an LEA for the base address of a switch table, which is an arbitrary
284 * address, currently of the first block after the prolog. */
285
286 /* find the emitter block and the offset for the fixup
287 * "addr" is the address of the immediate */
288
289 {
290 DIS::INSTRUCTION instr;
291 DIS::OPERAND ops[DISARM64::coperandMax];
292 bool ok = pdis->FDecode(&instr, ops, ArrLen(ops));
293 if (ok)
294 {
295 bool isAddress = false;
296 switch ((DISARM64::OPA)instr.opa)
297 {
298 case DISARM64::opaAdr:
299 case DISARM64::opaAdrp:
300 isAddress = true;
301 break;
302 default:
303 break;
304 }
305
306 if (isAddress && 0 <= addr && addr < disTotalCodeSize)
307 {
308 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[addr]);
309 retval = 1;
310 }
311 }
312 }
313 break;
314
315 default:
316
317 printf("Termination type is %d\n", (int)terminationType);
318 assert(!"treat this case\n");
319 break;
320 }
321
322#else // _TARGET_*
323#error Unsupported or unset target architecture
324#endif // _TARGET_*
325
326 if (retval == 0)
327 {
328 if (disDiffable)
329 {
330 swprintf_s(wz, cchMax, W("%p"), dspAddr((void*)1));
331 }
332 }
333 else
334 {
335 /* no displacement */
336
337 *pdwDisp = 0x0;
338 }
339
340 return retval;
341}
342
343/*****************************************************************************
344 * We annotate some instructions to get info needed to display the symbols
345 * for that instruction.
346 *
347 * Return 1 if a name was written representing the address, 0 otherwise.
348 */
349
350/* static */
351size_t __stdcall DisAssembler::disCchFixup(
352 const DIS* pdis, DIS::ADDR addr, size_t size, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
353{
354 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
355 assert(pDisAsm);
356
357 return pDisAsm->disCchFixupMember(pdis, addr, size, wz, cchMax, pdwDisp);
358}
359
360size_t DisAssembler::disCchFixupMember(
361 const DIS* pdis, DIS::ADDR addr, size_t size, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
362{
363#if defined(_TARGET_XARCH_)
364
365 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
366 // DIS::ADDR disIndAddr;
367
368 DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
369 terminationType);
370
371 // Is there a relocation registered for the address?
372
373 size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
374 size_t targetAddr;
375 bool anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
376
377 switch (terminationType)
378 {
379 DIS::ADDR disCallSize;
380
381 case DISX86::trmtaFallThrough:
382
383 /* memory indirect case */
384
385 assert(addr > pdis->Addr());
386
387 /* find the emitter block and the offset for the fixup
388 * "addr" is the address of the immediate */
389
390 if (anyReloc)
391 {
392 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
393 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
394 break;
395 }
396
397 return 0;
398
399 case DISX86::trmtaJmpInd:
400
401 /* pretty rare case - something like "jmp [eax*4]"
402 * not a function call or anything worth annotating */
403
404 return 0;
405
406 case DISX86::trmtaTrap:
407 case DISX86::trmtaTrapCc:
408
409 /* some instructions like division have a TRAP termination type - ignore it */
410
411 return 0;
412
413 case DISX86::trmtaJmpShort:
414 case DISX86::trmtaJmpCcShort:
415
416 case DISX86::trmtaJmpNear:
417 case DISX86::trmtaJmpCcNear:
418
419 /* these are treated by the CchAddr callback - skip them */
420
421 return 0;
422
423 case DISX86::trmtaCallNear16:
424 case DISX86::trmtaCallNear32:
425
426 if (anyReloc)
427 {
428 const char* name = disGetMethodFullName(targetAddr);
429 if (name != nullptr)
430 {
431 swprintf_s(wz, cchMax, W("%zx %S"), dspAddr(targetAddr), name);
432 break;
433 }
434 }
435
436 /* these are treated by the CchAddr callback - skip them */
437
438 return 0;
439
440 case DISX86::trmtaCallInd:
441
442 /* here we have an indirect call - find the indirect address */
443
444 // BYTE * code = disGetLinearAddr((size_t)addr);
445 // disIndAddr = (DIS::ADDR) (code+0);
446
447 /* find the size of the call opcode - less the immediate */
448 /* for the fixup offset we have to add the opcode size for the call */
449 /* addr is the address of the immediate, pdis->Addr() returns the address of the disassembled instruction */
450
451 assert(addr > pdis->Addr());
452 disCallSize = addr - pdis->Addr();
453
454 /* find the emitter block and the offset of the call fixup */
455
456 return 0;
457
458 default:
459
460 printf("Termination type is %d\n", (int)terminationType);
461 assert(!"treat this case\n");
462 break;
463 }
464
465#elif defined(_TARGET_ARM64_)
466
467 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
468 // DIS::ADDR disIndAddr;
469
470 DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
471 terminationType);
472
473 // Is there a relocation registered for the address?
474
475 size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
476 size_t targetAddr;
477 bool anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
478
479 switch (terminationType)
480 {
481 DIS::ADDR disCallSize;
482
483 case DISARM64::TRMTA::trmtaUnknown:
484 return 0;
485
486 case DISARM64::TRMTA::trmtaFallThrough:
487
488 if (anyReloc)
489 {
490 /* memory indirect case */
491
492 assert(addr > pdis->Addr());
493
494 /* find the emitter block and the offset for the fixup
495 * "addr" is the address of the immediate */
496
497 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
498 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
499 break;
500 }
501
502 return 0;
503
504 case DISARM64::TRMTA::trmtaBraInd:
505 case DISARM64::TRMTA::trmtaBraCcInd:
506
507 /* pretty rare case - something like "jmp [eax*4]"
508 * not a function call or anything worth annotating */
509
510 return 0;
511
512 case DISARM64::TRMTA::trmtaTrap:
513 case DISARM64::TRMTA::trmtaTrapCc:
514
515 /* some instructions like division have a TRAP termination type - ignore it */
516
517 return 0;
518
519 case DISARM64::TRMTA::trmtaBra:
520 case DISARM64::TRMTA::trmtaBraCase:
521 case DISARM64::TRMTA::trmtaBraCc:
522 case DISARM64::TRMTA::trmtaBraCcCase:
523
524 /* these are treated by the CchAddr callback - skip them */
525
526 return 0;
527
528 case DISARM64::TRMTA::trmtaCall:
529 case DISARM64::TRMTA::trmtaCallCc:
530
531 if (anyReloc)
532 {
533 const char* name = disGetMethodFullName(targetAddr);
534 if (name != nullptr)
535 {
536 swprintf_s(wz, cchMax, W("%zx %S"), dspAddr(targetAddr), name);
537 break;
538 }
539 }
540
541 /* these are treated by the CchAddr callback - skip them */
542
543 return 0;
544
545 case DISARM64::TRMTA::trmtaCallInd:
546 case DISARM64::TRMTA::trmtaCallCcInd:
547
548 /* here we have an indirect call - find the indirect address */
549
550 // BYTE * code = disGetLinearAddr((size_t)addr);
551 // disIndAddr = (DIS::ADDR) (code+0);
552
553 /* find the size of the call opcode - less the immediate */
554 /* for the fixup offset we have to add the opcode size for the call */
555 /* addr is the address of the immediate, pdis->Addr() returns the address of the disassembled instruction */
556
557 assert(addr > pdis->Addr());
558 disCallSize = addr - pdis->Addr();
559
560 /* find the emitter block and the offset of the call fixup */
561
562 return 0;
563
564 default:
565
566 printf("Termination type is %d\n", (int)terminationType);
567 assert(!"treat this case\n");
568 break;
569 }
570
571#else // _TARGET_*
572#error Unsupported or unset target architecture
573#endif // _TARGET_*
574
575 /* no displacement */
576
577 *pdwDisp = 0x0;
578
579 return 1;
580}
581
582/*****************************************************************************
583 * This the callback for register-relative operands in an instruction.
584 * If the register is ESP or EBP, the operand may be a local variable
585 * or a parameter, else the operand may be an instance variable
586 *
587 * Return 1 if a name was written representing the register-relative operand, 0 otherwise.
588 */
589
590/* static */
591size_t __stdcall DisAssembler::disCchRegRel(
592 const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
593{
594 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
595 assert(pDisAsm);
596
597 return pDisAsm->disCchRegRelMember(pdis, reg, disp, wz, cchMax, pdwDisp);
598}
599
600size_t DisAssembler::disCchRegRelMember(
601 const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
602{
603#if defined(_TARGET_XARCH_)
604
605 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
606 // DIS::ADDR disIndAddr;
607
608 DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
609
610 switch (terminationType)
611 {
612 int disOpcodeSize;
613 const char* var;
614
615 case DISX86::trmtaFallThrough:
616
617 /* some instructions like division have a TRAP termination type - ignore it */
618
619 case DISX86::trmtaTrap:
620 case DISX86::trmtaTrapCc:
621
622 var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
623 if (var)
624 {
625 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
626 *pdwDisp = 0;
627
628 return 1;
629 }
630
631 /* This case consists of non-static members */
632
633 /* find the emitter block and the offset for the fixup
634 * fixup is emited after the coding of the instruction - size = word (2 bytes)
635 * GRRRR!!! - for the 16 bit case we have to check for the address size prefix = 0x66
636 */
637
638 if (*disGetLinearAddr(disCurOffset) == 0x66)
639 {
640 disOpcodeSize = 3;
641 }
642 else
643 {
644 disOpcodeSize = 2;
645 }
646
647 return 0;
648
649 case DISX86::trmtaCallNear16:
650 case DISX86::trmtaCallNear32:
651 case DISX86::trmtaJmpInd:
652
653 break;
654
655 case DISX86::trmtaCallInd:
656
657 /* check if this is a one byte displacement */
658
659 if ((signed char)disp == (int)disp)
660 {
661 /* we have a one byte displacement -> there were no previous callbacks */
662
663 /* find the size of the call opcode - less the immediate */
664 /* this is a call R/M indirect -> opcode size is 2 */
665
666 disOpcodeSize = 2;
667
668 /* find the emitter block and the offset of the call fixup */
669
670 return 0;
671 }
672 else
673 {
674 /* check if we already have a symbol name as replacement */
675
676 if (disHasName)
677 {
678 /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
679
680 swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
681 *pdwDisp = 0;
682 disHasName = false;
683 return 1;
684 }
685 else
686 {
687 return 0;
688 }
689 }
690
691 default:
692
693 printf("Termination type is %d\n", (int)terminationType);
694 assert(!"treat this case\n");
695
696 break;
697 }
698
699#elif defined(_TARGET_ARM64_)
700
701 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
702
703 DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
704
705 switch (terminationType)
706 {
707 int disOpcodeSize;
708 const char* var;
709
710 case DISARM64::TRMTA::trmtaFallThrough:
711
712 /* some instructions like division have a TRAP termination type - ignore it */
713
714 case DISARM64::TRMTA::trmtaTrap:
715 case DISARM64::TRMTA::trmtaTrapCc:
716
717 var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
718 if (var)
719 {
720 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
721 *pdwDisp = 0;
722
723 return 1;
724 }
725
726 /* This case consists of non-static members */
727
728 // TODO-ARM64-Bug?: Is this correct?
729 disOpcodeSize = 2;
730 return 0;
731
732 case DISARM64::TRMTA::trmtaCall:
733 case DISARM64::TRMTA::trmtaCallCc:
734 case DISARM64::TRMTA::trmtaBraInd:
735 case DISARM64::TRMTA::trmtaBraCcInd:
736 break;
737
738 case DISARM64::TRMTA::trmtaCallInd:
739 case DISARM64::TRMTA::trmtaCallCcInd:
740
741 /* check if this is a one byte displacement */
742
743 if ((signed char)disp == (int)disp)
744 {
745 /* we have a one byte displacement -> there were no previous callbacks */
746
747 /* find the size of the call opcode - less the immediate */
748 /* this is a call R/M indirect -> opcode size is 2 */
749
750 // TODO-ARM64-Bug?: Is this correct?
751 disOpcodeSize = 2;
752
753 /* find the emitter block and the offset of the call fixup */
754
755 return 0;
756 }
757 else
758 {
759 /* check if we already have a symbol name as replacement */
760
761 if (disHasName)
762 {
763 /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
764
765 swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
766 *pdwDisp = 0;
767 disHasName = false;
768 return 1;
769 }
770 else
771 {
772 return 0;
773 }
774 }
775
776 default:
777
778 printf("Termination type is %d\n", (int)terminationType);
779 assert(!"treat this case\n");
780
781 break;
782 }
783
784#else // _TARGET_*
785#error Unsupported or unset target architecture
786#endif // _TARGET_*
787
788 /* save displacement */
789
790 *pdwDisp = disp;
791
792 return 1;
793}
794
795/*****************************************************************************
796 *
797 * Callback for register operands. Most probably, this is a local variable or
798 * a parameter
799 *
800 * Return 1 if a name was written representing the register, 0 otherwise.
801 */
802
803/* static */
804size_t __stdcall DisAssembler::disCchReg(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
805{
806 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
807 assert(pDisAsm);
808
809 return pDisAsm->disCchRegMember(pdis, reg, wz, cchMax);
810}
811
812size_t DisAssembler::disCchRegMember(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
813{
814 // TODO-Review: DIS::REGA does not directly map to our regNumber! E.g., look at DISARM64::REGA --
815 // the Wt registers come first (and do map to our regNumber), but the Xt registers follow.
816 // Until this is fixed, don't use this function!
817 disHasName = false;
818 return 0;
819
820#if 0
821 const char * var = disComp->codeGen->siRegVarName(
822 (size_t)(pdis->Addr() - disStartAddr),
823 pdis->Cb(),
824 reg);
825
826 if (var)
827 {
828 if (disHasName)
829 {
830 /* CchRegRel has been called before - we have a symbol name saved in global var disFuncTempBuf */
831
832 swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), var, disFuncTempBuf);
833 disHasName = false;
834 return 1;
835 }
836 else
837 {
838 swprintf_s(wz, cchMax, W("%hs'%hs'"), getRegName(reg), var);
839 return 1;
840 }
841 }
842 else
843 {
844 if (disHasName)
845 {
846 /* this is the ugly case when a variable is incorrectly presumed dead */
847
848 swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), "<InstVar>", disFuncTempBuf);
849 disHasName = false;
850 return 1;
851 }
852
853 /* just to make sure we didn't bungle if var returns NULL */
854 disHasName = false;
855 return 0;
856 }
857#endif // 0
858}
859
860/*****************************************************************************
861 * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
862 */
863AddrToMethodHandleMap* DisAssembler::GetAddrToMethodHandleMap()
864{
865 if (disAddrToMethodHandleMap == nullptr)
866 {
867 disAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
868 }
869 return disAddrToMethodHandleMap;
870}
871
872/*****************************************************************************
873 * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
874 */
875AddrToMethodHandleMap* DisAssembler::GetHelperAddrToMethodHandleMap()
876{
877 if (disHelperAddrToMethodHandleMap == nullptr)
878 {
879 disHelperAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
880 }
881 return disHelperAddrToMethodHandleMap;
882}
883
884/*****************************************************************************
885 * Helper function to lazily create a map from relocation address to relocation target address.
886 */
887AddrToAddrMap* DisAssembler::GetRelocationMap()
888{
889 if (disRelocationMap == nullptr)
890 {
891 disRelocationMap = new (disComp->getAllocator()) AddrToAddrMap(disComp->getAllocator());
892 }
893 return disRelocationMap;
894}
895
896/*****************************************************************************
897 * Return the count of bytes disassembled.
898 */
899
900size_t DisAssembler::CbDisassemble(DIS* pdis,
901 size_t offs,
902 DIS::ADDR addr,
903 const BYTE* pb,
904 size_t cbMax,
905 FILE* pfile,
906 bool findLabels,
907 bool printit /* = false */,
908 bool dispOffs /* = false */,
909 bool dispCodeBytes /* = false */)
910{
911 assert(pdis);
912
913 size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
914
915 if (cb == 0)
916 {
917 DISASM_DUMP("CbDisassemble offs %Iu addr %I64u\n", offs, addr);
918 // assert(!"can't disassemble instruction!!!");
919 fprintf(pfile, "MSVCDIS can't disassemble instruction @ offset %Iu (0x%02x)!!!\n", offs, offs);
920#if defined(_TARGET_ARM64_)
921 fprintf(pfile, "%08Xh\n", *(unsigned int*)pb);
922 return 4;
923#else
924 fprintf(pfile, "%02Xh\n", *pb);
925 return 1;
926#endif
927 }
928
929#if defined(_TARGET_ARM64_)
930 assert(cb == 4); // all instructions are 4 bytes!
931#endif // _TARGET_ARM64_
932
933 /* remember current offset and instruction size */
934
935 disCurOffset = (size_t)addr;
936 disInstSize = cb;
937
938 /* Set the disTarget address */
939
940 disTarget = (size_t)pdis->AddrTarget();
941
942 if (findLabels)
943 {
944#if defined(_TARGET_XARCH_)
945 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
946
947 /* check the termination type of the instruction */
948
949 switch (terminationType)
950 {
951 case DISX86::trmtaCallNear16:
952 case DISX86::trmtaCallNear32:
953 case DISX86::trmtaCallFar:
954
955 {
956 // Don't count addresses in the relocation table
957 size_t targetAddr;
958 size_t absoluteAddr =
959 (size_t)disGetLinearAddr((size_t)pdis->AddrAddress(1)); // Get the address in the instruction of the
960 // call target address (the address the
961 // reloc is applied to).
962 if (GetRelocationMap()->Lookup(absoluteAddr, &targetAddr))
963 {
964 break;
965 }
966 }
967
968 __fallthrough;
969
970 case DISX86::trmtaJmpShort:
971 case DISX86::trmtaJmpNear:
972 case DISX86::trmtaJmpFar:
973 case DISX86::trmtaJmpCcShort:
974 case DISX86::trmtaJmpCcNear:
975
976 /* a CALL is local iff the disTarget is within the block boundary */
977
978 /* mark the jump label in the disTarget vector and return */
979
980 if (disTarget != DIS::addrNil) // There seems to be an assumption that you can't branch to the first
981 // address of the function (prolog).
982 {
983 if (0 <= disTarget && disTarget < disTotalCodeSize)
984 {
985 /* we're OK, disTarget within block boundary */
986
987 disLabels[disTarget] = 1;
988 }
989 }
990 break;
991
992 case DISX86::trmtaFallThrough:
993 // We'd like to be able to get a label for code like "lea rcx, [4]" that we use for jump tables, but I
994 // can't figure out how.
995 break;
996
997 default:
998
999 /* jump is not in the current code block */
1000 break;
1001
1002 } // end switch
1003#elif defined(_TARGET_ARM64_)
1004 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
1005
1006 /* check the termination type of the instruction */
1007
1008 switch (terminationType)
1009 {
1010 case DISARM64::TRMTA::trmtaCall:
1011 case DISARM64::TRMTA::trmtaCallCc:
1012
1013 {
1014 // Don't count addresses in the relocation table
1015 size_t targetAddr;
1016 size_t absoluteAddr =
1017 (size_t)disGetLinearAddr((size_t)pdis->AddrAddress(1)); // Get the address in the instruction of the
1018 // call target address (the address the
1019 // reloc is applied to).
1020 if (GetRelocationMap()->Lookup(absoluteAddr, &targetAddr))
1021 {
1022 break;
1023 }
1024 }
1025
1026 __fallthrough;
1027
1028 case DISARM64::TRMTA::trmtaBra:
1029 case DISARM64::TRMTA::trmtaBraCase:
1030 case DISARM64::TRMTA::trmtaBraCc:
1031 case DISARM64::TRMTA::trmtaBraCcCase:
1032
1033 /* a CALL is local iff the disTarget is within the block boundary */
1034
1035 /* mark the jump label in the disTarget vector and return */
1036
1037 if (disTarget != DIS::addrNil) // There seems to be an assumption that you can't branch to the first
1038 // address of the function (prolog).
1039 {
1040 if (0 <= disTarget && disTarget < disTotalCodeSize)
1041 {
1042 /* we're OK, disTarget within block boundary */
1043
1044 disLabels[disTarget] = 1;
1045 }
1046 }
1047 break;
1048
1049 case DISARM64::TRMTA::trmtaFallThrough:
1050 {
1051 DIS::INSTRUCTION instr;
1052 DIS::OPERAND ops[DISARM64::coperandMax];
1053 bool ok = pdis->FDecode(&instr, ops, ArrLen(ops));
1054 if (ok)
1055 {
1056 switch ((DISARM64::OPA)instr.opa)
1057 {
1058 case DISARM64::opaAdr:
1059 case DISARM64::opaAdrp:
1060 // operand 1 is an address
1061 assert(instr.coperand >= 2);
1062 assert(ops[1].opcls == DIS::opclsImmediate);
1063 assert(ops[1].imcls == DIS::imclsAddress);
1064 disTarget = ops[1].dwl;
1065 break;
1066 default:
1067 break;
1068 }
1069
1070 if (0 <= disTarget && disTarget < disTotalCodeSize)
1071 {
1072 /* we're OK, disTarget within block boundary */
1073
1074 disLabels[disTarget] = 1;
1075 }
1076 }
1077 }
1078 break;
1079
1080 default:
1081
1082 /* jump is not in the current code block */
1083 break;
1084
1085 } // end switch
1086#else // _TARGET_*
1087#error Unsupported or unset target architecture
1088#endif // _TARGET_*
1089
1090 return cb;
1091 } // end if
1092
1093 /* check if we have a label here */
1094
1095 if (printit)
1096 {
1097 if (disLabels[addr])
1098 {
1099 /* print the label and the offset */
1100
1101 fprintf(pfile, "L_%02u:\n", disLabels[addr]);
1102 }
1103 }
1104
1105 wchar_t wz[MAX_CLASSNAME_LENGTH];
1106 pdis->CchFormatInstr(wz, _countof(wz));
1107
1108 if (printit)
1109 {
1110 if (dispOffs)
1111 {
1112 fprintf(pfile, "%03X", offs);
1113 }
1114
1115#ifdef _TARGET_ARM64_
1116#define CCH_INDENT 8 // fixed sized instructions, always 8 characters
1117#elif defined(_TARGET_AMD64_)
1118#define CCH_INDENT 30 // large constants sometimes
1119#else
1120#define CCH_INDENT 24
1121#endif
1122
1123 size_t cchIndent = CCH_INDENT;
1124
1125 if (dispCodeBytes)
1126 {
1127 static size_t cchBytesMax = -1;
1128
1129 if (cchBytesMax == -1)
1130 {
1131 cchBytesMax = pdis->CchFormatBytesMax();
1132 }
1133
1134 wchar_t wzBytes[MAX_CLASSNAME_LENGTH];
1135 assert(cchBytesMax < MAX_CLASSNAME_LENGTH);
1136
1137 size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1138
1139 if (cchBytes > CCH_INDENT)
1140 {
1141 // Truncate the bytes if they are too long
1142
1143 static const wchar_t* elipses = W("...\0");
1144 const size_t cchElipses = 4;
1145
1146 memcpy(&wzBytes[CCH_INDENT - cchElipses], elipses, cchElipses * sizeof(wchar_t));
1147
1148 cchBytes = CCH_INDENT;
1149 }
1150
1151 fprintf(pfile, " %ls", wzBytes);
1152 cchIndent = CCH_INDENT - cchBytes;
1153 }
1154
1155 // print the dis-assembled instruction
1156
1157 fprintf(pfile, "%*c %ls\n", cchIndent, ' ', wz);
1158 }
1159
1160 return cb;
1161}
1162
1163// TODO-Cleanup: this is currently unused, unreferenced.
1164size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t cbMax, FILE* pfile)
1165{
1166 assert(pdis);
1167 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
1168 assert(pDisAsm);
1169
1170 wchar_t wz[MAX_CLASSNAME_LENGTH];
1171
1172 pdis->CchFormatAddr(addr, wz, _countof(wz));
1173
1174 size_t cchIndent = (size_t)fprintf(pfile, " %ls: ", wz);
1175
1176 size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
1177
1178 if (cb == 0)
1179 {
1180 fprintf(pfile, "%02Xh\n", *pb);
1181 return (1);
1182 }
1183
1184 size_t cchBytesMax = pdis->CchFormatBytesMax();
1185
1186 if (cchBytesMax > 18)
1187 {
1188 // Limit bytes coded to 18 characters
1189
1190 cchBytesMax = 18;
1191 }
1192
1193 wchar_t wzBytes[64];
1194 size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1195
1196 wchar_t* pwzBytes;
1197 wchar_t* pwzNext;
1198
1199 for (pwzBytes = wzBytes; pwzBytes != NULL; pwzBytes = pwzNext)
1200 {
1201 BOOL fFirst = (pwzBytes == wzBytes);
1202
1203 cchBytes = wcslen(pwzBytes);
1204
1205 if (cchBytes <= cchBytesMax)
1206 {
1207 pwzNext = NULL;
1208 }
1209
1210 else
1211 {
1212 wchar_t ch = pwzBytes[cchBytesMax];
1213 pwzBytes[cchBytesMax] = '\0';
1214
1215 if (ch == W(' '))
1216 {
1217 pwzNext = pwzBytes + cchBytesMax + 1;
1218 }
1219
1220 else
1221 {
1222 pwzNext = wcsrchr(pwzBytes, W(' '));
1223 assert(pwzNext);
1224
1225 pwzBytes[cchBytesMax] = ch;
1226 *pwzNext++ = '\0';
1227 }
1228 }
1229
1230 if (fFirst)
1231 {
1232 pdis->CchFormatInstr(wz, _countof(wz));
1233 fprintf(pfile, "%-*ls %ls\n", cchBytesMax, pwzBytes, wz);
1234 }
1235
1236 else
1237 {
1238 fprintf(pfile, "%*c%ls\n", cchIndent, ' ', pwzBytes);
1239 }
1240 }
1241
1242 return (cb);
1243}
1244
1245void DisAssembler::DisasmBuffer(FILE* pfile, bool printit)
1246{
1247 DIS* pdis = NULL;
1248
1249#ifdef _TARGET_X86_
1250 pdis = DIS::PdisNew(DIS::distX86);
1251#elif defined(_TARGET_AMD64_)
1252 pdis = DIS::PdisNew(DIS::distX8664);
1253#elif defined(_TARGET_ARM64_)
1254 pdis = DIS::PdisNew(DIS::distArm64);
1255#else // _TARGET_*
1256#error Unsupported or unset target architecture
1257#endif
1258
1259 if (pdis == NULL)
1260 {
1261 assert(!"out of memory in disassembler?");
1262 return;
1263 }
1264
1265#ifdef _TARGET_64BIT_
1266 pdis->SetAddr64(true);
1267#endif
1268
1269 // Store a pointer to the DisAssembler so that the callback functions
1270 // can get to it.
1271
1272 pdis->PvClientSet((void*)this);
1273
1274 /* Calculate addresses */
1275
1276 size_t ibCur = 0;
1277 DIS::ADDR addr = 0; // Always emit code with respect to a "0" base address.
1278
1279 /* First walk the code to find all jump targets */
1280
1281 while (ibCur < disTotalCodeSize)
1282 {
1283 size_t cb;
1284
1285 cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1286 true); // find labels
1287
1288 // CbDisassemble returning > MAX_INT... give me a break.
1289 ibCur += cb;
1290 }
1291
1292 /* reset the label counter and start assigning consecutive number labels to the label locations */
1293
1294 BYTE label = 0;
1295 for (unsigned i = 0; i < disTotalCodeSize; i++)
1296 {
1297 if (disLabels[i] != 0)
1298 {
1299 disLabels[i] = ++label;
1300 }
1301 }
1302
1303 /* Re-initialize addresses for disassemble phase */
1304
1305 ibCur = 0;
1306 addr = 0;
1307
1308 // Set callbacks only if we are displaying it. Else, the scheduler has called it
1309
1310 if (printit)
1311 {
1312 /* Set the callback functions for symbol lookup */
1313
1314 pdis->PfncchaddrSet(disCchAddr);
1315 pdis->PfncchfixupSet(disCchFixup);
1316 pdis->PfncchregrelSet(disCchRegRel);
1317 pdis->PfncchregSet(disCchReg);
1318 }
1319
1320 while (ibCur < disTotalCodeSize)
1321 {
1322 size_t cb;
1323
1324 cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1325 false, // find labels
1326 printit,
1327 !disDiffable, // display relative offset
1328#ifdef DEBUG
1329 !disDiffable // Display code bytes?
1330#else
1331 false // Display code bytes?
1332#endif
1333 );
1334
1335 ibCur += (unsigned)cb;
1336 }
1337
1338 delete pdis;
1339}
1340
1341/*****************************************************************************
1342 * Given a linear offset into the code, find a pointer to the actual code (either in the hot or cold section)
1343 *
1344 * Arguments:
1345 * offset - The linear offset into the code. It must point within the code.
1346 */
1347
1348const BYTE* DisAssembler::disGetLinearAddr(size_t offset)
1349{
1350 if (offset < disHotCodeSize)
1351 {
1352 return (const BYTE*)disHotCodeBlock + offset;
1353 }
1354 else
1355 {
1356 return (const BYTE*)disColdCodeBlock + offset - disHotCodeSize;
1357 }
1358}
1359
1360/*****************************************************************************
1361 * Given a linear offset into the code, determine how many bytes are remaining in the buffer.
1362 * This will only return the number of bytes left in either the hot or cold buffer. This is used
1363 * to avoid walking off the end of the buffer.
1364 *
1365 * Arguments:
1366 * offset - The linear offset into the code. It must point within the code.
1367 */
1368
1369size_t DisAssembler::disGetBufferSize(size_t offset)
1370{
1371 if (offset < disHotCodeSize)
1372 {
1373 return disHotCodeSize - offset;
1374 }
1375 else
1376 {
1377 return disHotCodeSize + disColdCodeSize - offset;
1378 }
1379}
1380
1381/*****************************************************************************
1382 * Get the function name for a given absolute address.
1383 */
1384
1385const char* DisAssembler::disGetMethodFullName(size_t addr)
1386{
1387 CORINFO_METHOD_HANDLE res;
1388
1389 // First check the JIT helper table: they're very common.
1390 if (GetHelperAddrToMethodHandleMap()->Lookup(addr, &res))
1391 {
1392 return disComp->eeGetMethodFullName(res);
1393 }
1394
1395 // Next check the "normal" registered call targets
1396 if (GetAddrToMethodHandleMap()->Lookup(addr, &res))
1397 {
1398 return disComp->eeGetMethodFullName(res);
1399 }
1400
1401 return nullptr;
1402}
1403
1404/*****************************************************************************
1405 * Register a called function address as associated with a CORINFO_METHOD_HANDLE.
1406 *
1407 * Arguments:
1408 * addr - The absolute address of the target function.
1409 * methHnd - The method handle associated with 'addr'.
1410 */
1411
1412void DisAssembler::disSetMethod(size_t addr, CORINFO_METHOD_HANDLE methHnd)
1413{
1414 if (!disComp->opts.doLateDisasm)
1415 {
1416 return;
1417 }
1418
1419 if (disComp->eeGetHelperNum(methHnd))
1420 {
1421 DISASM_DUMP("Helper function: %p => %p\n", addr, methHnd);
1422 GetHelperAddrToMethodHandleMap()->Set(addr, methHnd);
1423 }
1424 else
1425 {
1426 DISASM_DUMP("Function: %p => %p\n", addr, methHnd);
1427 GetAddrToMethodHandleMap()->Set(addr, methHnd);
1428 }
1429}
1430
1431/*****************************************************************************
1432 * Register a relocation.
1433 *
1434 * Arguments:
1435 * relocAddr - The absolute address the relocation applies to.
1436 * targetAddr - The absolute address the relocation points to.
1437 */
1438
1439void DisAssembler::disRecordRelocation(size_t relocAddr, size_t targetAddr)
1440{
1441 if (!disComp->opts.doLateDisasm)
1442 {
1443 return;
1444 }
1445
1446 DISASM_DUMP("Relocation %p => %p\n", relocAddr, targetAddr);
1447 GetRelocationMap()->Set(relocAddr, targetAddr);
1448}
1449
1450/*****************************************************************************
1451 *
1452 * Disassemble the code which has been generated
1453 */
1454
1455void DisAssembler::disAsmCode(BYTE* hotCodePtr, size_t hotCodeSize, BYTE* coldCodePtr, size_t coldCodeSize)
1456{
1457 if (!disComp->opts.doLateDisasm)
1458 {
1459 return;
1460 }
1461
1462#ifdef DEBUG
1463 // Should we make it diffable?
1464 disDiffable = disComp->opts.dspDiffable;
1465#else // !DEBUG
1466 // NOTE: non-debug builds are always diffable!
1467 disDiffable = true;
1468#endif // !DEBUG
1469
1470#ifdef DEBUG
1471 const wchar_t* fileName = JitConfig.JitLateDisasmTo();
1472 if (fileName != nullptr)
1473 {
1474 errno_t ec = _wfopen_s(&disAsmFile, fileName, W("a+"));
1475 if (ec != 0)
1476 {
1477 disAsmFile = nullptr;
1478 }
1479 }
1480#else // !DEBUG
1481 // NOTE: non-DEBUG builds always use jitstdout currently!
1482 disAsmFile = jitstdout;
1483#endif // !DEBUG
1484
1485 if (disAsmFile == nullptr)
1486 {
1487 disAsmFile = jitstdout;
1488 }
1489
1490 // As this writes to a common file, this is not reentrant.
1491
1492 assert(hotCodeSize > 0);
1493 if (coldCodeSize == 0)
1494 {
1495 fprintf(disAsmFile, "************************** %hs:%hs size 0x%04IX **************************\n\n",
1496 disCurClassName, disCurMethodName, hotCodeSize);
1497
1498 fprintf(disAsmFile, "Base address : %ph\n", dspAddr(hotCodePtr));
1499 }
1500 else
1501 {
1502 fprintf(disAsmFile,
1503 "************************** %hs:%hs hot size 0x%04IX cold size 0x%04IX **************************\n\n",
1504 disCurClassName, disCurMethodName, hotCodeSize, coldCodeSize);
1505
1506 fprintf(disAsmFile, "Hot address : %ph\n", dspAddr(hotCodePtr));
1507 fprintf(disAsmFile, "Cold address : %ph\n", dspAddr(coldCodePtr));
1508 }
1509
1510 disStartAddr = 0;
1511 disHotCodeBlock = (size_t)hotCodePtr;
1512 disHotCodeSize = hotCodeSize;
1513 disColdCodeBlock = (size_t)coldCodePtr;
1514 disColdCodeSize = coldCodeSize;
1515
1516 disTotalCodeSize = disHotCodeSize + disColdCodeSize;
1517
1518 disLabels = new (disComp, CMK_DebugOnly) BYTE[disTotalCodeSize]();
1519
1520 DisasmBuffer(disAsmFile, /* printIt */ true);
1521 fprintf(disAsmFile, "\n");
1522
1523 if (disAsmFile != jitstdout)
1524 {
1525 fclose(disAsmFile);
1526 }
1527 else
1528 {
1529 fflush(disAsmFile);
1530 }
1531}
1532
1533/*****************************************************************************/
1534// This function is called for every method. Checks if we are supposed to disassemble
1535// the method, and where to send the disassembly output.
1536
1537void DisAssembler::disOpenForLateDisAsm(const char* curMethodName, const char* curClassName, PCCOR_SIGNATURE sig)
1538{
1539 if (!disComp->opts.doLateDisasm)
1540 {
1541 return;
1542 }
1543
1544 disCurMethodName = curMethodName;
1545 disCurClassName = curClassName;
1546}
1547
1548/*****************************************************************************/
1549
1550void DisAssembler::disInit(Compiler* pComp)
1551{
1552 assert(pComp);
1553 disComp = pComp;
1554 disHasName = false;
1555 disLabels = nullptr;
1556 disAddrToMethodHandleMap = nullptr;
1557 disHelperAddrToMethodHandleMap = nullptr;
1558 disRelocationMap = nullptr;
1559 disDiffable = false;
1560 disAsmFile = nullptr;
1561}
1562
1563/*****************************************************************************/
1564#endif // LATE_DISASM
1565/*****************************************************************************/
1566