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
);