VirtualBox

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

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

Need to include the source file headers.

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