VirtualBox

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

Last change on this file since 41658 was 41658, checked in by vboxsync, 12 years ago

DIS,VMM,REM,IPRT: Disassembler API adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 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 one instruction
37 *
38 * @returns VBox error code
39 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
40 * set correctly.
41 * @param uInstrAddr Pointer to the structure to disassemble.
42 * @param pcbSize Where to store the size of the instruction.
43 * NULL is allowed.
44 * @param pszOutput Storage for disassembled instruction
45 *
46 * @todo Define output callback.
47 */
48DISDECL(int) DISInstr(RTUINTPTR uInstrAddr, DISCPUMODE enmCpuMode, PDISCPUSTATE pCpu, uint32_t *pcbSize, char *pszOutput)
49{
50 return DISInstrEx(uInstrAddr, 0, enmCpuMode, NULL, NULL, OPTYPE_ALL,
51 pCpu, pcbSize, pszOutput);
52}
53
54/**
55 * Disassembles one instruction
56 *
57 * @returns VBox error code
58 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
59 * set correctly.
60 * @param uInstrAddr Pointer to the structure to disassemble.
61 * @param offRealAddr Offset to add to instruction address to get the real
62 * virtual address.
63 * @param pcbSize Where to store the size of the instruction.
64 * NULL is allowed.
65 * @param pszOutput Storage for disassembled instruction
66 *
67 * @todo Define output callback.
68 */
69DISDECL(int) DISInstrWithOff(PDISCPUSTATE pCpu, RTUINTPTR uInstrAddr, RTUINTPTR offRealAddr,
70 unsigned *pcbSize, char *pszOutput)
71{
72 return DISInstrEx(uInstrAddr, offRealAddr, pCpu->mode, pCpu->pfnReadBytes, pCpu->apvUserData[0], OPTYPE_ALL,
73 pCpu, pcbSize, pszOutput);
74}
75
76/**
77 * Disassembles one instruction with a byte fetcher caller.
78 *
79 * @returns VBox error code
80 * @param uInstrAddr Pointer to the structure to disassemble.
81 * @param enmCpuMode The CPU mode.
82 * @param pfnCallback The byte fetcher callback.
83 * @param pvUser The user argument (found in
84 * DISCPUSTATE::apvUserData[0]).
85 * @param pCpu Where to return the disassembled instruction.
86 * @param pcbSize Where to store the size of the instruction.
87 * NULL is allowed.
88 * @param pszOutput Storage for disassembled instruction
89 *
90 * @todo Define output callback.
91 */
92DISDECL(int) DISInstrWithReader(RTUINTPTR uInstrAddr, DISCPUMODE enmCpuMode, PFNDISREADBYTES pfnReadBytes, void *pvUser,
93 PDISCPUSTATE pCpu, uint32_t *pcbSize, char *pszOutput)
94
95{
96 return DISInstrEx(uInstrAddr, 0, enmCpuMode, pfnReadBytes, pvUser, OPTYPE_ALL,
97 pCpu, pcbSize, pszOutput);
98}
99
100/**
101 * Disassembles one instruction; only fully disassembly an instruction if it matches the filter criteria
102 *
103 * @returns VBox error code
104 * @param pCpu Pointer to cpu structure which have DISCPUSTATE::mode
105 * set correctly.
106 * @param uInstrAddr Pointer to the structure to disassemble.
107 * @param u32EipOffset Offset to add to instruction address to get the real virtual address
108 * @param pcbSize Where to store the size of the instruction.
109 * NULL is allowed.
110 * @param pszOutput Storage for disassembled instruction
111 * @param uFilter Instruction type filter
112 *
113 * @todo Define output callback.
114 */
115DISDECL(int) DISInstrEx(RTUINTPTR uInstrAddr, RTUINTPTR offRealAddr, DISCPUMODE enmCpuMode,
116 PFNDISREADBYTES pfnReadBytes, void *pvUser, uint32_t uFilter,
117 PDISCPUSTATE pCpu, uint32_t *pcbSize, char *pszOutput)
118{
119 const OPCODE *paOneByteMap;
120
121 /*
122 * Initialize the CPU state.
123 * Note! The RT_BZERO make ASSUMPTIONS about the placement of apvUserData.
124 */
125 RT_BZERO(pCpu, RT_OFFSETOF(DISCPUSTATE, apvUserData));
126
127 pCpu->mode = enmCpuMode;
128 if (enmCpuMode == CPUMODE_64BIT)
129 {
130 paOneByteMap = g_aOneByteMapX64;
131 pCpu->addrmode = CPUMODE_64BIT;
132 pCpu->opmode = CPUMODE_32BIT;
133 }
134 else
135 {
136 paOneByteMap = g_aOneByteMapX86;
137 pCpu->addrmode = enmCpuMode;
138 pCpu->opmode = enmCpuMode;
139 }
140 pCpu->prefix = PREFIX_NONE;
141 pCpu->enmPrefixSeg = DIS_SELREG_DS;
142 pCpu->uInstrAddr = uInstrAddr;
143 pCpu->opaddr = uInstrAddr + offRealAddr;
144 pCpu->pfnDisasmFnTable = pfnFullDisasm;
145 pCpu->uFilter = uFilter;
146 pCpu->rc = VINF_SUCCESS;
147 pCpu->pfnReadBytes = pfnReadBytes ? pfnReadBytes : disReadBytesDefault;
148 pCpu->apvUserData[0] = pvUser;
149
150 /*
151 * Parse the instruction byte by byte.
152 */
153 unsigned i = 0;
154 unsigned cbPrefix = 0; /** @todo this isn't really needed, is it? Seems to be a bit too many variables tracking the same stuff here. cbInc, i, cbPrefix, idx... */
155 for (;;)
156 {
157 uint8_t codebyte = DISReadByte(pCpu, uInstrAddr+i);
158 uint8_t opcode = paOneByteMap[codebyte].opcode;
159
160 /* Hardcoded assumption about OP_* values!! */
161 if (opcode <= OP_LAST_PREFIX)
162 {
163 /* The REX prefix must precede the opcode byte(s). Any other placement is ignored. */
164 if (opcode != OP_REX)
165 {
166 pCpu->lastprefix = opcode;
167 pCpu->prefix &= ~PREFIX_REX;
168 }
169
170 switch (opcode)
171 {
172 case OP_INVALID:
173 if (pcbSize)
174 *pcbSize = pCpu->opsize;
175 return pCpu->rc = VERR_DIS_INVALID_OPCODE;
176
177 // segment override prefix byte
178 case OP_SEG:
179 pCpu->enmPrefixSeg = (DIS_SELREG)(paOneByteMap[codebyte].param1 - OP_PARM_REG_SEG_START);
180 /* Segment prefixes for CS, DS, ES and SS are ignored in long mode. */
181 if ( pCpu->mode != CPUMODE_64BIT
182 || pCpu->enmPrefixSeg >= DIS_SELREG_FS)
183 pCpu->prefix |= PREFIX_SEG;
184 i += sizeof(uint8_t);
185 cbPrefix++;
186 continue; //fetch the next byte
187
188 // lock prefix byte
189 case OP_LOCK:
190 pCpu->prefix |= PREFIX_LOCK;
191 i += sizeof(uint8_t);
192 cbPrefix++;
193 continue; //fetch the next byte
194
195 // address size override prefix byte
196 case OP_ADDRSIZE:
197 pCpu->prefix |= PREFIX_ADDRSIZE;
198 if (pCpu->mode == CPUMODE_16BIT)
199 pCpu->addrmode = CPUMODE_32BIT;
200 else
201 if (pCpu->mode == CPUMODE_32BIT)
202 pCpu->addrmode = CPUMODE_16BIT;
203 else
204 pCpu->addrmode = CPUMODE_32BIT; /* 64 bits */
205
206 i += sizeof(uint8_t);
207 cbPrefix++;
208 continue; //fetch the next byte
209
210 // operand size override prefix byte
211 case OP_OPSIZE:
212 pCpu->prefix |= PREFIX_OPSIZE;
213 if (pCpu->mode == CPUMODE_16BIT)
214 pCpu->opmode = CPUMODE_32BIT;
215 else
216 pCpu->opmode = CPUMODE_16BIT; /* for 32 and 64 bits mode (there is no 32 bits operand size override prefix) */
217
218 i += sizeof(uint8_t);
219 cbPrefix++;
220 continue; //fetch the next byte
221
222 // rep and repne are not really prefixes, but we'll treat them as such
223 case OP_REPE:
224 pCpu->prefix |= PREFIX_REP;
225 i += sizeof(uint8_t);
226 cbPrefix += sizeof(uint8_t);
227 continue; //fetch the next byte
228
229 case OP_REPNE:
230 pCpu->prefix |= PREFIX_REPNE;
231 i += sizeof(uint8_t);
232 cbPrefix += sizeof(uint8_t);
233 continue; //fetch the next byte
234
235 case OP_REX:
236 Assert(pCpu->mode == CPUMODE_64BIT);
237 /* REX prefix byte */
238 pCpu->prefix |= PREFIX_REX;
239 pCpu->prefix_rex = PREFIX_REX_OP_2_FLAGS(paOneByteMap[codebyte].param1);
240 i += sizeof(uint8_t);
241 cbPrefix += sizeof(uint8_t);
242
243 if (pCpu->prefix_rex & PREFIX_REX_FLAGS_W)
244 pCpu->opmode = CPUMODE_64BIT; /* overrides size prefix byte */
245 continue; //fetch the next byte
246 }
247 }
248
249 pCpu->cbPrefix = i; Assert(cbPrefix == i);
250 pCpu->opcode = codebyte;
251
252 unsigned idx = i;
253 i += sizeof(uint8_t); //first opcode byte
254
255 unsigned cbInc = ParseInstruction(uInstrAddr + i, &paOneByteMap[pCpu->opcode], pCpu);
256
257 AssertMsg(pCpu->opsize == cbPrefix + cbInc + sizeof(uint8_t),
258 ("%u %u\n", pCpu->opsize, cbPrefix + cbInc + sizeof(uint8_t)));
259 pCpu->opsize = cbPrefix + cbInc + sizeof(uint8_t);
260
261 if (pszOutput)
262 disasmSprintf(pszOutput, uInstrAddr+i-1-cbPrefix, pCpu, &pCpu->param1, &pCpu->param2, &pCpu->param3);
263
264 i += cbInc;
265 cbPrefix = 0;
266 break;
267 }
268
269 if (pcbSize)
270 *pcbSize = i;
271
272 if (pCpu->prefix & PREFIX_LOCK)
273 disValidateLockSequence(pCpu);
274
275 return pCpu->rc;
276}
277//*****************************************************************************
278//*****************************************************************************
279static size_t DbgBytesToString(PDISCPUSTATE pCpu, char *pszOutput, size_t offStart)
280{
281 unsigned off = offStart;
282
283 while (off < 40)
284 pszOutput[off++] = ' ';
285 pszOutput[off++] = ' ';
286 pszOutput[off++] = '[';
287
288 for (unsigned i = 0; i < pCpu->opsize; i++)
289 off += RTStrPrintf(&pszOutput[off], 64, "%02X ", pCpu->abInstr[i]);
290
291 pszOutput[off - 1] = ']'; // replaces space
292 return off - offStart;
293}
294//*****************************************************************************
295//*****************************************************************************
296void disasmSprintf(char *pszOutput, RTUINTPTR uInstrAddr, PDISCPUSTATE pCpu,
297 OP_PARAMETER *pParam1, OP_PARAMETER *pParam2, OP_PARAMETER *pParam3)
298{
299 const char *pszFormat = pCpu->pszOpcode;
300 int param = 1;
301
302
303 size_t off = RTStrPrintf(pszOutput, 64, "%08RTptr: ", pCpu->opaddr);
304 if (pCpu->prefix & PREFIX_LOCK)
305 off += RTStrPrintf(&pszOutput[off], 64, "lock ");
306 if (pCpu->prefix & PREFIX_REP)
307 off += RTStrPrintf(&pszOutput[off], 64, "rep(e) ");
308 else if(pCpu->prefix & PREFIX_REPNE)
309 off += RTStrPrintf(&pszOutput[off], 64, "repne ");
310
311 if (!strcmp("Invalid Opcode", pszFormat))
312 {
313 if (pCpu->opsize >= 2)
314 off += RTStrPrintf(&pszOutput[off], 64, "Invalid Opcode [%02X][%02X]", pCpu->abInstr[0], pCpu->abInstr[1]);
315 else
316 off += RTStrPrintf(&pszOutput[off], 64, "Invalid Opcode [%02X]", pCpu->abInstr[0]);
317 }
318 else
319 while (*pszFormat)
320 {
321 switch (*pszFormat)
322 {
323 case '%':
324 switch (pszFormat[1])
325 {
326 case 'J': //Relative jump offset
327 {
328 int32_t disp;
329
330 AssertMsg(param == 1, ("Invalid branch parameter nr"));
331 if (pParam1->flags & USE_IMMEDIATE8_REL)
332 disp = (int32_t)(char)pParam1->parval;
333 else if (pParam1->flags & USE_IMMEDIATE16_REL)
334 disp = (int32_t)(uint16_t)pParam1->parval;
335 else if (pParam1->flags & USE_IMMEDIATE32_REL)
336 disp = (int32_t)pParam1->parval;
337 else if(pParam1->flags & USE_IMMEDIATE64_REL)
338 /** @todo: is this correct? */
339 disp = (int32_t)pParam1->parval;
340 else
341 {
342 AssertMsgFailed(("Oops!\n"));
343 return;
344 }
345 uint32_t addr = (uint32_t)(pCpu->opaddr + pCpu->opsize) + disp;
346 off += RTStrPrintf(&pszOutput[off], 64, "[%08X]", addr);
347 }
348
349 //no break;
350
351 case 'A': //direct address
352 case 'C': //control register
353 case 'D': //debug register
354 case 'E': //ModRM specifies parameter
355 case 'F': //Eflags register
356 case 'G': //ModRM selects general register
357 case 'I': //Immediate data
358 case 'M': //ModRM may only refer to memory
359 case 'O': //No ModRM byte
360 case 'P': //ModRM byte selects MMX register
361 case 'Q': //ModRM byte selects MMX register or memory address
362 case 'R': //ModRM byte may only refer to a general register
363 case 'S': //ModRM byte selects a segment register
364 case 'T': //ModRM byte selects a test register
365 case 'V': //ModRM byte selects an XMM/SSE register
366 case 'W': //ModRM byte selects an XMM/SSE register or a memory address
367 case 'X': //DS:SI
368 case 'Y': //ES:DI
369 switch(param)
370 {
371 case 1:
372 off += RTStrPrintf(&pszOutput[off], 64, pParam1->szParam);
373 break;
374 case 2:
375 off += RTStrPrintf(&pszOutput[off], 64, pParam2->szParam);
376 break;
377 case 3:
378 off += RTStrPrintf(&pszOutput[off], 64, pParam3->szParam);
379 break;
380 }
381 break;
382
383 case 'e': //register based on operand size (e.g. %eAX)
384 if(pCpu->opmode == CPUMODE_32BIT)
385 off += RTStrPrintf(&pszOutput[off], 64, "E");
386 if(pCpu->opmode == CPUMODE_64BIT)
387 off += RTStrPrintf(&pszOutput[off], 64, "R");
388
389 off += RTStrPrintf(&pszOutput[off], 64, "%c%c", pszFormat[2], pszFormat[3]);
390 break;
391
392 default:
393 AssertMsgFailed(("Oops!\n"));
394 break;
395 }
396
397 //Go to the next parameter in the format string
398 while (*pszFormat && *pszFormat != ',')
399 pszFormat++;
400 if (*pszFormat == ',')
401 pszFormat--;
402
403 break;
404
405 case ',':
406 param++;
407 //no break
408
409 default:
410 off += RTStrPrintf(&pszOutput[off], 64, "%c", *pszFormat);
411 break;
412 }
413
414 if (*pszFormat)
415 pszFormat++;
416 }
417
418 off += DbgBytesToString(pCpu, pszOutput, off);
419 off += RTStrPrintf(&pszOutput[off], 64, "\n");
420}
421//*****************************************************************************
422//*****************************************************************************
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