VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp@ 102270

最後變更 在這個檔案從102270是 102270,由 vboxsync 提交於 16 月 前

bs3kit: Fixed some issues with loading of a fake 63.5 MB floppy. Added optional checksumming of the images we load (disabled by default, as it's slow in IEM). bugref:10371

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 30.5 KB
 
1/* $Id: VBoxBs3Linker.cpp 102270 2023-11-23 00:40:38Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Boot Sector 3 "linker".
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <stdio.h>
42#include <stdlib.h>
43
44#include <iprt/assert.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/getopt.h>
48#include <iprt/initterm.h>
49#include <iprt/ldr.h>
50#include <iprt/mem.h>
51#include <iprt/message.h>
52#include <iprt/path.h>
53#include <iprt/string.h>
54
55#include "bs3kit-linker.h"
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61typedef struct BS3LNKINPUT
62{
63 const char *pszFile;
64 FILE *pFile;
65 uint32_t cbFile;
66 uint32_t cbBits;
67 uint32_t uLoadAddr;
68 uint32_t offInImage;
69 uint8_t *pbBits;
70 RTLDRMOD hLdrMod;
71} BS3LNKINPUT;
72
73
74typedef struct BS3LNKIMPORTSTATE
75{
76 FILE *pOutput;
77 RTSTRSPACE hImportNames;
78 unsigned cImports;
79 unsigned cExports;
80 size_t cbStrings;
81} BS3LNKIMPORTSTATE;
82
83typedef struct BS3LNKIMPORTNAME
84{
85 RTSTRSPACECORE Core;
86 size_t offString;
87 RT_FLEXIBLE_ARRAY_EXTENSION
88 char szName[RT_FLEXIBLE_ARRAY];
89} BS3LNKIMPORTNAME;
90
91
92/**
93 * @callback_method_impl{FNRTLDRENUMSYMS}
94 */
95static DECLCALLBACK(int) GenerateHighDllAsmOutputExportTable(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
96 RTLDRADDR Value, void *pvUser)
97{
98 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
99 if (!pszSymbol || !*pszSymbol)
100 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All exports must be by name. uSymbol=%#x Value=%RX64", uSymbol, (uint64_t)Value);
101
102 // BS3HIGHDLLEXPORTENTRY
103
104 fprintf(pState->pOutput,
105 "BS3_GLOBAL_DATA g_pfn%s, 8\n"
106 " dd 0\n"
107 " dd 0\n"
108 "BS3_GLOBAL_DATA g_fpfn48%s, 6\n"
109 " dd 0\n"
110 " dw 0\n"
111 " dw %#08x\n",
112 pszSymbol + (*pszSymbol == '_'),
113 pszSymbol + (*pszSymbol == '_'),
114 (unsigned)pState->cbStrings);
115 pState->cbStrings += strlen(pszSymbol) + 1;
116 pState->cExports += 1;
117
118 RT_NOREF(hLdrMod);
119 return VINF_SUCCESS;
120}
121
122
123/**
124 * @callback_method_impl{FNRTSTRSPACECALLBACK}
125 */
126static DECLCALLBACK(int) GenerateHighDllAsmOutputImportTable(PRTSTRSPACECORE pStr, void *pvUser)
127{
128 FILE *pOutput = (FILE *)pvUser;
129 BS3LNKIMPORTNAME *pName = (BS3LNKIMPORTNAME *)pStr;
130
131 // BS3HIGHDLLIMPORTENTRY
132 fprintf(pOutput,
133 " dw %#06x\n"
134 " dw seg %s\n"
135 " dd %s wrt BS3FLAT\n"
136 , (unsigned)pName->offString, pName->szName, pName->szName);
137
138 return VINF_SUCCESS;
139}
140
141
142/**
143 * @callback_method_impl{FNRTLDRENUMSYMS}
144 */
145static DECLCALLBACK(int) GenerateHighDllAsmOutputExportStrings(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
146 RTLDRADDR Value, void *pvUser)
147{
148 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
149 if (!pszSymbol || !*pszSymbol)
150 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All exports must be by name. uSymbol=%#x Value=%RX64", uSymbol, (uint64_t)Value);
151
152 fprintf(pState->pOutput, " db '%s', 0\n", pszSymbol);
153 pState->cbStrings += strlen(pszSymbol) + 1;
154
155 RT_NOREF(hLdrMod);
156 return VINF_SUCCESS;
157}
158
159
160/**
161 * @callback_method_impl{FNRTSTRSPACECALLBACK}
162 */
163static DECLCALLBACK(int) GenerateHighDllAsmOutputImportStrings(PRTSTRSPACECORE pStr, void *pvUser)
164{
165 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
166 BS3LNKIMPORTNAME * const pName = (BS3LNKIMPORTNAME *)pStr;
167
168 pName->offString = pState->cbStrings;
169 fprintf(pState->pOutput, " db '%s', 0\n", pName->szName);
170 pState->cbStrings += pName->Core.cchString + 1;
171
172 return VINF_SUCCESS;
173}
174
175
176/**
177 * @callback_method_impl{FNRTLDRIMPORT}
178 */
179static DECLCALLBACK(int) GenerateHighDllAsmImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
180 unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
181{
182 BS3LNKIMPORTSTATE *pState = (BS3LNKIMPORTSTATE *)pvUser;
183 if (!pszSymbol)
184 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All imports must be by name. pszModule=%s uSymbol=%#x", pszModule, uSymbol);
185 if (!RTStrSpaceGet(&pState->hImportNames, pszSymbol))
186 {
187 size_t const cchSymbol = strlen(pszSymbol);
188 BS3LNKIMPORTNAME * const pName = (BS3LNKIMPORTNAME *)RTMemAlloc(RT_UOFFSETOF_DYN(BS3LNKIMPORTNAME,
189 szName[cchSymbol + 1]));
190 AssertReturn(pName, VERR_NO_MEMORY);
191
192 pName->Core.cchString = cchSymbol;
193 pName->Core.pszString = (char *)memcpy(pName->szName, pszSymbol, cchSymbol + 1);
194 pName->offString = UINT16_MAX;
195
196 AssertReturnStmt(RTStrSpaceInsert(&pState->hImportNames, &pName->Core), RTMemFree(pName),
197 RTMsgErrorRc(VERR_INTERNAL_ERROR, "IPE #1"));
198 pState->cImports++;
199 }
200 *pValue = 0x10042;
201 RT_NOREF(hLdrMod);
202 return VINF_SUCCESS;
203}
204
205
206static RTEXITCODE GenerateHighDllImportTableAssembly(FILE *pOutput, const char *pszGenAsmFor)
207{
208 RTERRINFOSTATIC ErrInfo;
209 RTLDRMOD hLdrMod;
210 int rc = RTLdrOpenEx(pszGenAsmFor, 0, RTLDRARCH_X86_32, &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
211 if (RT_FAILURE(rc))
212 return RTMsgErrorExitFailure("RTLdrOpenEx failed to open '%s': %Rrc%#RTeim", pszGenAsmFor, rc, &ErrInfo.Core);
213
214 RTEXITCODE rcExit;
215 size_t cbImage = RTLdrSize(hLdrMod);
216 if (cbImage != ~(size_t)0)
217 {
218 void *pbBits = (uint8_t *)RTMemAlloc(cbImage);
219 if (pbBits)
220 {
221 BS3LNKIMPORTSTATE State = { pOutput, NULL, 0, 0, 0 };
222 rc = RTLdrGetBits(hLdrMod, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmImportCallback, pOutput);
223 if (RT_SUCCESS(rc))
224 {
225 /** @todo move more of this to bs3kit*.h? */
226 fprintf(pOutput,
227 ";\n"
228 "; Automatically generated - DO NOT MODIFY!\n"
229 ";\n"
230 "%%include \"bs3kit.mac\"\n"
231 "\n"
232 "section BS3HIGHDLLEXPORTS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
233 "section BS3HIGHDLLIMPORTS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
234 "section BS3HIGHDLLSTRINGS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
235 "section BS3HIGHDLLTABLE align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
236 "section BS3HIGHDLLTABLE_END align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
237 "GROUP BS3HIGHDLLGROUP BS3HIGHDLLIMPORTS BS3HIGHDLLEXPORTS BS3HIGHDLLSTRINGS BS3HIGHDLLTABLE BS3HIGHDLLTABLE_END\n"
238 "\n");
239
240 /* Populate the string table with imports. */
241 const char *pszFilename = RTPathFilename(pszGenAsmFor);
242 fprintf(pOutput,
243 "section BS3HIGHDLLSTRINGS\n"
244 "start_strings:\n"
245 " db 0\n"
246 " db '%s', 0 ; module name\n"
247 " ; imports\n",
248 pszFilename);
249 State.cbStrings = 1 + strlen(pszFilename) + 1;
250 rc = RTStrSpaceEnumerate(&State.hImportNames, GenerateHighDllAsmOutputImportStrings, &State);
251 AssertRC(rc);
252 fprintf(pOutput, " ; exports\n");
253
254 /* Populate the string table with exports. */
255 size_t const offExportStrings = State.cbStrings;
256 rc = RTLdrEnumSymbols(hLdrMod, 0, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmOutputExportStrings, &State);
257 size_t const cbStrings = State.cbStrings;
258 if (RT_SUCCESS(rc) && cbStrings < _64K)
259 {
260 /* Output the import table. */
261 fprintf(pOutput,
262 "section BS3HIGHDLLIMPORTS\n"
263 "start_imports:\n");
264 rc = RTStrSpaceEnumerate(&State.hImportNames, GenerateHighDllAsmOutputImportTable, &State);
265 AssertRC(rc);
266 fprintf(pOutput, "\n");
267
268 /* Output the export table (ASSUMES stable enumeration order). */
269 fprintf(pOutput,
270 "section BS3HIGHDLLEXPORTS\n"
271 "start_exports:\n");
272 State.cbStrings = offExportStrings;
273 rc = RTLdrEnumSymbols(hLdrMod, 0, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmOutputExportTable, &State);
274 AssertRC(rc);
275 fprintf(pOutput, "\n");
276
277 /* Generate the table entry. */
278 fprintf(pOutput,
279 "section BS3HIGHDLLTABLE\n"
280 "start_entry: ; struct BS3HIGHDLLENTRY \n"
281 " db '%s', 0 ; achMagic[8]\n"
282 " dd 0 ; uLoadAddress\n"
283 " dd %#08zx ; cbLoaded\n"
284 " dd 0 ; offInImage\n"
285 " dd %#08zx ; cbInImage\n"
286 " dd %#04x ; cImports\n"
287 " dd start_imports - start_entry\n"
288 " dd %#04x ; cExports\n"
289 " dd start_exports - start_entry\n"
290 " dd %#05x ; cbStrings\n"
291 " dd start_strings - start_entry\n"
292 " dd 1 ; offDllName\n"
293 " dd 0 ; uChecksum\n"
294 , BS3HIGHDLLENTRY_MAGIC, cbImage, cbImage, State.cImports, State.cExports, (unsigned)cbStrings);
295 rcExit = RTEXITCODE_SUCCESS;
296 }
297 else if (RT_FAILURE(rc))
298 rcExit = RTMsgErrorExitFailure("RTLdrEnumSymbols failed: %Rrc", rc);
299 else
300 rcExit = RTMsgErrorExitFailure("Too many import/export strings: %#x bytes, max 64KiB", cbStrings);
301 }
302 else
303 rcExit = RTMsgErrorExitFailure("RTLdrGetBits failed: %Rrc", rc);
304 RTMemFree(pbBits);
305 }
306 else
307 rcExit = RTMsgErrorExitFailure("Out of memory!");
308 }
309 else
310 rcExit = RTMsgErrorExitFailure("RTLdrSize failed on '%s'", pszGenAsmFor);
311
312 RTLdrClose(hLdrMod);
313 return rcExit;
314}
315
316
317static BS3HIGHDLLENTRY *LocateHighDllEntry(uint8_t const *pbBits, uint32_t cbBits, const char *pszFilename)
318{
319 /*
320 * We search backwards for up to 4 KB.
321 */
322 size_t const offStop = cbBits > _4K ? cbBits - _4K : 0;
323 size_t off = cbBits >= sizeof(BS3HIGHDLLENTRY) ? cbBits - sizeof(BS3HIGHDLLENTRY) : 0;
324 while (off > offStop)
325 {
326 BS3HIGHDLLENTRY const *pEntry = (BS3HIGHDLLENTRY const *)&pbBits[off];
327 if ( pEntry->achMagic[0] == BS3HIGHDLLENTRY_MAGIC[0]
328 && memcmp(pEntry->achMagic, BS3HIGHDLLENTRY_MAGIC, sizeof(pEntry->achMagic)) == 0)
329 {
330 if (pEntry->cbStrings < _64K && pEntry->cbStrings >= 8)
331 {
332 if (off + pEntry->offStrings > 0 && off + pEntry->offStrings + pEntry->cbStrings <= off)
333 {
334 if (off + pEntry->offExports > 0 && off + pEntry->offExports + pEntry->cExports * 8 <= off)
335 {
336 if (off + pEntry->offImports > 0 && off + pEntry->offImports + pEntry->cImports * 8 <= off)
337 {
338 if (pEntry->offFilename > 0 && pEntry->offFilename < pEntry->cbStrings)
339 {
340 const char *psz = (const char *)&pbBits[off + pEntry->offStrings + pEntry->offFilename];
341 if (strcmp(pszFilename, psz) == 0)
342 return (BS3HIGHDLLENTRY *)pEntry;
343 }
344 }
345 }
346 }
347 }
348 }
349 off--;
350 }
351 RTMsgError("Failed to find the BS3HIGHDLLENTRY structure for '%s'!", pszFilename);
352 return NULL;
353}
354
355
356/**
357 * @callback_method_impl{FNRTLDRIMPORT}
358 */
359static DECLCALLBACK(int) ResolveHighDllImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
360 unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
361{
362 BS3HIGHDLLENTRY * const pEntry = (BS3HIGHDLLENTRY *)pvUser;
363 if (!pszSymbol)
364 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All imports must be by name. pszModule=%s uSymbol=%#x", pszModule, uSymbol);
365
366 /* Search the import table: */
367 BS3HIGHDLLIMPORTENTRY const *paImports = (BS3HIGHDLLIMPORTENTRY const *)((uintptr_t)pEntry + pEntry->offImports);
368 const char * const pszzStrings = (const char *)((uintptr_t)pEntry + pEntry->offStrings);
369 size_t i = pEntry->cImports;
370 while (i-- > 0)
371 {
372 if (strcmp(pszSymbol, &pszzStrings[paImports[i].offName]) == 0)
373 {
374 *pValue = paImports[i].offFlat;
375 return VINF_SUCCESS;
376 }
377 }
378 RT_NOREF(hLdrMod);
379 return RTMsgErrorRc(VERR_SYMBOL_NOT_FOUND, "Unable to locate import %s (in %s)!", pszSymbol, pszModule);
380}
381
382
383static RTEXITCODE DoTheLinking(FILE *pOutput, BS3LNKINPUT *paInputs, unsigned cInputs)
384{
385 if (cInputs < 2)
386 return RTMsgErrorExitFailure("Require at least two input files when linking!");
387
388 /*
389 * Read all the files into memory.
390 *
391 * The first two are binary blobs, i.e. the boot sector and the base image.
392 * Any additional files are DLLs and we need to do linking.
393 */
394 uint32_t uHiLoadAddr = BS3HIGHDLL_LOAD_ADDRESS;
395 uint32_t off = 0;
396 for (unsigned i = 0; i < cInputs; i++)
397 {
398 paInputs[i].offInImage = off;
399 if (i < 2)
400 {
401 paInputs[i].cbBits = RT_ALIGN_32(paInputs[i].cbFile, 512);
402 paInputs[i].pbBits = (uint8_t *)RTMemAllocZ(paInputs[i].cbBits);
403 if (!paInputs[i].pbBits)
404 return RTMsgErrorExitFailure("Out of memory (%#x)\n", paInputs[i].cbBits);
405 size_t cbRead = fread(paInputs[i].pbBits, sizeof(uint8_t), paInputs[i].cbFile, paInputs[i].pFile);
406 if (cbRead != paInputs[i].cbFile)
407 return RTMsgErrorExitFailure("Error reading '%s' (got %d bytes, wanted %u).",
408 paInputs[i].pszFile, (int)cbRead, (unsigned)paInputs[i].cbFile);
409 paInputs[i].uLoadAddr = i == 90 ? 0x7c00 : 0x10000;
410 }
411 else
412 {
413 RTERRINFOSTATIC ErrInfo;
414 int rc = RTLdrOpenEx(paInputs[i].pszFile, 0, RTLDRARCH_X86_32, &paInputs[i].hLdrMod, RTErrInfoInitStatic(&ErrInfo));
415 if (RT_FAILURE(rc))
416 return RTMsgErrorExitFailure("RTLdrOpenEx failed to open '%s': %Rrc%#RTeim",
417 paInputs[i].pszFile, rc, &ErrInfo.Core);
418
419 size_t const cbImage = RTLdrSize(paInputs[i].hLdrMod);
420 if (cbImage == ~(size_t)0)
421 return RTMsgErrorExitFailure("RTLdrSize failed on '%s'!", paInputs[i].pszFile);
422 if (cbImage > _64M)
423 return RTMsgErrorExitFailure("Image '%s' is definitely too large: %#zx", paInputs[i].pszFile, cbImage);
424
425 paInputs[i].cbBits = RT_ALIGN_32((uint32_t)cbImage, 4096); /* Bs3InitHighDlls_rm depend on the 4KiB alignment. */
426 paInputs[i].pbBits = (uint8_t *)RTMemAllocZ(paInputs[i].cbBits);
427 if (!paInputs[i].pbBits)
428 return RTMsgErrorExitFailure("Out of memory (%#x)\n", paInputs[i].cbBits);
429
430 /* Locate the entry for this high dll in the base image. */
431 BS3HIGHDLLENTRY *pHighDllEntry = LocateHighDllEntry(paInputs[1].pbBits, paInputs[1].cbFile,
432 RTPathFilename(paInputs[i].pszFile));
433 AssertReturn(pHighDllEntry, RTEXITCODE_FAILURE);
434 if ( pHighDllEntry->cbLoaded != paInputs[i].cbBits
435 || pHighDllEntry->cbInImage != paInputs[i].cbBits)
436 return RTMsgErrorExitFailure("HighDllEntry fields cbLoaded=%#x and/or cbInImage=%#x differs from cbBits=%#x!",
437 pHighDllEntry->cbLoaded, pHighDllEntry->cbInImage, paInputs[i].cbBits);
438
439 /* Get the fixed up image bits. */
440 rc = RTLdrGetBits(paInputs[i].hLdrMod, paInputs[i].pbBits, uHiLoadAddr, ResolveHighDllImportCallback, pHighDllEntry);
441 if (RT_FAILURE(rc))
442 return RTMsgErrorExitFailure("RTLdrGetBits failed on '%s': %Rrc", paInputs[i].pszFile, rc);
443
444 /* Update the export addresses. */
445 BS3HIGHDLLEXPORTENTRY *paExports = (BS3HIGHDLLEXPORTENTRY *)((uintptr_t)pHighDllEntry + pHighDllEntry->offExports);
446 const char * const pszzStrings = (const char *)((uintptr_t)pHighDllEntry + pHighDllEntry->offStrings);
447 size_t iExport = pHighDllEntry->cExports;
448 while (iExport-- > 0)
449 {
450 const char * const pszSymbol = (const char *)&pszzStrings[paExports[iExport].offName];
451 RTLDRADDR Value = 0;
452 rc = RTLdrGetSymbolEx(paInputs[i].hLdrMod, paInputs[i].pbBits, uHiLoadAddr, UINT32_MAX, pszSymbol, &Value);
453 if (RT_SUCCESS(rc))
454 paExports[iExport].offFlat = (uint32_t)Value;
455 else
456 return RTMsgErrorExitFailure("Failed to resolve '%s' in '%s': %Rrc", pszSymbol, paInputs[i].pszFile, rc);
457 /** @todo Do BS3HIGHDLLEXPORTENTRY::offSeg and idxSel. */
458 }
459
460 /* Update the DLL entry with the load address and file address: */
461 pHighDllEntry->offInImage = off;
462 pHighDllEntry->uLoadAddr = uHiLoadAddr;
463 pHighDllEntry->uChecksum = Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, paInputs[i].pbBits, paInputs[i].cbBits);
464 paInputs[i].uLoadAddr = uHiLoadAddr;
465 uHiLoadAddr += paInputs[i].cbBits;
466#if 0
467 RTMsgInfo("offInImage=%#RX32 / sector #%u LB %#x\n", paInputs[i].offInImage, paInputs[i].offInImage / 512, paInputs[i].cbBits);
468 uint32_t uChecksum = BS3_CALC_CHECKSUM_INITIAL_VALUE;
469 for (unsigned j = 0, cSectors = paInputs[i].cbBits / 512; j < cSectors; j++)
470 RTMsgInfo("sector #%u: %#010RX32 %#010RX32\n", j,
471 uChecksum = Bs3CalcChecksum(uChecksum, &paInputs[i].pbBits[j * 512], 512),
472 Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, &paInputs[i].pbBits[j * 512], 512));
473 if (uChecksum != pHighDllEntry->uChecksum)
474 return RTMsgErrorExitFailure("Checksum calculation error: %#x, expected %#x", uChecksum, pHighDllEntry->uChecksum);
475#endif
476 }
477 Assert(!(off & 0x1ff));
478 off += paInputs[i].cbBits;
479 Assert(!(off & 0x1ff));
480 }
481
482 /** @todo image size check. */
483
484 /*
485 * Patch the BPB with base image sector count and image checksum.
486 */
487 PBS3BOOTSECTOR pBs = (PBS3BOOTSECTOR)paInputs[0].pbBits;
488 if ( memcmp(pBs->abLabel, RT_STR_TUPLE(BS3_LABEL)) == 0
489 && memcmp(pBs->abFSType, RT_STR_TUPLE(BS3_FSTYPE)) == 0
490 && memcmp(pBs->abOemId, RT_STR_TUPLE(BS3_OEMID)) == 0)
491 {
492 pBs->cLargeTotalSectors = (paInputs[0].cbBits + paInputs[1].cbBits) / 512;
493 pBs->cTotalSectors = RT_MIN(paInputs[1].cbBits, _1M) / 512;
494 pBs->dwSerialNumber = Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, paInputs[1].pbBits,
495 pBs->cTotalSectors * 512); /* dwSerialNumber is misaligned */
496 }
497 else
498 return RTMsgErrorExitFailure("Didn't find magic strings in the first file (%s).", paInputs[0].pszFile);
499
500 /*
501 * Write out the image.
502 */
503 for (unsigned i = 0; i < cInputs; i++)
504 {
505 Assert(ftell(pOutput) == (int32_t)paInputs[i].offInImage);
506 ssize_t cbWritten = fwrite(paInputs[i].pbBits, sizeof(uint8_t), paInputs[i].cbBits, pOutput);
507 if (cbWritten != (ssize_t)paInputs[i].cbBits)
508 return RTMsgErrorExitFailure("fwrite failed (%zd vs %zu)", cbWritten, paInputs[i].cbBits);
509 }
510
511 /*
512 * Avoid output sizes that makes the FDC code think it's a single sided
513 * floppy. The BIOS always report double sided floppies, and even if we
514 * the bootsector adjust it's bMaxHeads value when getting a 20h error
515 * we end up with a garbaged image (seems somewhere in the BIOS/FDC it is
516 * still treated as a double sided floppy and we get half the data we want
517 * and with gaps).
518 *
519 * Similarly, if the size is 320KB or 360KB the FDC detects it as a double
520 * sided 5.25" floppy with 40 tracks, while the BIOS keeps reporting a
521 * 1.44MB 3.5" floppy. So, just avoid those sizes too.
522 */
523 uint32_t cbOutput = ftell(pOutput);
524 if ( cbOutput == 512 * 8 * 40 * 1 /* 160kB 5"1/4 SS */
525 || cbOutput == 512 * 9 * 40 * 1 /* 180kB 5"1/4 SS */
526 || cbOutput == 512 * 8 * 40 * 2 /* 320kB 5"1/4 DS */
527 || cbOutput == 512 * 9 * 40 * 2 /* 360kB 5"1/4 DS */ )
528 {
529 static uint8_t const s_abZeroSector[512] = { 0 };
530 if (fwrite(s_abZeroSector, sizeof(uint8_t), sizeof(s_abZeroSector), pOutput) != sizeof(s_abZeroSector))
531 return RTMsgErrorExitFailure("fwrite failed (padding)");
532 }
533
534 return RTEXITCODE_SUCCESS;
535}
536
537int main(int argc, char **argv)
538{
539 int rc = RTR3InitExe(argc, &argv, 0);
540 if (RT_FAILURE(rc))
541 return RTMsgInitFailure(rc);
542
543 /*
544 * Scan the arguments.
545 */
546 static const RTGETOPTDEF s_aOptions[] =
547 {
548 { "--output", 'o', RTGETOPT_REQ_STRING },
549 { "--generate-high-dll-import-table", 'g', RTGETOPT_REQ_STRING },
550 };
551
552 const char *pszOutput = NULL;
553 const char *pszGenAsmFor = NULL;
554 BS3LNKINPUT aInputs[3]; /* 3 = bootsector, low image, high image */
555 unsigned cInputs = 0;
556 uint32_t cSectors = 0;
557
558 RTGETOPTSTATE GetState;
559 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
560 AssertRCReturn(rc, RTEXITCODE_SYNTAX);
561 RTGETOPTUNION ValueUnion;
562 int ch;
563 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
564 {
565 switch (ch)
566 {
567 case 'o':
568 if (pszOutput)
569 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one output file is allowed. You've specified '%s' and '%s'",
570 pszOutput, ValueUnion.psz);
571 pszOutput = ValueUnion.psz;
572 break;
573
574 case 'g':
575 if (pszGenAsmFor)
576 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-high-dll-import-table can only be used once (first for %s, now for %s)",
577 pszGenAsmFor, ValueUnion.psz);
578 pszGenAsmFor = ValueUnion.psz;
579 break;
580
581 case VINF_GETOPT_NOT_OPTION:
582 {
583 if (pszGenAsmFor)
584 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-high-dll-import-table don't take any non-option arguments!");
585
586 /*
587 * Add to input file collection.
588 */
589 if (cInputs >= RT_ELEMENTS(aInputs))
590 return RTMsgErrorExitFailure("Too many input files!");
591 aInputs[cInputs].pszFile = ValueUnion.psz;
592#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
593 FILE *pFile = fopen(aInputs[cInputs].pszFile, "rb");
594#else
595 FILE *pFile = fopen(aInputs[cInputs].pszFile, "r");
596#endif
597 if (pFile)
598 {
599 if (fseek(pFile, 0, SEEK_END) == 0)
600 {
601 aInputs[cInputs].cbFile = (uint32_t)ftell(pFile);
602 if (fseek(pFile, 0, SEEK_SET) == 0)
603 {
604 if (cInputs != 0 || aInputs[cInputs].cbFile == 512)
605 {
606 cSectors += RT_ALIGN_32(aInputs[cInputs].cbFile, 512) / 512;
607 if (cSectors <= BS3_MAX_SIZE / 512 || cInputs > 0)
608 {
609 if (cSectors > 0)
610 {
611 aInputs[cInputs].pbBits = NULL;
612 aInputs[cInputs].cbBits = 0;
613 aInputs[cInputs].hLdrMod = NIL_RTLDRMOD;
614 aInputs[cInputs++].pFile = pFile;
615 pFile = NULL;
616 break;
617 }
618 RTMsgError("empty input file: '%s'", aInputs[cInputs].pszFile);
619 }
620 else
621 RTMsgError("input is too big: %u bytes, %u sectors (max %u bytes, %u sectors)\n"
622 "detected loading '%s'",
623 cSectors * 512, cSectors, BS3_MAX_SIZE, BS3_MAX_SIZE / 512,
624 aInputs[cInputs].pszFile);
625 }
626 else
627 RTMsgError("first input file (%s) must be exactly 512 bytes", aInputs[cInputs].pszFile);
628 }
629 else
630 RTMsgError("seeking to start of '%s' failed", aInputs[cInputs].pszFile);
631 }
632 else
633 RTMsgError("seeking to end of '%s' failed", aInputs[cInputs].pszFile);
634 }
635 else
636 RTMsgError("Failed to open input file '%s' for reading", aInputs[cInputs].pszFile);
637 if (pFile)
638 fclose(pFile);
639 return RTEXITCODE_FAILURE;
640 }
641
642 case 'V':
643 printf("%s\n", "$Revision: 102270 $");
644 return RTEXITCODE_SUCCESS;
645
646 case 'h':
647 printf("usage: %s --output <output> <basemod> [high-dll1... [high-dllN]]\n"
648 " or: %s --output <high-dll.asm> --generate-high-dll-import-table <some.high-dll>\n"
649 " or: %s --help\n"
650 " or: %s --version\n"
651 , argv[0], argv[0], argv[0], argv[0]);
652 return RTEXITCODE_SUCCESS;
653
654 default:
655 return RTGetOptPrintError(ch, &ValueUnion);
656 }
657 }
658
659 if (!pszOutput)
660 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file was specified (-o or --output).");
661
662 if (cInputs == 0 && !pszGenAsmFor)
663 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No input files was specified.");
664
665 /*
666 * Do the job.
667 */
668 /* Open the output file */
669#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
670 FILE *pOutput = fopen(pszOutput, !pszGenAsmFor ? "wb" : "w");
671#else
672 FILE *pOutput = fopen(pszOutput, "w");
673#endif
674 if (!pOutput)
675 RTMsgErrorExitFailure("Failed to open output file '%s' for writing!", pszOutput);
676
677 RTEXITCODE rcExit;
678 if (pszGenAsmFor)
679 rcExit = GenerateHighDllImportTableAssembly(pOutput, pszGenAsmFor);
680 else
681 rcExit = DoTheLinking(pOutput, aInputs, cInputs);
682
683 /* Finally, close the output file (can fail because of buffered data). */
684 if (fclose(pOutput) != 0)
685 rcExit = RTMsgErrorExitFailure("Error closing '%s'!", pszOutput);
686
687 /* Delete the output file on failure. */
688 if (rcExit != RTEXITCODE_SUCCESS)
689 RTFileDelete(pszOutput);
690
691 /* Close the input files and free memory associated with them. */
692 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
693 {
694 if (aInputs[i].pFile)
695 fclose(aInputs[i].pFile);
696 if (aInputs[i].hLdrMod != NIL_RTLDRMOD)
697 RTLdrClose(aInputs[i].hLdrMod);
698 if (aInputs[i].pbBits)
699 RTMemFree(aInputs[i].pbBits);
700 }
701
702 /* Close stderr to make sure it's flushed properly. */
703 fclose(stderr);
704 return rcExit;
705}
706
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette