VirtualBox

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

Last change on this file since 9663 was 9271, checked in by vboxsync, 17 years ago

Implemented symbol lookup, strict / relaxed assembly, and relative (+/-off) branch formatting.

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