3 Copyright (c) 2000-2001 Goran Devic
4 Modified (c) 2001 Klaus P. Gerlicher
25 17-Mar-2000: Original (Goran Devic)
26 26-Apr-2000: Major rewrite, added coprocessor instructions (Goran Devic)
27 04-Nov-2000: Modified for LinIce (Goran Devic)
28 05-Jan-2001: Modified for pICE (Klaus P. Gerlicher)
33 This file may be distributed under the terms of the GNU Public License.
37 /*******************************************************************************
39 ******************************************************************************/
43 #include "disassemblerdata.h" // Include its own data
45 /******************************************************************************
47 * This structure is used to pass parameters and options to the
50 ******************************************************************************/
53 ULONG dwFlags; // Generic flags (described below)
54 USHORT wSel; // Selector to use to fetch code
55 UCHAR *bpTarget; // Target pointer to disassemble
56 UCHAR *szDisasm; // String where to put ascii result
57 UCHAR Codes[20]; // Buffer where to store code UCHARs
59 UCHAR bAsciiLen; // Length of the ascii result
60 UCHAR bInstrLen; // Instruction lenght in UCHARs
62 int nDisplacement; // Scanner: possible constant displacement
63 int nScanEnum; // Scanner: specific flags SCAN_*
67 // dwFlags contains a set of boolean flags with the following functionality
69 #define DIS_DATA32 0x0001 // Data size 16/32 bits (0/1)
70 #define DIS_GETDATASIZE(flags) ((flags)&DIS_DATA32)
71 #define DIS_ADDRESS32 0x0002 // Address size 16/32 bits (0/1)
72 #define DIS_GETADDRSIZE(flags) (((flags)&DIS_ADDRESS32)?1:0)
74 #define DIS_SEGOVERRIDE 0x0004 // Default segment has been overriden
76 #define DIS_REP 0x0100 // Return: REP prefix found (followed by..)
77 #define DIS_REPNE 0x0200 // Return: REPNE prefix found
78 #define DIS_GETREPENUM(flags) (((flags)>>8)&3)
79 #define DIS_ILLEGALOP 0x8000 // Return: illegal opcode
82 /******************************************************************************
86 ******************************************************************************/
89 /******************************************************************************
91 * External functions (optional) *
93 ******************************************************************************/
95 /******************************************************************************
97 * Local Defines, Variables and Macros *
99 ******************************************************************************/
100 UCHAR GetUCHAR(ULONG addr)
102 if(IsAddressValid(addr))
103 return *(PUCHAR)addr;
105 return 0x82; // INVALID OPCODE
108 static UCHAR GetNextUCHAR(USHORT sel, UCHAR *offset, UCHAR *pCode)
110 pCode[0] = GetUCHAR((ULONG) offset + 0) & 0xFF;
115 static USHORT GetNextUSHORT(USHORT sel, UCHAR *offset, UCHAR *pCode)
117 pCode[0] = GetUCHAR((ULONG) offset + 0) & 0xFF;
118 pCode[1] = GetUCHAR((ULONG) offset + 1) & 0xFF;
120 return( *(USHORT *) pCode );
123 static ULONG GetNextULONG(USHORT sel, UCHAR *offset, UCHAR *pCode)
125 pCode[0] = GetUCHAR((ULONG) offset + 0) & 0xFF;
126 pCode[1] = GetUCHAR((ULONG) offset + 1) & 0xFF;
127 pCode[2] = GetUCHAR((ULONG) offset + 2) & 0xFF;
128 pCode[3] = GetUCHAR((ULONG) offset + 3) & 0xFF;
130 return( *(ULONG *) pCode );
134 #define NEXTUCHAR GetNextUCHAR( pDis->wSel, bpTarget, bpCode); bpCode += 1; bpTarget += 1; bInstrLen += 1
136 #define NEXTUSHORT GetNextUSHORT( pDis->wSel, bpTarget, bpCode); bpCode += 2; bpTarget += 2; bInstrLen += 2
138 #define NEXTULONG GetNextULONG(pDis->wSel, bpTarget, bpCode); bpCode += 4; bpTarget += 4; bInstrLen += 4
141 /******************************************************************************
145 ******************************************************************************/
147 /******************************************************************************
149 * UCHAR Disassembler( TDisassembler *pDis ); *
151 *******************************************************************************
153 * This is a generic Intel line disassembler.
157 * bpTarget is the address of instruction to disassemble
158 * szDisasm is the address of the buffer to print a line into
159 * dwFlags contains the default operand and address bits
160 * pCode is the address to store code UCHARs (up to 16)
162 * Disassembled instruction is stored as an ASCIIZ string pointed by
163 * szDisasm pointer (from the pDis structure).
167 * *szDisasm contains the disassembled instruction string
168 * bAsciiLen is set to the length of the printed string
169 * bInstrLen is set to instruction length in UCHARs
170 * dwFlags - has operand and address size flags adjusted
171 * - DIS_ILLEGALOP set if that was illegal instruction
172 * UCHAR - instruction length in UCHARs
174 ******************************************************************************/
175 UCHAR Disassembler( TDisassembler *pDis )
177 TOpcodeData *p; // Pointer to a current instruction record
178 UCHAR *bpTarget; // Pointer to the target code to be disassembled
179 UCHAR *bpCode; // Pointer to code UCHARs
180 ULONG arg; // Argument counter
181 char *sPtr; // Message selection pointer
182 int nPos; // Printing position in the output string
183 UCHAR *pArg; // Pointer to record where instruction arguments are
184 ULONG dwULONG; // Temporary ULONG storage
185 USHORT wUSHORT; // Temporary USHORT storage
186 UCHAR bUCHAR; // Temporary UCHAR storage
187 UCHAR bInstrLen; // Current instruction lenght in UCHARs
188 UCHAR bOpcode; // Current opcode that is being disassembled
189 UCHAR bSegOverride; // 0 default segment. >0, segment index
190 UCHAR bMod=0; // Mod field of the instruction
191 UCHAR bReg=0; // Register field of the instruction
192 UCHAR bRm=0; // R/M field of the instruction
193 UCHAR bW; // Width bit for the register selection
195 UCHAR bSib; // S-I-B UCHAR for the instruction
196 UCHAR bSs; // SS field of the s-i-b UCHAR
197 UCHAR bIndex; // Index field of the s-i-b UCHAR
198 UCHAR bBase; // Base field of the s-i-b UCHAR
199 LPSTR pSymbolName; // used to symbolic name of value
201 bInstrLen = 0; // Reset instruction lenght to zero
202 bSegOverride = 0; // Set default segment (no override)
203 nPos = 0; // Reset printing position
204 sPtr = NULL; // Points to no message by default
205 bpTarget = pDis->bpTarget; // Set internal pointer to a target address
206 bpCode = pDis->Codes; // Set internal pointer to code UCHARs
210 bOpcode = NEXTUCHAR; // Get the first opcode UCHAR from the target address
211 p = &Op1[bOpcode]; // Get the address of the instruction record
213 if( p->flags & DIS_SPECIAL )
215 // Opcode is one of the special ones, so do what needs to be done there
226 case _EscDF: // Coprocessor escape: UCHARs D8 - DF
227 bOpcode = NEXTUCHAR; // Get the modRM UCHAR of the instruction
231 // Opcodes 00-BF use Coproc1 table
233 bReg = (bOpcode >> 3) & 7;
234 p = &Coproc1[ p->name - _EscD8 ][ bReg ];
236 goto StartInstructionParseMODRM;
238 // Opcodes C0-FF use Coproc2 table
240 p = &Coproc2[ p->name - _EscD8 ][ bOpcode - 0xC0 ];
242 goto StartInstructionNoMODRM;
244 case _S_ES: // Segment override
250 bSegOverride = p->name - _S_ES + 1;
253 case _OPSIZ: // Operand size override - toggle
254 pDis->dwFlags ^= DIS_DATA32;
257 case _ADSIZ: // Address size override - toggle
258 pDis->dwFlags ^= DIS_ADDRESS32;
261 case _REPNE: // REPNE/REPNZ prefix
262 pDis->dwFlags |= DIS_REPNE;
265 case _REP: // REP/REPE/REPZ prefix
266 pDis->dwFlags |= DIS_REP;
269 case _2BESC: // 2 UCHAR escape code 0x0F
270 bOpcode = NEXTUCHAR; // Get the second UCHAR of the instruction
271 p = &Op2[bOpcode]; // Get the address of the instruction record
273 if( !(p->flags & DIS_SPECIAL) ) goto StartInstruction;
274 if( p->name < _GRP6 ) goto IllegalOpcode;
276 case _GRP1a: // Additional groups of instructions
294 bOpcode = NEXTUCHAR; // Get the Mod R/M UCHAR whose...
295 // bits 3,4,5 select instruction
297 bReg = (bOpcode >> 3) & 7;
298 p = &Groups[p->name - _GRP1a][ bReg ];
300 if( !(p->flags & DIS_SPECIAL) ) goto StartInstructionParseMODRM;
302 case _NDEF : // Not defined or illegal opcode
305 default :; // Should not happen
309 goto StartInstruction;
311 while( bInstrLen < 15 );
315 nPos += PICE_sprintf( pDis->szDisasm+nPos, "invalid");
316 pDis->dwFlags |= DIS_ILLEGALOP;
322 // If this instruction needs additional Mod R/M UCHAR, fetch it
324 if( p->flags & DIS_MODRM )
326 // Get the next UCHAR (modR/M bit field)
329 bReg = (bOpcode >> 3) & 7;
331 StartInstructionParseMODRM:
333 // Parse that UCHAR and get mod, reg and rm fields
338 StartInstructionNoMODRM:
340 // Print the possible repeat prefix followed by the instruction
342 if( p->flags & DIS_COPROC )
343 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%-6s ", sCoprocNames[ p->name ]);
345 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s%-6s ",
346 sRep[DIS_GETREPENUM(pDis->dwFlags)],
347 sNames[ p->name + (DIS_GETNAMEFLAG(p->flags) & DIS_GETDATASIZE(pDis->dwFlags)) ] );
349 // Do instruction argument processing, up to 3 times
353 for( arg=p->args; arg!=0; arg--, pArg++, arg? nPos += PICE_sprintf( pDis->szDisasm+nPos,", ") : 0 )
357 case _Eb : // modR/M used - bW = 0
361 case _Ev : // modR/M used - bW = 1
365 case _Ew : // always USHORT size
366 pDis->dwFlags &= ~DIS_DATA32;
370 case _Ms : // fword ptr (sgdt,sidt,lgdt,lidt)
374 case _Mq : // qword ptr (cmpxchg8b)
378 case _Mp : // 32 or 48 bit pointer (les,lds,lfs,lss,lgs)
379 case _Ep : // Always a memory pointer (call, jmp)
380 if( pDis->dwFlags & DIS_DATA32 )
387 // Do registers first so that the rest may be done together
390 // Registers depending on the w field and data size
391 nPos+=PICE_sprintf(pDis->szDisasm+nPos, "%s", sRegs1[DIS_GETDATASIZE(pDis->dwFlags)][bW][bRm] );
399 if( pDis->dwFlags & DIS_DATA32 )
404 case _M : // Pure memory pointer (lea,invlpg,floats)
405 if( bMod == 3 ) goto IllegalOpcode;
410 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sPtr );
412 case _Ma : // Used by bound instruction, skip the pointer info
414 // Print the segment if it is overriden
416 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s", sSegOverride[ bSegOverride ] );
419 // Special case when sib UCHAR is present in 32 address encoding
421 if( (bRm==4) && (pDis->dwFlags & DIS_ADDRESS32) )
424 // Get the s-i-b UCHAR and parse it
429 bIndex = (bSib >> 3) & 7;
432 // Special case for base=5 && mod==0 -> fetch 32 bit offset
433 if( (bBase==5) && (bMod==0) )
436 if(ScanExportsByAddress(&pSymbolName,dwULONG))
438 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%s", pSymbolName );
442 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%08X", (unsigned int) dwULONG );
446 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%s", sGenReg16_32[ 1 ][ bBase ] );
448 // Scaled index, no index if bIndex is 4
450 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%s%s", sScale[ bSs ], sGenReg16_32[ 1 ][ bIndex ] );
453 nPos += PICE_sprintf( pDis->szDisasm+nPos,"<INVALID MODE>" );
455 // Offset 8 bit or 32 bit
459 if( (signed char)bUCHAR < 0 )
460 nPos += PICE_sprintf( pDis->szDisasm+nPos,"-%02X", 0-(signed char)bUCHAR );
462 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%02X", bUCHAR );
468 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%08X", (unsigned int) dwULONG );
471 // Wrap up the instruction
472 nPos += PICE_sprintf( pDis->szDisasm+nPos,"]" );
477 // 16 or 32 address bit cases with mod zero, one or two
479 // Special cases when r/m is 5 and mod is 0, immediate d16 or d32
480 if( bMod==0 && ((bRm==6 && !(pDis->dwFlags & DIS_ADDRESS32)) || (bRm==5 && (pDis->dwFlags & DIS_ADDRESS32))) )
482 if( pDis->dwFlags & DIS_ADDRESS32 )
485 if(ScanExportsByAddress(&pSymbolName,dwULONG))
486 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%s]", pSymbolName );
488 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%08X]", (unsigned int) dwULONG );
492 wUSHORT = NEXTUSHORT;
493 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%04X]", wUSHORT );
499 // Print the start of the line
500 nPos += PICE_sprintf( pDis->szDisasm+nPos,"[%s", sAdr1[DIS_GETADDRSIZE(pDis->dwFlags)][ bRm ] );
502 // Offset (8 or 16) or (8 or 32) bit - 16, 32 bits are unsigned
506 if( (signed char)bUCHAR < 0 )
507 nPos += PICE_sprintf( pDis->szDisasm+nPos,"-%02X", 0-(signed char)bUCHAR );
509 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%02X", bUCHAR );
514 if( pDis->dwFlags & DIS_ADDRESS32 )
517 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%08X", (unsigned int) dwULONG );
521 wUSHORT = NEXTUSHORT;
522 nPos += PICE_sprintf( pDis->szDisasm+nPos,"+%04X", wUSHORT );
526 // Wrap up the instruction
527 nPos += PICE_sprintf( pDis->szDisasm+nPos,"]" );
531 case _Gb : // general, UCHAR register
532 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sRegs1[0][0][ bReg ] );
535 case _Gv : // general, (d)USHORT register
536 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sGenReg16_32[DIS_GETDATASIZE(pDis->dwFlags)][ bReg ] );
539 case _Yb : // ES:(E)DI pointer
541 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s%s", sSegOverrideDefaultES[ bSegOverride ], sYptr[DIS_GETADDRSIZE(pDis->dwFlags)] );
544 case _Xb : // DS:(E)SI pointer
546 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s%s", sSegOverrideDefaultDS[ bSegOverride ], sXptr[DIS_GETADDRSIZE(pDis->dwFlags)] );
549 case _Rd : // general register double USHORT
550 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sGenReg16_32[ 1 ][ bRm ] );
553 case _Rw : // register USHORT
554 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sGenReg16_32[ 0 ][ bMod ] );
557 case _Sw : // segment register
558 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sSeg[ bReg ] );
561 case _Cd : // control register
562 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sControl[ bReg ] );
565 case _Dd : // debug register
566 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sDebug[ bReg ] );
569 case _Td : // test register
570 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sTest[ bReg ] );
574 case _Jb : // immediate UCHAR, relative offset
576 nPos += PICE_sprintf( pDis->szDisasm+nPos, "short %08X", (unsigned int)(pDis->bpTarget + (signed char)bUCHAR + bInstrLen) );
579 case _Jv : // immediate USHORT or ULONG, relative offset
580 if( pDis->dwFlags & DIS_DATA32 )
583 if(ScanExportsByAddress(&pSymbolName,(unsigned int)(pDis->bpTarget + (signed long)dwULONG + bInstrLen)))
584 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", pSymbolName );
586 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%08X", (unsigned int)(pDis->bpTarget + (signed long)dwULONG + bInstrLen) );
590 wUSHORT = NEXTUSHORT;
591 if(ScanExportsByAddress(&pSymbolName,(unsigned int)(pDis->bpTarget + (signed short)wUSHORT + bInstrLen)))
592 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", pSymbolName );
594 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%08X", (unsigned int)(pDis->bpTarget + (signed short)wUSHORT + bInstrLen) );
598 case _O : // Simple USHORT or ULONG offset
599 if( pDis->dwFlags & DIS_ADDRESS32 ) // depending on the address size
602 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s[%08X]", sSegOverride[ bSegOverride ], (unsigned int) dwULONG );
606 wUSHORT = NEXTUSHORT;
607 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s[%04X]", sSegOverride[ bSegOverride ], wUSHORT );
611 case _Ib : // immediate UCHAR
613 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%02X", bUCHAR );
616 case _Iv : // immediate USHORT or ULONG
617 if( pDis->dwFlags & DIS_DATA32 )
620 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%08X", (unsigned int) dwULONG );
624 wUSHORT = NEXTUSHORT;
625 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%04X", wUSHORT );
629 case _Iw : // Immediate USHORT
630 wUSHORT = NEXTUSHORT;
631 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%04X", wUSHORT );
634 case _Ap : // 32 bit or 48 bit pointer (call far, jump far)
635 if( pDis->dwFlags & DIS_DATA32 )
638 wUSHORT = NEXTUSHORT;
639 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%04X:%08X", wUSHORT, (unsigned int) dwULONG );
644 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%08X", (unsigned int) dwULONG );
648 case _1 : // numerical 1
649 nPos += PICE_sprintf( pDis->szDisasm+nPos,"1" );
652 case _3 : // numerical 3
653 nPos += PICE_sprintf( pDis->szDisasm+nPos,"3" );
656 // Hard coded registers
657 case _DX: case _AL: case _AH: case _BL: case _BH: case _CL: case _CH:
658 case _DL: case _DH: case _CS: case _DS: case _ES: case _SS: case _FS:
660 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s", sRegs2[ *pArg - _DX ] );
663 case _eAX: case _eBX: case _eCX: case _eDX:
664 case _eSP: case _eBP: case _eSI: case _eDI:
665 nPos += PICE_sprintf( pDis->szDisasm+nPos, "%s", sGenReg16_32[DIS_GETDATASIZE(pDis->dwFlags)][ *pArg - _eAX ]);
668 case _ST: // Coprocessor ST
669 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s", sST[9] );
672 case _ST0: // Coprocessor ST(0) - ST(7)
680 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s", sST[ *pArg - _ST0 ] );
683 case _AX: // Coprocessor AX
684 nPos += PICE_sprintf( pDis->szDisasm+nPos,"%s", sGenReg16_32[0][0] );
691 // Set the returning values and return with the bInstrLen field
693 pDis->bAsciiLen = (UCHAR) nPos;
694 pDis->bInstrLen = bInstrLen;
699 /******************************************************************************
701 * BOOLEAN Disasm(PULONG pOffset,PUCHAR pchDst) *
703 * entry point for disassembly from other modules *
704 ******************************************************************************/
705 BOOLEAN Disasm(PULONG pOffset,PUCHAR pchDst)
709 dis.dwFlags = DIS_DATA32 | DIS_ADDRESS32;
710 dis.bpTarget = (UCHAR*)*pOffset;
711 dis.szDisasm = pchDst;
712 dis.wSel = CurrentCS;
714 *pOffset += (ULONG)Disassembler( &dis);