VirtualBox

source: vbox/trunk/src/VBox/Devices/BiosCommonCode/MakeAlternativeSource.cpp@ 76077

Last change on this file since 76077 was 74982, checked in by vboxsync, 6 years ago

MakeAlternativeSource: Use codeview debug info to avoid jump tables generated by the compiler. Increased verbosity to include address, source file and line numbers in the output. Don't allow address override prefix when no operands accessing memory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.8 KB
Line 
1/* $Id: MakeAlternativeSource.cpp 74982 2018-10-22 19:53:20Z vboxsync $ */
2/** @file
3 * MakeAlternative - Generate an Alternative BIOS Source that requires less tools.
4 */
5
6/*
7 * Copyright (C) 2012-2017 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/list.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/string.h>
33#include <iprt/stream.h>
34#include <iprt/x86.h>
35
36#include <VBox/dis.h>
37
38
39/*********************************************************************************************************************************
40* Structures and Typedefs *
41*********************************************************************************************************************************/
42/**
43 * A BIOS segment.
44 */
45typedef struct BIOSSEG
46{
47 char szName[32];
48 char szClass[32];
49 char szGroup[32];
50 RTFAR16 Address;
51 uint32_t uFlatAddr;
52 uint32_t cb;
53 /** RVA into g_hSymMod. */
54 uint32_t uRva;
55} BIOSSEG;
56/** Pointer to a BIOS segment. */
57typedef BIOSSEG *PBIOSSEG;
58
59
60/**
61 * A BIOS object file.
62 */
63typedef struct BIOSOBJFILE
64{
65 RTLISTNODE Node;
66 char *pszSource;
67 char *pszObject;
68} BIOSOBJFILE;
69/** A BIOS object file. */
70typedef BIOSOBJFILE *PBIOSOBJFILE;
71
72
73/**
74 * Pointer to a BIOS map parser handle.
75 */
76typedef struct BIOSMAP
77{
78 /** The stream pointer. */
79 PRTSTREAM hStrm;
80 /** The file name. */
81 const char *pszMapFile;
82 /** Set when EOF has been reached. */
83 bool fEof;
84 /** The current line number (0 based).*/
85 uint32_t iLine;
86 /** The length of the current line. */
87 uint32_t cch;
88 /** The offset of the first non-white character on the line. */
89 uint32_t offNW;
90 /** The line buffer. */
91 char szLine[16384];
92} BIOSMAP;
93/** Pointer to a BIOS map parser handle. */
94typedef BIOSMAP *PBIOSMAP;
95
96
97/*********************************************************************************************************************************
98* Global Variables *
99*********************************************************************************************************************************/
100/** The verbosity level.*/
101static unsigned g_cVerbose = 2 /*0*/;
102/** Pointer to the BIOS image. */
103static uint8_t const *g_pbImg;
104/** The size of the BIOS image. */
105static size_t g_cbImg;
106
107/** Debug module for the map file. */
108static RTDBGMOD g_hMapMod = NIL_RTDBGMOD;
109/** The number of BIOS segments found in the map file. */
110static uint32_t g_cSegs = 0;
111/** Array of BIOS segments from the map file. */
112static BIOSSEG g_aSegs[32];
113/** List of BIOSOBJFILE. */
114static RTLISTANCHOR g_ObjList;
115
116/** Debug module with symbols. */
117static RTDBGMOD g_hSymMod = NIL_RTDBGMOD;
118
119/** The output stream. */
120static PRTSTREAM g_hStrmOutput = NULL;
121
122/** The type of BIOS we're working on. */
123static enum BIOSTYPE
124{
125 kBiosType_System = 0,
126 kBiosType_Vga
127} g_enmBiosType = kBiosType_System;
128/** The flat ROM base address. */
129static uint32_t g_uBiosFlatBase = 0xf0000;
130
131
132static bool outputPrintfV(const char *pszFormat, va_list va)
133{
134 int rc = RTStrmPrintfV(g_hStrmOutput, pszFormat, va);
135 if (RT_FAILURE(rc))
136 {
137 RTMsgError("Output error: %Rrc\n", rc);
138 return false;
139 }
140 return true;
141}
142
143
144static bool outputPrintf(const char *pszFormat, ...)
145{
146 va_list va;
147 va_start(va, pszFormat);
148 bool fRc = outputPrintfV(pszFormat, va);
149 va_end(va);
150 return fRc;
151}
152
153
154/**
155 * Opens the output file for writing.
156 *
157 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
158 * @param pszOutput Path to the output file.
159 */
160static RTEXITCODE OpenOutputFile(const char *pszOutput)
161{
162 if (!pszOutput)
163 g_hStrmOutput = g_pStdOut;
164 else
165 {
166 int rc = RTStrmOpen(pszOutput, "w", &g_hStrmOutput);
167 if (RT_FAILURE(rc))
168 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open output file '%s': %Rrc", pszOutput, rc);
169 }
170 return RTEXITCODE_SUCCESS;
171}
172
173
174/**
175 * Displays a disassembly error and returns @c false.
176 *
177 * @returns @c false.
178 * @param pszFormat The error format string.
179 * @param ... Format argument.
180 */
181static bool disError(const char *pszFormat, ...)
182{
183 va_list va;
184 va_start(va, pszFormat);
185 RTMsgErrorV(pszFormat, va);
186 va_end(va);
187 return false;
188}
189
190
191/**
192 * Output the disassembly file header.
193 *
194 * @returns @c true on success,
195 */
196static bool disFileHeader(void)
197{
198 bool fRc;
199 fRc = outputPrintf("; $Id: MakeAlternativeSource.cpp 74982 2018-10-22 19:53:20Z vboxsync $ \n"
200 ";; @file\n"
201 "; Auto Generated source file. Do not edit.\n"
202 ";\n"
203 );
204 if (!fRc)
205 return fRc;
206
207 /*
208 * List the header of each source file, up to and including the
209 * copyright notice.
210 */
211 bool fNeedLgplDisclaimer = false;
212 PBIOSOBJFILE pObjFile;
213 RTListForEach(&g_ObjList, pObjFile, BIOSOBJFILE, Node)
214 {
215 PRTSTREAM hStrm;
216 int rc = RTStrmOpen(pObjFile->pszSource, "r", &hStrm);
217 if (RT_SUCCESS(rc))
218 {
219 fRc = outputPrintf("\n"
220 ";\n"
221 "; Source file: %Rbn\n"
222 ";\n"
223 , pObjFile->pszSource);
224 uint32_t iLine = 0;
225 bool fSeenCopyright = false;
226 char szLine[4096];
227 while ((rc = RTStrmGetLine(hStrm, szLine, sizeof(szLine))) == VINF_SUCCESS)
228 {
229 iLine++;
230
231 /* Check if we're done. */
232 char *psz = RTStrStrip(szLine);
233 if ( fSeenCopyright
234 && ( (psz[0] == '*' && psz[1] == '/')
235 || psz[0] == '\0') )
236 break;
237
238 /* Strip comment suffix. */
239 size_t cch = strlen(psz);
240 if (cch >= 2 && psz[cch - 1] == '/' && psz[cch - 2] == '*')
241 {
242 psz[cch - 2] = '\0';
243 RTStrStripR(psz);
244 }
245
246 /* Skip line prefix. */
247 if (psz[0] == '/' && psz[1] == '*')
248 psz += 2;
249 else if (psz[0] == '*')
250 psz += 1;
251 else
252 while (*psz == ';')
253 psz++;
254 if (RT_C_IS_SPACE(*psz))
255 psz++;
256
257 /* Skip the doxygen file tag line. */
258 if (!strcmp(psz, "* @file") || !strcmp(psz, "@file"))
259 continue;
260
261 /* Detect copyright section. */
262 if ( !fSeenCopyright
263 && ( strstr(psz, "Copyright")
264 || strstr(psz, "copyright")) )
265 fSeenCopyright = true;
266
267 /* Detect LGPL. */
268 if (strstr(psz, "LGPL"))
269 fNeedLgplDisclaimer = true;
270
271 fRc = outputPrintf("; %s\n", psz) && fRc;
272 }
273
274 RTStrmClose(hStrm);
275 if (rc != VINF_SUCCESS)
276 return disError("Error reading '%s': rc=%Rrc iLine=%u", pObjFile->pszSource, rc, iLine);
277 }
278 }
279
280 /*
281 * Add Oracle LGPL disclaimer.
282 */
283 if (fNeedLgplDisclaimer)
284 outputPrintf("\n"
285 ";\n"
286 "; Oracle LGPL Disclaimer: For the avoidance of doubt, except that if any license choice\n"
287 "; other than GPL or LGPL is available it will apply instead, Oracle elects to use only\n"
288 "; the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where\n"
289 "; a choice of LGPL license versions is made available with the language indicating\n"
290 "; that LGPLv2 or any later version may be used, or where a choice of which version\n"
291 "; of the LGPL is applied is otherwise unspecified.\n"
292 ";\n"
293 "\n");
294
295 /*
296 * Set the org.
297 */
298 fRc = outputPrintf("\n"
299 "\n"
300 "\n"
301 ) && fRc;
302 return fRc;
303}
304
305
306/**
307 * Checks if a byte sequence could be a string litteral.
308 *
309 * @returns @c true if it is, @c false if it isn't.
310 * @param uFlatAddr The address of the byte sequence.
311 * @param cb The length of the sequence.
312 */
313static bool disIsString(uint32_t uFlatAddr, uint32_t cb)
314{
315 if (cb < 6)
316 return false;
317
318 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
319 while (cb > 0)
320 {
321 if ( !RT_C_IS_PRINT(*pb)
322 && *pb != '\r'
323 && *pb != '\n'
324 && *pb != '\t')
325 {
326 if (*pb == '\0')
327 {
328 do
329 {
330 pb++;
331 cb--;
332 } while (cb > 0 && *pb == '\0');
333 return cb == 0;
334 }
335 return false;
336 }
337 pb++;
338 cb--;
339 }
340
341 return true;
342}
343
344
345#if 0 /* unused */
346/**
347 * Checks if a dword could be a far 16:16 BIOS address.
348 *
349 * @returns @c true if it is, @c false if it isn't.
350 * @param uFlatAddr The address of the dword.
351 */
352static bool disIsFarBiosAddr(uint32_t uFlatAddr)
353{
354 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - g_uBiosFlatBase];
355 if (pu16[1] < 0xf000)
356 return false;
357 if (pu16[1] > 0xfff0)
358 return false;
359 uint32_t uFlatAddr2 = (uint32_t)(pu16[1] << 4) | pu16[0];
360 if (uFlatAddr2 >= g_uBiosFlatBase + g_cbImg)
361 return false;
362 return true;
363}
364#endif
365
366
367static bool disByteData(uint32_t uFlatAddr, uint32_t cb)
368{
369 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
370 size_t cbOnLine = 0;
371 while (cb-- > 0)
372 {
373 bool fRc;
374 if (cbOnLine >= 16)
375 {
376 fRc = outputPrintf("\n"
377 " db 0%02xh", *pb);
378 cbOnLine = 1;
379 }
380 else if (!cbOnLine)
381 {
382 fRc = outputPrintf(" db 0%02xh", *pb);
383 cbOnLine = 1;
384 }
385 else
386 {
387 fRc = outputPrintf(", 0%02xh", *pb);
388 cbOnLine++;
389 }
390 if (!fRc)
391 return false;
392 pb++;
393 }
394 return outputPrintf("\n");
395}
396
397
398static bool disWordData(uint32_t uFlatAddr, uint32_t cb)
399{
400 if (cb & 1)
401 return disError("disWordData expects word aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
402
403 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - g_uBiosFlatBase];
404 size_t cbOnLine = 0;
405 while (cb > 0)
406 {
407 bool fRc;
408 if (cbOnLine >= 16)
409 {
410 fRc = outputPrintf("\n"
411 " dw 0%04xh", *pu16);
412 cbOnLine = 2;
413 }
414 else if (!cbOnLine)
415 {
416 fRc = outputPrintf(" dw 0%04xh", *pu16);
417 cbOnLine = 2;
418 }
419 else
420 {
421 fRc = outputPrintf(", 0%04xh", *pu16);
422 cbOnLine += 2;
423 }
424 if (!fRc)
425 return false;
426 pu16++;
427 cb -= 2;
428 }
429 return outputPrintf("\n");
430}
431
432
433static bool disDWordData(uint32_t uFlatAddr, uint32_t cb)
434{
435 if (cb & 3)
436 return disError("disWordData expects dword aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
437
438 uint32_t const *pu32 = (uint32_t const *)&g_pbImg[uFlatAddr - g_uBiosFlatBase];
439 size_t cbOnLine = 0;
440 while (cb > 0)
441 {
442 bool fRc;
443 if (cbOnLine >= 16)
444 {
445 fRc = outputPrintf("\n"
446 " dd 0%08xh", *pu32);
447 cbOnLine = 4;
448 }
449 else if (!cbOnLine)
450 {
451 fRc = outputPrintf(" dd 0%08xh", *pu32);
452 cbOnLine = 4;
453 }
454 else
455 {
456 fRc = outputPrintf(", 0%08xh", *pu32);
457 cbOnLine += 4;
458 }
459 if (!fRc)
460 return false;
461 pu32++;
462 cb -= 4;
463 }
464 return outputPrintf("\n");
465}
466
467
468static bool disStringData(uint32_t uFlatAddr, uint32_t cb)
469{
470 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
471 uint32_t cchOnLine = 0;
472 while (cb > 0)
473 {
474 /* Line endings and beginnings. */
475 if (cchOnLine >= 72)
476 {
477 if (!outputPrintf("\n"))
478 return false;
479 cchOnLine = 0;
480 }
481 if ( !cchOnLine
482 && !outputPrintf(" db "))
483 return false;
484
485 /* See how many printable character we've got. */
486 uint32_t cchPrintable = 0;
487 while ( cchPrintable < cb
488 && RT_C_IS_PRINT(pb[cchPrintable])
489 && pb[cchPrintable] != '\'')
490 cchPrintable++;
491
492 bool fRc = true;
493 if (cchPrintable)
494 {
495 if (cchPrintable + cchOnLine > 72)
496 cchPrintable = 72 - cchOnLine;
497 if (cchOnLine)
498 {
499 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
500 cchOnLine += 4 + cchPrintable;
501 }
502 else
503 {
504 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
505 cchOnLine += 2 + cchPrintable;
506 }
507 pb += cchPrintable;
508 cb -= cchPrintable;
509 }
510 else
511 {
512 if (cchOnLine)
513 {
514 fRc = outputPrintf(", 0%02xh", *pb);
515 cchOnLine += 6;
516 }
517 else
518 {
519 fRc = outputPrintf("0%02xh", *pb);
520 cchOnLine += 4;
521 }
522 pb++;
523 cb--;
524 }
525 if (!fRc)
526 return false;
527 }
528 return outputPrintf("\n");
529}
530
531
532/**
533 * For dumping a portion of a string table.
534 *
535 * @returns @c true on success, @c false on failure.
536 * @param uFlatAddr The start address.
537 * @param cb The size of the string table.
538 */
539static bool disStringsData(uint32_t uFlatAddr, uint32_t cb)
540{
541 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
542 uint32_t cchOnLine = 0;
543 uint8_t bPrev = 255;
544 while (cb > 0)
545 {
546 /* Line endings and beginnings. */
547 if ( cchOnLine >= 72
548 || (bPrev == '\0' && *pb != '\0'))
549 {
550 if (!outputPrintf("\n"))
551 return false;
552 cchOnLine = 0;
553 }
554 if ( !cchOnLine
555 && !outputPrintf(" db "))
556 return false;
557
558 /* See how many printable character we've got. */
559 uint32_t cchPrintable = 0;
560 while ( cchPrintable < cb
561 && RT_C_IS_PRINT(pb[cchPrintable])
562 && pb[cchPrintable] != '\'')
563 cchPrintable++;
564
565 bool fRc = true;
566 if (cchPrintable)
567 {
568 if (cchPrintable + cchOnLine > 72)
569 cchPrintable = 72 - cchOnLine;
570 if (cchOnLine)
571 {
572 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
573 cchOnLine += 4 + cchPrintable;
574 }
575 else
576 {
577 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
578 cchOnLine += 2 + cchPrintable;
579 }
580 pb += cchPrintable;
581 cb -= cchPrintable;
582 }
583 else
584 {
585 if (cchOnLine)
586 {
587 fRc = outputPrintf(", 0%02xh", *pb);
588 cchOnLine += 6;
589 }
590 else
591 {
592 fRc = outputPrintf("0%02xh", *pb);
593 cchOnLine += 4;
594 }
595 pb++;
596 cb--;
597 }
598 if (!fRc)
599 return false;
600 bPrev = pb[-1];
601 }
602 return outputPrintf("\n");
603}
604
605
606/**
607 * Minds the gap between two segments.
608 *
609 * Gaps should generally be zero filled.
610 *
611 * @returns @c true on success, @c false on failure.
612 * @param uFlatAddr The address of the gap.
613 * @param cbPadding The size of the gap.
614 */
615static bool disCopySegmentGap(uint32_t uFlatAddr, uint32_t cbPadding)
616{
617 if (g_cVerbose > 0)
618 outputPrintf("\n"
619 " ; Padding %#x bytes at %#x\n", cbPadding, uFlatAddr);
620 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
621 if (ASMMemIsZero(pb, cbPadding))
622 return outputPrintf(" times %u db 0\n", cbPadding);
623
624 return disByteData(uFlatAddr, cbPadding);
625}
626
627
628/**
629 * Worker for disGetNextSymbol that only does the looking up, no RTDBSYMBOL::cb
630 * calc.
631 *
632 * @param uFlatAddr The address to start searching at.
633 * @param cbMax The size of the search range.
634 * @param poff Where to return the offset between the symbol
635 * and @a uFlatAddr.
636 * @param pSym Where to return the symbol data.
637 */
638static void disGetNextSymbolWorker(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
639{
640 RTINTPTR offMap = RTINTPTR_MAX;
641 RTDBGSYMBOL MapSym;
642 int rcMap = RTDbgModSymbolByAddr(g_hMapMod, RTDBGSEGIDX_RVA, uFlatAddr, RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL, &offMap, &MapSym);
643
644 RTINTPTR off = RTINTPTR_MAX;
645 int rc = RTDbgModSymbolByAddr(g_hSymMod, RTDBGSEGIDX_RVA, uFlatAddr - g_uBiosFlatBase,
646 RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL, &off, pSym);
647 if ( RT_SUCCESS(rc)
648 && RT_ABS(off) <= RT_ABS(offMap))
649 pSym->Value += g_uBiosFlatBase;
650 else
651 {
652 *pSym = MapSym;
653 off = offMap;
654 rc = rcMap;
655 }
656 if (RT_SUCCESS(rc))
657 {
658 /* negative offset, indicates beyond. */
659 if (off <= 0)
660 {
661 *poff = (uint32_t)-off;
662
663 /* Mangle symbols the assembler might confuse with instructions. */
664 size_t cchName = strlen(pSym->szName);
665 if ( cchName <= 4
666 && ( strcmp("wait", pSym->szName) == 0
667 || strcmp("hlt", pSym->szName) == 0))
668 {
669 memmove(&pSym->szName[1], &pSym->szName[0], cchName);
670 pSym->szName[0] = '_';
671 pSym->szName[cchName + 1] = '_';
672 pSym->szName[cchName + 2] = '\0';
673 }
674 return;
675 }
676
677 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> off=%RTptr cb=%RTptr uValue=%RTptr '%s'\n",
678 uFlatAddr, off, pSym->cb, pSym->Value, pSym->szName);
679 }
680 else if (rc != VERR_SYMBOL_NOT_FOUND)
681 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> %Rrc\n", uFlatAddr, rc);
682
683 RTStrPrintf(pSym->szName, sizeof(pSym->szName), "_dummy_addr_%#x", uFlatAddr + cbMax);
684 pSym->Value = uFlatAddr + cbMax;
685 pSym->cb = 0;
686 pSym->offSeg = uFlatAddr + cbMax;
687 pSym->iSeg = RTDBGSEGIDX_RVA;
688 pSym->iOrdinal = 0;
689 pSym->fFlags = 0;
690 *poff = cbMax;
691}
692
693
694/**
695 * Gets the symbol at or after the given address.
696 *
697 * If there are no symbols in the specified range, @a pSym and @a poff will be
698 * set up to indicate a symbol at the first byte after the range.
699 *
700 * @param uFlatAddr The address to start searching at.
701 * @param cbMax The size of the search range.
702 * @param poff Where to return the offset between the symbol
703 * and @a uFlatAddr.
704 * @param pSym Where to return the symbol data.
705 */
706static void disGetNextSymbol(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
707{
708 disGetNextSymbolWorker(uFlatAddr, cbMax, poff, pSym);
709 if ( *poff < cbMax
710 && pSym->cb == 0)
711 {
712 if (*poff + 1 < cbMax)
713 {
714 uint32_t off2;
715 RTDBGSYMBOL Sym2;
716 disGetNextSymbolWorker(uFlatAddr + *poff + 1, cbMax - *poff - 1, &off2, &Sym2);
717 pSym->cb = off2 + 1;
718 }
719 else
720 pSym->cb = 1;
721 }
722 if (pSym->cb > cbMax - *poff)
723 pSym->cb = cbMax - *poff;
724
725 if (g_cVerbose > 1)
726 outputPrintf(" ; disGetNextSymbol %#x LB %#x -> off=%#x cb=%RTptr uValue=%RTptr '%s'\n",
727 uFlatAddr, cbMax, *poff, pSym->cb, pSym->Value, pSym->szName);
728
729}
730
731
732/**
733 * For dealing with the const segment (string constants).
734 *
735 * @returns @c true on success, @c false on failure.
736 * @param iSeg The segment.
737 */
738static bool disConstSegment(uint32_t iSeg)
739{
740 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
741 uint32_t cb = g_aSegs[iSeg].cb;
742
743 while (cb > 0)
744 {
745 uint32_t off;
746 RTDBGSYMBOL Sym;
747 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
748
749 if (off > 0)
750 {
751 if (!disStringsData(uFlatAddr, off))
752 return false;
753 cb -= off;
754 uFlatAddr += off;
755 off = 0;
756 if (!cb)
757 break;
758 }
759
760 bool fRc;
761 if (off == 0)
762 {
763 size_t cchName = strlen(Sym.szName);
764 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
765 if (!fRc)
766 return false;
767 fRc = disStringsData(uFlatAddr, Sym.cb);
768 uFlatAddr += Sym.cb;
769 cb -= Sym.cb;
770 }
771 else
772 {
773 fRc = disStringsData(uFlatAddr, Sym.cb);
774 uFlatAddr += cb;
775 cb = 0;
776 }
777 if (!fRc)
778 return false;
779 }
780
781 return true;
782}
783
784
785
786static bool disDataSegment(uint32_t iSeg)
787{
788 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
789 uint32_t cb = g_aSegs[iSeg].cb;
790
791 while (cb > 0)
792 {
793 uint32_t off;
794 RTDBGSYMBOL Sym;
795 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
796
797 if (off > 0)
798 {
799 if (!disByteData(uFlatAddr, off))
800 return false;
801 cb -= off;
802 uFlatAddr += off;
803 off = 0;
804 if (!cb)
805 break;
806 }
807
808 bool fRc;
809 if (off == 0)
810 {
811 size_t cchName = strlen(Sym.szName);
812 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
813 if (!fRc)
814 return false;
815
816 if (Sym.cb == 2)
817 fRc = disWordData(uFlatAddr, 2);
818 //else if (Sym.cb == 4 && disIsFarBiosAddr(uFlatAddr))
819 // fRc = disDWordData(uFlatAddr, 4);
820 else if (Sym.cb == 4)
821 fRc = disDWordData(uFlatAddr, 4);
822 else if (disIsString(uFlatAddr, Sym.cb))
823 fRc = disStringData(uFlatAddr, Sym.cb);
824 else
825 fRc = disByteData(uFlatAddr, Sym.cb);
826
827 uFlatAddr += Sym.cb;
828 cb -= Sym.cb;
829 }
830 else
831 {
832 fRc = disByteData(uFlatAddr, cb);
833 uFlatAddr += cb;
834 cb = 0;
835 }
836 if (!fRc)
837 return false;
838 }
839
840 return true;
841}
842
843
844static bool disIsCodeAndAdjustSize(uint32_t uFlatAddr, PRTDBGSYMBOL pSym, PBIOSSEG pSeg)
845{
846 RT_NOREF_PV(uFlatAddr);
847
848 switch (g_enmBiosType)
849 {
850 /*
851 * This is for the PC BIOS.
852 */
853 case kBiosType_System:
854 if (!strcmp(pSeg->szName, "BIOSSEG"))
855 {
856 if ( !strcmp(pSym->szName, "rom_fdpt")
857 || !strcmp(pSym->szName, "pmbios_gdt")
858 || !strcmp(pSym->szName, "pmbios_gdt_desc")
859 || !strcmp(pSym->szName, "_pmode_IDT")
860 || !strcmp(pSym->szName, "_rmode_IDT")
861 || !strncmp(pSym->szName, RT_STR_TUPLE("font"))
862 || !strcmp(pSym->szName, "bios_string")
863 || !strcmp(pSym->szName, "vector_table")
864 || !strcmp(pSym->szName, "pci_routing_table_structure")
865 || !strcmp(pSym->szName, "_pci_routing_table")
866 )
867 return false;
868 }
869
870 if (!strcmp(pSym->szName, "cpu_reset"))
871 pSym->cb = RT_MIN(pSym->cb, 5);
872 else if (!strcmp(pSym->szName, "pci_init_end"))
873 pSym->cb = RT_MIN(pSym->cb, 3);
874 break;
875
876 /*
877 * This is for the VGA BIOS.
878 */
879 case kBiosType_Vga:
880 break;
881 }
882
883 return true;
884}
885
886
887static bool disIs16BitCode(const char *pszSymbol)
888{
889 RT_NOREF_PV(pszSymbol);
890 return true;
891}
892
893
894static bool disIsMemoryParameter(PCDISOPPARAM pParam, uint16_t fParam)
895{
896 return fParam != OP_PARM_NONE
897 && DISUSE_IS_EFFECTIVE_ADDR(pParam->fUse);
898}
899
900
901static bool disAccessesMemory(PCDISCPUSTATE pCpuState)
902{
903 PCDISOPCODE pCurInstr = pCpuState->pCurInstr;
904 return disIsMemoryParameter(&pCpuState->Param1, pCurInstr->fParam1)
905 || disIsMemoryParameter(&pCpuState->Param2, pCurInstr->fParam2)
906 || disIsMemoryParameter(&pCpuState->Param3, pCurInstr->fParam3)
907 || disIsMemoryParameter(&pCpuState->Param4, pCurInstr->fParam4);
908}
909
910
911/**
912 * Deals with instructions that YASM will assemble differently than WASM/WCC.
913 */
914static size_t disHandleYasmDifferences(PDISCPUSTATE pCpuState, uint32_t uFlatAddr, uint32_t cbInstr,
915 char *pszBuf, size_t cbBuf, size_t cchUsed)
916{
917 bool fDifferent = DISFormatYasmIsOddEncoding(pCpuState);
918 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
919
920 /*
921 * Disassembler bugs.
922 */
923 /** @todo Group 1a and 11 seems to be disassembled incorrectly when
924 * modrm.reg != 0. Those encodings should be invalid AFAICT. */
925
926 if ( ( pCpuState->bOpCode == 0x8f /* group 1a */
927 || pCpuState->bOpCode == 0xc7 /* group 11 */
928 || pCpuState->bOpCode == 0xc6 /* group 11 - not verified */
929 )
930 && pCpuState->ModRM.Bits.Reg != 0)
931 fDifferent = true;
932 /*
933 * Check these out and consider adding them to DISFormatYasmIsOddEncoding.
934 */
935 else if ( pb[0] == 0xf3
936 && pb[1] == 0x66
937 && pb[2] == 0x6d)
938 fDifferent = true; /* rep insd - prefix switched. */
939 else if ( pb[0] == 0xc6
940 && pb[1] == 0xc5
941 && pb[2] == 0xba)
942 fDifferent = true; /* mov ch, 0bah - yasm uses a short sequence: 0xb5 0xba. */
943
944 /*
945 * 32-bit retf.
946 */
947 else if ( pb[0] == 0x66
948 && pb[1] == 0xcb)
949 fDifferent = true;
950
951 /*
952 * Handle different stuff.
953 */
954 if (fDifferent)
955 {
956 disByteData(uFlatAddr, cbInstr); /* lazy bird. */
957
958 if (cchUsed + 2 < cbBuf)
959 {
960 memmove(pszBuf + 2, pszBuf, cchUsed + 1); /* include terminating \0 */
961 cchUsed += 2;
962 }
963
964 pszBuf[0] = ';';
965 pszBuf[1] = ' ';
966 }
967
968 return cchUsed;
969}
970
971
972/**
973 * @callback_method_impl{FNDISREADBYTES}
974 *
975 * @remarks @a uSrcAddr is the flat address.
976 */
977static DECLCALLBACK(int) disReadOpcodeBytes(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead)
978{
979 RT_NOREF_PV(cbMinRead);
980
981 RTUINTPTR offBios = pDis->uInstrAddr + offInstr - g_uBiosFlatBase;
982 size_t cbToRead = cbMaxRead;
983 if (offBios + cbToRead > g_cbImg)
984 {
985 if (offBios >= g_cbImg)
986 cbToRead = 0;
987 else
988 cbToRead = g_cbImg - offBios;
989 }
990 memcpy(&pDis->abInstr[offInstr], &g_pbImg[offBios], cbToRead);
991 pDis->cbCachedInstr = (uint8_t)(offInstr + cbToRead);
992 return VINF_SUCCESS;
993}
994
995
996/**
997 * Disassembles code.
998 *
999 * @returns @c true on success, @c false on failure.
1000 * @param uFlatAddr The address where the code starts.
1001 * @param cb The amount of code to disassemble.
1002 * @param fIs16Bit Is is 16-bit (@c true) or 32-bit (@c false).
1003 */
1004static bool disCode(uint32_t uFlatAddr, uint32_t cb, bool fIs16Bit)
1005{
1006 uint8_t const *pb = &g_pbImg[uFlatAddr - g_uBiosFlatBase];
1007
1008 while (cb > 0)
1009 {
1010 /* Trailing zero padding detection. */
1011 if ( *pb == '\0'
1012 && ASMMemIsZero(pb, RT_MIN(cb, 8)))
1013 {
1014 void *pv = ASMMemFirstNonZero(pb, cb);
1015 uint32_t cbZeros = pv ? (uint32_t)((uint8_t const *)pv - pb) : cb;
1016 if (!outputPrintf(" times %#x db 0\n", cbZeros))
1017 return false;
1018 cb -= cbZeros;
1019 pb += cbZeros;
1020 uFlatAddr += cbZeros;
1021 if ( cb == 2
1022 && pb[0] == 'X'
1023 && pb[1] == 'M')
1024 return disStringData(uFlatAddr, cb);
1025 }
1026 /* Work arounds for switch tables and such (disas assertions). */
1027 else if ( 0
1028 || ( pb[0] == 0x50 /* int13_cdemu switch */
1029 && pb[1] == 0x4e
1030 && pb[2] == 0x49
1031 && pb[3] == 0x48
1032 && pb[4] == 0x47
1033 )
1034 || ( pb[0] == 0x8b /* _int13_harddisk_ext switch */
1035 && pb[1] == 0x46
1036 && pb[2] == 0x16
1037 && pb[3] == 0x30
1038 && pb[4] == 0xe8
1039 && pb[5] == 0x80
1040 )
1041 || ( pb[0] == 0xd8
1042 && pb[1] == 0x5f
1043 && pb[2] == 0x0b
1044 && pb[3] == 0x60
1045 && pb[4] == 0x0b
1046 && pb[5] == 0x60
1047 )
1048 || ( pb[0] == 0x67 /* _pci16_function switch */
1049 && pb[1] == 0x92
1050 && pb[2] == 0x81
1051 && pb[3] == 0x92
1052 && pb[4] == 0x94
1053 && pb[5] == 0x92
1054 )
1055 || ( pb[0] == 0xa3 /* _int1a_function switch */
1056 && pb[1] == 0x67
1057 && pb[2] == 0xca
1058 && pb[3] == 0x67
1059 && pb[4] == 0xef
1060 && pb[5] == 0x67
1061 )
1062 || ( pb[0] == 0x0b /* _ahci_init byte table */
1063 && pb[1] == 0x05
1064 && pb[2] == 0x04
1065 && pb[3] == 0x03
1066 && pb[4] == 0x02
1067 && pb[5] == 0x01
1068 )
1069 || ( pb[0] == 0x00 /* bytes after apm_out_str_ */
1070 && pb[1] == 0x00
1071 && pb[2] == 0x00
1072 && pb[3] == 0x00
1073 && pb[4] == 0x00
1074 && pb[5] == 0x00
1075 && pb[6] == 0xe0
1076 && pb[7] == 0xa0
1077 && pb[8] == 0xe2
1078 && pb[9] == 0xa0
1079 )
1080 || ( pb[0] == 0xf0 /* switch for apm_worker */
1081 && pb[1] == 0xa0
1082 && pb[2] == 0xf2
1083 && pb[3] == 0xa0
1084 && pb[4] == 0xf6
1085 && pb[5] == 0xa0
1086 )
1087 || ( pb[0] == 0xd4
1088 && pb[1] == 0xc6
1089 && pb[2] == 0xc5
1090 && pb[3] == 0xba
1091 && pb[4] == 0xb8
1092 && pb[5] == 0xb6
1093 )
1094 || ( pb[0] == 0xec /* _int15_function switch */
1095 && pb[1] == 0xe9
1096 && pb[2] == 0xd8
1097 && pb[3] == 0xc1
1098 && pb[4] == 0xc0
1099 && pb[5] == 0xbf
1100 )
1101 || ( pb[0] == 0x21 /* _int15_function32 switch */
1102 && pb[1] == 0x66
1103 && pb[2] == 0x43
1104 && pb[3] == 0x66
1105 && pb[4] == 0x66
1106 && pb[5] == 0x66
1107 )
1108 || ( pb[0] == 0xf0 /* int15_function_mouse switch */
1109 && pb[1] == 0x75
1110 && pb[2] == 0x66
1111 && pb[3] == 0x76
1112 && pb[4] == 0xe9
1113 && pb[5] == 0x76
1114 )
1115 || ( pb[0] == 0x60
1116 && pb[1] == 0xa0
1117 && pb[2] == 0x62
1118 && pb[3] == 0xa0
1119 && pb[4] == 0x66
1120 && pb[5] == 0xa0
1121 )
1122 || 0
1123 )
1124 return disByteData(uFlatAddr, cb);
1125 else
1126 {
1127 unsigned cbInstr;
1128 DISCPUSTATE CpuState;
1129 CpuState.ModRM.Bits.Mod = 3;
1130 int rc = DISInstrWithReader(uFlatAddr, fIs16Bit ? DISCPUMODE_16BIT : DISCPUMODE_32BIT,
1131 disReadOpcodeBytes, NULL, &CpuState, &cbInstr);
1132 if ( RT_SUCCESS(rc)
1133 && cbInstr <= cb
1134 && CpuState.pCurInstr
1135 && CpuState.pCurInstr->uOpcode != OP_INVALID
1136 && CpuState.pCurInstr->uOpcode != OP_ILLUD2
1137 && ( !(CpuState.fPrefix & DISPREFIX_ADDRSIZE)
1138 || disAccessesMemory(&CpuState)))
1139 {
1140 char szTmp[4096];
1141 size_t cch = DISFormatYasmEx(&CpuState, szTmp, sizeof(szTmp),
1142 DIS_FMT_FLAGS_STRICT
1143 | DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_COMMENT | DIS_FMT_FLAGS_BYTES_SPACED,
1144 NULL, NULL);
1145 cch = disHandleYasmDifferences(&CpuState, uFlatAddr, cbInstr, szTmp, sizeof(szTmp), cch);
1146 Assert(cch < sizeof(szTmp));
1147
1148 if (g_cVerbose > 1)
1149 {
1150 while (cch < 72)
1151 szTmp[cch++] = ' ';
1152
1153 RTDBGLINE LineInfo = {0};
1154 RTINTPTR offLine = -1;
1155 int rcLine = RTDbgModLineByAddr(g_hSymMod, RTDBGSEGIDX_RVA, uFlatAddr - g_uBiosFlatBase, &offLine, &LineInfo);
1156 if (RT_SUCCESS(rcLine) && offLine == 0 && cch < sizeof(szTmp) - 16)
1157 RTStrPrintf(&szTmp[cch], sizeof(szTmp) - cch, "; %#x %Rbn:%u",
1158 uFlatAddr, LineInfo.szFilename, LineInfo.uLineNo);
1159 else
1160 RTStrPrintf(&szTmp[cch], sizeof(szTmp) - cch, "; %#x", uFlatAddr);
1161 }
1162
1163 if (!outputPrintf(" %s\n", szTmp))
1164 return false;
1165 cb -= cbInstr;
1166 pb += cbInstr;
1167 uFlatAddr += cbInstr;
1168 }
1169 else
1170 {
1171 if (!disByteData(uFlatAddr, 1))
1172 return false;
1173 cb--;
1174 pb++;
1175 uFlatAddr++;
1176 }
1177 }
1178 }
1179 return true;
1180}
1181
1182
1183static bool disCodeSegment(uint32_t iSeg)
1184{
1185 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
1186 uint32_t cb = g_aSegs[iSeg].cb;
1187
1188 while (cb > 0)
1189 {
1190 uint32_t off;
1191 RTDBGSYMBOL Sym;
1192 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
1193
1194 if (off > 0)
1195 {
1196 if (!disByteData(uFlatAddr, off))
1197 return false;
1198 cb -= off;
1199 uFlatAddr += off;
1200 off = 0;
1201 if (!cb)
1202 break;
1203 }
1204
1205 bool fRc;
1206 if (off == 0)
1207 {
1208 size_t cchName = strlen(Sym.szName);
1209 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
1210 if (!fRc)
1211 return false;
1212
1213 if (disIsCodeAndAdjustSize(uFlatAddr, &Sym, &g_aSegs[iSeg]))
1214 fRc = disCode(uFlatAddr, Sym.cb, disIs16BitCode(Sym.szName));
1215 else
1216 fRc = disByteData(uFlatAddr, Sym.cb);
1217
1218 uFlatAddr += Sym.cb;
1219 cb -= Sym.cb;
1220 }
1221 else
1222 {
1223 fRc = disByteData(uFlatAddr, cb);
1224 uFlatAddr += cb;
1225 cb = 0;
1226 }
1227 if (!fRc)
1228 return false;
1229 }
1230
1231 return true;
1232}
1233
1234
1235static RTEXITCODE DisassembleBiosImage(void)
1236{
1237 if (!disFileHeader())
1238 return RTEXITCODE_FAILURE;
1239
1240 /*
1241 * Work the image segment by segment.
1242 */
1243 bool fRc = true;
1244 uint32_t uFlatAddr = g_uBiosFlatBase;
1245 for (uint32_t iSeg = 0; iSeg < g_cSegs && fRc; iSeg++)
1246 {
1247 /* Is there a gap between the segments? */
1248 if (uFlatAddr < g_aSegs[iSeg].uFlatAddr)
1249 {
1250 fRc = disCopySegmentGap(uFlatAddr, g_aSegs[iSeg].uFlatAddr - uFlatAddr);
1251 if (!fRc)
1252 break;
1253 uFlatAddr = g_aSegs[iSeg].uFlatAddr;
1254 }
1255 else if (uFlatAddr > g_aSegs[iSeg].uFlatAddr)
1256 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Overlapping segments: %u and %u; uFlatAddr=%#x\n", iSeg - 1, iSeg, uFlatAddr);
1257
1258 /* Disassemble the segment. */
1259 fRc = outputPrintf("\n"
1260 "section %s progbits vstart=%#x align=1 ; size=%#x class=%s group=%s\n",
1261 g_aSegs[iSeg].szName, g_aSegs[iSeg].uFlatAddr - g_uBiosFlatBase,
1262 g_aSegs[iSeg].cb, g_aSegs[iSeg].szClass, g_aSegs[iSeg].szGroup);
1263 if (!fRc)
1264 return RTEXITCODE_FAILURE;
1265 if (!strcmp(g_aSegs[iSeg].szName, "CONST"))
1266 fRc = disConstSegment(iSeg);
1267 else if (!strcmp(g_aSegs[iSeg].szClass, "DATA"))
1268 fRc = disDataSegment(iSeg);
1269 else
1270 fRc = disCodeSegment(iSeg);
1271
1272 /* Advance. */
1273 uFlatAddr += g_aSegs[iSeg].cb;
1274 }
1275
1276 /* Final gap. */
1277 if (uFlatAddr < g_uBiosFlatBase + g_cbImg)
1278 fRc = disCopySegmentGap(uFlatAddr, (uint32_t)(g_uBiosFlatBase + g_cbImg - uFlatAddr));
1279 else if (uFlatAddr > g_uBiosFlatBase + g_cbImg)
1280 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Last segment spills beyond 1MB; uFlatAddr=%#x\n", uFlatAddr);
1281
1282 if (!fRc)
1283 return RTEXITCODE_FAILURE;
1284 return RTEXITCODE_SUCCESS;
1285}
1286
1287
1288
1289/**
1290 * Parses the symbol file for the BIOS.
1291 *
1292 * This is in ELF/DWARF format.
1293 *
1294 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1295 * @param pszBiosSym Path to the sym file.
1296 */
1297static RTEXITCODE ParseSymFile(const char *pszBiosSym)
1298{
1299 int rc = RTDbgModCreateFromImage(&g_hSymMod, pszBiosSym, "VBoxBios", RTLDRARCH_WHATEVER, NIL_RTDBGCFG);
1300 if (RT_FAILURE(rc))
1301 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosSym, rc);
1302
1303 if (g_cVerbose > 0)
1304 {
1305 /* Show segments */
1306 RTDBGSEGIDX cSegs = RTDbgModSegmentCount(g_hSymMod);
1307 for (RTDBGSEGIDX iSeg = 0; iSeg < cSegs; iSeg++)
1308 {
1309 RTDBGSEGMENT SegInfo;
1310 rc = RTDbgModSegmentByIndex(g_hSymMod, iSeg, &SegInfo);
1311 if (RT_SUCCESS(rc))
1312 RTMsgInfo("Seg#%u: %05RX64 LB %04RX64 rva %04RX64 %s\n", iSeg, SegInfo.Address, SegInfo.cb, SegInfo.uRva, SegInfo.szName);
1313 else
1314 RTMsgInfo("Seg#%u: RTDbgModSegmentByIndex -> %Rrc\n", iSeg, rc);
1315
1316 }
1317 }
1318 return RTEXITCODE_SUCCESS;
1319}
1320
1321
1322/**
1323 * Display an error with the mapfile name and current line, return false.
1324 *
1325 * @returns @c false.
1326 * @param pMap The map file handle.
1327 * @param pszFormat The format string.
1328 * @param ... Format arguments.
1329 */
1330static bool mapError(PBIOSMAP pMap, const char *pszFormat, ...)
1331{
1332 va_list va;
1333 va_start(va, pszFormat);
1334 RTMsgError("%s:%d: %N", pMap->pszMapFile, pMap->iLine, pszFormat, &va);
1335 va_end(va);
1336 return false;
1337}
1338
1339
1340/**
1341 * Reads a line from the file.
1342 *
1343 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1344 * @param pMap The map file handle.
1345 */
1346static bool mapReadLine(PBIOSMAP pMap)
1347{
1348 int rc = RTStrmGetLine(pMap->hStrm, pMap->szLine, sizeof(pMap->szLine));
1349 if (RT_FAILURE(rc))
1350 {
1351 if (rc == VERR_EOF)
1352 {
1353 pMap->fEof = true;
1354 pMap->cch = 0;
1355 pMap->offNW = 0;
1356 pMap->szLine[0] = '\0';
1357 }
1358 else
1359 RTMsgError("%s:%d: Read error %Rrc", pMap->pszMapFile, pMap->iLine + 1, rc);
1360 return false;
1361 }
1362 pMap->iLine++;
1363 pMap->cch = (uint32_t)strlen(pMap->szLine);
1364
1365 /* Check out leading white space. */
1366 if (!RT_C_IS_SPACE(pMap->szLine[0]))
1367 pMap->offNW = 0;
1368 else
1369 {
1370 uint32_t off = 1;
1371 while (RT_C_IS_SPACE(pMap->szLine[off]))
1372 off++;
1373 pMap->offNW = off;
1374 }
1375
1376 return true;
1377}
1378
1379
1380/**
1381 * Checks if it is an empty line.
1382 * @returns @c true if empty, @c false if not.
1383 * @param pMap The map file handle.
1384 */
1385static bool mapIsEmptyLine(PBIOSMAP pMap)
1386{
1387 Assert(pMap->offNW <= pMap->cch);
1388 return pMap->offNW == pMap->cch;
1389}
1390
1391
1392/**
1393 * Reads ahead in the map file until a non-empty line or EOF is encountered.
1394 *
1395 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1396 * @param pMap The map file handle.
1397 */
1398static bool mapSkipEmptyLines(PBIOSMAP pMap)
1399{
1400 for (;;)
1401 {
1402 if (!mapReadLine(pMap))
1403 return false;
1404 if (pMap->offNW < pMap->cch)
1405 return true;
1406 }
1407}
1408
1409
1410/**
1411 * Reads ahead in the map file until an empty line or EOF is encountered.
1412 *
1413 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1414 * @param pMap The map file handle.
1415 */
1416static bool mapSkipNonEmptyLines(PBIOSMAP pMap)
1417{
1418 for (;;)
1419 {
1420 if (!mapReadLine(pMap))
1421 return false;
1422 if (pMap->offNW == pMap->cch)
1423 return true;
1424 }
1425}
1426
1427
1428/**
1429 * Strips the current line.
1430 *
1431 * The string length may change.
1432 *
1433 * @returns Pointer to the first non-space character.
1434 * @param pMap The map file handle.
1435 * @param pcch Where to return the length of the unstripped
1436 * part. Optional.
1437 */
1438static char *mapStripCurrentLine(PBIOSMAP pMap, size_t *pcch)
1439{
1440 char *psz = &pMap->szLine[pMap->offNW];
1441 char *pszEnd = &pMap->szLine[pMap->cch];
1442 while ( (uintptr_t)pszEnd > (uintptr_t)psz
1443 && RT_C_IS_SPACE(pszEnd[-1]))
1444 {
1445 *--pszEnd = '\0';
1446 pMap->cch--;
1447 }
1448 if (pcch)
1449 *pcch = pszEnd - psz;
1450 return psz;
1451}
1452
1453
1454/**
1455 * Reads a line from the file and right strips it.
1456 *
1457 * @returns Pointer to szLine on success, @c NULL + msg on failure, @c NULL on
1458 * EOF.
1459 * @param pMap The map file handle.
1460 * @param pcch Where to return the length of the unstripped
1461 * part. Optional.
1462 */
1463static char *mapReadLineStripRight(PBIOSMAP pMap, size_t *pcch)
1464{
1465 if (!mapReadLine(pMap))
1466 return NULL;
1467 mapStripCurrentLine(pMap, NULL);
1468 if (pcch)
1469 *pcch = pMap->cch;
1470 return pMap->szLine;
1471}
1472
1473
1474/**
1475 * mapReadLine() + mapStripCurrentLine().
1476 *
1477 * @returns Pointer to the first non-space character in the new line. NULL on
1478 * read error (bitched already) or end of file.
1479 * @param pMap The map file handle.
1480 * @param pcch Where to return the length of the unstripped
1481 * part. Optional.
1482 */
1483static char *mapReadLineStrip(PBIOSMAP pMap, size_t *pcch)
1484{
1485 if (!mapReadLine(pMap))
1486 return NULL;
1487 return mapStripCurrentLine(pMap, pcch);
1488}
1489
1490
1491/**
1492 * Parses a word, copying it into the supplied buffer, and skipping any spaces
1493 * following it.
1494 *
1495 * @returns @c true on success, @c false on failure.
1496 * @param ppszCursor Pointer to the cursor variable.
1497 * @param pszBuf The output buffer.
1498 * @param cbBuf The size of the output buffer.
1499 */
1500static bool mapParseWord(char **ppszCursor, char *pszBuf, size_t cbBuf)
1501{
1502 /* Check that we start on a non-blank. */
1503 char *pszStart = *ppszCursor;
1504 if (!*pszStart || RT_C_IS_SPACE(*pszStart))
1505 return false;
1506
1507 /* Find the end of the word. */
1508 char *psz = pszStart + 1;
1509 while (*psz && !RT_C_IS_SPACE(*psz))
1510 psz++;
1511
1512 /* Copy it. */
1513 size_t cchWord = (uintptr_t)psz - (uintptr_t)pszStart;
1514 if (cchWord >= cbBuf)
1515 return false;
1516 memcpy(pszBuf, pszStart, cchWord);
1517 pszBuf[cchWord] = '\0';
1518
1519 /* Skip blanks following it. */
1520 while (RT_C_IS_SPACE(*psz))
1521 psz++;
1522 *ppszCursor = psz;
1523 return true;
1524}
1525
1526
1527/**
1528 * Parses an 16:16 address.
1529 *
1530 * @returns @c true on success, @c false on failure.
1531 * @param ppszCursor Pointer to the cursor variable.
1532 * @param pAddr Where to return the address.
1533 */
1534static bool mapParseAddress(char **ppszCursor, PRTFAR16 pAddr)
1535{
1536 char szWord[32];
1537 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1538 return false;
1539 size_t cchWord = strlen(szWord);
1540
1541 /* An address is at least 16:16 format. It may be 16:32. It may also be flagged. */
1542 size_t cchAddr = 4 + 1 + 4;
1543 if (cchWord < cchAddr)
1544 return false;
1545 if ( !RT_C_IS_XDIGIT(szWord[0])
1546 || !RT_C_IS_XDIGIT(szWord[1])
1547 || !RT_C_IS_XDIGIT(szWord[2])
1548 || !RT_C_IS_XDIGIT(szWord[3])
1549 || szWord[4] != ':'
1550 || !RT_C_IS_XDIGIT(szWord[5])
1551 || !RT_C_IS_XDIGIT(szWord[6])
1552 || !RT_C_IS_XDIGIT(szWord[7])
1553 || !RT_C_IS_XDIGIT(szWord[8])
1554 )
1555 return false;
1556 if ( cchWord > cchAddr
1557 && RT_C_IS_XDIGIT(szWord[9])
1558 && RT_C_IS_XDIGIT(szWord[10])
1559 && RT_C_IS_XDIGIT(szWord[11])
1560 && RT_C_IS_XDIGIT(szWord[12]))
1561 cchAddr += 4;
1562
1563 /* Drop flag if present. */
1564 if (cchWord > cchAddr)
1565 {
1566 if (RT_C_IS_XDIGIT(szWord[cchAddr]))
1567 return false;
1568 szWord[cchAddr] = '\0';
1569 cchWord = cchAddr;
1570 }
1571
1572 /* Convert it. */
1573 szWord[4] = '\0';
1574 int rc1 = RTStrToUInt16Full(szWord, 16, &pAddr->sel);
1575 if (rc1 != VINF_SUCCESS)
1576 return false;
1577
1578 int rc2 = RTStrToUInt16Full(szWord + 5, 16, &pAddr->off);
1579 if (rc2 != VINF_SUCCESS)
1580 return false;
1581 return true;
1582}
1583
1584
1585/**
1586 * Parses a size.
1587 *
1588 * @returns @c true on success, @c false on failure.
1589 * @param ppszCursor Pointer to the cursor variable.
1590 * @param pcb Where to return the size.
1591 */
1592static bool mapParseSize(char **ppszCursor, uint32_t *pcb)
1593{
1594 char szWord[32];
1595 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1596 return false;
1597 size_t cchWord = strlen(szWord);
1598 if (cchWord != 8)
1599 return false;
1600
1601 int rc = RTStrToUInt32Full(szWord, 16, pcb);
1602 if (rc != VINF_SUCCESS)
1603 return false;
1604 return true;
1605}
1606
1607
1608/**
1609 * Parses a section box and the following column header.
1610 *
1611 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1612 * @param pMap Map file handle.
1613 * @param pszSectionNm The expected section name.
1614 * @param cColumns The number of columns.
1615 * @param ... The column names.
1616 */
1617static bool mapSkipThruColumnHeadings(PBIOSMAP pMap, const char *pszSectionNm, uint32_t cColumns, ...)
1618{
1619 if ( mapIsEmptyLine(pMap)
1620 && !mapSkipEmptyLines(pMap))
1621 return false;
1622
1623 /* +------------+ */
1624 size_t cch;
1625 char *psz = mapStripCurrentLine(pMap, &cch);
1626 if (!psz)
1627 return false;
1628
1629 if ( psz[0] != '+'
1630 || psz[1] != '-'
1631 || psz[2] != '-'
1632 || psz[3] != '-'
1633 || psz[cch - 4] != '-'
1634 || psz[cch - 3] != '-'
1635 || psz[cch - 2] != '-'
1636 || psz[cch - 1] != '+'
1637 )
1638 {
1639 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1640 return false;
1641 }
1642
1643 /* | pszSectionNm | */
1644 psz = mapReadLineStrip(pMap, &cch);
1645 if (!psz)
1646 return false;
1647
1648 size_t cchSectionNm = strlen(pszSectionNm);
1649 if ( psz[0] != '|'
1650 || psz[1] != ' '
1651 || psz[2] != ' '
1652 || psz[3] != ' '
1653 || psz[cch - 4] != ' '
1654 || psz[cch - 3] != ' '
1655 || psz[cch - 2] != ' '
1656 || psz[cch - 1] != '|'
1657 || cch != 1 + 3 + cchSectionNm + 3 + 1
1658 || strncmp(&psz[4], pszSectionNm, cchSectionNm)
1659 )
1660 {
1661 RTMsgError("%s:%d: Expected section box: | %s |", pMap->pszMapFile, pMap->iLine, pszSectionNm);
1662 return false;
1663 }
1664
1665 /* +------------+ */
1666 psz = mapReadLineStrip(pMap, &cch);
1667 if (!psz)
1668 return false;
1669 if ( psz[0] != '+'
1670 || psz[1] != '-'
1671 || psz[2] != '-'
1672 || psz[3] != '-'
1673 || psz[cch - 4] != '-'
1674 || psz[cch - 3] != '-'
1675 || psz[cch - 2] != '-'
1676 || psz[cch - 1] != '+'
1677 )
1678 {
1679 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1680 return false;
1681 }
1682
1683 /* There may be a few lines describing the table notation now, surrounded by blank lines. */
1684 do
1685 {
1686 psz = mapReadLineStripRight(pMap, &cch);
1687 if (!psz)
1688 return false;
1689 } while ( *psz == '\0'
1690 || ( !RT_C_IS_SPACE(psz[0])
1691 && RT_C_IS_SPACE(psz[1])
1692 && psz[2] == '='
1693 && RT_C_IS_SPACE(psz[3]))
1694 );
1695
1696 /* Should have the column heading now. */
1697 va_list va;
1698 va_start(va, cColumns);
1699 for (uint32_t i = 0; i < cColumns; i++)
1700 {
1701 const char *pszColumn = va_arg(va, const char *);
1702 size_t cchColumn = strlen(pszColumn);
1703 if ( strncmp(psz, pszColumn, cchColumn)
1704 || ( psz[cchColumn] != '\0'
1705 && !RT_C_IS_SPACE(psz[cchColumn])))
1706 {
1707 va_end(va);
1708 RTMsgError("%s:%d: Expected column '%s' found '%s'", pMap->pszMapFile, pMap->iLine, pszColumn, psz);
1709 return false;
1710 }
1711 psz += cchColumn;
1712 while (RT_C_IS_SPACE(*psz))
1713 psz++;
1714 }
1715 va_end(va);
1716
1717 /* The next line is the underlining. */
1718 psz = mapReadLineStripRight(pMap, &cch);
1719 if (!psz)
1720 return false;
1721 if (*psz != '=' || psz[cch - 1] != '=')
1722 {
1723 RTMsgError("%s:%d: Expected column header underlining", pMap->pszMapFile, pMap->iLine);
1724 return false;
1725 }
1726
1727 /* Skip one blank line. */
1728 psz = mapReadLineStripRight(pMap, &cch);
1729 if (!psz)
1730 return false;
1731 if (*psz)
1732 {
1733 RTMsgError("%s:%d: Expected blank line beneath the column headers", pMap->pszMapFile, pMap->iLine);
1734 return false;
1735 }
1736
1737 return true;
1738}
1739
1740
1741/**
1742 * Parses a segment list.
1743 *
1744 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1745 * @param pMap The map file handle.
1746 */
1747static bool mapParseSegments(PBIOSMAP pMap)
1748{
1749 for (;;)
1750 {
1751 if (!mapReadLineStripRight(pMap, NULL))
1752 return false;
1753
1754 /* The end? The line should be empty. Expectes segment name to not
1755 start with a space. */
1756 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1757 {
1758 if (!pMap->szLine[0])
1759 return true;
1760 RTMsgError("%s:%u: Malformed segment line", pMap->pszMapFile, pMap->iLine);
1761 return false;
1762 }
1763
1764 /* Parse the segment line. */
1765 uint32_t iSeg = g_cSegs;
1766 if (iSeg >= RT_ELEMENTS(g_aSegs))
1767 {
1768 RTMsgError("%s:%u: Too many segments", pMap->pszMapFile, pMap->iLine);
1769 return false;
1770 }
1771
1772 char *psz = pMap->szLine;
1773 if (!mapParseWord(&psz, g_aSegs[iSeg].szName, sizeof(g_aSegs[iSeg].szName)))
1774 RTMsgError("%s:%u: Segment name parser error", pMap->pszMapFile, pMap->iLine);
1775 else if (!mapParseWord(&psz, g_aSegs[iSeg].szClass, sizeof(g_aSegs[iSeg].szClass)))
1776 RTMsgError("%s:%u: Segment class parser error", pMap->pszMapFile, pMap->iLine);
1777 else if (!mapParseWord(&psz, g_aSegs[iSeg].szGroup, sizeof(g_aSegs[iSeg].szGroup)))
1778 RTMsgError("%s:%u: Segment group parser error", pMap->pszMapFile, pMap->iLine);
1779 else if (!mapParseAddress(&psz, &g_aSegs[iSeg].Address))
1780 RTMsgError("%s:%u: Segment address parser error", pMap->pszMapFile, pMap->iLine);
1781 else if (!mapParseSize(&psz, &g_aSegs[iSeg].cb))
1782 RTMsgError("%s:%u: Segment size parser error", pMap->pszMapFile, pMap->iLine);
1783 else
1784 {
1785 g_aSegs[iSeg].uFlatAddr = ((uint32_t)g_aSegs[iSeg].Address.sel << 4) + g_aSegs[iSeg].Address.off;
1786 g_cSegs++;
1787 if (g_cVerbose > 2)
1788 RTStrmPrintf(g_pStdErr, "read segment at %08x / %04x:%04x LB %04x %s / %s / %s\n",
1789 g_aSegs[iSeg].uFlatAddr,
1790 g_aSegs[iSeg].Address.sel,
1791 g_aSegs[iSeg].Address.off,
1792 g_aSegs[iSeg].cb,
1793 g_aSegs[iSeg].szName,
1794 g_aSegs[iSeg].szClass,
1795 g_aSegs[iSeg].szGroup);
1796
1797 while (RT_C_IS_SPACE(*psz))
1798 psz++;
1799 if (!*psz)
1800 continue;
1801 RTMsgError("%s:%u: Junk at end of line", pMap->pszMapFile, pMap->iLine);
1802 }
1803 return false;
1804 }
1805}
1806
1807
1808/**
1809 * Sorts the segment array by flat address and adds them to the debug module.
1810 *
1811 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1812 */
1813static bool mapSortAndAddSegments(void)
1814{
1815 for (uint32_t i = 0; i < g_cSegs; i++)
1816 {
1817 for (uint32_t j = i + 1; j < g_cSegs; j++)
1818 if (g_aSegs[j].uFlatAddr < g_aSegs[i].uFlatAddr)
1819 {
1820 BIOSSEG Tmp = g_aSegs[i];
1821 g_aSegs[i] = g_aSegs[j];
1822 g_aSegs[j] = Tmp;
1823 }
1824 g_aSegs[i].uRva = g_aSegs[i].uFlatAddr - g_aSegs[0].uFlatAddr;
1825
1826 if (g_cVerbose > 0)
1827 RTStrmPrintf(g_pStdErr, "segment at %08x / %04x / %04x:%04x LB %04x %s / %s / %s\n",
1828 g_aSegs[i].uFlatAddr,
1829 g_aSegs[i].uRva,
1830 g_aSegs[i].Address.sel,
1831 g_aSegs[i].Address.off,
1832 g_aSegs[i].cb,
1833 g_aSegs[i].szName,
1834 g_aSegs[i].szClass,
1835 g_aSegs[i].szGroup);
1836
1837 RTDBGSEGIDX idx = i;
1838 int rc = RTDbgModSegmentAdd(g_hMapMod, g_aSegs[i].uFlatAddr, g_aSegs[i].cb, g_aSegs[i].szName, 0 /*fFlags*/, &idx);
1839 if (RT_FAILURE(rc))
1840 {
1841 RTMsgError("RTDbgModSegmentAdd failed on %s: %Rrc", g_aSegs[i].szName);
1842 return false;
1843 }
1844 }
1845 return true;
1846}
1847
1848
1849/**
1850 * Parses a segment list.
1851 *
1852 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1853 * @param pMap The map file handle.
1854 */
1855static bool mapParseSymbols(PBIOSMAP pMap)
1856{
1857 for (;;)
1858 {
1859 if (!mapReadLineStripRight(pMap, NULL))
1860 return false;
1861
1862 /* The end? The line should be empty. Expectes segment name to not
1863 start with a space. */
1864 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1865 {
1866 if (!pMap->szLine[0])
1867 return true;
1868 return mapError(pMap, "Malformed symbol line");
1869 }
1870
1871 if (!strncmp(pMap->szLine, RT_STR_TUPLE("Module: ")))
1872 {
1873 /* Parse the module line. */
1874 size_t offObj = sizeof("Module: ") - 1;
1875 while (RT_C_IS_SPACE(pMap->szLine[offObj]))
1876 offObj++;
1877 size_t offSrc = offObj;
1878 char ch;
1879 while ((ch = pMap->szLine[offSrc]) != '(' && ch != '\0')
1880 offSrc++;
1881 size_t cchObj = offSrc - offObj;
1882
1883 offSrc++;
1884 size_t cchSrc = offSrc;
1885 while ((ch = pMap->szLine[cchSrc]) != ')' && ch != '\0')
1886 cchSrc++;
1887 cchSrc -= offSrc;
1888 if (ch != ')')
1889 return mapError(pMap, "Symbol/Module line parse error");
1890
1891 PBIOSOBJFILE pObjFile = (PBIOSOBJFILE)RTMemAllocZ(sizeof(*pObjFile) + cchSrc + cchObj + 2);
1892 if (!pObjFile)
1893 return mapError(pMap, "Out of memory");
1894 char *psz = (char *)(pObjFile + 1);
1895 pObjFile->pszObject = psz;
1896 memcpy(psz, &pMap->szLine[offObj], cchObj);
1897 psz += cchObj;
1898 *psz++ = '\0';
1899 pObjFile->pszSource = psz;
1900 memcpy(psz, &pMap->szLine[offSrc], cchSrc);
1901 psz[cchSrc] = '\0';
1902 RTListAppend(&g_ObjList, &pObjFile->Node);
1903 }
1904 else
1905 {
1906 /* Parse the segment line. */
1907 RTFAR16 Addr;
1908 char *psz = pMap->szLine;
1909 if (!mapParseAddress(&psz, &Addr))
1910 return mapError(pMap, "Symbol address parser error");
1911
1912 char szName[4096];
1913 if (!mapParseWord(&psz, szName, sizeof(szName)))
1914 return mapError(pMap, "Symbol name parser error");
1915
1916 uint32_t uFlatAddr = ((uint32_t)Addr.sel << 4) + Addr.off;
1917 if (uFlatAddr != 0)
1918 {
1919 int rc = RTDbgModSymbolAdd(g_hMapMod, szName, RTDBGSEGIDX_RVA, uFlatAddr, 0 /*cb*/, 0 /*fFlags*/, NULL);
1920 if (RT_FAILURE(rc) && rc != VERR_DBG_ADDRESS_CONFLICT)
1921 {
1922 /* HACK ALERT! For dealing with lables at segment size. */ /** @todo fix end labels. */
1923 rc = RTDbgModSymbolAdd(g_hMapMod, szName, RTDBGSEGIDX_RVA, uFlatAddr - 1, 0 /*cb*/, 0 /*fFlags*/, NULL);
1924 if (RT_FAILURE(rc) && rc != VERR_DBG_ADDRESS_CONFLICT)
1925 return mapError(pMap, "RTDbgModSymbolAdd failed: %Rrc", rc);
1926 }
1927
1928 if (g_cVerbose > 2)
1929 RTStrmPrintf(g_pStdErr, "read symbol - %08x %s\n", uFlatAddr, szName);
1930 while (RT_C_IS_SPACE(*psz))
1931 psz++;
1932 if (*psz)
1933 return mapError(pMap, "Junk at end of line");
1934 }
1935
1936 }
1937 }
1938}
1939
1940
1941/**
1942 * Parses the given map file.
1943 *
1944 * @returns RTEXITCODE_SUCCESS and lots of globals, or RTEXITCODE_FAILURE and a
1945 * error message.
1946 * @param pMap The map file handle.
1947 */
1948static RTEXITCODE mapParseFile(PBIOSMAP pMap)
1949{
1950 int rc = RTDbgModCreate(&g_hMapMod, "VBoxBios", 0 /*cbSeg*/, 0 /*fFlags*/);
1951 if (RT_FAILURE(rc))
1952 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreate failed: %Rrc", rc);
1953
1954 /*
1955 * Read the header.
1956 */
1957 if (!mapReadLine(pMap))
1958 return RTEXITCODE_FAILURE;
1959 if (strncmp(pMap->szLine, RT_STR_TUPLE("Open Watcom Linker Version")))
1960 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected map-file header: '%s'", pMap->szLine);
1961 if ( !mapSkipNonEmptyLines(pMap)
1962 || !mapSkipEmptyLines(pMap))
1963 return RTEXITCODE_FAILURE;
1964
1965 /*
1966 * Skip groups.
1967 */
1968 if (!mapSkipThruColumnHeadings(pMap, "Groups", 3, "Group", "Address", "Size", NULL))
1969 return RTEXITCODE_FAILURE;
1970 if (!mapSkipNonEmptyLines(pMap))
1971 return RTEXITCODE_FAILURE;
1972
1973 /*
1974 * Parse segments.
1975 */
1976 if (!mapSkipThruColumnHeadings(pMap, "Segments", 5, "Segment", "Class", "Group", "Address", "Size"))
1977 return RTEXITCODE_FAILURE;
1978 if (!mapParseSegments(pMap))
1979 return RTEXITCODE_FAILURE;
1980 if (!mapSortAndAddSegments())
1981 return RTEXITCODE_FAILURE;
1982
1983 /*
1984 * Parse symbols.
1985 */
1986 if (!mapSkipThruColumnHeadings(pMap, "Memory Map", 2, "Address", "Symbol"))
1987 return RTEXITCODE_FAILURE;
1988 if (!mapParseSymbols(pMap))
1989 return RTEXITCODE_FAILURE;
1990
1991 /* Ignore the rest of the file. */
1992 return RTEXITCODE_SUCCESS;
1993}
1994
1995
1996/**
1997 * Parses the linker map file for the BIOS.
1998 *
1999 * This is generated by the Watcom linker.
2000 *
2001 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2002 * @param pszBiosMap Path to the map file.
2003 */
2004static RTEXITCODE ParseMapFile(const char *pszBiosMap)
2005{
2006 BIOSMAP Map;
2007 Map.pszMapFile = pszBiosMap;
2008 Map.hStrm = NULL;
2009 Map.iLine = 0;
2010 Map.fEof = false;
2011 Map.cch = 0;
2012 Map.offNW = 0;
2013 int rc = RTStrmOpen(pszBiosMap, "r", &Map.hStrm);
2014 if (RT_FAILURE(rc))
2015 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosMap, rc);
2016 RTEXITCODE rcExit = mapParseFile(&Map);
2017 RTStrmClose(Map.hStrm);
2018 return rcExit;
2019}
2020
2021
2022/**
2023 * Reads the BIOS image into memory (g_pbImg and g_cbImg).
2024 *
2025 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
2026 * @param pszBiosImg Path to the image file.
2027 */
2028static RTEXITCODE ReadBiosImage(const char *pszBiosImg)
2029{
2030 void *pvImg;
2031 size_t cbImg;
2032 int rc = RTFileReadAll(pszBiosImg, &pvImg, &cbImg);
2033 if (RT_FAILURE(rc))
2034 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading '%s': %Rrc", pszBiosImg, rc);
2035
2036 size_t cbImgExpect;
2037 switch (g_enmBiosType)
2038 {
2039 case kBiosType_System: cbImgExpect = _64K; break;
2040 case kBiosType_Vga: cbImgExpect = _32K; break;
2041 default: cbImgExpect = 0; break;
2042 }
2043 if (cbImg != cbImgExpect)
2044 {
2045 RTFileReadAllFree(pvImg, cbImg);
2046 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The BIOS image %u bytes intead of %u bytes", cbImg, cbImgExpect);
2047 }
2048
2049 g_pbImg = (uint8_t *)pvImg;
2050 g_cbImg = cbImg;
2051 return RTEXITCODE_SUCCESS;
2052}
2053
2054
2055int main(int argc, char **argv)
2056{
2057 int rc = RTR3InitExe(argc, &argv, 0);
2058 if (RT_FAILURE(rc))
2059 return RTMsgInitFailure(rc);
2060
2061 RTListInit(&g_ObjList);
2062
2063 /*
2064 * Option config.
2065 */
2066 static RTGETOPTDEF const s_aOpts[] =
2067 {
2068 { "--bios-image", 'i', RTGETOPT_REQ_STRING },
2069 { "--bios-map", 'm', RTGETOPT_REQ_STRING },
2070 { "--bios-sym", 's', RTGETOPT_REQ_STRING },
2071 { "--bios-type", 't', RTGETOPT_REQ_STRING },
2072 { "--output", 'o', RTGETOPT_REQ_STRING },
2073 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2074 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2075 };
2076
2077 const char *pszBiosMap = NULL;
2078 const char *pszBiosSym = NULL;
2079 const char *pszBiosImg = NULL;
2080 const char *pszOutput = NULL;
2081
2082 RTGETOPTUNION ValueUnion;
2083 RTGETOPTSTATE GetOptState;
2084 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2085 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2086
2087 /*
2088 * Process the options.
2089 */
2090 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2091 {
2092 switch (rc)
2093 {
2094 case 'i':
2095 if (pszBiosImg)
2096 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is given more than once");
2097 pszBiosImg = ValueUnion.psz;
2098 break;
2099
2100 case 'm':
2101 if (pszBiosMap)
2102 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is given more than once");
2103 pszBiosMap = ValueUnion.psz;
2104 break;
2105
2106 case 's':
2107 if (pszBiosSym)
2108 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is given more than once");
2109 pszBiosSym = ValueUnion.psz;
2110 break;
2111
2112 case 'o':
2113 if (pszOutput)
2114 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--output is given more than once");
2115 pszOutput = ValueUnion.psz;
2116 break;
2117
2118 case 't':
2119 if (!strcmp(ValueUnion.psz, "system"))
2120 {
2121 g_enmBiosType = kBiosType_System;
2122 g_uBiosFlatBase = 0xf0000;
2123 }
2124 else if (!strcmp(ValueUnion.psz, "vga"))
2125 {
2126 g_enmBiosType = kBiosType_Vga;
2127 g_uBiosFlatBase = 0xc0000;
2128 }
2129 else
2130 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown bios type '%s'", ValueUnion.psz);
2131 break;
2132
2133 case 'v':
2134 g_cVerbose++;
2135 break;
2136
2137 case 'q':
2138 g_cVerbose = 0;
2139 break;
2140
2141 case 'H':
2142 RTPrintf("usage: %Rbn --bios-image <file.img> --bios-map <file.map> [--output <file.asm>]\n",
2143 argv[0]);
2144 return RTEXITCODE_SUCCESS;
2145
2146 case 'V':
2147 {
2148 /* The following is assuming that svn does it's job here. */
2149 char szRev[] = "$Revision: 74982 $";
2150 char *psz = szRev;
2151 while (*psz && !RT_C_IS_DIGIT(*psz))
2152 psz++;
2153 size_t i = strlen(psz);
2154 while (i > 0 && !RT_C_IS_DIGIT(psz[i - 1]))
2155 psz[--i] = '\0';
2156
2157 RTPrintf("r%s\n", psz);
2158 return RTEXITCODE_SUCCESS;
2159 }
2160
2161 default:
2162 return RTGetOptPrintError(rc, &ValueUnion);
2163 }
2164 }
2165
2166 /*
2167 * Got it all?
2168 */
2169 if (!pszBiosImg)
2170 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is required");
2171 if (!pszBiosMap)
2172 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is required");
2173 if (!pszBiosSym)
2174 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is required");
2175
2176 /*
2177 * Do the job.
2178 */
2179 RTEXITCODE rcExit;
2180 rcExit = ReadBiosImage(pszBiosImg);
2181 if (rcExit == RTEXITCODE_SUCCESS)
2182 rcExit = ParseMapFile(pszBiosMap);
2183 if (rcExit == RTEXITCODE_SUCCESS)
2184 rcExit = ParseSymFile(pszBiosSym);
2185 if (rcExit == RTEXITCODE_SUCCESS)
2186 rcExit = OpenOutputFile(pszOutput);
2187 if (rcExit == RTEXITCODE_SUCCESS)
2188 rcExit = DisassembleBiosImage();
2189
2190 return rcExit;
2191}
2192
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