VirtualBox

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

Last change on this file since 102157 was 102157, checked in by vboxsync, 16 months ago

ValKit/bs3kit,bs3-cpu-basic-3: Experimental support for loading a 2nd test image above 1MB (at 8MB by default). This should allow for larger testcases, esp. for 32-bit and 64-bit code. bugref:10371

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