VirtualBox

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

Last change on this file since 27023 was 26517, checked in by vboxsync, 15 years ago

*: RTGetOpt cleanup related to --help and --version (now standard option). Use RTGetOptPrintError.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.8 KB
Line 
1/* $Id: tstDisasm-2.cpp 26517 2010-02-14 21:39:00Z 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.virtualbox.org. 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 <VBox/err.h>
27#include <iprt/alloc.h>
28#include <iprt/assert.h>
29#include <iprt/initterm.h>
30#include <iprt/getopt.h>
31#include <iprt/file.h>
32#include <iprt/path.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/ctype.h>
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41typedef enum { kAsmStyle_Default, kAsmStyle_yasm, kAsmStyle_masm, kAsmStyle_gas, kAsmStyle_invalid } ASMSTYLE;
42typedef enum { kUndefOp_Fail, kUndefOp_All, kUndefOp_DefineByte, kUndefOp_End } UNDEFOPHANDLING;
43
44typedef struct MYDISSTATE
45{
46 DISCPUSTATE Cpu;
47 uint64_t uAddress; /**< The current instruction address. */
48 uint8_t *pbInstr; /**< The current instruction (pointer). */
49 uint32_t cbInstr; /**< The size of the current instruction. */
50 bool fUndefOp; /**< Whether the current instruction is really an undefined opcode.*/
51 UNDEFOPHANDLING enmUndefOp; /**< How to treat undefined opcodes. */
52 int rc; /**< Set if we hit EOF. */
53 size_t cbLeft; /**< The number of bytes left. (read) */
54 uint8_t *pbNext; /**< The next byte. (read) */
55 uint64_t uNextAddr; /**< The address of the next byte. (read) */
56 char szLine[256]; /**< The disassembler text output. */
57} MYDISSTATE;
58typedef MYDISSTATE *PMYDISSTATE;
59
60
61
62/**
63 * Default style.
64 *
65 * @param pState The disassembler state.
66 */
67static void MyDisasDefaultFormatter(PMYDISSTATE pState)
68{
69 RTPrintf("%s", pState->szLine);
70}
71
72
73/**
74 * Yasm style.
75 *
76 * @param pState The disassembler state.
77 */
78static void MyDisasYasmFormatter(PMYDISSTATE pState)
79{
80 char szTmp[256];
81#if 0
82 /* a very quick hack. */
83 strcpy(szTmp, RTStrStripL(strchr(pState->szLine, ':') + 1));
84
85 char *psz = strrchr(szTmp, '[');
86 *psz = '\0';
87 RTStrStripR(szTmp);
88
89 psz = strstr(szTmp, " ptr ");
90 if (psz)
91 memset(psz, ' ', 5);
92
93 char *pszEnd = strchr(szTmp, '\0');
94 while (pszEnd - &szTmp[0] < 71)
95 *pszEnd++ = ' ';
96 *pszEnd = '\0';
97
98#else
99 size_t cch = DISFormatYasmEx(&pState->Cpu, szTmp, sizeof(szTmp),
100 DIS_FMT_FLAGS_STRICT | DIS_FMT_FLAGS_ADDR_RIGHT | DIS_FMT_FLAGS_ADDR_COMMENT
101 | DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_COMMENT | DIS_FMT_FLAGS_BYTES_SPACED,
102 NULL, NULL);
103 Assert(cch < sizeof(szTmp));
104 while (cch < 71)
105 szTmp[cch++] = ' ';
106 szTmp[cch] = '\0';
107#endif
108
109 RTPrintf(" %s ; %08llu %s", szTmp, pState->uAddress, pState->szLine);
110}
111
112
113/**
114 * Checks if the encoding of the current instruction is something
115 * we can never get the assembler to produce.
116 *
117 * @returns true if it's odd, false if it isn't.
118 * @param pCpu The disassembler output.
119 */
120static bool MyDisasYasmFormatterIsOddEncoding(PMYDISSTATE pState)
121{
122 /*
123 * Mod rm + SIB: Check for duplicate EBP encodings that yasm won't use for very good reasons.
124 */
125 if ( pState->Cpu.addrmode != CPUMODE_16BIT ///@todo correct?
126 && pState->Cpu.ModRM.Bits.Rm == 4
127 && pState->Cpu.ModRM.Bits.Mod != 3)
128 {
129 /* No scaled index SIB (index=4), except for ESP. */
130 if ( pState->Cpu.SIB.Bits.Index == 4
131 && pState->Cpu.SIB.Bits.Base != 4)
132 return true;
133
134 /* EBP + displacement */
135 if ( pState->Cpu.ModRM.Bits.Mod != 0
136 && pState->Cpu.SIB.Bits.Base == 5
137 && pState->Cpu.SIB.Bits.Scale == 0)
138 return true;
139 }
140
141 /*
142 * Seems to be an instruction alias here, but I cannot find any docs on it... hrmpf!
143 */
144 if ( pState->Cpu.pCurInstr->opcode == OP_SHL
145 && pState->Cpu.ModRM.Bits.Reg == 6)
146 return true;
147
148 /*
149 * Check for multiple prefixes of the same kind.
150 */
151 uint32_t fPrefixes = 0;
152 for (uint8_t const *pu8 = pState->pbInstr;; pu8++)
153 {
154 uint32_t f;
155 switch (*pu8)
156 {
157 case 0xf0:
158 f = PREFIX_LOCK;
159 break;
160
161 case 0xf2:
162 case 0xf3:
163 f = PREFIX_REP; /* yes, both */
164 break;
165
166 case 0x2e:
167 case 0x3e:
168 case 0x26:
169 case 0x36:
170 case 0x64:
171 case 0x65:
172 f = PREFIX_SEG;
173 break;
174
175 case 0x66:
176 f = PREFIX_OPSIZE;
177 break;
178
179 case 0x67:
180 f = PREFIX_ADDRSIZE;
181 break;
182
183 case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
184 case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
185 f = pState->Cpu.mode == CPUMODE_64BIT ? PREFIX_REX : 0;
186 break;
187
188 default:
189 f = 0;
190 break;
191 }
192 if (!f)
193 break; /* done */
194 if (fPrefixes & f)
195 return true;
196 fPrefixes |= f;
197 }
198
199 /* segment overrides are fun */
200 if (fPrefixes & PREFIX_SEG)
201 {
202 /* no effective address which it may apply to. */
203 Assert((pState->Cpu.prefix & PREFIX_SEG) || pState->Cpu.mode == CPUMODE_64BIT);
204 if ( !DIS_IS_EFFECTIVE_ADDR(pState->Cpu.param1.flags)
205 && !DIS_IS_EFFECTIVE_ADDR(pState->Cpu.param2.flags)
206 && !DIS_IS_EFFECTIVE_ADDR(pState->Cpu.param3.flags))
207 return true;
208 }
209
210 /* fixed register + addr override doesn't go down all that well. */
211 if (fPrefixes & PREFIX_ADDRSIZE)
212 {
213 Assert(pState->Cpu.prefix & PREFIX_ADDRSIZE);
214 if ( pState->Cpu.pCurInstr->param3 == OP_PARM_NONE
215 && pState->Cpu.pCurInstr->param2 == OP_PARM_NONE
216 && ( pState->Cpu.pCurInstr->param1 >= OP_PARM_REG_GEN32_START
217 && pState->Cpu.pCurInstr->param1 <= OP_PARM_REG_GEN32_END))
218 return true;
219 }
220
221 /* Almost all prefixes are bad. */
222 if (fPrefixes)
223 {
224 switch (pState->Cpu.pCurInstr->opcode)
225 {
226 /* nop w/ prefix(es). */
227 case OP_NOP:
228 return true;
229
230 case OP_JMP:
231 if ( pState->Cpu.pCurInstr->param1 != OP_PARM_Jb
232 && pState->Cpu.pCurInstr->param1 != OP_PARM_Jv)
233 break;
234 /* fall thru */
235 case OP_JO:
236 case OP_JNO:
237 case OP_JC:
238 case OP_JNC:
239 case OP_JE:
240 case OP_JNE:
241 case OP_JBE:
242 case OP_JNBE:
243 case OP_JS:
244 case OP_JNS:
245 case OP_JP:
246 case OP_JNP:
247 case OP_JL:
248 case OP_JNL:
249 case OP_JLE:
250 case OP_JNLE:
251 /** @todo branch hinting 0x2e/0x3e... */
252 return true;
253 }
254
255 }
256
257 /* All but the segment prefix is bad news. */
258 if (fPrefixes & ~PREFIX_SEG)
259 {
260 switch (pState->Cpu.pCurInstr->opcode)
261 {
262 case OP_POP:
263 case OP_PUSH:
264 if ( pState->Cpu.pCurInstr->param1 >= OP_PARM_REG_SEG_START
265 && pState->Cpu.pCurInstr->param1 <= OP_PARM_REG_SEG_END)
266 return true;
267 if ( (fPrefixes & ~PREFIX_OPSIZE)
268 && pState->Cpu.pCurInstr->param1 >= OP_PARM_REG_GEN32_START
269 && pState->Cpu.pCurInstr->param1 <= OP_PARM_REG_GEN32_END)
270 return true;
271 break;
272
273 case OP_POPA:
274 case OP_POPF:
275 case OP_PUSHA:
276 case OP_PUSHF:
277 if (fPrefixes & ~PREFIX_OPSIZE)
278 return true;
279 break;
280 }
281 }
282
283 /* Implicit 8-bit register instructions doesn't mix with operand size. */
284 if ( (fPrefixes & PREFIX_OPSIZE)
285 && ( ( pState->Cpu.pCurInstr->param1 == OP_PARM_Gb /* r8 */
286 && pState->Cpu.pCurInstr->param2 == OP_PARM_Eb /* r8/mem8 */)
287 || ( pState->Cpu.pCurInstr->param2 == OP_PARM_Gb /* r8 */
288 && pState->Cpu.pCurInstr->param1 == OP_PARM_Eb /* r8/mem8 */))
289 )
290 {
291 switch (pState->Cpu.pCurInstr->opcode)
292 {
293 case OP_ADD:
294 case OP_OR:
295 case OP_ADC:
296 case OP_SBB:
297 case OP_AND:
298 case OP_SUB:
299 case OP_XOR:
300 case OP_CMP:
301 return true;
302 default:
303 break;
304 }
305 }
306
307
308 /*
309 * Check for the version of xyz reg,reg instruction that the assembler doesn't use.
310 *
311 * For example:
312 * expected: 1aee sbb ch, dh ; SBB r8, r/m8
313 * yasm: 18F5 sbb ch, dh ; SBB r/m8, r8
314 */
315 if (pState->Cpu.ModRM.Bits.Mod == 3 /* reg,reg */)
316 {
317 switch (pState->Cpu.pCurInstr->opcode)
318 {
319 case OP_ADD:
320 case OP_OR:
321 case OP_ADC:
322 case OP_SBB:
323 case OP_AND:
324 case OP_SUB:
325 case OP_XOR:
326 case OP_CMP:
327 if ( ( pState->Cpu.pCurInstr->param1 == OP_PARM_Gb /* r8 */
328 && pState->Cpu.pCurInstr->param2 == OP_PARM_Eb /* r8/mem8 */)
329 || ( pState->Cpu.pCurInstr->param1 == OP_PARM_Gv /* rX */
330 && pState->Cpu.pCurInstr->param2 == OP_PARM_Ev /* rX/memX */))
331 return true;
332
333 /* 82 (see table A-6). */
334 if (pState->Cpu.opcode == 0x82)
335 return true;
336 break;
337
338 /* ff /0, fe /0, ff /1, fe /0 */
339 case OP_DEC:
340 case OP_INC:
341 return true;
342
343 case OP_POP:
344 case OP_PUSH:
345 Assert(pState->Cpu.opcode == 0x8f);
346 return true;
347
348 default:
349 break;
350 }
351 }
352
353 /* shl eax,1 will be assembled to the form without the immediate byte. */
354 if ( pState->Cpu.pCurInstr->param2 == OP_PARM_Ib
355 && (uint8_t)pState->Cpu.param2.parval == 1)
356 {
357 switch (pState->Cpu.pCurInstr->opcode)
358 {
359 case OP_SHL:
360 case OP_SHR:
361 case OP_SAR:
362 case OP_RCL:
363 case OP_RCR:
364 case OP_ROL:
365 case OP_ROR:
366 return true;
367 }
368 }
369
370 /* And some more - see table A-6. */
371 if (pState->Cpu.opcode == 0x82)
372 {
373 switch (pState->Cpu.pCurInstr->opcode)
374 {
375 case OP_ADD:
376 case OP_OR:
377 case OP_ADC:
378 case OP_SBB:
379 case OP_AND:
380 case OP_SUB:
381 case OP_XOR:
382 case OP_CMP:
383 return true;
384 break;
385 }
386 }
387
388
389 /* check for REX.X = 1 without SIB. */
390
391 /* Yasm encodes setnbe al with /2 instead of /0 like the AMD manual
392 says (intel doesn't appear to care). */
393 switch (pState->Cpu.pCurInstr->opcode)
394 {
395 case OP_SETO:
396 case OP_SETNO:
397 case OP_SETC:
398 case OP_SETNC:
399 case OP_SETE:
400 case OP_SETNE:
401 case OP_SETBE:
402 case OP_SETNBE:
403 case OP_SETS:
404 case OP_SETNS:
405 case OP_SETP:
406 case OP_SETNP:
407 case OP_SETL:
408 case OP_SETNL:
409 case OP_SETLE:
410 case OP_SETNLE:
411 AssertMsg(pState->Cpu.opcode >= 0x90 && pState->Cpu.opcode <= 0x9f, ("%#x\n", pState->Cpu.opcode));
412 if (pState->Cpu.ModRM.Bits.Reg != 2)
413 return true;
414 break;
415 }
416
417 /*
418 * The MOVZX reg32,mem16 instruction without an operand size prefix
419 * doesn't quite make sense...
420 */
421 if ( pState->Cpu.pCurInstr->opcode == OP_MOVZX
422 && pState->Cpu.opcode == 0xB7
423 && (pState->Cpu.mode == CPUMODE_16BIT) != !!(fPrefixes & PREFIX_OPSIZE))
424 return true;
425
426 return false;
427}
428
429
430/**
431 * Masm style.
432 *
433 * @param pState The disassembler state.
434 */
435static void MyDisasMasmFormatter(PMYDISSTATE pState)
436{
437 RTPrintf("masm not implemented: %s", pState->szLine);
438}
439
440
441/**
442 * This is a temporary workaround for catching a few illegal opcodes
443 * that the disassembler is currently letting thru, just enough to make
444 * the assemblers happy.
445 *
446 * We're too close to a release to dare mess with these things now as
447 * they may consequences for performance and let alone introduce bugs.
448 *
449 * @returns true if it's valid. false if it isn't.
450 *
451 * @param pCpu The disassembler output.
452 */
453static bool MyDisasIsValidInstruction(DISCPUSTATE const *pCpu)
454{
455 switch (pCpu->pCurInstr->opcode)
456 {
457 /* These doesn't take memory operands. */
458 case OP_MOV_CR:
459 case OP_MOV_DR:
460 case OP_MOV_TR:
461 if (pCpu->ModRM.Bits.Mod != 3)
462 return false;
463 break;
464
465 /* The 0x8f /0 variant of this instruction doesn't get its /r value verified. */
466 case OP_POP:
467 if ( pCpu->opcode == 0x8f
468 && pCpu->ModRM.Bits.Reg != 0)
469 return false;
470 break;
471
472 /* The 0xc6 /0 and 0xc7 /0 variants of this instruction don't get their /r values verified. */
473 case OP_MOV:
474 if ( ( pCpu->opcode == 0xc6
475 || pCpu->opcode == 0xc7)
476 && pCpu->ModRM.Bits.Reg != 0)
477 return false;
478 break;
479
480 default:
481 break;
482 }
483
484 return true;
485}
486
487
488/**
489 * Callback for reading bytes.
490 *
491 * @todo This should check that the disassembler doesn't do unnecessary reads,
492 * however the current doesn't do this and is just complicated...
493 */
494static DECLCALLBACK(int) MyDisasInstrRead(RTUINTPTR uSrcAddr, uint8_t *pbDst, uint32_t cbRead, void *pvDisCpu)
495{
496 PMYDISSTATE pState = (PMYDISSTATE)pvDisCpu;
497 if (RT_LIKELY( pState->uNextAddr == uSrcAddr
498 && pState->cbLeft >= cbRead))
499 {
500 /*
501 * Straight forward reading.
502 */
503 if (cbRead == 1)
504 {
505 pState->cbLeft--;
506 *pbDst = *pState->pbNext++;
507 pState->uNextAddr++;
508 }
509 else
510 {
511 memcpy(pbDst, pState->pbNext, cbRead);
512 pState->pbNext += cbRead;
513 pState->cbLeft -= cbRead;
514 pState->uNextAddr += cbRead;
515 }
516 }
517 else
518 {
519 /*
520 * Jumping up the stream.
521 * This occures when the byte sequence is added to the output string.
522 */
523 uint64_t offReq64 = uSrcAddr - pState->uAddress;
524 if (offReq64 < 32)
525 {
526 uint32_t offReq = offReq64;
527 uintptr_t off = pState->pbNext - pState->pbInstr;
528 if (off + pState->cbLeft <= offReq)
529 {
530 pState->pbNext += pState->cbLeft;
531 pState->uNextAddr += pState->cbLeft;
532 pState->cbLeft = 0;
533
534 memset(pbDst, 0xcc, cbRead);
535 pState->rc = VERR_EOF;
536 return VERR_EOF;
537 }
538
539 /* reset the stream. */
540 pState->cbLeft += off;
541 pState->pbNext = pState->pbInstr;
542 pState->uNextAddr = pState->uAddress;
543
544 /* skip ahead. */
545 pState->cbLeft -= offReq;
546 pState->pbNext += offReq;
547 pState->uNextAddr += offReq;
548
549 /* do the reading. */
550 if (pState->cbLeft >= cbRead)
551 {
552 memcpy(pbDst, pState->pbNext, cbRead);
553 pState->cbLeft -= cbRead;
554 pState->pbNext += cbRead;
555 pState->uNextAddr += cbRead;
556 }
557 else
558 {
559 if (pState->cbLeft > 0)
560 {
561 memcpy(pbDst, pState->pbNext, pState->cbLeft);
562 pbDst += pState->cbLeft;
563 cbRead -= (uint32_t)pState->cbLeft;
564 pState->pbNext += pState->cbLeft;
565 pState->uNextAddr += pState->cbLeft;
566 pState->cbLeft = 0;
567 }
568 memset(pbDst, 0xcc, cbRead);
569 pState->rc = VERR_EOF;
570 return VERR_EOF;
571 }
572 }
573 else
574 {
575 RTStrmPrintf(g_pStdErr, "Reading before current instruction!\n");
576 memset(pbDst, 0x90, cbRead);
577 pState->rc = VERR_INTERNAL_ERROR;
578 return VERR_INTERNAL_ERROR;
579 }
580 }
581
582 return VINF_SUCCESS;
583}
584
585
586/**
587 * Disassembles a block of memory.
588 *
589 * @returns VBox status code.
590 * @param argv0 Program name (for errors and warnings).
591 * @param enmCpuMode The cpu mode to disassemble in.
592 * @param uAddress The address we're starting to disassemble at.
593 * @param pbFile Where to start disassemble.
594 * @param cbFile How much to disassemble.
595 * @param enmStyle The assembly output style.
596 * @param fListing Whether to print in a listing like mode.
597 * @param enmUndefOp How to deal with undefined opcodes.
598 */
599static int MyDisasmBlock(const char *argv0, DISCPUMODE enmCpuMode, uint64_t uAddress, uint8_t *pbFile, size_t cbFile,
600 ASMSTYLE enmStyle, bool fListing, UNDEFOPHANDLING enmUndefOp)
601{
602 /*
603 * Initialize the CPU context.
604 */
605 MYDISSTATE State;
606 State.Cpu.mode = enmCpuMode;
607 State.Cpu.pfnReadBytes = MyDisasInstrRead;
608 State.uAddress = uAddress;
609 State.pbInstr = pbFile;
610 State.cbInstr = 0;
611 State.enmUndefOp = enmUndefOp;
612 State.rc = VINF_SUCCESS;
613 State.cbLeft = cbFile;
614 State.pbNext = pbFile;
615 State.uNextAddr = uAddress;
616
617 void (*pfnFormatter)(PMYDISSTATE pState);
618 switch (enmStyle)
619 {
620 case kAsmStyle_Default:
621 pfnFormatter = MyDisasDefaultFormatter;
622 break;
623
624 case kAsmStyle_yasm:
625 RTPrintf(" BITS %d\n", enmCpuMode == CPUMODE_16BIT ? 16 : enmCpuMode == CPUMODE_32BIT ? 32 : 64);
626 pfnFormatter = MyDisasYasmFormatter;
627 break;
628
629 case kAsmStyle_masm:
630 pfnFormatter = MyDisasMasmFormatter;
631 break;
632
633 default:
634 AssertFailedReturn(VERR_INTERNAL_ERROR);
635 }
636
637 /*
638 * The loop.
639 */
640 int rcRet = VINF_SUCCESS;
641 while (State.cbLeft > 0)
642 {
643 /*
644 * Disassemble it.
645 */
646 State.cbInstr = 0;
647 State.cbLeft += State.pbNext - State.pbInstr;
648 State.uNextAddr = State.uAddress;
649 State.pbNext = State.pbInstr;
650
651 int rc = DISInstr(&State.Cpu, State.uAddress, 0, &State.cbInstr, State.szLine);
652 if ( RT_SUCCESS(rc)
653 || ( ( rc == VERR_DIS_INVALID_OPCODE
654 || rc == VERR_DIS_GEN_FAILURE)
655 && State.enmUndefOp == kUndefOp_DefineByte))
656 {
657 State.fUndefOp = rc == VERR_DIS_INVALID_OPCODE
658 || rc == VERR_DIS_GEN_FAILURE
659 || State.Cpu.pCurInstr->opcode == OP_INVALID
660 || State.Cpu.pCurInstr->opcode == OP_ILLUD2
661 || ( State.enmUndefOp == kUndefOp_DefineByte
662 && !MyDisasIsValidInstruction(&State.Cpu));
663 if (State.fUndefOp && State.enmUndefOp == kUndefOp_DefineByte)
664 {
665 RTPrintf(" db");
666 if (!State.cbInstr)
667 State.cbInstr = 1;
668 for (unsigned off = 0; off < State.cbInstr; off++)
669 {
670 uint8_t b;
671 State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu);
672 RTPrintf(off ? ", %03xh" : " %03xh", b);
673 }
674 RTPrintf(" ; %s\n", State.szLine);
675 }
676 else if (!State.fUndefOp && State.enmUndefOp == kUndefOp_All)
677 {
678 RTPrintf("%s: error at %#RX64: unexpected valid instruction (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
679 pfnFormatter(&State);
680 rcRet = VERR_GENERAL_FAILURE;
681 }
682 else if (State.fUndefOp && State.enmUndefOp == kUndefOp_Fail)
683 {
684 RTPrintf("%s: error at %#RX64: undefined opcode (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
685 pfnFormatter(&State);
686 rcRet = VERR_GENERAL_FAILURE;
687 }
688 else
689 {
690 /* Use db for odd encodings that we can't make the assembler use. */
691 if ( State.enmUndefOp == kUndefOp_DefineByte
692 && MyDisasYasmFormatterIsOddEncoding(&State))
693 {
694 RTPrintf(" db");
695 for (unsigned off = 0; off < State.cbInstr; off++)
696 {
697 uint8_t b;
698 State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu);
699 RTPrintf(off ? ", %03xh" : " %03xh", b);
700 }
701 RTPrintf(" ; ");
702 }
703
704 pfnFormatter(&State);
705 }
706 }
707 else
708 {
709 State.cbInstr = State.pbNext - State.pbInstr;
710 if (!State.cbLeft)
711 RTPrintf("%s: error at %#RX64: read beyond the end (%Rrc)\n", argv0, State.uAddress, rc);
712 else if (State.cbInstr)
713 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d\n", argv0, State.uAddress, rc, State.cbInstr);
714 else
715 {
716 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d!\n", argv0, State.uAddress, rc, State.cbInstr);
717 if (rcRet == VINF_SUCCESS)
718 rcRet = rc;
719 break;
720 }
721 }
722
723
724 /* next */
725 State.uAddress += State.cbInstr;
726 State.pbInstr += State.cbInstr;
727 }
728
729 return rcRet;
730}
731
732/**
733 * Converts a hex char to a number.
734 *
735 * @returns 0..15 on success, -1 on failure.
736 * @param ch The character.
737 */
738static int HexDigitToNum(char ch)
739{
740 switch (ch)
741 {
742 case '0': return 0;
743 case '1': return 1;
744 case '2': return 2;
745 case '3': return 3;
746 case '4': return 4;
747 case '5': return 5;
748 case '6': return 6;
749 case '7': return 7;
750 case '8': return 8;
751 case '9': return 9;
752 case 'A':
753 case 'a': return 0xa;
754 case 'B':
755 case 'b': return 0xb;
756 case 'C':
757 case 'c': return 0xc;
758 case 'D':
759 case 'd': return 0xd;
760 case 'E':
761 case 'e': return 0xe;
762 case 'F':
763 case 'f': return 0xf;
764 default:
765 RTPrintf("error: Invalid hex digig '%c'\n", ch);
766 return -1;
767 }
768}
769
770/**
771 * Prints usage info.
772 *
773 * @returns 1.
774 * @param argv0 The program name.
775 */
776static int Usage(const char *argv0)
777{
778 RTStrmPrintf(g_pStdErr,
779"usage: %s [options] <file1> [file2..fileN]\n"
780" or: %s [options] <-x|--hex-bytes> <hex byte> [more hex..]\n"
781" or: %s <--help|-h>\n"
782"\n"
783"Options:\n"
784" --address|-a <address>\n"
785" The base address. Default: 0\n"
786" --max-bytes|-b <bytes>\n"
787" The maximum number of bytes to disassemble. Default: 1GB\n"
788" --cpumode|-c <16|32|64>\n"
789" The cpu mode. Default: 32\n"
790" --listing|-l, --no-listing|-L\n"
791" Enables or disables listing mode. Default: --no-listing\n"
792" --offset|-o <offset>\n"
793" The file offset at which to start disassembling. Default: 0\n"
794" --style|-s <default|yasm|masm>\n"
795" The assembly output style. Default: default\n"
796" --undef-op|-u <fail|all|db>\n"
797" How to treat undefined opcodes. Default: fail\n"
798 , argv0, argv0);
799 return 1;
800}
801
802
803int main(int argc, char **argv)
804{
805 RTR3Init();
806 const char * const argv0 = RTPathFilename(argv[0]);
807
808 /* options */
809 uint64_t uAddress = 0;
810 ASMSTYLE enmStyle = kAsmStyle_Default;
811 UNDEFOPHANDLING enmUndefOp = kUndefOp_Fail;
812 bool fListing = true;
813 DISCPUMODE enmCpuMode = CPUMODE_32BIT;
814 RTFOFF off = 0;
815 RTFOFF cbMax = _1G;
816 bool fHexBytes = false;
817
818 /*
819 * Parse arguments.
820 */
821 static const RTGETOPTDEF g_aOptions[] =
822 {
823 { "--address", 'a', RTGETOPT_REQ_UINT64 },
824 { "--cpumode", 'c', RTGETOPT_REQ_UINT32 },
825 { "--bytes", 'b', RTGETOPT_REQ_INT64 },
826 { "--listing", 'l', RTGETOPT_REQ_NOTHING },
827 { "--no-listing", 'L', RTGETOPT_REQ_NOTHING },
828 { "--offset", 'o', RTGETOPT_REQ_INT64 },
829 { "--style", 's', RTGETOPT_REQ_STRING },
830 { "--undef-op", 'u', RTGETOPT_REQ_STRING },
831 { "--hex-bytes", 'x', RTGETOPT_REQ_NOTHING },
832 };
833
834 int ch;
835 RTGETOPTUNION ValueUnion;
836 RTGETOPTSTATE GetState;
837 RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
838 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
839 && ch != VINF_GETOPT_NOT_OPTION)
840 {
841 switch (ch)
842 {
843 case 'a':
844 uAddress = ValueUnion.u64;
845 break;
846
847 case 'b':
848 cbMax = ValueUnion.i;
849 break;
850
851 case 'c':
852 if (ValueUnion.u32 == 16)
853 enmCpuMode = CPUMODE_16BIT;
854 else if (ValueUnion.u32 == 32)
855 enmCpuMode = CPUMODE_32BIT;
856 else if (ValueUnion.u32 == 64)
857 enmCpuMode = CPUMODE_64BIT;
858 else
859 {
860 RTStrmPrintf(g_pStdErr, "%s: Invalid CPU mode value %RU32\n", argv0, ValueUnion.u32);
861 return 1;
862 }
863 break;
864
865 case 'h':
866 return Usage(argv0);
867
868 case 'l':
869 fListing = true;
870 break;
871
872 case 'L':
873 fListing = false;
874 break;
875
876 case 'o':
877 off = ValueUnion.i;
878 break;
879
880 case 's':
881 if (!strcmp(ValueUnion.psz, "default"))
882 enmStyle = kAsmStyle_Default;
883 else if (!strcmp(ValueUnion.psz, "yasm"))
884 enmStyle = kAsmStyle_yasm;
885 else if (!strcmp(ValueUnion.psz, "masm"))
886 {
887 enmStyle = kAsmStyle_masm;
888 RTStrmPrintf(g_pStdErr, "%s: masm style isn't implemented yet\n", argv0);
889 return 1;
890 }
891 else
892 {
893 RTStrmPrintf(g_pStdErr, "%s: unknown assembly style: %s\n", argv0, ValueUnion.psz);
894 return 1;
895 }
896 break;
897
898 case 'u':
899 if (!strcmp(ValueUnion.psz, "fail"))
900 enmUndefOp = kUndefOp_Fail;
901 else if (!strcmp(ValueUnion.psz, "all"))
902 enmUndefOp = kUndefOp_All;
903 else if (!strcmp(ValueUnion.psz, "db"))
904 enmUndefOp = kUndefOp_DefineByte;
905 else
906 {
907 RTStrmPrintf(g_pStdErr, "%s: unknown undefined opcode handling method: %s\n", argv0, ValueUnion.psz);
908 return 1;
909 }
910 break;
911
912 case 'x':
913 fHexBytes = true;
914 break;
915
916 case 'V':
917 RTPrintf("$Revision: $\n");
918 return 0;
919
920 default:
921 return RTGetOptPrintError(ch, &ValueUnion);
922 }
923 }
924 int iArg = GetState.iNext - 1; /** @todo Not pretty, add RTGetOptInit flag for this. */
925 if (iArg >= argc)
926 return Usage(argv0);
927
928 int rc = VINF_SUCCESS;
929 if (fHexBytes)
930 {
931 /*
932 * Convert the remaining arguments from a hex byte string into
933 * a buffer that we disassemble.
934 */
935 size_t cb = 0;
936 uint8_t *pb = NULL;
937 for ( ; iArg < argc; iArg++)
938 {
939 const char *psz = argv[iArg];
940 while (*psz)
941 {
942 /** @todo this stuff belongs in IPRT, same stuff as mac address reading. Could be reused for IPv6 with a different item size.*/
943 /* skip white space */
944 while (RT_C_IS_SPACE(*psz))
945 psz++;
946 if (!*psz)
947 break;
948
949 /* one digit followed by a space or EOS, or two digits. */
950 int iNum = HexDigitToNum(*psz++);
951 if (iNum == -1)
952 return 1;
953 if (!RT_C_IS_SPACE(*psz) && *psz)
954 {
955 int iDigit = HexDigitToNum(*psz++);
956 if (iDigit == -1)
957 return 1;
958 iNum = iNum * 16 + iDigit;
959 }
960
961 /* add the byte */
962 if (!(cb % 4 /*64*/))
963 {
964 pb = (uint8_t *)RTMemRealloc(pb, cb + 64);
965 if (!pb)
966 {
967 RTPrintf("%s: error: RTMemRealloc failed\n", argv[0]);
968 return 1;
969 }
970 }
971 pb[cb++] = (uint8_t)iNum;
972 }
973 }
974
975 /*
976 * Disassemble it.
977 */
978 rc = MyDisasmBlock(argv0, enmCpuMode, uAddress, pb, cb, enmStyle, fListing, enmUndefOp);
979 }
980 else
981 {
982 /*
983 * Process the files.
984 */
985 for ( ; iArg < argc; iArg++)
986 {
987 /*
988 * Read the file into memory.
989 */
990 void *pvFile;
991 size_t cbFile;
992 rc = RTFileReadAllEx(argv[iArg], off, cbMax, RTFILE_RDALL_O_DENY_NONE, &pvFile, &cbFile);
993 if (RT_FAILURE(rc))
994 {
995 RTStrmPrintf(g_pStdErr, "%s: %s: %Rrc\n", argv0, argv[iArg], rc);
996 break;
997 }
998
999 /*
1000 * Disassemble it.
1001 */
1002 rc = MyDisasmBlock(argv0, enmCpuMode, uAddress, (uint8_t *)pvFile, cbFile, enmStyle, fListing, enmUndefOp);
1003 if (RT_FAILURE(rc))
1004 break;
1005 }
1006 }
1007
1008 return RT_SUCCESS(rc) ? 0 : 1;
1009}
1010
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