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 /***********************************************************************
12 * This file handles disassembly. It is adapted from the MS linker.
14 ***********************************************************************/
21 /*****************************************************************************/
23 /*****************************************************************************/
25 // Define DISASM_DEBUG to get verbose output of late disassembler inner workings.
26 //#define DISASM_DEBUG
29 #define DISASM_DUMP(...) \
33 #define DISASM_DUMP(...) printf(__VA_ARGS__)
35 #else // !DISASM_DEBUG
36 #define DISASM_DUMP(...)
37 #endif // !DISASM_DEBUG
39 /*****************************************************************************/
41 #define MAX_CLASSNAME_LENGTH 1024
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")
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")
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")
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")
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")
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")
68 "/ALTERNATENAME:__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPAGIPA_K@ZP6GI012I3@Z@Z=__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPA_WIPA_K@ZP6GI012I3@Z@Z")
71 "/ALTERNATENAME:__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PAGI@ZP6GI012I@Z@Z=__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PA_WI@ZP6GI012I@Z@Z")
74 "/ALTERNATENAME:__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPAGIPAK@ZP6GI01K2I3@Z@Z=__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPA_WIPAK@ZP6GI01K2I3@Z@Z")
77 "/ALTERNATENAME:__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPAGIPA_K@ZP6GI01I2I3@Z@Z=__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPA_WIPA_K@ZP6GI01I2I3@Z@Z")
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.
89 // These structs were defined in emit.h. Fake them here so DisAsm.cpp can compile
91 typedef struct codeFix
97 typedef struct codeBlk
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.
107 * Return 1 if a name was written representing the address, 0 otherwise.
111 size_t __stdcall DisAssembler::disCchAddr(
112 const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
114 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
116 return pDisAsm->disCchAddrMember(pdis, addr, wz, cchMax, pdwDisp);
119 size_t DisAssembler::disCchAddrMember(
120 const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
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 */
126 size_t retval = 0; // assume we don't know
128 #if defined(_TARGET_XARCH_)
130 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
132 DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
134 switch (terminationType)
138 case DISX86::trmtaJmpShort:
139 case DISX86::trmtaJmpCcShort:
141 /* We have a short jump in the current code block - generate the label to which we jump */
143 assert(0 <= disTarget && disTarget < disTotalCodeSize);
144 swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
148 case DISX86::trmtaJmpNear:
149 case DISX86::trmtaJmpCcNear:
151 /* We have a near jump. Check if is in the current code block.
152 * Otherwise we have no target for it. */
154 if (0 <= disTarget && disTarget < disTotalCodeSize)
156 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
161 case DISX86::trmtaCallNear16:
162 case DISX86::trmtaCallNear32:
164 /* check for local calls (i.e. CALL label) */
166 if (0 <= disTarget && disTarget < disTotalCodeSize)
168 /* not a "call ds:[0000]" - go ahead */
169 /* disTarget within block boundary -> local call */
171 swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
176 /* this is a near call - in our case usually VM helper functions */
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 */
184 size_t absoluteTarget = (size_t)disGetLinearAddr(disTarget);
185 const char* name = disGetMethodFullName(absoluteTarget);
188 swprintf_s(wz, cchMax, W("%p %S"), dspAddr(absoluteTarget), name);
196 #ifdef _TARGET_AMD64_
198 case DISX86::trmtaFallThrough:
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. */
203 /* find the emitter block and the offset for the fixup
204 * "addr" is the address of the immediate */
208 #endif // _TARGET_AMD64_
212 printf("Termination type is %d\n", (int)terminationType);
213 assert(!"treat this case\n");
217 #elif defined(_TARGET_ARM64_)
219 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
221 DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
223 switch (terminationType)
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:
234 /* We have a jump. Check if is in the current code block.
235 * Otherwise we have no target for it. */
237 if (0 <= disTarget && disTarget < disTotalCodeSize)
239 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
244 case DISARM64::trmtaCall:
245 case DISARM64::trmtaCallCc:
246 case DISARM64::trmtaCallCcInd:
247 case DISARM64::trmtaCallInd:
249 /* check for local calls (i.e. CALL label) */
251 if (0 <= disTarget && disTarget < disTotalCodeSize)
253 /* not a "call [0000]" - go ahead */
254 /* disTarget within block boundary -> local call */
256 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
261 /* this is a near call - in our case usually VM helper functions */
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 */
269 size_t absoluteTarget = (size_t)disGetLinearAddr(disTarget);
270 const char* name = disGetMethodFullName(absoluteTarget);
273 swprintf_s(wz, cchMax, W("%p %S"), dspAddr(absoluteTarget), name);
281 case DISARM64::trmtaFallThrough:
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. */
286 /* find the emitter block and the offset for the fixup
287 * "addr" is the address of the immediate */
290 DIS::INSTRUCTION instr;
291 DIS::OPERAND ops[DISARM64::coperandMax];
292 bool ok = pdis->FDecode(&instr, ops, ArrLen(ops));
295 bool isAddress = false;
296 switch ((DISARM64::OPA)instr.opa)
298 case DISARM64::opaAdr:
299 case DISARM64::opaAdrp:
306 if (isAddress && 0 <= addr && addr < disTotalCodeSize)
308 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[addr]);
317 printf("Termination type is %d\n", (int)terminationType);
318 assert(!"treat this case\n");
323 #error Unsupported or unset target architecture
330 swprintf_s(wz, cchMax, W("%p"), dspAddr((void*)1));
335 /* no displacement */
343 /*****************************************************************************
344 * We annotate some instructions to get info needed to display the symbols
345 * for that instruction.
347 * Return 1 if a name was written representing the address, 0 otherwise.
351 size_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)
354 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
357 return pDisAsm->disCchFixupMember(pdis, addr, size, wz, cchMax, pdwDisp);
360 size_t DisAssembler::disCchFixupMember(
361 const DIS* pdis, DIS::ADDR addr, size_t size, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
363 #if defined(_TARGET_XARCH_)
365 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
366 // DIS::ADDR disIndAddr;
368 DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
371 // Is there a relocation registered for the address?
373 size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
375 bool anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
377 switch (terminationType)
379 DIS::ADDR disCallSize;
381 case DISX86::trmtaFallThrough:
383 /* memory indirect case */
385 assert(addr > pdis->Addr());
387 /* find the emitter block and the offset for the fixup
388 * "addr" is the address of the immediate */
392 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
393 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
399 case DISX86::trmtaJmpInd:
401 /* pretty rare case - something like "jmp [eax*4]"
402 * not a function call or anything worth annotating */
406 case DISX86::trmtaTrap:
407 case DISX86::trmtaTrapCc:
409 /* some instructions like division have a TRAP termination type - ignore it */
413 case DISX86::trmtaJmpShort:
414 case DISX86::trmtaJmpCcShort:
416 case DISX86::trmtaJmpNear:
417 case DISX86::trmtaJmpCcNear:
419 /* these are treated by the CchAddr callback - skip them */
423 case DISX86::trmtaCallNear16:
424 case DISX86::trmtaCallNear32:
428 const char* name = disGetMethodFullName(targetAddr);
431 swprintf_s(wz, cchMax, W("%p %S"), dspAddr(targetAddr), name);
436 /* these are treated by the CchAddr callback - skip them */
440 case DISX86::trmtaCallInd:
442 /* here we have an indirect call - find the indirect address */
444 // BYTE * code = disGetLinearAddr((size_t)addr);
445 // disIndAddr = (DIS::ADDR) (code+0);
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 */
451 assert(addr > pdis->Addr());
452 disCallSize = addr - pdis->Addr();
454 /* find the emitter block and the offset of the call fixup */
460 printf("Termination type is %d\n", (int)terminationType);
461 assert(!"treat this case\n");
465 #elif defined(_TARGET_ARM64_)
467 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
468 // DIS::ADDR disIndAddr;
470 DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
473 // Is there a relocation registered for the address?
475 size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
477 bool anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
479 switch (terminationType)
481 DIS::ADDR disCallSize;
483 case DISARM64::TRMTA::trmtaUnknown:
486 case DISARM64::TRMTA::trmtaFallThrough:
490 /* memory indirect case */
492 assert(addr > pdis->Addr());
494 /* find the emitter block and the offset for the fixup
495 * "addr" is the address of the immediate */
497 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
498 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
504 case DISARM64::TRMTA::trmtaBraInd:
505 case DISARM64::TRMTA::trmtaBraCcInd:
507 /* pretty rare case - something like "jmp [eax*4]"
508 * not a function call or anything worth annotating */
512 case DISARM64::TRMTA::trmtaTrap:
513 case DISARM64::TRMTA::trmtaTrapCc:
515 /* some instructions like division have a TRAP termination type - ignore it */
519 case DISARM64::TRMTA::trmtaBra:
520 case DISARM64::TRMTA::trmtaBraCase:
521 case DISARM64::TRMTA::trmtaBraCc:
522 case DISARM64::TRMTA::trmtaBraCcCase:
524 /* these are treated by the CchAddr callback - skip them */
528 case DISARM64::TRMTA::trmtaCall:
529 case DISARM64::TRMTA::trmtaCallCc:
533 const char* name = disGetMethodFullName(targetAddr);
536 swprintf_s(wz, cchMax, W("%p %S"), dspAddr(targetAddr), name);
541 /* these are treated by the CchAddr callback - skip them */
545 case DISARM64::TRMTA::trmtaCallInd:
546 case DISARM64::TRMTA::trmtaCallCcInd:
548 /* here we have an indirect call - find the indirect address */
550 // BYTE * code = disGetLinearAddr((size_t)addr);
551 // disIndAddr = (DIS::ADDR) (code+0);
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 */
557 assert(addr > pdis->Addr());
558 disCallSize = addr - pdis->Addr();
560 /* find the emitter block and the offset of the call fixup */
566 printf("Termination type is %d\n", (int)terminationType);
567 assert(!"treat this case\n");
572 #error Unsupported or unset target architecture
575 /* no displacement */
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
587 * Return 1 if a name was written representing the register-relative operand, 0 otherwise.
591 size_t __stdcall DisAssembler::disCchRegRel(
592 const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
594 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
597 return pDisAsm->disCchRegRelMember(pdis, reg, disp, wz, cchMax, pdwDisp);
600 size_t DisAssembler::disCchRegRelMember(
601 const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
603 #if defined(_TARGET_XARCH_)
605 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
606 // DIS::ADDR disIndAddr;
608 DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
610 switch (terminationType)
615 case DISX86::trmtaFallThrough:
617 /* some instructions like division have a TRAP termination type - ignore it */
619 case DISX86::trmtaTrap:
620 case DISX86::trmtaTrapCc:
622 var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
625 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
631 /* This case consists of non-static members */
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
638 if (*disGetLinearAddr(disCurOffset) == 0x66)
649 case DISX86::trmtaCallNear16:
650 case DISX86::trmtaCallNear32:
651 case DISX86::trmtaJmpInd:
655 case DISX86::trmtaCallInd:
657 /* check if this is a one byte displacement */
659 if ((signed char)disp == (int)disp)
661 /* we have a one byte displacement -> there were no previous callbacks */
663 /* find the size of the call opcode - less the immediate */
664 /* this is a call R/M indirect -> opcode size is 2 */
668 /* find the emitter block and the offset of the call fixup */
674 /* check if we already have a symbol name as replacement */
678 /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
680 swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
693 printf("Termination type is %d\n", (int)terminationType);
694 assert(!"treat this case\n");
699 #elif defined(_TARGET_ARM64_)
701 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
703 DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
705 switch (terminationType)
710 case DISARM64::TRMTA::trmtaFallThrough:
712 /* some instructions like division have a TRAP termination type - ignore it */
714 case DISARM64::TRMTA::trmtaTrap:
715 case DISARM64::TRMTA::trmtaTrapCc:
717 var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
720 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
726 /* This case consists of non-static members */
728 // TODO-ARM64-Bug?: Is this correct?
732 case DISARM64::TRMTA::trmtaCall:
733 case DISARM64::TRMTA::trmtaCallCc:
734 case DISARM64::TRMTA::trmtaBraInd:
735 case DISARM64::TRMTA::trmtaBraCcInd:
738 case DISARM64::TRMTA::trmtaCallInd:
739 case DISARM64::TRMTA::trmtaCallCcInd:
741 /* check if this is a one byte displacement */
743 if ((signed char)disp == (int)disp)
745 /* we have a one byte displacement -> there were no previous callbacks */
747 /* find the size of the call opcode - less the immediate */
748 /* this is a call R/M indirect -> opcode size is 2 */
750 // TODO-ARM64-Bug?: Is this correct?
753 /* find the emitter block and the offset of the call fixup */
759 /* check if we already have a symbol name as replacement */
763 /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
765 swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
778 printf("Termination type is %d\n", (int)terminationType);
779 assert(!"treat this case\n");
785 #error Unsupported or unset target architecture
788 /* save displacement */
795 /*****************************************************************************
797 * Callback for register operands. Most probably, this is a local variable or
800 * Return 1 if a name was written representing the register, 0 otherwise.
804 size_t __stdcall DisAssembler::disCchReg(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
806 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
809 return pDisAsm->disCchRegMember(pdis, reg, wz, cchMax);
812 size_t DisAssembler::disCchRegMember(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
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!
821 const char * var = disComp->codeGen->siRegVarName(
822 (size_t)(pdis->Addr() - disStartAddr),
830 /* CchRegRel has been called before - we have a symbol name saved in global var disFuncTempBuf */
832 swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), var, disFuncTempBuf);
838 swprintf_s(wz, cchMax, W("%hs'%hs'"), getRegName(reg), var);
846 /* this is the ugly case when a variable is incorrectly presumed dead */
848 swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), "<InstVar>", disFuncTempBuf);
853 /* just to make sure we didn't bungle if var returns NULL */
860 /*****************************************************************************
861 * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
863 AddrToMethodHandleMap* DisAssembler::GetAddrToMethodHandleMap()
865 if (disAddrToMethodHandleMap == nullptr)
867 disAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
869 return disAddrToMethodHandleMap;
872 /*****************************************************************************
873 * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
875 AddrToMethodHandleMap* DisAssembler::GetHelperAddrToMethodHandleMap()
877 if (disHelperAddrToMethodHandleMap == nullptr)
879 disHelperAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
881 return disHelperAddrToMethodHandleMap;
884 /*****************************************************************************
885 * Helper function to lazily create a map from relocation address to relocation target address.
887 AddrToAddrMap* DisAssembler::GetRelocationMap()
889 if (disRelocationMap == nullptr)
891 disRelocationMap = new (disComp->getAllocator()) AddrToAddrMap(disComp->getAllocator());
893 return disRelocationMap;
896 /*****************************************************************************
897 * Return the count of bytes disassembled.
900 size_t DisAssembler::CbDisassemble(DIS* pdis,
907 bool printit /* = false */,
908 bool dispOffs /* = false */,
909 bool dispCodeBytes /* = false */)
913 size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
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);
924 fprintf(pfile, "%02Xh\n", *pb);
929 #if defined(_TARGET_ARM64_)
930 assert(cb == 4); // all instructions are 4 bytes!
931 #endif // _TARGET_ARM64_
933 /* remember current offset and instruction size */
935 disCurOffset = (size_t)addr;
938 /* Set the disTarget address */
940 disTarget = (size_t)pdis->AddrTarget();
944 #if defined(_TARGET_XARCH_)
945 DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
947 /* check the termination type of the instruction */
949 switch (terminationType)
951 case DISX86::trmtaCallNear16:
952 case DISX86::trmtaCallNear32:
953 case DISX86::trmtaCallFar:
956 // Don't count addresses in the relocation table
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))
970 case DISX86::trmtaJmpShort:
971 case DISX86::trmtaJmpNear:
972 case DISX86::trmtaJmpFar:
973 case DISX86::trmtaJmpCcShort:
974 case DISX86::trmtaJmpCcNear:
976 /* a CALL is local iff the disTarget is within the block boundary */
978 /* mark the jump label in the disTarget vector and return */
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).
983 if (0 <= disTarget && disTarget < disTotalCodeSize)
985 /* we're OK, disTarget within block boundary */
987 disLabels[disTarget] = 1;
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.
999 /* jump is not in the current code block */
1003 #elif defined(_TARGET_ARM64_)
1004 DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
1006 /* check the termination type of the instruction */
1008 switch (terminationType)
1010 case DISARM64::TRMTA::trmtaCall:
1011 case DISARM64::TRMTA::trmtaCallCc:
1014 // Don't count addresses in the relocation table
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))
1028 case DISARM64::TRMTA::trmtaBra:
1029 case DISARM64::TRMTA::trmtaBraCase:
1030 case DISARM64::TRMTA::trmtaBraCc:
1031 case DISARM64::TRMTA::trmtaBraCcCase:
1033 /* a CALL is local iff the disTarget is within the block boundary */
1035 /* mark the jump label in the disTarget vector and return */
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).
1040 if (0 <= disTarget && disTarget < disTotalCodeSize)
1042 /* we're OK, disTarget within block boundary */
1044 disLabels[disTarget] = 1;
1049 case DISARM64::TRMTA::trmtaFallThrough:
1051 DIS::INSTRUCTION instr;
1052 DIS::OPERAND ops[DISARM64::coperandMax];
1053 bool ok = pdis->FDecode(&instr, ops, ArrLen(ops));
1056 switch ((DISARM64::OPA)instr.opa)
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;
1070 if (0 <= disTarget && disTarget < disTotalCodeSize)
1072 /* we're OK, disTarget within block boundary */
1074 disLabels[disTarget] = 1;
1082 /* jump is not in the current code block */
1087 #error Unsupported or unset target architecture
1093 /* check if we have a label here */
1097 if (disLabels[addr])
1099 /* print the label and the offset */
1101 fprintf(pfile, "L_%02u:\n", disLabels[addr]);
1105 wchar_t wz[MAX_CLASSNAME_LENGTH];
1106 pdis->CchFormatInstr(wz, _countof(wz));
1112 fprintf(pfile, "%03X", offs);
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
1120 #define CCH_INDENT 24
1123 size_t cchIndent = CCH_INDENT;
1127 static size_t cchBytesMax = -1;
1129 if (cchBytesMax == -1)
1131 cchBytesMax = pdis->CchFormatBytesMax();
1134 wchar_t wzBytes[MAX_CLASSNAME_LENGTH];
1135 assert(cchBytesMax < MAX_CLASSNAME_LENGTH);
1137 size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1139 if (cchBytes > CCH_INDENT)
1141 // Truncate the bytes if they are too long
1143 static const wchar_t* elipses = W("...\0");
1144 const size_t cchElipses = 4;
1146 memcpy(&wzBytes[CCH_INDENT - cchElipses], elipses, cchElipses * sizeof(wchar_t));
1148 cchBytes = CCH_INDENT;
1151 fprintf(pfile, " %ls", wzBytes);
1152 cchIndent = CCH_INDENT - cchBytes;
1155 // print the dis-assembled instruction
1157 fprintf(pfile, "%*c %ls\n", cchIndent, ' ', wz);
1163 // TODO-Cleanup: this is currently unused, unreferenced.
1164 size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t cbMax, FILE* pfile)
1167 DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
1170 wchar_t wz[MAX_CLASSNAME_LENGTH];
1172 pdis->CchFormatAddr(addr, wz, _countof(wz));
1174 size_t cchIndent = (size_t)fprintf(pfile, " %ls: ", wz);
1176 size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
1180 fprintf(pfile, "%02Xh\n", *pb);
1184 size_t cchBytesMax = pdis->CchFormatBytesMax();
1186 if (cchBytesMax > 18)
1188 // Limit bytes coded to 18 characters
1193 wchar_t wzBytes[64];
1194 size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1199 for (pwzBytes = wzBytes; pwzBytes != NULL; pwzBytes = pwzNext)
1201 BOOL fFirst = (pwzBytes == wzBytes);
1203 cchBytes = wcslen(pwzBytes);
1205 if (cchBytes <= cchBytesMax)
1212 wchar_t ch = pwzBytes[cchBytesMax];
1213 pwzBytes[cchBytesMax] = '\0';
1217 pwzNext = pwzBytes + cchBytesMax + 1;
1222 pwzNext = wcsrchr(pwzBytes, W(' '));
1225 pwzBytes[cchBytesMax] = ch;
1232 pdis->CchFormatInstr(wz, _countof(wz));
1233 fprintf(pfile, "%-*ls %ls\n", cchBytesMax, pwzBytes, wz);
1238 fprintf(pfile, "%*c%ls\n", cchIndent, ' ', pwzBytes);
1245 void DisAssembler::DisasmBuffer(FILE* pfile, bool printit)
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);
1256 #error Unsupported or unset target architecture
1261 assert(!"out of memory in disassembler?");
1265 #ifdef _TARGET_64BIT_
1266 pdis->SetAddr64(true);
1269 // Store a pointer to the DisAssembler so that the callback functions
1272 pdis->PvClientSet((void*)this);
1274 /* Calculate addresses */
1277 DIS::ADDR addr = 0; // Always emit code with respect to a "0" base address.
1279 /* First walk the code to find all jump targets */
1281 while (ibCur < disTotalCodeSize)
1285 cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1286 true); // find labels
1288 // CbDisassemble returning > MAX_INT... give me a break.
1292 /* reset the label counter and start assigning consecutive number labels to the label locations */
1295 for (unsigned i = 0; i < disTotalCodeSize; i++)
1297 if (disLabels[i] != 0)
1299 disLabels[i] = ++label;
1303 /* Re-initialize addresses for disassemble phase */
1308 // Set callbacks only if we are displaying it. Else, the scheduler has called it
1312 /* Set the callback functions for symbol lookup */
1314 pdis->PfncchaddrSet(disCchAddr);
1315 pdis->PfncchfixupSet(disCchFixup);
1316 pdis->PfncchregrelSet(disCchRegRel);
1317 pdis->PfncchregSet(disCchReg);
1320 while (ibCur < disTotalCodeSize)
1324 cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1325 false, // find labels
1327 !disDiffable, // display relative offset
1329 !disDiffable // Display code bytes?
1331 false // Display code bytes?
1335 ibCur += (unsigned)cb;
1341 /*****************************************************************************
1342 * Given a linear offset into the code, find a pointer to the actual code (either in the hot or cold section)
1345 * offset - The linear offset into the code. It must point within the code.
1348 const BYTE* DisAssembler::disGetLinearAddr(size_t offset)
1350 if (offset < disHotCodeSize)
1352 return (const BYTE*)disHotCodeBlock + offset;
1356 return (const BYTE*)disColdCodeBlock + offset - disHotCodeSize;
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.
1366 * offset - The linear offset into the code. It must point within the code.
1369 size_t DisAssembler::disGetBufferSize(size_t offset)
1371 if (offset < disHotCodeSize)
1373 return disHotCodeSize - offset;
1377 return disHotCodeSize + disColdCodeSize - offset;
1381 /*****************************************************************************
1382 * Get the function name for a given absolute address.
1385 const char* DisAssembler::disGetMethodFullName(size_t addr)
1387 CORINFO_METHOD_HANDLE res;
1389 // First check the JIT helper table: they're very common.
1390 if (GetHelperAddrToMethodHandleMap()->Lookup(addr, &res))
1392 return disComp->eeGetMethodFullName(res);
1395 // Next check the "normal" registered call targets
1396 if (GetAddrToMethodHandleMap()->Lookup(addr, &res))
1398 return disComp->eeGetMethodFullName(res);
1404 /*****************************************************************************
1405 * Register a called function address as associated with a CORINFO_METHOD_HANDLE.
1408 * addr - The absolute address of the target function.
1409 * methHnd - The method handle associated with 'addr'.
1412 void DisAssembler::disSetMethod(size_t addr, CORINFO_METHOD_HANDLE methHnd)
1414 if (!disComp->opts.doLateDisasm)
1419 if (disComp->eeGetHelperNum(methHnd))
1421 DISASM_DUMP("Helper function: %p => %p\n", addr, methHnd);
1422 GetHelperAddrToMethodHandleMap()->Set(addr, methHnd);
1426 DISASM_DUMP("Function: %p => %p\n", addr, methHnd);
1427 GetAddrToMethodHandleMap()->Set(addr, methHnd);
1431 /*****************************************************************************
1432 * Register a relocation.
1435 * relocAddr - The absolute address the relocation applies to.
1436 * targetAddr - The absolute address the relocation points to.
1439 void DisAssembler::disRecordRelocation(size_t relocAddr, size_t targetAddr)
1441 if (!disComp->opts.doLateDisasm)
1446 DISASM_DUMP("Relocation %p => %p\n", relocAddr, targetAddr);
1447 GetRelocationMap()->Set(relocAddr, targetAddr);
1450 /*****************************************************************************
1452 * Disassemble the code which has been generated
1455 void DisAssembler::disAsmCode(BYTE* hotCodePtr, size_t hotCodeSize, BYTE* coldCodePtr, size_t coldCodeSize)
1457 if (!disComp->opts.doLateDisasm)
1463 // Should we make it diffable?
1464 disDiffable = disComp->opts.dspDiffable;
1466 // NOTE: non-debug builds are always diffable!
1471 const wchar_t* fileName = JitConfig.JitLateDisasmTo();
1472 if (fileName != nullptr)
1474 errno_t ec = _wfopen_s(&disAsmFile, fileName, W("a+"));
1477 disAsmFile = nullptr;
1481 // NOTE: non-DEBUG builds always use jitstdout currently!
1482 disAsmFile = jitstdout;
1485 if (disAsmFile == nullptr)
1487 disAsmFile = jitstdout;
1490 // As this writes to a common file, this is not reentrant.
1492 assert(hotCodeSize > 0);
1493 if (coldCodeSize == 0)
1495 fprintf(disAsmFile, "************************** %hs:%hs size 0x%04IX **************************\n\n",
1496 disCurClassName, disCurMethodName, hotCodeSize);
1498 fprintf(disAsmFile, "Base address : %ph\n", dspAddr(hotCodePtr));
1503 "************************** %hs:%hs hot size 0x%04IX cold size 0x%04IX **************************\n\n",
1504 disCurClassName, disCurMethodName, hotCodeSize, coldCodeSize);
1506 fprintf(disAsmFile, "Hot address : %ph\n", dspAddr(hotCodePtr));
1507 fprintf(disAsmFile, "Cold address : %ph\n", dspAddr(coldCodePtr));
1511 disHotCodeBlock = (size_t)hotCodePtr;
1512 disHotCodeSize = hotCodeSize;
1513 disColdCodeBlock = (size_t)coldCodePtr;
1514 disColdCodeSize = coldCodeSize;
1516 disTotalCodeSize = disHotCodeSize + disColdCodeSize;
1518 disLabels = new (disComp, CMK_DebugOnly) BYTE[disTotalCodeSize]();
1520 DisasmBuffer(disAsmFile, /* printIt */ true);
1521 fprintf(disAsmFile, "\n");
1523 if (disAsmFile != jitstdout)
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.
1537 void DisAssembler::disOpenForLateDisAsm(const char* curMethodName, const char* curClassName, PCCOR_SIGNATURE sig)
1539 if (!disComp->opts.doLateDisasm)
1544 disCurMethodName = curMethodName;
1545 disCurClassName = curClassName;
1548 /*****************************************************************************/
1550 void DisAssembler::disInit(Compiler* pComp)
1555 disLabels = nullptr;
1556 disAddrToMethodHandleMap = nullptr;
1557 disHelperAddrToMethodHandleMap = nullptr;
1558 disRelocationMap = nullptr;
1559 disDiffable = false;
1560 disAsmFile = nullptr;
1563 /*****************************************************************************/
1564 #endif // LATE_DISASM
1565 /*****************************************************************************/