VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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