VirtualBox

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

Last change on this file since 40654 was 38636, checked in by vboxsync, 13 years ago

*,IPRT: Redid the ring-3 init to always convert the arguments to UTF-8.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 31.4 KB
Line 
1/* $Id: tstDisasm-2.cpp 38636 2011-09-05 13:49:45Z 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 occurs 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 uHighlightAddr The address of the instruction that should be
590 * highlighted. Pass UINT64_MAX to keep quiet.
591 * @param pbFile Where to start disassemble.
592 * @param cbFile How much to disassemble.
593 * @param enmStyle The assembly output style.
594 * @param fListing Whether to print in a listing like mode.
595 * @param enmUndefOp How to deal with undefined opcodes.
596 */
597static int MyDisasmBlock(const char *argv0, DISCPUMODE enmCpuMode, uint64_t uAddress,
598 uint64_t uHighlightAddr, uint8_t *pbFile, size_t cbFile,
599 ASMSTYLE enmStyle, bool fListing, UNDEFOPHANDLING enmUndefOp)
600{
601 /*
602 * Initialize the CPU context.
603 */
604 MYDISSTATE State;
605 State.Cpu.mode = enmCpuMode;
606 State.Cpu.pfnReadBytes = MyDisasInstrRead;
607 State.uAddress = uAddress;
608 State.pbInstr = pbFile;
609 State.cbInstr = 0;
610 State.enmUndefOp = enmUndefOp;
611 State.rc = VINF_SUCCESS;
612 State.cbLeft = cbFile;
613 State.pbNext = pbFile;
614 State.uNextAddr = uAddress;
615
616 void (*pfnFormatter)(PMYDISSTATE pState);
617 switch (enmStyle)
618 {
619 case kAsmStyle_Default:
620 pfnFormatter = MyDisasDefaultFormatter;
621 break;
622
623 case kAsmStyle_yasm:
624 RTPrintf(" BITS %d\n", enmCpuMode == CPUMODE_16BIT ? 16 : enmCpuMode == CPUMODE_32BIT ? 32 : 64);
625 pfnFormatter = MyDisasYasmFormatter;
626 break;
627
628 case kAsmStyle_masm:
629 pfnFormatter = MyDisasMasmFormatter;
630 break;
631
632 default:
633 AssertFailedReturn(VERR_INTERNAL_ERROR);
634 }
635
636 /*
637 * The loop.
638 */
639 int rcRet = VINF_SUCCESS;
640 while (State.cbLeft > 0)
641 {
642 /*
643 * Disassemble it.
644 */
645 State.cbInstr = 0;
646 State.cbLeft += State.pbNext - State.pbInstr;
647 State.uNextAddr = State.uAddress;
648 State.pbNext = State.pbInstr;
649
650 int rc = DISInstr(&State.Cpu, State.uAddress, 0, &State.cbInstr, State.szLine);
651 if ( RT_SUCCESS(rc)
652 || ( ( rc == VERR_DIS_INVALID_OPCODE
653 || rc == VERR_DIS_GEN_FAILURE)
654 && State.enmUndefOp == kUndefOp_DefineByte))
655 {
656 State.fUndefOp = rc == VERR_DIS_INVALID_OPCODE
657 || rc == VERR_DIS_GEN_FAILURE
658 || State.Cpu.pCurInstr->opcode == OP_INVALID
659 || State.Cpu.pCurInstr->opcode == OP_ILLUD2
660 || ( State.enmUndefOp == kUndefOp_DefineByte
661 && !MyDisasIsValidInstruction(&State.Cpu));
662 if (State.fUndefOp && State.enmUndefOp == kUndefOp_DefineByte)
663 {
664 RTPrintf(" db");
665 if (!State.cbInstr)
666 State.cbInstr = 1;
667 for (unsigned off = 0; off < State.cbInstr; off++)
668 {
669 uint8_t b;
670 State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu);
671 RTPrintf(off ? ", %03xh" : " %03xh", b);
672 }
673 RTPrintf(" ; %s\n", State.szLine);
674 }
675 else if (!State.fUndefOp && State.enmUndefOp == kUndefOp_All)
676 {
677 RTPrintf("%s: error at %#RX64: unexpected valid instruction (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
678 pfnFormatter(&State);
679 rcRet = VERR_GENERAL_FAILURE;
680 }
681 else if (State.fUndefOp && State.enmUndefOp == kUndefOp_Fail)
682 {
683 RTPrintf("%s: error at %#RX64: undefined opcode (op=%d)\n", argv0, State.uAddress, State.Cpu.pCurInstr->opcode);
684 pfnFormatter(&State);
685 rcRet = VERR_GENERAL_FAILURE;
686 }
687 else
688 {
689 /* Use db for odd encodings that we can't make the assembler use. */
690 if ( State.enmUndefOp == kUndefOp_DefineByte
691 && MyDisasYasmFormatterIsOddEncoding(&State))
692 {
693 RTPrintf(" db");
694 for (unsigned off = 0; off < State.cbInstr; off++)
695 {
696 uint8_t b;
697 State.Cpu.pfnReadBytes(State.uAddress + off, &b, 1, &State.Cpu);
698 RTPrintf(off ? ", %03xh" : " %03xh", b);
699 }
700 RTPrintf(" ; ");
701 }
702
703 pfnFormatter(&State);
704 }
705 }
706 else
707 {
708 State.cbInstr = State.pbNext - State.pbInstr;
709 if (!State.cbLeft)
710 RTPrintf("%s: error at %#RX64: read beyond the end (%Rrc)\n", argv0, State.uAddress, rc);
711 else if (State.cbInstr)
712 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d\n", argv0, State.uAddress, rc, State.cbInstr);
713 else
714 {
715 RTPrintf("%s: error at %#RX64: %Rrc cbInstr=%d!\n", argv0, State.uAddress, rc, State.cbInstr);
716 if (rcRet == VINF_SUCCESS)
717 rcRet = rc;
718 break;
719 }
720 }
721
722 /* Highlight this instruction? */
723 if (uHighlightAddr - State.uAddress < State.cbInstr)
724 RTPrintf("; ^^^^^^^^^^^^^^^^^^^^^\n");
725
726 /* next */
727 State.uAddress += State.cbInstr;
728 State.pbInstr += State.cbInstr;
729 }
730
731 return rcRet;
732}
733
734/**
735 * Converts a hex char to a number.
736 *
737 * @returns 0..15 on success, -1 on failure.
738 * @param ch The character.
739 */
740static int HexDigitToNum(char ch)
741{
742 switch (ch)
743 {
744 case '0': return 0;
745 case '1': return 1;
746 case '2': return 2;
747 case '3': return 3;
748 case '4': return 4;
749 case '5': return 5;
750 case '6': return 6;
751 case '7': return 7;
752 case '8': return 8;
753 case '9': return 9;
754 case 'A':
755 case 'a': return 0xa;
756 case 'B':
757 case 'b': return 0xb;
758 case 'C':
759 case 'c': return 0xc;
760 case 'D':
761 case 'd': return 0xd;
762 case 'E':
763 case 'e': return 0xe;
764 case 'F':
765 case 'f': return 0xf;
766 default:
767 RTPrintf("error: Invalid hex digig '%c'\n", ch);
768 return -1;
769 }
770}
771
772/**
773 * Prints usage info.
774 *
775 * @returns 1.
776 * @param argv0 The program name.
777 */
778static int Usage(const char *argv0)
779{
780 RTStrmPrintf(g_pStdErr,
781"usage: %s [options] <file1> [file2..fileN]\n"
782" or: %s [options] <-x|--hex-bytes> <hex byte> [more hex..]\n"
783" or: %s <--help|-h>\n"
784"\n"
785"Options:\n"
786" --address|-a <address>\n"
787" The base address. Default: 0\n"
788" --max-bytes|-b <bytes>\n"
789" The maximum number of bytes to disassemble. Default: 1GB\n"
790" --cpumode|-c <16|32|64>\n"
791" The cpu mode. Default: 32\n"
792" --listing|-l, --no-listing|-L\n"
793" Enables or disables listing mode. Default: --no-listing\n"
794" --offset|-o <offset>\n"
795" The file offset at which to start disassembling. Default: 0\n"
796" --style|-s <default|yasm|masm>\n"
797" The assembly output style. Default: default\n"
798" --undef-op|-u <fail|all|db>\n"
799" How to treat undefined opcodes. Default: fail\n"
800 , argv0, argv0);
801 return 1;
802}
803
804
805int main(int argc, char **argv)
806{
807 RTR3InitExe(argc, &argv, 0);
808 const char * const argv0 = RTPathFilename(argv[0]);
809
810 /* options */
811 uint64_t uAddress = 0;
812 uint64_t uHighlightAddr = UINT64_MAX;
813 ASMSTYLE enmStyle = kAsmStyle_Default;
814 UNDEFOPHANDLING enmUndefOp = kUndefOp_Fail;
815 bool fListing = true;
816 DISCPUMODE enmCpuMode = CPUMODE_32BIT;
817 RTFOFF off = 0;
818 RTFOFF cbMax = _1G;
819 bool fHexBytes = false;
820
821 /*
822 * Parse arguments.
823 */
824 static const RTGETOPTDEF g_aOptions[] =
825 {
826 { "--address", 'a', RTGETOPT_REQ_UINT64 },
827 { "--cpumode", 'c', RTGETOPT_REQ_UINT32 },
828 { "--bytes", 'b', RTGETOPT_REQ_INT64 },
829 { "--listing", 'l', RTGETOPT_REQ_NOTHING },
830 { "--no-listing", 'L', RTGETOPT_REQ_NOTHING },
831 { "--offset", 'o', RTGETOPT_REQ_INT64 },
832 { "--style", 's', RTGETOPT_REQ_STRING },
833 { "--undef-op", 'u', RTGETOPT_REQ_STRING },
834 { "--hex-bytes", 'x', RTGETOPT_REQ_NOTHING },
835 };
836
837 int ch;
838 RTGETOPTUNION ValueUnion;
839 RTGETOPTSTATE GetState;
840 RTGetOptInit(&GetState, argc, argv, g_aOptions, RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
841 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
842 && ch != VINF_GETOPT_NOT_OPTION)
843 {
844 switch (ch)
845 {
846 case 'a':
847 uAddress = ValueUnion.u64;
848 break;
849
850 case 'b':
851 cbMax = ValueUnion.i64;
852 break;
853
854 case 'c':
855 if (ValueUnion.u32 == 16)
856 enmCpuMode = CPUMODE_16BIT;
857 else if (ValueUnion.u32 == 32)
858 enmCpuMode = CPUMODE_32BIT;
859 else if (ValueUnion.u32 == 64)
860 enmCpuMode = CPUMODE_64BIT;
861 else
862 {
863 RTStrmPrintf(g_pStdErr, "%s: Invalid CPU mode value %RU32\n", argv0, ValueUnion.u32);
864 return 1;
865 }
866 break;
867
868 case 'h':
869 return Usage(argv0);
870
871 case 'l':
872 fListing = true;
873 break;
874
875 case 'L':
876 fListing = false;
877 break;
878
879 case 'o':
880 off = ValueUnion.i64;
881 break;
882
883 case 's':
884 if (!strcmp(ValueUnion.psz, "default"))
885 enmStyle = kAsmStyle_Default;
886 else if (!strcmp(ValueUnion.psz, "yasm"))
887 enmStyle = kAsmStyle_yasm;
888 else if (!strcmp(ValueUnion.psz, "masm"))
889 {
890 enmStyle = kAsmStyle_masm;
891 RTStrmPrintf(g_pStdErr, "%s: masm style isn't implemented yet\n", argv0);
892 return 1;
893 }
894 else
895 {
896 RTStrmPrintf(g_pStdErr, "%s: unknown assembly style: %s\n", argv0, ValueUnion.psz);
897 return 1;
898 }
899 break;
900
901 case 'u':
902 if (!strcmp(ValueUnion.psz, "fail"))
903 enmUndefOp = kUndefOp_Fail;
904 else if (!strcmp(ValueUnion.psz, "all"))
905 enmUndefOp = kUndefOp_All;
906 else if (!strcmp(ValueUnion.psz, "db"))
907 enmUndefOp = kUndefOp_DefineByte;
908 else
909 {
910 RTStrmPrintf(g_pStdErr, "%s: unknown undefined opcode handling method: %s\n", argv0, ValueUnion.psz);
911 return 1;
912 }
913 break;
914
915 case 'x':
916 fHexBytes = true;
917 break;
918
919 case 'V':
920 RTPrintf("$Revision: $\n");
921 return 0;
922
923 default:
924 return RTGetOptPrintError(ch, &ValueUnion);
925 }
926 }
927 int iArg = GetState.iNext - 1; /** @todo Not pretty, add RTGetOptInit flag for this. */
928 if (iArg >= argc)
929 return Usage(argv0);
930
931 int rc = VINF_SUCCESS;
932 if (fHexBytes)
933 {
934 /*
935 * Convert the remaining arguments from a hex byte string into
936 * a buffer that we disassemble.
937 */
938 size_t cb = 0;
939 uint8_t *pb = NULL;
940 for ( ; iArg < argc; iArg++)
941 {
942 char ch2;
943 const char *psz = argv[iArg];
944 while (*psz)
945 {
946 /** @todo this stuff belongs in IPRT, same stuff as mac address reading. Could be reused for IPv6 with a different item size.*/
947 /* skip white space, and for the benefit of linux panics '<' and '>'. */
948 while (RT_C_IS_SPACE(ch2 = *psz) || ch2 == '<' || ch2 == '>')
949 {
950 if (ch2 == '<')
951 uHighlightAddr = uAddress + cb;
952 psz++;
953 }
954 if (!ch2)
955 break;
956
957 /* one digit followed by a space or EOS, or two digits. */
958 int iNum = HexDigitToNum(*psz++);
959 if (iNum == -1)
960 return 1;
961 if (!RT_C_IS_SPACE(ch2 = *psz) && ch2 != '\0' && ch2 != '>')
962 {
963 int iDigit = HexDigitToNum(*psz++);
964 if (iDigit == -1)
965 return 1;
966 iNum = iNum * 16 + iDigit;
967 }
968
969 /* add the byte */
970 if (!(cb % 4 /*64*/))
971 {
972 pb = (uint8_t *)RTMemRealloc(pb, cb + 64);
973 if (!pb)
974 {
975 RTPrintf("%s: error: RTMemRealloc failed\n", argv[0]);
976 return 1;
977 }
978 }
979 pb[cb++] = (uint8_t)iNum;
980 }
981 }
982
983 /*
984 * Disassemble it.
985 */
986 rc = MyDisasmBlock(argv0, enmCpuMode, uAddress, uHighlightAddr, pb, cb, enmStyle, fListing, enmUndefOp);
987 }
988 else
989 {
990 /*
991 * Process the files.
992 */
993 for ( ; iArg < argc; iArg++)
994 {
995 /*
996 * Read the file into memory.
997 */
998 void *pvFile;
999 size_t cbFile;
1000 rc = RTFileReadAllEx(argv[iArg], off, cbMax, RTFILE_RDALL_O_DENY_NONE, &pvFile, &cbFile);
1001 if (RT_FAILURE(rc))
1002 {
1003 RTStrmPrintf(g_pStdErr, "%s: %s: %Rrc\n", argv0, argv[iArg], rc);
1004 break;
1005 }
1006
1007 /*
1008 * Disassemble it.
1009 */
1010 rc = MyDisasmBlock(argv0, enmCpuMode, uAddress, uHighlightAddr, (uint8_t *)pvFile, cbFile, enmStyle, fListing, enmUndefOp);
1011 if (RT_FAILURE(rc))
1012 break;
1013 }
1014 }
1015
1016 return RT_SUCCESS(rc) ? 0 : 1;
1017}
1018
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