VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS-new/MakeDebianBiosAssembly.cpp@ 41509

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

...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.0 KB
Line 
1/* $Id: MakeDebianBiosAssembly.cpp 41509 2012-05-30 20:05:12Z vboxsync $ */
2/** @file
3 * MakeDebianBiosAssembly - Generate Assembly Source for Debian-minded Distros.
4 */
5
6/*
7 * Copyright (C) 2012 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/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/asm.h>
23#include <iprt/buildconfig.h>
24#include <iprt/ctype.h>
25#include <iprt/dbg.h>
26#include <iprt/file.h>
27#include <iprt/getopt.h>
28#include <iprt/initterm.h>
29#include <iprt/mem.h>
30#include <iprt/message.h>
31#include <iprt/string.h>
32#include <iprt/stream.h>
33#include <iprt/x86.h>
34
35#include <VBox/dis.h>
36
37
38/*******************************************************************************
39* Defined Constants And Macros *
40*******************************************************************************/
41/** The flat ROM base address. */
42#define VBOX_BIOS_BASE UINT32_C(0xf0000)
43
44
45/*******************************************************************************
46* Structures and Typedefs *
47*******************************************************************************/
48/**
49 * A BIOS segment.
50 */
51typedef struct BIOSSEG
52{
53 char szName[32];
54 char szClass[32];
55 char szGroup[32];
56 RTFAR16 Address;
57 uint32_t uFlatAddr;
58 uint32_t cb;
59} BIOSSEG;
60/** Pointer to a BIOS segment. */
61typedef BIOSSEG *PBIOSSEG;
62
63
64/**
65 * Pointer to a BIOS map parser handle.
66 */
67typedef struct BIOSMAP
68{
69 /** The stream pointer. */
70 PRTSTREAM hStrm;
71 /** The file name. */
72 const char *pszMapFile;
73 /** Set when EOF has been reached. */
74 bool fEof;
75 /** The current line number (0 based).*/
76 uint32_t iLine;
77 /** The length of the current line. */
78 uint32_t cch;
79 /** The offset of the first non-white character on the line. */
80 uint32_t offNW;
81 /** The line buffer. */
82 char szLine[16384];
83} BIOSMAP;
84/** Pointer to a BIOS map parser handle. */
85typedef BIOSMAP *PBIOSMAP;
86
87
88/*******************************************************************************
89* Global Variables *
90*******************************************************************************/
91static unsigned g_cVerbose = 1 /*0*/;
92/** Pointer to the BIOS image. */
93static uint8_t const *g_pbImg;
94/** The size of the BIOS image. */
95static size_t g_cbImg;
96
97/** Debug module for the map file. */
98static RTDBGMOD g_hMapMod = NIL_RTDBGMOD;
99/** The number of BIOS segments found in the map file. */
100static uint32_t g_cSegs = 0;
101/** Array of BIOS segments from the map file. */
102static BIOSSEG g_aSegs[32];
103
104/** The output stream. */
105static PRTSTREAM g_hStrmOutput = NULL;
106
107
108static bool outputPrintfV(const char *pszFormat, va_list va)
109{
110 int rc = RTStrmPrintfV(g_hStrmOutput, pszFormat, va);
111 if (RT_FAILURE(rc))
112 {
113 RTMsgError("Output error: %Rrc\n", rc);
114 return false;
115 }
116 return true;
117}
118
119
120static bool outputPrintf(const char *pszFormat, ...)
121{
122 va_list va;
123 va_start(va, pszFormat);
124 bool fRc = outputPrintfV(pszFormat, va);
125 va_end(va);
126 return fRc;
127}
128
129
130/**
131 * Opens the output file for writing.
132 *
133 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
134 * @param pszOutput Path to the output file.
135 */
136static RTEXITCODE OpenOutputFile(const char *pszOutput)
137{
138 if (!pszOutput)
139 g_hStrmOutput = g_pStdOut;
140 else
141 {
142 int rc = RTStrmOpen(pszOutput, "w", &g_hStrmOutput);
143 if (RT_FAILURE(rc))
144 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open output file '%s': %Rrc", pszOutput, rc);
145 }
146 return RTEXITCODE_SUCCESS;
147}
148
149
150/**
151 * Displays a disassembly error and returns @c false.
152 *
153 * @returns @c false.
154 * @param pszFormat The error format string.
155 * @param ... Format argument.
156 */
157static bool disError(const char *pszFormat, ...)
158{
159 va_list va;
160 va_start(va, pszFormat);
161 RTMsgErrorV(pszFormat, va);
162 va_end(va);
163 return false;
164}
165
166
167/**
168 * Output the disassembly file header.
169 *
170 * @returns @c true on success,
171 */
172static bool disFileHeader(void)
173{
174 return outputPrintf("; $Id: MakeDebianBiosAssembly.cpp 41509 2012-05-30 20:05:12Z vboxsync $ \n"
175 ";; @file\n"
176 "; Auto Generated source file. Do not edit.\n"
177 ";\n"
178 "\n"
179 "org 0xf000\n"
180 "\n"
181 );
182}
183
184
185/**
186 * Checks if a byte sequence could be a string litteral.
187 *
188 * @returns @c true if it is, @c false if it isn't.
189 * @param uFlatAddr The address of the byte sequence.
190 * @param cb The length of the sequence.
191 */
192static bool disIsString(uint32_t uFlatAddr, uint32_t cb)
193{
194 if (cb < 6)
195 return false;
196
197 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
198 while (cb > 0)
199 {
200 if ( !RT_C_IS_PRINT(*pb)
201 && *pb != '\r'
202 && *pb != '\n'
203 && *pb != '\t')
204 {
205 if (*pb == '\0')
206 {
207 do
208 {
209 pb++;
210 cb--;
211 } while (cb > 0 && *pb == '\0');
212 return cb == 0;
213 }
214 return false;
215 }
216 pb++;
217 cb--;
218 }
219
220 return true;
221}
222
223
224/**
225 * Checks if a dword could be a far 16:16 BIOS address.
226 *
227 * @returns @c true if it is, @c false if it isn't.
228 * @param uFlatAddr The address of the dword.
229 */
230static bool disIsFarBiosAddr(uint32_t uFlatAddr)
231{
232 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
233 if (pu16[1] < 0xf000)
234 return false;
235 if (pu16[1] > 0xfff0)
236 return false;
237 uint32_t uFlatAddr2 = (uint32_t)(pu16[1] << 4) | pu16[0];
238 if (uFlatAddr2 >= VBOX_BIOS_BASE + _64K)
239 return false;
240 return true;
241}
242
243
244static bool disByteData(uint32_t uFlatAddr, uint32_t cb)
245{
246 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
247 size_t cbOnLine = 0;
248 while (cb-- > 0)
249 {
250 bool fRc;
251 if (cbOnLine >= 16)
252 {
253 fRc = outputPrintf("\n"
254 " db 0%02xh", *pb);
255 cbOnLine = 1;
256 }
257 else if (!cbOnLine)
258 {
259 fRc = outputPrintf(" db 0%02xh", *pb);
260 cbOnLine = 1;
261 }
262 else
263 {
264 fRc = outputPrintf(", 0%02xh", *pb);
265 cbOnLine++;
266 }
267 if (!fRc)
268 return false;
269 pb++;
270 }
271 return outputPrintf("\n");
272}
273
274
275static bool disWordData(uint32_t uFlatAddr, uint32_t cb)
276{
277 if (cb & 1)
278 return disError("disWordData expects word aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
279
280 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
281 size_t cbOnLine = 0;
282 while (cb > 0)
283 {
284 bool fRc;
285 if (cbOnLine >= 16)
286 {
287 fRc = outputPrintf("\n"
288 " dw 0%04xh", *pu16);
289 cbOnLine = 2;
290 }
291 else if (!cbOnLine)
292 {
293 fRc = outputPrintf(" dw 0%04xh", *pu16);
294 cbOnLine = 2;
295 }
296 else
297 {
298 fRc = outputPrintf(", 0%04xh", *pu16);
299 cbOnLine += 2;
300 }
301 if (!fRc)
302 return false;
303 pu16++;
304 cb -= 2;
305 }
306 return outputPrintf("\n");
307}
308
309
310static bool disDWordData(uint32_t uFlatAddr, uint32_t cb)
311{
312 if (cb & 3)
313 return disError("disWordData expects dword aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
314
315 uint32_t const *pu32 = (uint32_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
316 size_t cbOnLine = 0;
317 while (cb > 0)
318 {
319 bool fRc;
320 if (cbOnLine >= 16)
321 {
322 fRc = outputPrintf("\n"
323 " dd 0%08xh", *pu32);
324 cbOnLine = 4;
325 }
326 else if (!cbOnLine)
327 {
328 fRc = outputPrintf(" dd 0%08xh", *pu32);
329 cbOnLine = 4;
330 }
331 else
332 {
333 fRc = outputPrintf(", 0%08xh", *pu32);
334 cbOnLine += 4;
335 }
336 if (!fRc)
337 return false;
338 pu32++;
339 cb -= 4;
340 }
341 return outputPrintf("\n");
342}
343
344
345static bool disStringData(uint32_t uFlatAddr, uint32_t cb)
346{
347 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
348 uint32_t cchOnLine = 0;
349 while (cb > 0)
350 {
351 /* Line endings and beginnings. */
352 if (cchOnLine >= 72)
353 {
354 if (!outputPrintf("\n"))
355 return false;
356 cchOnLine = 0;
357 }
358 if ( !cchOnLine
359 && !outputPrintf(" db "))
360 return false;
361
362 /* See how many printable character we've got. */
363 uint32_t cchPrintable = 0;
364 while ( cchPrintable < cb
365 && RT_C_IS_PRINT(pb[cchPrintable])
366 && pb[cchPrintable] != '\'')
367 cchPrintable++;
368
369 bool fRc = true;
370 if (cchPrintable)
371 {
372 if (cchPrintable + cchOnLine > 72)
373 cchPrintable = 72 - cchOnLine;
374 if (cchOnLine)
375 {
376 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
377 cchOnLine += 4 + cchPrintable;
378 }
379 else
380 {
381 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
382 cchOnLine += 2 + cchPrintable;
383 }
384 pb += cchPrintable;
385 cb -= cchPrintable;
386 }
387 else
388 {
389 if (cchOnLine)
390 {
391 fRc = outputPrintf(", 0%02xh", *pb);
392 cchOnLine += 6;
393 }
394 else
395 {
396 fRc = outputPrintf("0%02xh", *pb);
397 cchOnLine += 4;
398 }
399 pb++;
400 cb--;
401 }
402 if (!fRc)
403 return false;
404 }
405 return outputPrintf("\n");
406}
407
408
409/**
410 * For dumping a portion of a string table.
411 *
412 * @returns @c true on success, @c false on failure.
413 * @param uFlatAddr The start address.
414 * @param cb The size of the string table.
415 */
416static bool disStringsData(uint32_t uFlatAddr, uint32_t cb)
417{
418 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
419 uint32_t cchOnLine = 0;
420 uint8_t bPrev = 255;
421 while (cb > 0)
422 {
423 /* Line endings and beginnings. */
424 if ( cchOnLine >= 72
425 || (bPrev == '\0' && *pb != '\0'))
426 {
427 if (!outputPrintf("\n"))
428 return false;
429 cchOnLine = 0;
430 }
431 if ( !cchOnLine
432 && !outputPrintf(" db "))
433 return false;
434
435 /* See how many printable character we've got. */
436 uint32_t cchPrintable = 0;
437 while ( cchPrintable < cb
438 && RT_C_IS_PRINT(pb[cchPrintable])
439 && pb[cchPrintable] != '\'')
440 cchPrintable++;
441
442 bool fRc = true;
443 if (cchPrintable)
444 {
445 if (cchPrintable + cchOnLine > 72)
446 cchPrintable = 72 - cchOnLine;
447 if (cchOnLine)
448 {
449 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
450 cchOnLine += 4 + cchPrintable;
451 }
452 else
453 {
454 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
455 cchOnLine += 2 + cchPrintable;
456 }
457 pb += cchPrintable;
458 cb -= cchPrintable;
459 }
460 else
461 {
462 if (cchOnLine)
463 {
464 fRc = outputPrintf(", 0%02xh", *pb);
465 cchOnLine += 6;
466 }
467 else
468 {
469 fRc = outputPrintf("0%02xh", *pb);
470 cchOnLine += 4;
471 }
472 pb++;
473 cb--;
474 }
475 if (!fRc)
476 return false;
477 bPrev = pb[-1];
478 }
479 return outputPrintf("\n");
480}
481
482
483/**
484 * Minds the gap between two segments.
485 *
486 * Gaps should generally be zero filled.
487 *
488 * @returns @c true on success, @c false on failure.
489 * @param uFlatAddr The address of the gap.
490 * @param cbPadding The size of the gap.
491 */
492static bool disCopySegmentGap(uint32_t uFlatAddr, uint32_t cbPadding)
493{
494 if (g_cVerbose > 0)
495 outputPrintf("\n"
496 " ; Padding %#x bytes at %#x\n", cbPadding, uFlatAddr);
497 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
498 if (!ASMMemIsAll8(pb, cbPadding, 0))
499 return outputPrintf(" times %u db 0\n", cbPadding);
500
501 return disByteData(uFlatAddr, cbPadding);
502}
503
504
505/**
506 * Worker for disGetNextSymbol that only does the looking up, no RTDBSYMBOL::cb
507 * calc.
508 *
509 * @param uFlatAddr The address to start searching at.
510 * @param cbMax The size of the search range.
511 * @param poff Where to return the offset between the symbol
512 * and @a uFlatAddr.
513 * @param pSym Where to return the symbol data.
514 */
515static void disGetNextSymbolWorker(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
516{
517 RTINTPTR off = 0;
518 int rc = RTDbgModSymbolByAddr(g_hMapMod, RTDBGSEGIDX_RVA, uFlatAddr, RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL, &off, pSym);
519 if (RT_SUCCESS(rc))
520 {
521 /* negative offset, indicates beyond. */
522 if (off <= 0)
523 {
524 *poff = (uint32_t)-off;
525 return;
526 }
527
528 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> off=%RTptr cb=%RTptr uValue=%RTptr '%s'\n",
529 uFlatAddr, off, pSym->cb, pSym->Value, pSym->szName);
530 }
531 else if (rc != VERR_SYMBOL_NOT_FOUND)
532 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> %Rrc\n", uFlatAddr, rc);
533
534 RTStrPrintf(pSym->szName, sizeof(pSym->szName), "_dummy_addr_%#x", uFlatAddr + cbMax);
535 pSym->Value = uFlatAddr + cbMax;
536 pSym->cb = 0;
537 pSym->offSeg = uFlatAddr + cbMax;
538 pSym->iSeg = RTDBGSEGIDX_RVA;
539 pSym->iOrdinal = 0;
540 pSym->fFlags = 0;
541 *poff = cbMax;
542}
543
544
545/**
546 * Gets the symbol at or after the given address.
547 *
548 * If there are no symbols in the specified range, @a pSym and @a poff will be
549 * set up to indicate a symbol at the first byte after the range.
550 *
551 * @param uFlatAddr The address to start searching at.
552 * @param cbMax The size of the search range.
553 * @param poff Where to return the offset between the symbol
554 * and @a uFlatAddr.
555 * @param pSym Where to return the symbol data.
556 */
557static void disGetNextSymbol(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
558{
559 disGetNextSymbolWorker(uFlatAddr, cbMax, poff, pSym);
560 if ( *poff < cbMax
561 && pSym->cb == 0)
562 {
563 if (*poff + 1 < cbMax)
564 {
565 uint32_t off2;
566 RTDBGSYMBOL Sym2;
567 disGetNextSymbolWorker(uFlatAddr + *poff + 1, cbMax - *poff - 1, &off2, &Sym2);
568 pSym->cb = off2 + 1;
569 }
570 else
571 pSym->cb = 1;
572 }
573 if (pSym->cb > cbMax - *poff)
574 pSym->cb = cbMax - *poff;
575
576 if (g_cVerbose > 1)
577 outputPrintf(" ; disGetNextSymbol %#x LB %#x -> off=%#x cb=%RTptr uValue=%RTptr '%s'\n",
578 uFlatAddr, cbMax, *poff, pSym->cb, pSym->Value, pSym->szName);
579
580}
581
582
583/**
584 * For dealing with the const segment (string constants).
585 *
586 * @returns @c true on success, @c false on failure.
587 * @param iSeg The segment.
588 */
589static bool disConstSegment(uint32_t iSeg)
590{
591 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
592 uint32_t cb = g_aSegs[iSeg].cb;
593
594 while (cb > 0)
595 {
596 uint32_t off;
597 RTDBGSYMBOL Sym;
598 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
599
600 if (off > 0)
601 {
602 if (!disStringsData(uFlatAddr, off))
603 return false;
604 cb -= off;
605 uFlatAddr += off;
606 off = 0;
607 if (!cb)
608 break;
609 }
610
611 bool fRc;
612 if (off == 0)
613 {
614 size_t cchName = strlen(Sym.szName);
615 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
616 if (!fRc)
617 return false;
618 fRc = disStringsData(uFlatAddr, Sym.cb);
619 uFlatAddr += Sym.cb;
620 cb -= Sym.cb;
621 }
622 else
623 {
624 fRc = disStringsData(uFlatAddr, Sym.cb);
625 uFlatAddr += cb;
626 cb = 0;
627 }
628 if (!fRc)
629 return false;
630 }
631
632 return true;
633}
634
635
636
637static bool disDataSegment(uint32_t iSeg)
638{
639 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
640 uint32_t cb = g_aSegs[iSeg].cb;
641
642 while (cb > 0)
643 {
644 uint32_t off;
645 RTDBGSYMBOL Sym;
646 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
647
648 if (off > 0)
649 {
650 if (!disByteData(uFlatAddr, off))
651 return false;
652 cb -= off;
653 uFlatAddr += off;
654 off = 0;
655 if (!cb)
656 break;
657 }
658
659 bool fRc;
660 if (off == 0)
661 {
662 size_t cchName = strlen(Sym.szName);
663 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
664 if (!fRc)
665 return false;
666
667 if (Sym.cb == 2)
668 fRc = disWordData(uFlatAddr, 2);
669 //else if (Sym.cb == 4 && disIsFarBiosAddr(uFlatAddr))
670 // fRc = disDWordData(uFlatAddr, 4);
671 else if (Sym.cb == 4)
672 fRc = disDWordData(uFlatAddr, 4);
673 else if (disIsString(uFlatAddr, Sym.cb))
674 fRc = disStringData(uFlatAddr, Sym.cb);
675 else
676 fRc = disByteData(uFlatAddr, Sym.cb);
677
678 uFlatAddr += Sym.cb;
679 cb -= Sym.cb;
680 }
681 else
682 {
683 fRc = disByteData(uFlatAddr, cb);
684 uFlatAddr += cb;
685 cb = 0;
686 }
687 if (!fRc)
688 return false;
689 }
690
691 return true;
692}
693
694
695static bool disIsCodeAndAdjustSize(uint32_t uFlatAddr, PRTDBGSYMBOL pSym, PBIOSSEG pSeg)
696{
697 if (!strcmp(pSeg->szName, "BIOSSEG"))
698 {
699 if ( !strcmp(pSym->szName, "rom_fdpt")
700 || !strcmp(pSym->szName, "pmbios_gdt")
701 || !strcmp(pSym->szName, "pmbios_gdt_desc")
702 || !strcmp(pSym->szName, "_pmode_IDT")
703 || !strcmp(pSym->szName, "_rmode_IDT")
704 || !strncmp(pSym->szName, RT_STR_TUPLE("font"))
705 || !strcmp(pSym->szName, "bios_string")
706 || !strcmp(pSym->szName, "vector_table")
707 || !strcmp(pSym->szName, "pci_routing_table_structure")
708 )
709 return false;
710 }
711
712 if (!strcmp(pSym->szName, "cpu_reset"))
713 pSym->cb = RT_MIN(pSym->cb, 5);
714 else if (!strcmp(pSym->szName, "pci_init_end"))
715 pSym->cb = RT_MIN(pSym->cb, 3);
716
717 return true;
718}
719
720
721static bool disIs16BitCode(const char *pszSymbol)
722{
723 return true;
724}
725
726
727/**
728 * Deals with instructions that YASM will assemble differently than WASM/WCC.
729 */
730static size_t disHandleYasmDifferences(PDISCPUSTATE pCpuState, uint32_t uFlatAddr, uint32_t cbInstr,
731 char *pszBuf, size_t cbBuf, size_t cchUsed)
732{
733 bool fDifferent = DISFormatYasmIsOddEncoding(pCpuState);
734 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
735
736 /*
737 * Disassembler bugs.
738 */
739 /** @todo Group 1a and 11 seems to be disassembled incorrectly when
740 * modrm.reg != 0. Those encodings should be invalid AFAICT. */
741
742 if ( ( pCpuState->opcode == 0x8f /* group 1a */
743 || pCpuState->opcode == 0xc7 /* group 11 */
744 || pCpuState->opcode == 0xc6 /* group 11 - not verified */
745 )
746 && pCpuState->ModRM.Bits.Reg != 0)
747 fDifferent = true;
748 /** @todo "TEST Eb,Ib" (f6 0f 08) ends up with no mnemonic as well as
749 * wrong length (2 instead of 3)! */
750 else if ( pCpuState->opcode == 0xf6
751 && pb[1] == 0x0f
752 && pb[2] == 0x08
753 && RT_C_IS_SPACE(*pszBuf) )
754 fDifferent = true;
755 /** @todo "INSB Yb,DX" (6c) ends up with no mnemonic here. */
756 else if (pCpuState->opcode == 0x6c && RT_C_IS_SPACE(*pszBuf))
757 fDifferent = true;
758 /*
759 * Check these out and consider adding them to DISFormatYasmIsOddEncoding.
760 */
761 else if ( pb[0] == 0xf3
762 && pb[1] == 0x66
763 && pb[2] == 0x6d)
764 fDifferent = true; /* rep insd - prefix switched. */
765 else if ( pb[0] == 0xc6
766 && pb[1] == 0xc5
767 && pb[2] == 0xba)
768 fDifferent = true; /* mov ch, 0bah - yasm uses a short sequence: 0xb5 0xba. */
769 /*
770 * Switch table fun (.sym may help):
771 */
772#if 0
773 else if ( pb[0] == 0x64
774 && pb[1] == 0x65
775 && pb[2] == 0x05
776 /*&& pb[3] == 0x61
777 && pb[4] == 0x19*/)
778 fDifferent = true; /* gs add ax, 01961h - both fs and gs prefix. Probably some switch table. */
779 else if ( pb[0] == 0x65
780 && pb[1] == 0x36
781 && pb[2] == 0x65
782 && pb[3] == 0xae)
783 fDifferent = true; /* gs scasb - switch table or smth. */
784 else if ( pb[0] == 0x67
785 && pb[1] == 0xe7
786 /*&& pb[2] == 0x67*/)
787 fDifferent = true; /* out 067h, ax - switch table or smth. */
788#endif
789
790
791 /*
792 * Handle different stuff.
793 */
794 if (fDifferent)
795 {
796 disByteData(uFlatAddr, cbInstr); /* lazy bird. */
797
798 if (cchUsed + 2 < cbBuf)
799 {
800 memmove(pszBuf + 2, pszBuf, cchUsed + 2);
801 cchUsed += 2;
802 }
803
804 pszBuf[0] = ';';
805 pszBuf[1] = ' ';
806 }
807
808 return cchUsed;
809}
810
811
812/**
813 * Disassembler callback for reading opcode bytes.
814 *
815 * @returns VINF_SUCCESS.
816 * @param uFlatAddr The address to read at.
817 * @param pbDst Where to store them.
818 * @param cbToRead How many to read.
819 * @param pvUser Unused.
820 */
821static DECLCALLBACK(int) disReadOpcodeBytes(RTUINTPTR uFlatAddr, uint8_t *pbDst, unsigned cbToRead, void *pvUser)
822{
823 if (uFlatAddr + cbToRead >= VBOX_BIOS_BASE + _64K)
824 {
825 RT_BZERO(pbDst, cbToRead);
826 if (uFlatAddr >= VBOX_BIOS_BASE + _64K)
827 cbToRead = 0;
828 else
829 cbToRead = VBOX_BIOS_BASE + _64K - uFlatAddr;
830 }
831 memcpy(pbDst, &g_pbImg[uFlatAddr - VBOX_BIOS_BASE], cbToRead);
832 NOREF(pvUser);
833 return VINF_SUCCESS;
834}
835
836
837/**
838 * Disassembles code.
839 *
840 * @returns @c true on success, @c false on failure.
841 * @param uFlatAddr The address where the code starts.
842 * @param cb The amount of code to disassemble.
843 * @param fIs16Bit Is is 16-bit (@c true) or 32-bit (@c false).
844 */
845static bool disCode(uint32_t uFlatAddr, uint32_t cb, bool fIs16Bit)
846{
847 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
848
849 while (cb > 0)
850 {
851 /* Trailing zero padding detection. */
852 if ( *pb == '\0'
853 && ASMMemIsAll8(pb, RT_MIN(cb, 8), 0) == NULL)
854 {
855 void *pv = ASMMemIsAll8(pb, cb, 0);
856 uint32_t cbZeros = pv ? (uint32_t)((uint8_t const *)pv - pb) : cb;
857 if (!outputPrintf(" times %#x db 0\n", cbZeros))
858 return false;
859 cb -= cbZeros;
860 pb += cbZeros;
861 uFlatAddr += cbZeros;
862 if ( cb == 2
863 && pb[0] == 'X'
864 && pb[1] == 'M')
865 return disStringData(uFlatAddr, cb);
866 }
867 /* Work arounds for switch tables and such (disas assertions). */
868 else if ( ( pb[0] == 0x11 /* int13_cdemu switch */
869 && pb[1] == 0xda
870 && pb[2] == 0x05
871 && pb[3] == 0xff
872 && pb[4] == 0xff
873 )
874 || 0
875 )
876 return disByteData(uFlatAddr, cb);
877 else
878 {
879 unsigned cbInstr;
880 DISCPUSTATE CpuState;
881 int rc = DISCoreOneEx(uFlatAddr, fIs16Bit ? CPUMODE_16BIT : CPUMODE_32BIT,
882 disReadOpcodeBytes, NULL, &CpuState, &cbInstr);
883 if ( RT_SUCCESS(rc)
884 && cbInstr <= cb)
885 {
886 char szTmp[4096];
887 size_t cch = DISFormatYasmEx(&CpuState, szTmp, sizeof(szTmp),
888 DIS_FMT_FLAGS_STRICT
889 | DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_COMMENT | DIS_FMT_FLAGS_BYTES_SPACED,
890 NULL, NULL);
891 cch = disHandleYasmDifferences(&CpuState, uFlatAddr, cbInstr, szTmp, sizeof(szTmp), cch);
892 Assert(cch < sizeof(szTmp));
893
894 if (g_cVerbose > 1)
895 {
896 while (cch < 72)
897 szTmp[cch++] = ' ';
898 RTStrPrintf(&szTmp[cch], sizeof(szTmp) - cch, "; %#x", uFlatAddr);
899 }
900
901 if (!outputPrintf(" %s\n", szTmp))
902 return false;
903 cb -= cbInstr;
904 pb += cbInstr;
905 uFlatAddr += cbInstr;
906 }
907 else
908 {
909 if (!disByteData(uFlatAddr, 1))
910 return false;
911 cb--;
912 pb++;
913 uFlatAddr++;
914 }
915 }
916 }
917 return true;
918}
919
920
921static bool disCodeSegment(uint32_t iSeg)
922{
923 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
924 uint32_t cb = g_aSegs[iSeg].cb;
925
926 while (cb > 0)
927 {
928 uint32_t off;
929 RTDBGSYMBOL Sym;
930 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
931
932 if (off > 0)
933 {
934 if (!disByteData(uFlatAddr, off))
935 return false;
936 cb -= off;
937 uFlatAddr += off;
938 off = 0;
939 if (!cb)
940 break;
941 }
942
943 bool fRc;
944 if (off == 0)
945 {
946 size_t cchName = strlen(Sym.szName);
947 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
948 if (!fRc)
949 return false;
950
951 if (disIsCodeAndAdjustSize(uFlatAddr, &Sym, &g_aSegs[iSeg]))
952 fRc = disCode(uFlatAddr, Sym.cb, disIs16BitCode(Sym.szName));
953 else
954 fRc = disByteData(uFlatAddr, Sym.cb);
955
956 uFlatAddr += Sym.cb;
957 cb -= Sym.cb;
958 }
959 else
960 {
961 fRc = disByteData(uFlatAddr, cb);
962 uFlatAddr += cb;
963 cb = 0;
964 }
965 if (!fRc)
966 return false;
967 }
968
969 return true;
970}
971
972
973static RTEXITCODE DisassembleBiosImage(void)
974{
975 if (!outputPrintf(""))
976 return RTEXITCODE_FAILURE;
977
978 /*
979 * Work the image segment by segment.
980 */
981 bool fRc = true;
982 uint32_t uFlatAddr = VBOX_BIOS_BASE;
983 for (uint32_t iSeg = 0; iSeg < g_cSegs && fRc; iSeg++)
984 {
985 /* Is there a gap between the segments? */
986 if (uFlatAddr < g_aSegs[iSeg].uFlatAddr)
987 {
988 fRc = disCopySegmentGap(uFlatAddr, g_aSegs[iSeg].uFlatAddr - uFlatAddr);
989 if (!fRc)
990 break;
991 uFlatAddr = g_aSegs[iSeg].uFlatAddr;
992 }
993 else if (uFlatAddr > g_aSegs[iSeg].uFlatAddr)
994 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Overlapping segments: %u and %u; uFlatAddr=%#x\n", iSeg - 1, iSeg, uFlatAddr);
995
996 /* Disassemble the segment. */
997 fRc = outputPrintf("\n"
998 "section %s progbits vstart=%#x align=1 ; size=%#x class=%s group=%s\n",
999 g_aSegs[iSeg].szName, g_aSegs[iSeg].uFlatAddr - VBOX_BIOS_BASE,
1000 g_aSegs[iSeg].cb, g_aSegs[iSeg].szClass, g_aSegs[iSeg].szGroup);
1001 if (!fRc)
1002 return RTEXITCODE_FAILURE;
1003 if (!strcmp(g_aSegs[iSeg].szName, "CONST"))
1004 fRc = disConstSegment(iSeg);
1005 else if (!strcmp(g_aSegs[iSeg].szClass, "DATA"))
1006 fRc = disDataSegment(iSeg);
1007 else
1008 fRc = disCodeSegment(iSeg);
1009
1010 /* Advance. */
1011 uFlatAddr += g_aSegs[iSeg].cb;
1012 }
1013
1014 /* Final gap. */
1015 if (uFlatAddr < VBOX_BIOS_BASE + _64K)
1016 fRc = disCopySegmentGap(uFlatAddr, VBOX_BIOS_BASE + _64K - uFlatAddr);
1017 else if (uFlatAddr > VBOX_BIOS_BASE + _64K)
1018 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Last segment spills beyond 1MB; uFlatAddr=%#x\n", uFlatAddr);
1019
1020 if (!fRc)
1021 return RTEXITCODE_FAILURE;
1022 return RTEXITCODE_SUCCESS;
1023}
1024
1025
1026
1027/**
1028 * Parses the symbol file for the BIOS.
1029 *
1030 * This is in ELF/DWARF format.
1031 *
1032 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1033 * @param pszBiosSym Path to the sym file.
1034 */
1035static RTEXITCODE ParseSymFile(const char *pszBiosSym)
1036{
1037#if 1
1038 /** @todo use RTDbg* later. (Just checking for existance currently.) */
1039 PRTSTREAM hStrm;
1040 int rc = RTStrmOpen(pszBiosSym, "rb", &hStrm);
1041 if (RT_FAILURE(rc))
1042 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosSym, rc);
1043 RTStrmClose(hStrm);
1044#else
1045 RTDBGMOD hDbgMod;
1046 int rc = RTDbgModCreateFromImage(&hDbgMod, pszBiosSym, "VBoxBios", 0 /*fFlags*/);
1047 RTMsgInfo("RTDbgModCreateFromImage -> %Rrc\n", rc);
1048#endif
1049 return RTEXITCODE_SUCCESS;
1050}
1051
1052
1053/**
1054 * Reads a line from the file.
1055 *
1056 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1057 * @param pMap The map file handle.
1058 */
1059static bool mapReadLine(PBIOSMAP pMap)
1060{
1061 int rc = RTStrmGetLine(pMap->hStrm, pMap->szLine, sizeof(pMap->szLine));
1062 if (RT_FAILURE(rc))
1063 {
1064 if (rc == VERR_EOF)
1065 {
1066 pMap->fEof = true;
1067 pMap->cch = 0;
1068 pMap->offNW = 0;
1069 pMap->szLine[0] = '\0';
1070 }
1071 else
1072 RTMsgError("%s:%d: Read error %Rrc", pMap->pszMapFile, pMap->iLine + 1, rc);
1073 return false;
1074 }
1075 pMap->iLine++;
1076 pMap->cch = (uint32_t)strlen(pMap->szLine);
1077
1078 /* Check out leading white space. */
1079 if (!RT_C_IS_SPACE(pMap->szLine[0]))
1080 pMap->offNW = 0;
1081 else
1082 {
1083 uint32_t off = 1;
1084 while (RT_C_IS_SPACE(pMap->szLine[off]))
1085 off++;
1086 pMap->offNW = off;
1087 }
1088
1089 return true;
1090}
1091
1092
1093/**
1094 * Checks if it is an empty line.
1095 * @returns @c true if empty, @c false if not.
1096 * @param pMap The map file handle.
1097 */
1098static bool mapIsEmptyLine(PBIOSMAP pMap)
1099{
1100 Assert(pMap->offNW <= pMap->cch);
1101 return pMap->offNW == pMap->cch;
1102}
1103
1104
1105/**
1106 * Reads ahead in the map file until a non-empty line or EOF is encountered.
1107 *
1108 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1109 * @param pMap The map file handle.
1110 */
1111static bool mapSkipEmptyLines(PBIOSMAP pMap)
1112{
1113 for (;;)
1114 {
1115 if (!mapReadLine(pMap))
1116 return false;
1117 if (pMap->offNW < pMap->cch)
1118 return true;
1119 }
1120}
1121
1122
1123/**
1124 * Reads ahead in the map file until an empty line or EOF is encountered.
1125 *
1126 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1127 * @param pMap The map file handle.
1128 */
1129static bool mapSkipNonEmptyLines(PBIOSMAP pMap)
1130{
1131 for (;;)
1132 {
1133 if (!mapReadLine(pMap))
1134 return false;
1135 if (pMap->offNW == pMap->cch)
1136 return true;
1137 }
1138}
1139
1140
1141/**
1142 * Strips the current line.
1143 *
1144 * The string length may change.
1145 *
1146 * @returns Pointer to the first non-space character.
1147 * @param pMap The map file handle.
1148 * @param pcch Where to return the length of the unstripped
1149 * part. Optional.
1150 */
1151static char *mapStripCurrentLine(PBIOSMAP pMap, size_t *pcch)
1152{
1153 char *psz = &pMap->szLine[pMap->offNW];
1154 char *pszEnd = &pMap->szLine[pMap->cch];
1155 while ( (uintptr_t)pszEnd > (uintptr_t)psz
1156 && RT_C_IS_SPACE(pszEnd[-1]))
1157 {
1158 *--pszEnd = '\0';
1159 pMap->cch--;
1160 }
1161 if (pcch)
1162 *pcch = pszEnd - psz;
1163 return psz;
1164}
1165
1166
1167/**
1168 * Reads a line from the file and right strips it.
1169 *
1170 * @returns Pointer to szLine on success, @c NULL + msg on failure, @c NULL on
1171 * EOF.
1172 * @param pMap The map file handle.
1173 * @param pcch Where to return the length of the unstripped
1174 * part. Optional.
1175 */
1176static char *mapReadLineStripRight(PBIOSMAP pMap, size_t *pcch)
1177{
1178 if (!mapReadLine(pMap))
1179 return NULL;
1180 mapStripCurrentLine(pMap, NULL);
1181 if (pcch)
1182 *pcch = pMap->cch;
1183 return pMap->szLine;
1184}
1185
1186
1187/**
1188 * mapReadLine() + mapStripCurrentLine().
1189 *
1190 * @returns Pointer to the first non-space character in the new line. NULL on
1191 * read error (bitched already) or end of file.
1192 * @param pMap The map file handle.
1193 * @param pcch Where to return the length of the unstripped
1194 * part. Optional.
1195 */
1196static char *mapReadLineStrip(PBIOSMAP pMap, size_t *pcch)
1197{
1198 if (!mapReadLine(pMap))
1199 return NULL;
1200 return mapStripCurrentLine(pMap, pcch);
1201}
1202
1203
1204/**
1205 * Parses a word, copying it into the supplied buffer, and skipping any spaces
1206 * following it.
1207 *
1208 * @returns @c true on success, @c false on failure.
1209 * @param ppszCursor Pointer to the cursor variable.
1210 * @param pszBuf The output buffer.
1211 * @param cbBuf The size of the output buffer.
1212 */
1213static bool mapParseWord(char **ppszCursor, char *pszBuf, size_t cbBuf)
1214{
1215 /* Check that we start on a non-blank. */
1216 char *pszStart = *ppszCursor;
1217 if (!*pszStart || RT_C_IS_SPACE(*pszStart))
1218 return false;
1219
1220 /* Find the end of the word. */
1221 char *psz = pszStart + 1;
1222 while (*psz && !RT_C_IS_SPACE(*psz))
1223 psz++;
1224
1225 /* Copy it. */
1226 size_t cchWord = (uintptr_t)psz - (uintptr_t)pszStart;
1227 if (cchWord >= cbBuf)
1228 return false;
1229 memcpy(pszBuf, pszStart, cchWord);
1230 pszBuf[cchWord] = '\0';
1231
1232 /* Skip blanks following it. */
1233 while (RT_C_IS_SPACE(*psz))
1234 psz++;
1235 *ppszCursor = psz;
1236 return true;
1237}
1238
1239
1240/**
1241 * Parses an 16:16 address.
1242 *
1243 * @returns @c true on success, @c false on failure.
1244 * @param ppszCursor Pointer to the cursor variable.
1245 * @param pAddr Where to return the address.
1246 */
1247static bool mapParseAddress(char **ppszCursor, PRTFAR16 pAddr)
1248{
1249 char szWord[32];
1250 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1251 return false;
1252 size_t cchWord = strlen(szWord);
1253
1254 /* An address is at least 16:16 format. It may be 16:32. It may also be flagged. */
1255 size_t cchAddr = 4 + 1 + 4;
1256 if (cchWord < cchAddr)
1257 return false;
1258 if ( !RT_C_IS_XDIGIT(szWord[0])
1259 || !RT_C_IS_XDIGIT(szWord[1])
1260 || !RT_C_IS_XDIGIT(szWord[2])
1261 || !RT_C_IS_XDIGIT(szWord[3])
1262 || szWord[4] != ':'
1263 || !RT_C_IS_XDIGIT(szWord[5])
1264 || !RT_C_IS_XDIGIT(szWord[6])
1265 || !RT_C_IS_XDIGIT(szWord[7])
1266 || !RT_C_IS_XDIGIT(szWord[8])
1267 )
1268 return false;
1269 if ( cchWord > cchAddr
1270 && RT_C_IS_XDIGIT(szWord[9])
1271 && RT_C_IS_XDIGIT(szWord[10])
1272 && RT_C_IS_XDIGIT(szWord[11])
1273 && RT_C_IS_XDIGIT(szWord[12]))
1274 cchAddr += 4;
1275
1276 /* Drop flag if present. */
1277 if (cchWord > cchAddr)
1278 {
1279 if (RT_C_IS_XDIGIT(szWord[4+1+4]))
1280 return false;
1281 szWord[cchAddr] = '\0';
1282 cchWord = cchAddr;
1283 }
1284
1285 /* Convert it. */
1286 szWord[4] = '\0';
1287 int rc1 = RTStrToUInt16Full(szWord, 16, &pAddr->sel);
1288 if (rc1 != VINF_SUCCESS)
1289 return false;
1290
1291 int rc2 = RTStrToUInt16Full(szWord + 5, 16, &pAddr->off);
1292 if (rc2 != VINF_SUCCESS)
1293 return false;
1294 return true;
1295}
1296
1297
1298/**
1299 * Parses a size.
1300 *
1301 * @returns @c true on success, @c false on failure.
1302 * @param ppszCursor Pointer to the cursor variable.
1303 * @param pcb Where to return the size.
1304 */
1305static bool mapParseSize(char **ppszCursor, uint32_t *pcb)
1306{
1307 char szWord[32];
1308 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1309 return false;
1310 size_t cchWord = strlen(szWord);
1311 if (cchWord != 8)
1312 return false;
1313
1314 int rc = RTStrToUInt32Full(szWord, 16, pcb);
1315 if (rc != VINF_SUCCESS)
1316 return false;
1317 return true;
1318}
1319
1320
1321/**
1322 * Parses a section box and the following column header.
1323 *
1324 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1325 * @param pMap Map file handle.
1326 * @param pszSectionNm The expected section name.
1327 * @param cColumns The number of columns.
1328 * @param ... The column names.
1329 */
1330static bool mapSkipThruColumnHeadings(PBIOSMAP pMap, const char *pszSectionNm, uint32_t cColumns, ...)
1331{
1332 if ( mapIsEmptyLine(pMap)
1333 && !mapSkipEmptyLines(pMap))
1334 return false;
1335
1336 /* +------------+ */
1337 size_t cch;
1338 char *psz = mapStripCurrentLine(pMap, &cch);
1339 if (!psz)
1340 return false;
1341
1342 if ( psz[0] != '+'
1343 || psz[1] != '-'
1344 || psz[2] != '-'
1345 || psz[3] != '-'
1346 || psz[cch - 4] != '-'
1347 || psz[cch - 3] != '-'
1348 || psz[cch - 2] != '-'
1349 || psz[cch - 1] != '+'
1350 )
1351 {
1352 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1353 return false;
1354 }
1355
1356 /* | pszSectionNm | */
1357 psz = mapReadLineStrip(pMap, &cch);
1358 if (!psz)
1359 return false;
1360
1361 size_t cchSectionNm = strlen(pszSectionNm);
1362 if ( psz[0] != '|'
1363 || psz[1] != ' '
1364 || psz[2] != ' '
1365 || psz[3] != ' '
1366 || psz[cch - 4] != ' '
1367 || psz[cch - 3] != ' '
1368 || psz[cch - 2] != ' '
1369 || psz[cch - 1] != '|'
1370 || cch != 1 + 3 + cchSectionNm + 3 + 1
1371 || strncmp(&psz[4], pszSectionNm, cchSectionNm)
1372 )
1373 {
1374 RTMsgError("%s:%d: Expected section box: | %s |", pMap->pszMapFile, pMap->iLine, pszSectionNm);
1375 return false;
1376 }
1377
1378 /* +------------+ */
1379 psz = mapReadLineStrip(pMap, &cch);
1380 if (!psz)
1381 return false;
1382 if ( psz[0] != '+'
1383 || psz[1] != '-'
1384 || psz[2] != '-'
1385 || psz[3] != '-'
1386 || psz[cch - 4] != '-'
1387 || psz[cch - 3] != '-'
1388 || psz[cch - 2] != '-'
1389 || psz[cch - 1] != '+'
1390 )
1391 {
1392 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1393 return false;
1394 }
1395
1396 /* There may be a few lines describing the table notation now, surrounded by blank lines. */
1397 do
1398 {
1399 psz = mapReadLineStripRight(pMap, &cch);
1400 if (!psz)
1401 return false;
1402 } while ( *psz == '\0'
1403 || ( !RT_C_IS_SPACE(psz[0])
1404 && RT_C_IS_SPACE(psz[1])
1405 && psz[2] == '='
1406 && RT_C_IS_SPACE(psz[3]))
1407 );
1408
1409 /* Should have the column heading now. */
1410 va_list va;
1411 va_start(va, cColumns);
1412 for (uint32_t i = 0; i < cColumns; i++)
1413 {
1414 const char *pszColumn = va_arg(va, const char *);
1415 size_t cchColumn = strlen(pszColumn);
1416 if ( strncmp(psz, pszColumn, cchColumn)
1417 || ( psz[cchColumn] != '\0'
1418 && !RT_C_IS_SPACE(psz[cchColumn])))
1419 {
1420 va_end(va);
1421 RTMsgError("%s:%d: Expected column '%s' found '%s'", pMap->pszMapFile, pMap->iLine, pszColumn, psz);
1422 return false;
1423 }
1424 psz += cchColumn;
1425 while (RT_C_IS_SPACE(*psz))
1426 psz++;
1427 }
1428 va_end(va);
1429
1430 /* The next line is the underlining. */
1431 psz = mapReadLineStripRight(pMap, &cch);
1432 if (!psz)
1433 return false;
1434 if (*psz != '=' || psz[cch - 1] != '=')
1435 {
1436 RTMsgError("%s:%d: Expected column header underlining", pMap->pszMapFile, pMap->iLine);
1437 return false;
1438 }
1439
1440 /* Skip one blank line. */
1441 psz = mapReadLineStripRight(pMap, &cch);
1442 if (!psz)
1443 return false;
1444 if (*psz)
1445 {
1446 RTMsgError("%s:%d: Expected blank line beneath the column headers", pMap->pszMapFile, pMap->iLine);
1447 return false;
1448 }
1449
1450 return true;
1451}
1452
1453
1454/**
1455 * Parses a segment list.
1456 *
1457 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1458 * @param pMap The map file handle.
1459 */
1460static bool mapParseSegments(PBIOSMAP pMap)
1461{
1462 for (;;)
1463 {
1464 if (!mapReadLineStripRight(pMap, NULL))
1465 return false;
1466
1467 /* The end? The line should be empty. Expectes segment name to not
1468 start with a space. */
1469 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1470 {
1471 if (!pMap->szLine[0])
1472 return true;
1473 RTMsgError("%s:%u: Malformed segment line", pMap->pszMapFile, pMap->iLine);
1474 return false;
1475 }
1476
1477 /* Parse the segment line. */
1478 uint32_t iSeg = g_cSegs;
1479 if (iSeg >= RT_ELEMENTS(g_aSegs))
1480 {
1481 RTMsgError("%s:%u: Too many segments", pMap->pszMapFile, pMap->iLine);
1482 return false;
1483 }
1484
1485 char *psz = pMap->szLine;
1486 if (!mapParseWord(&psz, g_aSegs[iSeg].szName, sizeof(g_aSegs[iSeg].szName)))
1487 RTMsgError("%s:%u: Segment name parser error", pMap->pszMapFile, pMap->iLine);
1488 else if (!mapParseWord(&psz, g_aSegs[iSeg].szClass, sizeof(g_aSegs[iSeg].szClass)))
1489 RTMsgError("%s:%u: Segment class parser error", pMap->pszMapFile, pMap->iLine);
1490 else if (!mapParseWord(&psz, g_aSegs[iSeg].szGroup, sizeof(g_aSegs[iSeg].szGroup)))
1491 RTMsgError("%s:%u: Segment group parser error", pMap->pszMapFile, pMap->iLine);
1492 else if (!mapParseAddress(&psz, &g_aSegs[iSeg].Address))
1493 RTMsgError("%s:%u: Segment address parser error", pMap->pszMapFile, pMap->iLine);
1494 else if (!mapParseSize(&psz, &g_aSegs[iSeg].cb))
1495 RTMsgError("%s:%u: Segment size parser error", pMap->pszMapFile, pMap->iLine);
1496 else
1497 {
1498 g_aSegs[iSeg].uFlatAddr = ((uint32_t)g_aSegs[iSeg].Address.sel << 4) + g_aSegs[iSeg].Address.off;
1499 g_cSegs++;
1500 if (g_cVerbose > 2)
1501 RTStrmPrintf(g_pStdErr, "read segment at %08x / %04x:%04x LB %04x %s / %s / %s\n",
1502 g_aSegs[iSeg].uFlatAddr,
1503 g_aSegs[iSeg].Address.sel,
1504 g_aSegs[iSeg].Address.off,
1505 g_aSegs[iSeg].cb,
1506 g_aSegs[iSeg].szName,
1507 g_aSegs[iSeg].szClass,
1508 g_aSegs[iSeg].szGroup);
1509
1510 while (RT_C_IS_SPACE(*psz))
1511 psz++;
1512 if (!*psz)
1513 continue;
1514 RTMsgError("%s:%u: Junk at end of line", pMap->pszMapFile, pMap->iLine);
1515 }
1516 return false;
1517 }
1518}
1519
1520
1521/**
1522 * Sorts the segment array by flat address and adds them to the debug module.
1523 *
1524 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1525 */
1526static bool mapSortAndAddSegments(void)
1527{
1528 for (uint32_t i = 0; i < g_cSegs; i++)
1529 {
1530 for (uint32_t j = i + 1; j < g_cSegs; j++)
1531 if (g_aSegs[j].uFlatAddr < g_aSegs[i].uFlatAddr)
1532 {
1533 BIOSSEG Tmp = g_aSegs[i];
1534 g_aSegs[i] = g_aSegs[j];
1535 g_aSegs[j] = Tmp;
1536 }
1537 if (g_cVerbose > 0)
1538 RTStrmPrintf(g_pStdErr, "segment at %08x / %04x:%04x LB %04x %s / %s / %s\n",
1539 g_aSegs[i].uFlatAddr,
1540 g_aSegs[i].Address.sel,
1541 g_aSegs[i].Address.off,
1542 g_aSegs[i].cb,
1543 g_aSegs[i].szName,
1544 g_aSegs[i].szClass,
1545 g_aSegs[i].szGroup);
1546
1547 RTDBGSEGIDX idx = i;
1548 int rc = RTDbgModSegmentAdd(g_hMapMod, g_aSegs[i].uFlatAddr, g_aSegs[i].cb, g_aSegs[i].szName, 0 /*fFlags*/, &idx);
1549 if (RT_FAILURE(rc))
1550 {
1551 RTMsgError("RTDbgModSegmentAdd failed on %s: %Rrc", g_aSegs[i].szName);
1552 return false;
1553 }
1554 }
1555 return true;
1556}
1557
1558
1559/**
1560 * Parses a segment list.
1561 *
1562 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1563 * @param pMap The map file handle.
1564 */
1565static bool mapParseSymbols(PBIOSMAP pMap)
1566{
1567 for (;;)
1568 {
1569 if (!mapReadLineStripRight(pMap, NULL))
1570 return false;
1571
1572 /* The end? The line should be empty. Expectes segment name to not
1573 start with a space. */
1574 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1575 {
1576 if (!pMap->szLine[0])
1577 return true;
1578 RTMsgError("%s:%u: Malformed symbol line", pMap->pszMapFile, pMap->iLine);
1579 return false;
1580 }
1581
1582 /* Skip the module name lines for now. */
1583 if (!strncmp(pMap->szLine, RT_STR_TUPLE("Module: ")))
1584 continue;
1585
1586 /* Parse the segment line. */
1587 char szName[4096];
1588 RTFAR16 Addr;
1589 char *psz = pMap->szLine;
1590 if (!mapParseAddress(&psz, &Addr))
1591 RTMsgError("%s:%u: Symbol address parser error", pMap->pszMapFile, pMap->iLine);
1592 else if (!mapParseWord(&psz, szName, sizeof(szName)))
1593 RTMsgError("%s:%u: Symbol name parser error", pMap->pszMapFile, pMap->iLine);
1594 else
1595 {
1596 uint32_t uFlatAddr = ((uint32_t)Addr.sel << 4) + Addr.off;
1597 if (uFlatAddr == 0)
1598 continue;
1599
1600 int rc = RTDbgModSymbolAdd(g_hMapMod, szName, RTDBGSEGIDX_RVA, uFlatAddr, 0 /*cb*/, 0 /*fFlags*/, NULL);
1601 if (RT_SUCCESS(rc) || rc == VERR_DBG_ADDRESS_CONFLICT)
1602 {
1603
1604 if (g_cVerbose > 2)
1605 RTStrmPrintf(g_pStdErr, "read symbol - %08x %s\n", uFlatAddr, szName);
1606 while (RT_C_IS_SPACE(*psz))
1607 psz++;
1608 if (!*psz)
1609 continue;
1610 RTMsgError("%s:%u: Junk at end of line", pMap->pszMapFile, pMap->iLine);
1611 }
1612 else
1613 RTMsgError("%s:%u: RTDbgModSymbolAdd failed: %Rrc", pMap->pszMapFile, pMap->iLine, rc);
1614 }
1615 return false;
1616 }
1617}
1618
1619
1620/**
1621 * Parses the given map file.
1622 *
1623 * @returns RTEXITCODE_SUCCESS and lots of globals, or RTEXITCODE_FAILURE and a
1624 * error message.
1625 * @param pMap The map file handle.
1626 */
1627static RTEXITCODE mapParseFile(PBIOSMAP pMap)
1628{
1629 int rc = RTDbgModCreate(&g_hMapMod, "VBoxBios", 0 /*cbSeg*/, 0 /*fFlags*/);
1630 if (RT_FAILURE(rc))
1631 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreate failed: %Rrc", rc);
1632
1633 /*
1634 * Read the header.
1635 */
1636 if (!mapReadLine(pMap))
1637 return RTEXITCODE_FAILURE;
1638 if (strncmp(pMap->szLine, RT_STR_TUPLE("Open Watcom Linker Version")))
1639 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected map-file header: '%s'", pMap->szLine);
1640 if ( !mapSkipNonEmptyLines(pMap)
1641 || !mapSkipEmptyLines(pMap))
1642 return RTEXITCODE_FAILURE;
1643
1644 /*
1645 * Skip groups.
1646 */
1647 if (!mapSkipThruColumnHeadings(pMap, "Groups", 3, "Group", "Address", "Size", NULL))
1648 return RTEXITCODE_FAILURE;
1649 if (!mapSkipNonEmptyLines(pMap))
1650 return RTEXITCODE_FAILURE;
1651
1652 /*
1653 * Parse segments.
1654 */
1655 if (!mapSkipThruColumnHeadings(pMap, "Segments", 5, "Segment", "Class", "Group", "Address", "Size"))
1656 return RTEXITCODE_FAILURE;
1657 if (!mapParseSegments(pMap))
1658 return RTEXITCODE_FAILURE;
1659 if (!mapSortAndAddSegments())
1660 return RTEXITCODE_FAILURE;
1661
1662 /*
1663 * Parse symbols.
1664 */
1665 if (!mapSkipThruColumnHeadings(pMap, "Memory Map", 2, "Address", "Symbol"))
1666 return RTEXITCODE_FAILURE;
1667 if (!mapParseSymbols(pMap))
1668 return RTEXITCODE_FAILURE;
1669
1670 /* Ignore the rest of the file. */
1671 return RTEXITCODE_SUCCESS;
1672}
1673
1674
1675/**
1676 * Parses the linker map file for the BIOS.
1677 *
1678 * This is generated by the Watcom linker.
1679 *
1680 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1681 * @param pszBiosMap Path to the map file.
1682 */
1683static RTEXITCODE ParseMapFile(const char *pszBiosMap)
1684{
1685 BIOSMAP Map;
1686 Map.pszMapFile = pszBiosMap;
1687 Map.hStrm = NULL;
1688 Map.iLine = 0;
1689 Map.fEof = false;
1690 Map.cch = 0;
1691 Map.offNW = 0;
1692 int rc = RTStrmOpen(pszBiosMap, "r", &Map.hStrm);
1693 if (RT_FAILURE(rc))
1694 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosMap, rc);
1695 RTEXITCODE rcExit = mapParseFile(&Map);
1696 RTStrmClose(Map.hStrm);
1697 return rcExit;
1698}
1699
1700
1701/**
1702 * Reads the BIOS image into memory (g_pbImg and g_cbImg).
1703 *
1704 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1705 * @param pszBiosImg Path to the image file.
1706 */
1707static RTEXITCODE ReadBiosImage(const char *pszBiosImg)
1708{
1709 void *pvImg;
1710 size_t cbImg;
1711 int rc = RTFileReadAll(pszBiosImg, &pvImg, &cbImg);
1712 if (RT_FAILURE(rc))
1713 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading '%s': %Rrc", pszBiosImg, rc);
1714 if (cbImg != _64K)
1715 {
1716 RTFileReadAllFree(pvImg, cbImg);
1717 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The BIOS image %u bytes intead of 64KB", cbImg);
1718 }
1719
1720 g_pbImg = (uint8_t *)pvImg;
1721 g_cbImg = cbImg;
1722 return RTEXITCODE_SUCCESS;
1723}
1724
1725
1726int main(int argc, char **argv)
1727{
1728 int rc = RTR3InitExe(argc, &argv, 0);
1729 if (RT_FAILURE(rc))
1730 return RTMsgInitFailure(rc);
1731
1732 /*
1733 * Option config.
1734 */
1735 static RTGETOPTDEF const s_aOpts[] =
1736 {
1737 { "--bios-image", 'i', RTGETOPT_REQ_STRING },
1738 { "--bios-map", 'm', RTGETOPT_REQ_STRING },
1739 { "--bios-sym", 's', RTGETOPT_REQ_STRING },
1740 { "--output", 'o', RTGETOPT_REQ_STRING },
1741 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1742 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1743 };
1744
1745 const char *pszBiosMap = NULL;
1746 const char *pszBiosSym = NULL;
1747 const char *pszBiosImg = NULL;
1748 const char *pszOutput = NULL;
1749
1750 RTGETOPTUNION ValueUnion;
1751 RTGETOPTSTATE GetOptState;
1752 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1753 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
1754
1755 /*
1756 * Process the options.
1757 */
1758 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1759 {
1760 switch (rc)
1761 {
1762 case 'i':
1763 if (pszBiosImg)
1764 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is given more than once");
1765 pszBiosImg = ValueUnion.psz;
1766 break;
1767
1768 case 'm':
1769 if (pszBiosMap)
1770 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is given more than once");
1771 pszBiosMap = ValueUnion.psz;
1772 break;
1773
1774 case 's':
1775 if (pszBiosSym)
1776 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is given more than once");
1777 pszBiosSym = ValueUnion.psz;
1778 break;
1779
1780 case 'o':
1781 if (pszOutput)
1782 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--output is given more than once");
1783 pszOutput = ValueUnion.psz;
1784 break;
1785
1786 case 'v':
1787 g_cVerbose++;
1788 break;
1789
1790 case 'q':
1791 g_cVerbose = 0;
1792 break;
1793
1794 case 'H':
1795 RTPrintf("usage: %Rbn --bios-image <file.img> --bios-map <file.map> [--output <file.asm>]\n",
1796 argv[0]);
1797 return RTEXITCODE_SUCCESS;
1798
1799 case 'V':
1800 {
1801 /* The following is assuming that svn does it's job here. */
1802 RTPrintf("r%u\n", RTBldCfgRevision());
1803 return RTEXITCODE_SUCCESS;
1804 }
1805
1806 default:
1807 return RTGetOptPrintError(rc, &ValueUnion);
1808 }
1809 }
1810
1811 /*
1812 * Got it all?
1813 */
1814 if (!pszBiosImg)
1815 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is required");
1816 if (!pszBiosMap)
1817 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is required");
1818 if (!pszBiosSym)
1819 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is required");
1820
1821 /*
1822 * Do the job.
1823 */
1824 RTEXITCODE rcExit;
1825 rcExit = ReadBiosImage(pszBiosImg);
1826 if (rcExit == RTEXITCODE_SUCCESS)
1827 rcExit = ParseMapFile(pszBiosMap);
1828 if (rcExit == RTEXITCODE_SUCCESS)
1829 rcExit = ParseSymFile(pszBiosSym);
1830 if (rcExit == RTEXITCODE_SUCCESS)
1831 rcExit = OpenOutputFile(pszOutput);
1832 if (rcExit == RTEXITCODE_SUCCESS)
1833 rcExit = DisassembleBiosImage();
1834
1835 return rcExit;
1836}
1837
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