VirtualBox

source: vbox/trunk/src/VBox/Disassembler/Disasm.cpp@ 36219

Last change on this file since 36219 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/** @file
2 *
3 * VBox disassembler:
4 * Main
5 */
6
7/*
8 * Copyright (C) 2006-2007 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#ifdef USING_VISUAL_STUDIO
24# include <stdafx.h>
25#endif
26#include <VBox/dis.h>
27#include <VBox/disopcode.h>
28#include <VBox/err.h>
29#include <iprt/assert.h>
30#include <iprt/string.h>
31#include "DisasmInternal.h"
32#include "DisasmTables.h"
33
34
35/**
36 * Disassembles a code block.
37 *
38 * @returns VBox error code
39 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
40 * set correctly.
41 * @param pvCodeBlock Pointer to the structure to disassemble.
42 * @param cbMax Maximum number of bytes to disassemble.
43 * @param pcbSize Where to store the size of the instruction.
44 * NULL is allowed.
45 *
46 *
47 * @todo Define output callback.
48 * @todo Using signed integers as sizes is a bit odd. There are still
49 * some GCC warnings about mixing signed and unsigned integers.
50 * @todo Need to extend this interface to include a code address so we
51 * can disassemble GC code. Perhaps a new function is better...
52 * @remark cbMax isn't respected as a boundary. DISInstr() will read beyond cbMax.
53 * This means *pcbSize >= cbMax sometimes.
54 */
55DISDECL(int) DISBlock(PDISCPUSTATE pCpu, RTUINTPTR pvCodeBlock, unsigned cbMax, unsigned *pSize)
56{
57 unsigned i = 0;
58 char szOutput[256];
59
60 while (i < cbMax)
61 {
62 unsigned cbInstr;
63 int rc = DISInstr(pCpu, pvCodeBlock + i, 0, &cbInstr, szOutput);
64 if (RT_FAILURE(rc))
65 return rc;
66
67 i += cbInstr;
68 }
69
70 if (pSize)
71 *pSize = i;
72 return true;
73}
74
75/**
76 * Disassembles one instruction
77 *
78 * @returns VBox error code
79 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
80 * set correctly.
81 * @param pu8Instruction Pointer to the structure to disassemble.
82 * @param u32EipOffset Offset to add to instruction address to get the real virtual address
83 * @param pcbSize Where to store the size of the instruction.
84 * NULL is allowed.
85 * @param pszOutput Storage for disassembled instruction
86 *
87 * @todo Define output callback.
88 */
89DISDECL(int) DISInstr(PDISCPUSTATE pCpu, RTUINTPTR pu8Instruction, unsigned u32EipOffset, unsigned *pcbSize,
90 char *pszOutput)
91{
92 return DISInstrEx(pCpu, pu8Instruction, u32EipOffset, pcbSize, pszOutput, OPTYPE_ALL);
93}
94
95/**
96 * Disassembles one instruction; only fully disassembly an instruction if it matches the filter criteria
97 *
98 * @returns VBox error code
99 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
100 * set correctly.
101 * @param pu8Instruction Pointer to the structure to disassemble.
102 * @param u32EipOffset Offset to add to instruction address to get the real virtual address
103 * @param pcbSize Where to store the size of the instruction.
104 * NULL is allowed.
105 * @param pszOutput Storage for disassembled instruction
106 * @param uFilter Instruction type filter
107 *
108 * @todo Define output callback.
109 */
110DISDECL(int) DISInstrEx(PDISCPUSTATE pCpu, RTUINTPTR pu8Instruction, unsigned u32EipOffset, unsigned *pcbSize,
111 char *pszOutput, unsigned uFilter)
112{
113 unsigned i = 0, prefixbytes;
114 unsigned idx, inc;
115 const OPCODE *paOneByteMap;
116#ifdef __L4ENV__
117 jmp_buf jumpbuffer;
118#endif
119
120 //reset instruction settings
121 pCpu->prefix = PREFIX_NONE;
122 pCpu->enmPrefixSeg = DIS_SELREG_DS;
123 pCpu->ModRM.u = 0;
124 pCpu->SIB.u = 0;
125 pCpu->lastprefix = 0;
126 pCpu->param1.parval = 0;
127 pCpu->param2.parval = 0;
128 pCpu->param3.parval = 0;
129 pCpu->param1.szParam[0] = 0;
130 pCpu->param2.szParam[0] = 0;
131 pCpu->param3.szParam[0] = 0;
132 pCpu->param1.size = 0;
133 pCpu->param2.size = 0;
134 pCpu->param3.size = 0;
135 pCpu->param1.flags = 0;
136 pCpu->param2.flags = 0;
137 pCpu->param3.flags = 0;
138 pCpu->uFilter = uFilter;
139 pCpu->pfnDisasmFnTable = pfnFullDisasm;
140
141 if (pszOutput)
142 *pszOutput = '\0';
143
144 if (pCpu->mode == CPUMODE_64BIT)
145 {
146 paOneByteMap = g_aOneByteMapX64;
147 pCpu->addrmode = CPUMODE_64BIT;
148 pCpu->opmode = CPUMODE_32BIT;
149 }
150 else
151 {
152 paOneByteMap = g_aOneByteMapX86;
153 pCpu->addrmode = pCpu->mode;
154 pCpu->opmode = pCpu->mode;
155 }
156
157 prefixbytes = 0;
158#ifndef __L4ENV__ /* Unfortunately, we have no exception handling in l4env */
159 try
160#else
161 pCpu->pJumpBuffer = &jumpbuffer;
162 if (setjmp(jumpbuffer) == 0)
163#endif
164 {
165 while(1)
166 {
167 uint8_t codebyte = DISReadByte(pCpu, pu8Instruction+i);
168 uint8_t opcode = paOneByteMap[codebyte].opcode;
169
170 /* Hardcoded assumption about OP_* values!! */
171 if (opcode <= OP_LAST_PREFIX)
172 {
173 /* The REX prefix must precede the opcode byte(s). Any other placement is ignored. */
174 if (opcode != OP_REX)
175 {
176 pCpu->lastprefix = opcode;
177 pCpu->prefix &= ~PREFIX_REX;
178 }
179
180 switch(opcode)
181 {
182 case OP_INVALID:
183#if 0 //defined (DEBUG_Sander)
184 AssertMsgFailed(("Invalid opcode!!\n"));
185#endif
186 return VERR_DIS_INVALID_OPCODE;
187
188 // segment override prefix byte
189 case OP_SEG:
190 pCpu->enmPrefixSeg = (DIS_SELREG)(paOneByteMap[codebyte].param1 - OP_PARM_REG_SEG_START);
191 /* Segment prefixes for CS, DS, ES and SS are ignored in long mode. */
192 if ( pCpu->mode != CPUMODE_64BIT
193 || pCpu->enmPrefixSeg >= DIS_SELREG_FS)
194 {
195 pCpu->prefix |= PREFIX_SEG;
196 }
197 i += sizeof(uint8_t);
198 prefixbytes++;
199 continue; //fetch the next byte
200
201 // lock prefix byte
202 case OP_LOCK:
203 pCpu->prefix |= PREFIX_LOCK;
204 i += sizeof(uint8_t);
205 prefixbytes++;
206 continue; //fetch the next byte
207
208 // address size override prefix byte
209 case OP_ADDRSIZE:
210 pCpu->prefix |= PREFIX_ADDRSIZE;
211 if (pCpu->mode == CPUMODE_16BIT)
212 pCpu->addrmode = CPUMODE_32BIT;
213 else
214 if (pCpu->mode == CPUMODE_32BIT)
215 pCpu->addrmode = CPUMODE_16BIT;
216 else
217 pCpu->addrmode = CPUMODE_32BIT; /* 64 bits */
218
219 i += sizeof(uint8_t);
220 prefixbytes++;
221 continue; //fetch the next byte
222
223 // operand size override prefix byte
224 case OP_OPSIZE:
225 pCpu->prefix |= PREFIX_OPSIZE;
226 if (pCpu->mode == CPUMODE_16BIT)
227 pCpu->opmode = CPUMODE_32BIT;
228 else
229 pCpu->opmode = CPUMODE_16BIT; /* for 32 and 64 bits mode (there is no 32 bits operand size override prefix) */
230
231 i += sizeof(uint8_t);
232 prefixbytes++;
233 continue; //fetch the next byte
234
235 // rep and repne are not really prefixes, but we'll treat them as such
236 case OP_REPE:
237 pCpu->prefix |= PREFIX_REP;
238 i += sizeof(uint8_t);
239 prefixbytes += sizeof(uint8_t);
240 continue; //fetch the next byte
241
242 case OP_REPNE:
243 pCpu->prefix |= PREFIX_REPNE;
244 i += sizeof(uint8_t);
245 prefixbytes += sizeof(uint8_t);
246 continue; //fetch the next byte
247
248 case OP_REX:
249 Assert(pCpu->mode == CPUMODE_64BIT);
250 /* REX prefix byte */
251 pCpu->prefix |= PREFIX_REX;
252 pCpu->prefix_rex = PREFIX_REX_OP_2_FLAGS(paOneByteMap[codebyte].param1);
253 i += sizeof(uint8_t);
254 prefixbytes += sizeof(uint8_t);
255
256 if (pCpu->prefix_rex & PREFIX_REX_FLAGS_W)
257 pCpu->opmode = CPUMODE_64BIT; /* overrides size prefix byte */
258 continue; //fetch the next byte
259 }
260 }
261
262 idx = i;
263 i += sizeof(uint8_t); //first opcode byte
264
265 pCpu->opcode = codebyte;
266 /* Prefix byte(s) is/are part of the instruction. */
267 pCpu->opaddr = pu8Instruction + idx + u32EipOffset - prefixbytes;
268
269 inc = ParseInstruction(pu8Instruction + i, &paOneByteMap[pCpu->opcode], pCpu);
270
271 pCpu->opsize = prefixbytes + inc + sizeof(uint8_t);
272
273 if(pszOutput) {
274 disasmSprintf(pszOutput, pu8Instruction+i-1-prefixbytes, pCpu, &pCpu->param1, &pCpu->param2, &pCpu->param3);
275 }
276
277 i += inc;
278 prefixbytes = 0;
279 break;
280 }
281 }
282#ifndef __L4ENV__
283 catch(...)
284#else
285 else /* setjmp has returned a non-zero value: an exception occurred */
286#endif
287 {
288 if (pcbSize)
289 *pcbSize = 0;
290 return VERR_DIS_GEN_FAILURE;
291 }
292
293 if (pcbSize)
294 *pcbSize = i;
295
296 if (pCpu->prefix & PREFIX_LOCK)
297 disValidateLockSequence(pCpu);
298
299 return VINF_SUCCESS;
300}
301//*****************************************************************************
302//*****************************************************************************
303char *DbgBytesToString(PDISCPUSTATE pCpu, RTUINTPTR pBytes, int size, char *pszOutput)
304{
305 char *psz = strchr(pszOutput, '\0');
306 size_t len;
307 int i;
308
309 for(len = psz - pszOutput; len < 40; len++)
310 *psz++ = ' ';
311 *psz++ = ' ';
312 *psz++ = '[';
313
314 for(i = 0; (int)i < size; i++)
315 psz += RTStrPrintf(psz, 64, "%02X ", DISReadByte(pCpu, pBytes+i));
316
317 psz[-1] = ']'; // replaces space
318
319 return pszOutput;
320}
321//*****************************************************************************
322//*****************************************************************************
323void disasmSprintf(char *pszOutput, RTUINTPTR pu8Instruction, PDISCPUSTATE pCpu, OP_PARAMETER *pParam1, OP_PARAMETER *pParam2, OP_PARAMETER *pParam3)
324{
325 const char *lpszFormat = pCpu->pszOpcode;
326 int param = 1;
327
328 RTStrPrintf(pszOutput, 64, "%08X: ", (unsigned)pCpu->opaddr);
329 if(pCpu->prefix & PREFIX_LOCK)
330 {
331 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "lock ");
332 }
333 if(pCpu->prefix & PREFIX_REP)
334 {
335 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "rep(e) ");
336 }
337 else
338 if(pCpu->prefix & PREFIX_REPNE)
339 {
340 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "repne ");
341 }
342
343 if(!strcmp("Invalid Opcode", lpszFormat))
344 {
345 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "Invalid Opcode [%02X][%02X]", DISReadByte(pCpu, pu8Instruction), DISReadByte(pCpu, pu8Instruction+1) );
346 }
347 else
348 while(*lpszFormat)
349 {
350 switch(*lpszFormat)
351 {
352 case '%':
353 switch(*(lpszFormat+1))
354 {
355 case 'J': //Relative jump offset
356 {
357 int32_t disp;
358
359 AssertMsg(param == 1, ("Invalid branch parameter nr"));
360 if(pParam1->flags & USE_IMMEDIATE8_REL)
361 {
362 disp = (int32_t)(char)pParam1->parval;
363 }
364 else
365 if(pParam1->flags & USE_IMMEDIATE16_REL)
366 {
367 disp = (int32_t)(uint16_t)pParam1->parval;
368 }
369 else
370 if(pParam1->flags & USE_IMMEDIATE32_REL)
371 {
372 disp = (int32_t)pParam1->parval;
373 }
374 else
375 if(pParam1->flags & USE_IMMEDIATE64_REL)
376 {
377 /* @todo: is this correct? */
378 disp = (int32_t)pParam1->parval;
379 }
380 else
381 {
382 AssertMsgFailed(("Oops!\n"));
383 return;
384 }
385 uint32_t addr = (uint32_t)(pCpu->opaddr + pCpu->opsize) + disp;
386 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "[%08X]", addr);
387 }
388
389 //no break;
390
391 case 'A': //direct address
392 case 'C': //control register
393 case 'D': //debug register
394 case 'E': //ModRM specifies parameter
395 case 'F': //Eflags register
396 case 'G': //ModRM selects general register
397 case 'I': //Immediate data
398 case 'M': //ModRM may only refer to memory
399 case 'O': //No ModRM byte
400 case 'P': //ModRM byte selects MMX register
401 case 'Q': //ModRM byte selects MMX register or memory address
402 case 'R': //ModRM byte may only refer to a general register
403 case 'S': //ModRM byte selects a segment register
404 case 'T': //ModRM byte selects a test register
405 case 'V': //ModRM byte selects an XMM/SSE register
406 case 'W': //ModRM byte selects an XMM/SSE register or a memory address
407 case 'X': //DS:SI
408 case 'Y': //ES:DI
409 switch(param)
410 {
411 case 1:
412 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, pParam1->szParam);
413 break;
414 case 2:
415 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, pParam2->szParam);
416 break;
417 case 3:
418 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, pParam3->szParam);
419 break;
420 }
421 break;
422
423 case 'e': //register based on operand size (e.g. %eAX)
424 if(pCpu->opmode == CPUMODE_32BIT)
425 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "E");
426 if(pCpu->opmode == CPUMODE_64BIT)
427 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "R");
428
429 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "%c%c", lpszFormat[2], lpszFormat[3]);
430 break;
431
432 default:
433 AssertMsgFailed(("Oops!\n"));
434 break;
435 }
436
437 //Go to the next parameter in the format string
438 while(*lpszFormat && *lpszFormat != ',') lpszFormat++;
439 if(*lpszFormat == ',') lpszFormat--;
440
441 break;
442
443 case ',':
444 param++;
445 //no break
446
447 default:
448 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "%c", *lpszFormat);
449 break;
450 }
451
452 if(*lpszFormat) lpszFormat++;
453 }
454 DbgBytesToString(pCpu, pu8Instruction, pCpu->opsize, pszOutput);
455 RTStrPrintf(&pszOutput[strlen(pszOutput)], 64, "\n");
456}
457//*****************************************************************************
458//*****************************************************************************
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette