VirtualBox

source: vbox/trunk/src/VBox/Disassembler/testcase/tstDisasm-2.cpp@ 9097

Last change on this file since 9097 was 8986, checked in by vboxsync, 17 years ago

Changed the undefined opcode handling; the default now is to fail. --all-invalid got changed to --undef-op=all. --raw got changed to --undef-op=db (define byte).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1/* $Id: tstDisasm-2.cpp 8986 2008-05-21 00:08:29Z vboxsync $ */
2/** @file
3 * Testcase - Generic Disassembler Tool.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#include <VBox/dis.h>
26#include <iprt/stream.h>
27#include <iprt/getopt.h>
28#include <iprt/file.h>
29#include <iprt/string.h>
30#include <iprt/runtime.h>
31#include <iprt/err.h>
32
33
34/*******************************************************************************
35* Structures and Typedefs *
36*******************************************************************************/
37typedef enum { kAsmStyle_Default, kAsmStyle_yasm, kAsmStyle_masm, kAsmStyle_invalid } ASMSTYLE;
38typedef enum { kUndefOp_Fail, kUndefOp_All, kUndefOp_DefineByte, kUndefOp_End } UNDEFOPHANDLING;
39
40typedef struct MYDISSTATE
41{
42 DISCPUSTATE Cpu;
43 uint64_t uAddress; /**< The current instruction address. */
44 uint8_t *pbInstr; /**< The current instruction (pointer). */
45 uint32_t cbInstr; /**< The size of the current instruction. */
46 bool fUndefOp; /**< Whether the current instruction is really an undefined opcode.*/
47 UNDEFOPHANDLING enmUndefOp; /**< How to treat undefined opcodes. */
48 int rc; /**< Set if we hit EOF. */
49 size_t cbLeft; /**< The number of bytes left. (read) */
50 uint8_t *pbNext; /**< The next byte. (read) */
51 uint64_t uNextAddr; /**< The address of the next byte. (read) */
52 char szLine[256]; /**< The disassembler text output. */
53} MYDISSTATE;
54typedef MYDISSTATE *PMYDISSTATE;
55
56
57/**
58 * Default style.
59 *
60 * @param pState The disassembler state.
61 */
62static void MyDisasDefaultFormatter(PMYDISSTATE pState)
63{
64 RTPrintf("%s", pState->szLine);
65}
66
67
68/**
69 * Yasm style.
70 *
71 * @param pState The disassembler state.
72 */
73static void MyDisasYasmFormatter(PMYDISSTATE pState)
74{
75 /* a very quick hack. */
76 char szTmp[256];
77 strcpy(szTmp, RTStrStripL(strchr(pState->szLine, ':') + 1));
78
79 char *psz = strrchr(szTmp, '[');
80 *psz = '\0';
81 RTStrStripR(szTmp);
82
83 psz = strstr(szTmp, " ptr ");
84 if (psz)
85 memset(psz, ' ', 5);
86
87 char *pszEnd = strchr(szTmp, '\0');
88 while (pszEnd - &szTmp[0] < 71)
89 *pszEnd++ = ' ';
90 *pszEnd = '\0';
91
92 RTPrintf(" %s ; %s", szTmp, pState->szLine);
93}
94
95
96/**
97 * Masm style.
98 *
99 * @param pState The disassembler state.
100 */
101static void MyDisasMasmFormatter(PMYDISSTATE pState)
102{
103 RTPrintf("masm not implemented: %s", pState->szLine);
104}
105
106
107
108/**
109 * Callback for reading bytes.
110 */
111static DECLCALLBACK(int) MyDisasInstrRead(RTUINTPTR uSrcAddr, uint8_t *pbDst, uint32_t cbRead, void *pvDisCpu)
112{
113 PMYDISSTATE pState = (PMYDISSTATE)pvDisCpu;
114 if (RT_LIKELY( pState->uNextAddr == uSrcAddr
115 && pState->cbLeft >= cbRead))
116 {
117 /*
118 * Straight forward reading.
119 */
120 if (cbRead == 1)
121 {
122 pState->cbLeft--;
123 *pbDst = *pState->pbNext++;
124 pState->uNextAddr++;
125 }
126 else
127 {
128 memcpy(pbDst, pState->pbNext, cbRead);
129 pState->pbNext += cbRead;
130 pState->cbLeft -= cbRead;
131 pState->uNextAddr += cbRead;
132 }
133 }
134 else
135 {
136 /*
137 * Jumping up the stream.
138 * This occures when the byte sequence is added to the output string.
139 */
140 uint64_t offReq64 = uSrcAddr - pState->uAddress;
141 if (offReq64 < 32)
142 {
143 uint32_t offReq = offReq64;
144 uintptr_t off = pState->pbNext - pState->pbInstr;
145 if (off + pState->cbLeft <= offReq)
146 {
147 pState->pbNext += pState->cbLeft;
148 pState->uNextAddr += pState->cbLeft;
149 pState->cbLeft = 0;
150
151 memset(pbDst, 0xcc, cbRead);
152 pState->rc = VERR_EOF;
153 return VERR_EOF;
154 }
155
156 /* reset the stream. */
157 pState->cbLeft += off;
158 pState->pbNext = pState->pbInstr;
159 pState->uNextAddr = pState->uAddress;
160
161 /* skip ahead. */
162 pState->cbLeft -= offReq;
163 pState->pbNext += offReq;
164 pState->uNextAddr += offReq;
165
166 /* do the reading. */
167 if (pState->cbLeft >= cbRead)
168 {
169 memcpy(pbDst, pState->pbNext, cbRead);
170 pState->cbLeft -= cbRead;
171 pState->pbNext += cbRead;
172 pState->uNextAddr += cbRead;
173 }
174 else
175 {
176 if (pState->cbLeft > 0)
177 {
178 memcpy(pbDst, pState->pbNext, pState->cbLeft);
179 pbDst += pState->cbLeft;
180 cbRead -= pState->cbLeft;
181 pState->pbNext += pState->cbLeft;
182 pState->uNextAddr += pState->cbLeft;
183 pState->cbLeft = 0;
184 }
185 memset(pbDst, 0xcc, cbRead);
186 pState->rc = VERR_EOF;
187 return VERR_EOF;
188 }
189 }
190 else
191 {
192 RTStrmPrintf(g_pStdErr, "Reading before current instruction!\n");
193 memset(pbDst, 0x90, cbRead);
194 pState->rc = VERR_INTERNAL_ERROR;
195 return VERR_INTERNAL_ERROR;
196 }
197 }
198
199 return VINF_SUCCESS;
200}
201
202
203/**
204 * Disassembles a block of memory.
205 *
206 * @returns VBox status code.
207 * @param argv0 Program name (for errors and warnings).
208 * @param enmCpuMode The cpu mode to disassemble in.
209 * @param uAddress The address we're starting to disassemble at.
210 * @param pbFile Where to start disassemble.
211 * @param cbFile How much to disassemble.
212 * @param enmStyle The assembly output style.
213 * @param fListing Whether to print in a listing like mode.
214 * @param enmUndefOp How to deal with undefined opcodes.
215 */
216static int MyDisasmBlock(const char *argv0, DISCPUMODE enmCpuMode, uint64_t uAddress, uint8_t *pbFile, size_t cbFile,
217 ASMSTYLE enmStyle, bool fListing, UNDEFOPHANDLING enmUndefOp)
218{
219 /*
220 * Initialize the CPU context.
221 */
222 MYDISSTATE State;
223 State.Cpu.mode = enmCpuMode;
224 State.Cpu.pfnReadBytes = MyDisasInstrRead;
225 State.uAddress = uAddress;
226 State.pbInstr = pbFile;
227 State.cbInstr = 0;
228 State.enmUndefOp = enmUndefOp;
229 State.rc = VINF_SUCCESS;
230 State.cbLeft = cbFile;
231 State.pbNext = pbFile;
232 State.uNextAddr = uAddress;
233
234 void (*pfnFormatter)(PMYDISSTATE pState);
235 switch (enmStyle)
236 {
237 case kAsmStyle_Default:
238 pfnFormatter = MyDisasDefaultFormatter;
239 break;
240
241 case kAsmStyle_yasm:
242 RTPrintf(" BITS %d\n", enmCpuMode == CPUMODE_16BIT ? 16 : enmCpuMode == CPUMODE_32BIT ? 32 : 64);
243 pfnFormatter = MyDisasYasmFormatter;
244 break;
245
246 case kAsmStyle_masm:
247 pfnFormatter = MyDisasMasmFormatter;
248 break;
249
250 default:
251 AssertFailedReturn(VERR_INTERNAL_ERROR);
252 }
253
254 /*
255 * The loop.
256 */
257 int rcRet = VINF_SUCCESS;
258 while (State.cbLeft > 0)
259 {
260 /*
261 * Disassemble it.
262 */
263 State.cbInstr = 0;
264 State.cbLeft += State.pbNext - State.pbInstr;
265 State.uNextAddr = State.uAddress;
266 State.pbNext = State.pbInstr;
267
268 int rc = DISInstr(&State.Cpu, State.uAddress, 0, &State.cbInstr, State.szLine);
269 if (RT_SUCCESS(rc))
270 {
271 State.fUndefOp = State.Cpu.pCurInstr->opcode == OP_INVALID
272 || State.Cpu.pCurInstr->opcode == OP_ILLUD2;
273 if (!State.fUndefOp && State.enmUndefOp == kUndefOp_All)
274 {
275 RTPrintf("%s: error at %#RX64: unexpected valid instruction (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
276 pfnFormatter(&State);
277 rcRet = VERR_GENERAL_FAILURE;
278 }
279 else if (State.fUndefOp && State.enmUndefOp == kUndefOp_Fail)
280 {
281 RTPrintf("%s: error at %#RX64: undefined opcode (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
282 pfnFormatter(&State);
283 rcRet = VERR_GENERAL_FAILURE;
284 }
285 else
286 pfnFormatter(&State);
287 }
288 else
289 {
290 State.cbInstr = State.pbNext - State.pbInstr;
291 if (!State.cbLeft)
292 RTPrintf("%s: error at %#RX64: read beyond the end (%Rrc)\n", argv0, State.uAddress, rc);
293 else if (State.cbInstr)
294 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d\n", argv0, State.uAddress, rc, State.cbInstr);
295 else
296 {
297 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d!\n", argv0, State.uAddress, rc, State.cbInstr);
298 if (rcRet == VINF_SUCCESS)
299 rcRet = rc;
300 break;
301 }
302 }
303
304
305 /* next */
306 State.uAddress += State.cbInstr;
307 State.pbInstr += State.cbInstr;
308 }
309
310 return rcRet;
311}
312
313
314/**
315 * Prints usage info.
316 *
317 * @returns 1.
318 * @param argv0 The program name.
319 */
320static int Usage(const char *argv0)
321{
322 RTStrmPrintf(g_pStdErr,
323"usage: %s [options] <file1> [file2..fileN]\n"
324" or: %s <--help|-h>\n"
325"\n"
326"Options:\n"
327" --address|-a <address>\n"
328" The base address. Default: 0\n"
329" --max-bytes|-b <bytes>\n"
330" The maximum number of bytes to disassemble. Default: 1GB\n"
331" --cpumode|-c <16|32|64>\n"
332" The cpu mode. Default: 32\n"
333" --listing|-l, --no-listing|-L\n"
334" Enables or disables listing mode. Default: --no-listing\n"
335" --offset|-o <offset>\n"
336" The file offset at which to start disassembling. Default: 0\n"
337" --style|-s <default|yasm|masm>\n"
338" The assembly output style. Default: default\n"
339" --undef-op|-u <fail|all|db>\n"
340" How to treat undefined opcodes. Default: fail\n"
341 , argv0, argv0);
342 return 1;
343}
344
345
346int main(int argc, char **argv)
347{
348 RTR3Init();
349 const char * const argv0 = RTPathFilename(argv[0]);
350
351 /* options */
352 uint64_t uAddress = 0;
353 ASMSTYLE enmStyle = kAsmStyle_Default;
354 UNDEFOPHANDLING enmUndefOp = kUndefOp_Fail;
355 bool fListing = true;
356 DISCPUMODE enmCpuMode = CPUMODE_32BIT;
357 RTFOFF off = 0;
358 RTFOFF cbMax = _1G;
359
360 /*
361 * Parse arguments.
362 */
363 static const RTOPTIONDEF g_aOptions[] =
364 {
365 { "--address", 'a', RTGETOPT_REQ_UINT64 },
366 { "--cpumode", 'c', RTGETOPT_REQ_UINT32 },
367 { "--help", 'h', 0 },
368 { "--bytes", 'b', RTGETOPT_REQ_INT64 },
369 { "--listing", 'l', 0 },
370 { "--no-listing", 'L', 0 },
371 { "--offset", 'o', RTGETOPT_REQ_INT64 },
372 { "--style", 's', RTGETOPT_REQ_STRING },
373 { "--undef-op", 'u', RTGETOPT_REQ_STRING },
374 };
375
376 int ch;
377 int iArg = 1;
378 RTOPTIONUNION ValueUnion;
379 while ((ch = RTGetOpt(argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), &iArg, &ValueUnion)))
380 {
381 switch (ch)
382 {
383 case 'a':
384 uAddress = ValueUnion.u64;
385 break;
386
387 case 'b':
388 cbMax = ValueUnion.i;
389 break;
390
391 case 'c':
392 if (ValueUnion.u32 == 16)
393 enmCpuMode = CPUMODE_16BIT;
394 else if (ValueUnion.u32 == 32)
395 enmCpuMode = CPUMODE_32BIT;
396 else if (ValueUnion.u32 == 64)
397 enmCpuMode = CPUMODE_64BIT;
398 else
399 {
400 RTStrmPrintf(g_pStdErr, "%s: Invalid CPU mode value %RU32\n", argv0, ValueUnion.u32);
401 return 1;
402 }
403 break;
404
405 case 'h':
406 return Usage(argv0);
407
408 case 'l':
409 fListing = true;
410 break;
411
412 case 'L':
413 fListing = false;
414 break;
415
416 case 'o':
417 off = ValueUnion.i;
418 break;
419
420 case 's':
421 if (!strcmp(ValueUnion.psz, "default"))
422 enmStyle = kAsmStyle_Default;
423 else if (!strcmp(ValueUnion.psz, "yasm"))
424 enmStyle = kAsmStyle_yasm;
425 else if (!strcmp(ValueUnion.psz, "masm"))
426 {
427 enmStyle = kAsmStyle_masm;
428 RTStrmPrintf(g_pStdErr, "%s: masm style isn't implemented yet\n", argv0);
429 return 1;
430 }
431 else
432 {
433 RTStrmPrintf(g_pStdErr, "%s: unknown assembly style: %s\n", argv0, ValueUnion.psz);
434 return 1;
435 }
436 break;
437
438 case 'u':
439 if (!strcmp(ValueUnion.psz, "fail"))
440 enmUndefOp = kUndefOp_Fail;
441 else if (!strcmp(ValueUnion.psz, "all"))
442 enmUndefOp = kUndefOp_All;
443 else if (!strcmp(ValueUnion.psz, "db"))
444 enmUndefOp = kUndefOp_DefineByte;
445 else
446 {
447 RTStrmPrintf(g_pStdErr, "%s: unknown undefined opcode handling method: %s\n", argv0, ValueUnion.psz);
448 return 1;
449 }
450 break;
451
452 default:
453 RTStrmPrintf(g_pStdErr, "%s: syntax error: %Rrc\n", argv0, ch);
454 return 1;
455 }
456 }
457 if (iArg >= argc)
458 return Usage(argv0);
459
460 /*
461 * Process the files.
462 */
463 int rc = VINF_SUCCESS;
464 for ( ; iArg < argc; iArg++)
465 {
466 /*
467 * Read the file into memory.
468 */
469 void *pvFile;
470 size_t cbFile;
471 rc = RTFileReadAllEx(argv[iArg], off, cbMax, 0, &pvFile, &cbFile);
472 if (RT_FAILURE(rc))
473 {
474 RTStrmPrintf(g_pStdErr, "%s: %s: %Rrc\n", argv0, argv[iArg], rc);
475 break;
476 }
477
478 /*
479 * Disassemble it.
480 */
481 rc = MyDisasmBlock(argv0, enmCpuMode, uAddress, (uint8_t *)pvFile, cbFile, enmStyle, fListing, enmUndefOp);
482 if (RT_FAILURE(rc))
483 break;
484 }
485
486 return RT_SUCCESS(rc) ? 0 : 1;
487}
488
Note: See TracBrowser for help on using the repository browser.

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