VirtualBox

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

Last change on this file since 101539 was 101539, checked in by vboxsync, 8 months ago

DIS,VMM,DBGC,IPRT,++: Some disassembler tweaks and TB disassembly work. bugref:10371 bugref:9898

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

© 2023 Oracle
ContactPrivacy policyTerms of Use