VirtualBox

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

Last change on this file since 106560 was 106061, checked in by vboxsync, 4 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/* $Id: VBoxBs3Linker.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Boot Sector 3 "linker".
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 uint8_t *pbBits;
70 RTLDRMOD hLdrMod;
71} BS3LNKINPUT;
72
73
74#define BS3LNK_MAX_SEGMENTS 24
75
76typedef struct BS3LNKIMPORTSTATE
77{
78 FILE *pOutput;
79 RTSTRSPACE hImportNames;
80 unsigned cImports;
81 unsigned cExports;
82 size_t cbStrings;
83 unsigned cSegments;
84 uint8_t *pbBits;
85 size_t cbBits;
86 BS3HIGHDLLSEGMENT aSegments[BS3LNK_MAX_SEGMENTS];
87} BS3LNKIMPORTSTATE;
88
89typedef struct BS3LNKIMPORTNAME
90{
91 RTSTRSPACECORE Core;
92 size_t offString;
93 RT_FLEXIBLE_ARRAY_EXTENSION
94 char szName[RT_FLEXIBLE_ARRAY];
95} BS3LNKIMPORTNAME;
96
97
98
99/**
100 * @callback_method_impl{FNRTLDRENUMSEGS,
101 * Construct a BS3LNKIMPORTSTATE::aSegments entry and adds it to the assembly.}
102 */
103static DECLCALLBACK(int) GenerateHighDllAsmOutputSegmentTable(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
104{
105 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
106 RT_NOREF(hLdrMod);
107
108 uint32_t const iSegment = pState->cSegments;
109 AssertReturn(iSegment < RT_ELEMENTS(pState->aSegments), VERR_OUT_OF_RANGE);
110 pState->cSegments++;
111
112 /* Address and size. */
113 if (pSeg->cbMapped != NIL_RTLDRADDR)
114 {
115 pState->aSegments[iSegment].uAddr = (uint32_t)(pSeg->RVA + BS3HIGHDLL_LOAD_ADDRESS);
116 pState->aSegments[iSegment].cb = (uint32_t)pSeg->cbMapped;
117 }
118 else
119 {
120 pState->aSegments[iSegment].uAddr = UINT32_MAX;
121 pState->aSegments[iSegment].cb = 0;
122 }
123
124 /* The selector is just the segment index at this point. We'll resolve it during linking. */
125 pState->aSegments[iSegment].idxSel = (uint16_t)iSegment;
126
127 /* Determine the flags. */
128 pState->aSegments[iSegment].fFlags = 0;
129 if (pSeg->fProt & RTMEM_PROT_EXEC)
130 pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_EXEC;
131 if (pSeg->fFlags & RTLDRSEG_FLAG_OS2_CONFORM)
132 pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_CONFORMING;
133 if (pSeg->fFlags & RTLDRSEG_FLAG_16BIT)
134 pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_16BIT;
135 else if (pSeg->RVA != NIL_RTLDRADDR)
136 {
137 /* Have to check the eyecatcher string to see if it's a 32-bit or 64-bit segment. */
138 Assert(pSeg->RVA + 16 < pState->cbBits);
139 char * const pchSeg = (char *)&pState->pbBits[pSeg->RVA];
140 char const chSaved32 = pchSeg[32];
141 pchSeg[32] = '\0';
142 if (RTStrStr(pchSeg, "32"))
143 pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_32BIT;
144 else if (RTStrStr(pchSeg, "64"))
145 pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_64BIT;
146 pchSeg[32] = chSaved32;
147 }
148 //if (pSeg->fFlags & RTLDRSEG_FLAG_OS2_ALIAS16) /* 32-bit or 64-bit segment, but allocate a 16-bit alias to it. no wlink sup. */
149 // pState->aSegments[iSegment].fFlags |= BS3HIGHDLLSEGMENT_F_16BIT;
150
151
152 // BS3HIGHDLLSEGMENT
153 fprintf(pState->pOutput,
154 " dd %#010x ; %u - %.*s\n"
155 " dd %#010x\n"
156 " dw %#x\n"
157 " dw %#x\n",
158 pState->aSegments[iSegment].uAddr, iSegment, pSeg->cchName, pSeg->pszName,
159 pState->aSegments[iSegment].cb,
160 pState->aSegments[iSegment].idxSel,
161 pState->aSegments[iSegment].fFlags);
162
163 return VINF_SUCCESS;
164}
165
166
167/**
168 * @callback_method_impl{FNRTLDRENUMSYMS, Outputs an export table entry.}
169 */
170static DECLCALLBACK(int) GenerateHighDllAsmOutputExportTable(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
171 RTLDRADDR Value, void *pvUser)
172{
173 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
174 if (!pszSymbol || !*pszSymbol)
175 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All exports must be by name. uSymbol=%#x Value=%RX64", uSymbol, (uint64_t)Value);
176
177 /* Translate it to segment+offset. We popuplate the table with indexes
178 here, later we'll replace them with BS3Kit selector values once
179 these are handed out during the linking phase. */
180 uint32_t iSeg = UINT32_MAX;
181 RTLDRADDR offSeg = NIL_RTLDRADDR;
182 int rc = RTLdrRvaToSegOffset(hLdrMod, Value - BS3HIGHDLL_LOAD_ADDRESS, &iSeg, &offSeg);
183 if (RT_SUCCESS(rc))
184 {
185 if (iSeg >= pState->cSegments || offSeg >= pState->aSegments[iSeg].cb)
186 return RTMsgErrorRc(VERR_ADDRESS_TOO_BIG, "Bogus segment + offset translation of '%s' at %#RX64: %x:%RX64",
187 pszSymbol, (uint64_t)Value, iSeg, (uint64_t)offSeg);
188 }
189 else
190 return RTMsgErrorRc(rc, "Failed to translate '%s' at %#RX64 into segment + offset: %Rrc", pszSymbol, (uint64_t)Value);
191
192 // BS3HIGHDLLEXPORTENTRY
193 const char *pszCSymbol = *pszSymbol == '_' ? &pszSymbol[1] : pszSymbol;
194 if (pState->aSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_EXEC)
195 fprintf(pState->pOutput,
196 "BS3_GLOBAL_DATA g_pfn%s, 8\n"
197 " dd 0\n"
198 " dd 0\n"
199 "BS3_GLOBAL_DATA g_fpfn48%s, 6\n"
200 " dd %#010x\n"
201 " dw %#06x\n"
202 " dw %#08x\n",
203 pszCSymbol,
204 pszCSymbol,
205 (uint32_t)offSeg,
206 (uint16_t)iSeg,
207 (unsigned)pState->cbStrings);
208 else
209 fprintf(pState->pOutput,
210 "BS3_GLOBAL_DATA g_p%s, 8\n"
211 " dd 0\n"
212 " dd 0\n"
213 "BS3_GLOBAL_DATA g_fp48%s, 6\n"
214 " dd %#010x\n"
215 " dw %#06x\n"
216 " dw %#08x\n",
217 pszCSymbol,
218 pszCSymbol,
219 (uint32_t)offSeg,
220 (uint16_t)iSeg,
221 (unsigned)pState->cbStrings);
222
223 pState->cbStrings += strlen(pszSymbol) + 1;
224 pState->cExports += 1;
225
226 RT_NOREF(hLdrMod);
227 return VINF_SUCCESS;
228}
229
230
231/**
232 * @callback_method_impl{FNRTSTRSPACECALLBACK, Outputs an import table entry.}
233 */
234static DECLCALLBACK(int) GenerateHighDllAsmOutputImportTable(PRTSTRSPACECORE pStr, void *pvUser)
235{
236 FILE *pOutput = (FILE *)pvUser;
237 BS3LNKIMPORTNAME *pName = (BS3LNKIMPORTNAME *)pStr;
238
239 // BS3HIGHDLLIMPORTENTRY
240 fprintf(pOutput,
241 " dw %#06x\n"
242 " dw seg %s\n"
243 " dd %s\n"
244 " dd %s wrt BS3FLAT\n"
245 , (unsigned)pName->offString, pName->szName, pName->szName, pName->szName);
246
247 return VINF_SUCCESS;
248}
249
250
251/**
252 * @callback_method_impl{FNRTLDRENUMSYMS, Outputs export name string.}
253 */
254static DECLCALLBACK(int) GenerateHighDllAsmOutputExportStrings(RTLDRMOD hLdrMod, const char *pszSymbol, unsigned uSymbol,
255 RTLDRADDR Value, void *pvUser)
256{
257 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
258 if (!pszSymbol || !*pszSymbol)
259 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All exports must be by name. uSymbol=%#x Value=%RX64", uSymbol, (uint64_t)Value);
260
261 fprintf(pState->pOutput, " db '%s', 0\n", pszSymbol);
262 pState->cbStrings += strlen(pszSymbol) + 1;
263
264 RT_NOREF(hLdrMod);
265 return VINF_SUCCESS;
266}
267
268
269/**
270 * @callback_method_impl{FNRTSTRSPACECALLBACK, Outputs import name string.}
271 */
272static DECLCALLBACK(int) GenerateHighDllAsmOutputImportStrings(PRTSTRSPACECORE pStr, void *pvUser)
273{
274 BS3LNKIMPORTSTATE * const pState = (BS3LNKIMPORTSTATE *)pvUser;
275 BS3LNKIMPORTNAME * const pName = (BS3LNKIMPORTNAME *)pStr;
276
277 pName->offString = pState->cbStrings;
278 fprintf(pState->pOutput, " db '%s', 0\n", pName->szName);
279 pState->cbStrings += pName->Core.cchString + 1;
280
281 return VINF_SUCCESS;
282}
283
284
285/**
286 * @callback_method_impl{FNRTLDRIMPORT, Adds import to the import strings.}
287 *
288 * Since LX doesn't have a single import table as such, we collect imported in a
289 * string space while doing RTLdrGetBits.
290 */
291static DECLCALLBACK(int) GenerateHighDllAsmImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
292 unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
293{
294 BS3LNKIMPORTSTATE *pState = (BS3LNKIMPORTSTATE *)pvUser;
295 if (!pszSymbol)
296 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All imports must be by name. pszModule=%s uSymbol=%#x", pszModule, uSymbol);
297 if (!RTStrSpaceGet(&pState->hImportNames, pszSymbol))
298 {
299 size_t const cchSymbol = strlen(pszSymbol);
300 BS3LNKIMPORTNAME * const pName = (BS3LNKIMPORTNAME *)RTMemAlloc(RT_UOFFSETOF_DYN(BS3LNKIMPORTNAME,
301 szName[cchSymbol + 1]));
302 AssertReturn(pName, VERR_NO_MEMORY);
303
304 pName->Core.cchString = cchSymbol;
305 pName->Core.pszString = (char *)memcpy(pName->szName, pszSymbol, cchSymbol + 1);
306 pName->offString = UINT16_MAX;
307
308 AssertReturnStmt(RTStrSpaceInsert(&pState->hImportNames, &pName->Core), RTMemFree(pName),
309 RTMsgErrorRc(VERR_INTERNAL_ERROR, "IPE #1"));
310 pState->cImports++;
311 }
312 *pValue = 0x10042;
313 RT_NOREF(hLdrMod);
314 return VINF_SUCCESS;
315}
316
317
318/**
319 * @callback_method_impl{FNRTLDRENUMSEGS, For counting segments.}
320 */
321static DECLCALLBACK(int) GenerateHighDllAsmCountSegments(RTLDRMOD hLdrMod, PCRTLDRSEG pSeg, void *pvUser)
322{
323 RT_NOREF(hLdrMod, pSeg);
324 *(uint32_t *)pvUser += 1;
325 return VINF_SUCCESS;
326}
327
328
329/**
330 * Main worker function for --generate-high-dll-import-table.
331 *
332 * @returns Exit status.
333 * @param pOutput The assembly output file.
334 * @param pszGenAsmFor The name of the DLL to generate info for.
335 */
336static RTEXITCODE GenerateHighDllImportTableAssembly(FILE *pOutput, const char *pszGenAsmFor)
337{
338 RTERRINFOSTATIC ErrInfo;
339 RTLDRMOD hLdrMod;
340 int rc = RTLdrOpenEx(pszGenAsmFor, 0, RTLDRARCH_X86_32, &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
341 if (RT_FAILURE(rc))
342 return RTMsgErrorExitFailure("RTLdrOpenEx failed to open '%s': %Rrc%#RTeim", pszGenAsmFor, rc, &ErrInfo.Core);
343
344 RTEXITCODE rcExit;
345 size_t cbImage = RTLdrSize(hLdrMod);
346 if (cbImage != ~(size_t)0)
347 {
348 uint32_t cSegments = 0;
349 rc = RTLdrEnumSegments(hLdrMod, GenerateHighDllAsmCountSegments, &cSegments);
350 if (RT_SUCCESS(rc) && cSegments >= 1 && cSegments <= BS3LNK_MAX_SEGMENTS)
351 {
352 uint8_t *pbBits = (uint8_t *)RTMemAlloc(cbImage);
353 if (pbBits)
354 {
355 BS3LNKIMPORTSTATE State = { pOutput, NULL, 0, 0, 0, 0, pbBits, cbImage };
356 rc = RTLdrGetBits(hLdrMod, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmImportCallback, pOutput);
357 if (RT_SUCCESS(rc))
358 {
359 /** @todo move more of this to bs3kit*.h? */
360 fprintf(pOutput,
361 ";\n"
362 "; Automatically generated - DO NOT MODIFY!\n"
363 ";\n"
364 "%%include \"bs3kit.mac\"\n"
365 "\n"
366 "section BS3HIGHDLLSEGMENTS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
367 "section BS3HIGHDLLEXPORTS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
368 "section BS3HIGHDLLIMPORTS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
369 "section BS3HIGHDLLSTRINGS align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
370 "section BS3HIGHDLLTABLE align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
371 "section BS3HIGHDLLTABLE_END align=4 CLASS=BS3HIGHDLLCLASS PUBLIC USE32 FLAT\n"
372 "GROUP BS3HIGHDLLGROUP BS3HIGHDLLSEGMENTS BS3HIGHDLLEXPORTS BS3HIGHDLLIMPORTS BS3HIGHDLLSTRINGS BS3HIGHDLLTABLE BS3HIGHDLLTABLE_END\n"
373 "\n");
374
375 /* Populate the string table with imports. */
376 const char *pszFilename = RTPathFilename(pszGenAsmFor);
377 fprintf(pOutput,
378 "section BS3HIGHDLLSTRINGS\n"
379 "start_strings:\n"
380 " db 0\n"
381 " db '%s', 0 ; module name\n"
382 " ; imports\n",
383 pszFilename);
384 State.cbStrings = 1 + strlen(pszFilename) + 1;
385 rc = RTStrSpaceEnumerate(&State.hImportNames, GenerateHighDllAsmOutputImportStrings, &State);
386 AssertRC(rc);
387 fprintf(pOutput, " ; exports\n");
388
389 /* Populate the string table with exports. */
390 size_t const offExportStrings = State.cbStrings;
391 rc = RTLdrEnumSymbols(hLdrMod, 0, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmOutputExportStrings, &State);
392 size_t const cbStrings = State.cbStrings;
393 if (RT_SUCCESS(rc) && cbStrings < _64K)
394 {
395 rcExit = RTEXITCODE_SUCCESS;
396
397 /* Output the import table. */
398 fprintf(pOutput,
399 "section BS3HIGHDLLIMPORTS\n"
400 "start_imports:\n");
401 rc = RTStrSpaceEnumerate(&State.hImportNames, GenerateHighDllAsmOutputImportTable, &State);
402 AssertRCStmt(rc, rcExit = RTEXITCODE_FAILURE);
403 fprintf(pOutput, "\n");
404
405 /* Output the segment table (before exports, so we get the segment info). */
406 fprintf(pOutput,
407 "section BS3HIGHDLLSEGMENTS\n"
408 "start_segments:\n");
409 rc = RTLdrEnumSegments(hLdrMod, GenerateHighDllAsmOutputSegmentTable, &State);
410 AssertRCStmt(rc, rcExit = RTEXITCODE_FAILURE);
411
412 /* Output the export table (ASSUMES stable enumeration order). */
413 fprintf(pOutput,
414 "section BS3HIGHDLLEXPORTS\n"
415 "start_exports:\n");
416 State.cbStrings = offExportStrings;
417 rc = RTLdrEnumSymbols(hLdrMod, 0, pbBits, BS3HIGHDLL_LOAD_ADDRESS, GenerateHighDllAsmOutputExportTable, &State);
418 AssertRCStmt(rc, rcExit = RTEXITCODE_FAILURE);
419 fprintf(pOutput, "\n");
420
421 /* Generate the table entry. */
422 fprintf(pOutput,
423 "section BS3HIGHDLLTABLE\n"
424 "start_entry: ; struct BS3HIGHDLLENTRY\n"
425 " db '%s', 0 ; achMagic[8]\n"
426 " dd 0 ; uLoadAddress\n"
427 " dd %#08zx ; cbLoaded\n"
428 " dd 0 ; offInImage\n"
429 " dd %#08zx ; cbInImage\n"
430 " dd %#04x ; cImports\n"
431 " dd start_imports - start_entry\n"
432 " dd %#04x ; cExports\n"
433 " dd start_exports - start_entry\n"
434 " dd %#04x ; cSegments\n"
435 " dd start_segments - start_entry\n"
436 " dd %#05x ; cbStrings\n"
437 " dd start_strings - start_entry\n"
438 " dd 1 ; offDllName\n"
439 " dd 0 ; uChecksum\n"
440 , BS3HIGHDLLENTRY_MAGIC, cbImage, cbImage,
441 State.cImports, State.cExports, State.cSegments,
442 (unsigned)cbStrings);
443 }
444 else if (RT_FAILURE(rc))
445 rcExit = RTMsgErrorExitFailure("RTLdrEnumSymbols failed: %Rrc", rc);
446 else
447 rcExit = RTMsgErrorExitFailure("Too many import/export strings: %#x bytes, max 64KiB", cbStrings);
448 }
449 else
450 rcExit = RTMsgErrorExitFailure("RTLdrGetBits failed: %Rrc", rc);
451 RTMemFree(pbBits);
452 }
453 else
454 rcExit = RTMsgErrorExitFailure("Out of memory!");
455 }
456 else if (RT_FAILURE(rc))
457 rcExit = RTMsgErrorExitFailure("RTLdrEnumSegment failed: %Rrc", rc);
458 else
459 rcExit = RTMsgErrorExitFailure("Bogus segment count: %#x (min 1, max 24)\n", cSegments);
460 }
461 else
462 rcExit = RTMsgErrorExitFailure("RTLdrSize failed on '%s'", pszGenAsmFor);
463
464 RTLdrClose(hLdrMod);
465 return rcExit;
466}
467
468
469static BS3HIGHDLLENTRY *LocateHighDllEntry(uint8_t const *pbBits, uint32_t cbBits, const char *pszFilename)
470{
471 /*
472 * We search backwards for up to 4 KB.
473 */
474 size_t const offStop = cbBits > _4K ? cbBits - _4K : 0;
475 size_t off = cbBits >= sizeof(BS3HIGHDLLENTRY) ? cbBits - sizeof(BS3HIGHDLLENTRY) : 0;
476 while (off > offStop)
477 {
478 BS3HIGHDLLENTRY const *pEntry = (BS3HIGHDLLENTRY const *)&pbBits[off];
479 if ( pEntry->achMagic[0] == BS3HIGHDLLENTRY_MAGIC[0]
480 && memcmp(pEntry->achMagic, BS3HIGHDLLENTRY_MAGIC, sizeof(pEntry->achMagic)) == 0)
481 {
482 if (pEntry->cbStrings < _64K && pEntry->cbStrings >= 8)
483 {
484 if (off + pEntry->offStrings > 0 && off + pEntry->offStrings + pEntry->cbStrings <= off)
485 {
486 if (off + pEntry->offExports > 0 && off + pEntry->offExports + pEntry->cExports * 8 <= off)
487 {
488 if (off + pEntry->offImports > 0 && off + pEntry->offImports + pEntry->cImports * 8 <= off)
489 {
490 if (pEntry->offFilename > 0 && pEntry->offFilename < pEntry->cbStrings)
491 {
492 const char *psz = (const char *)&pbBits[off + pEntry->offStrings + pEntry->offFilename];
493 if (strcmp(pszFilename, psz) == 0)
494 return (BS3HIGHDLLENTRY *)pEntry;
495 }
496 }
497 }
498 }
499 }
500 }
501 off--;
502 }
503 RTMsgError("Failed to find the BS3HIGHDLLENTRY structure for '%s'!", pszFilename);
504 return NULL;
505}
506
507
508/**
509 * @callback_method_impl{FNRTLDRIMPORT}
510 */
511static DECLCALLBACK(int) ResolveHighDllImportCallback(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
512 unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
513{
514 BS3HIGHDLLENTRY * const pEntry = (BS3HIGHDLLENTRY *)pvUser;
515 if (!pszSymbol)
516 return RTMsgErrorRc(VERR_LDR_BAD_FIXUP, "All imports must be by name. pszModule=%s uSymbol=%#x", pszModule, uSymbol);
517
518 /* Search the import table: */
519 BS3HIGHDLLIMPORTENTRY const *paImports = (BS3HIGHDLLIMPORTENTRY const *)((uintptr_t)pEntry + pEntry->offImports);
520 const char * const pszzStrings = (const char *)((uintptr_t)pEntry + pEntry->offStrings);
521 size_t i = pEntry->cImports;
522 while (i-- > 0)
523 {
524 if (strcmp(pszSymbol, &pszzStrings[paImports[i].offName]) == 0)
525 {
526 /** @todo the import interface isn't good enough for segmented fixups like LX
527 * uses. So we need to fix that at some point... */
528 *pValue = paImports[i].offFlat;
529 return VINF_SUCCESS;
530 }
531 }
532 RT_NOREF(hLdrMod);
533 return RTMsgErrorRc(VERR_SYMBOL_NOT_FOUND, "Unable to locate import %s (in %s)!", pszSymbol, pszModule);
534}
535
536
537/**
538 * Main worker for linking a floppy image.
539 */
540static RTEXITCODE DoTheLinking(FILE *pOutput, BS3LNKINPUT *paInputs, unsigned cInputs)
541{
542 if (cInputs < 2)
543 return RTMsgErrorExitFailure("Require at least two input files when linking!");
544
545 /*
546 * Read all the files into memory.
547 *
548 * The first two are binary blobs, i.e. the boot sector and the base image.
549 * Any additional files are DLLs and we need to do linking.
550 */
551 uint32_t uHiLoadAddr = BS3HIGHDLL_LOAD_ADDRESS;
552 uint16_t idxHi16BitCs = 0;
553 uint16_t idxHi16BitDs = 0;
554 uint32_t off = 0;
555 for (unsigned i = 0; i < cInputs; i++)
556 {
557 paInputs[i].offInImage = off;
558 if (i < 2)
559 {
560 /* Bootsector or the base image. */
561 paInputs[i].cbBits = RT_ALIGN_32(paInputs[i].cbFile, 512);
562 paInputs[i].pbBits = (uint8_t *)RTMemAllocZ(paInputs[i].cbBits);
563 if (!paInputs[i].pbBits)
564 return RTMsgErrorExitFailure("Out of memory (%#x)\n", paInputs[i].cbBits);
565 size_t cbRead = fread(paInputs[i].pbBits, sizeof(uint8_t), paInputs[i].cbFile, paInputs[i].pFile);
566 if (cbRead != paInputs[i].cbFile)
567 return RTMsgErrorExitFailure("Error reading '%s' (got %d bytes, wanted %u).",
568 paInputs[i].pszFile, (int)cbRead, (unsigned)paInputs[i].cbFile);
569 paInputs[i].uLoadAddr = i == 90 ? 0x7c00 : 0x10000;
570 }
571 else
572 {
573 /*
574 * A DLL that will be loaded above 1MB.
575 */
576 RTERRINFOSTATIC ErrInfo;
577 int rc = RTLdrOpenEx(paInputs[i].pszFile, 0, RTLDRARCH_X86_32, &paInputs[i].hLdrMod, RTErrInfoInitStatic(&ErrInfo));
578 if (RT_FAILURE(rc))
579 return RTMsgErrorExitFailure("RTLdrOpenEx failed to open '%s': %Rrc%#RTeim",
580 paInputs[i].pszFile, rc, &ErrInfo.Core);
581
582 size_t const cbImage = RTLdrSize(paInputs[i].hLdrMod);
583 if (cbImage == ~(size_t)0)
584 return RTMsgErrorExitFailure("RTLdrSize failed on '%s'!", paInputs[i].pszFile);
585 if (cbImage > _64M)
586 return RTMsgErrorExitFailure("Image '%s' is definitely too large: %#zx", paInputs[i].pszFile, cbImage);
587
588 paInputs[i].cbBits = RT_ALIGN_32((uint32_t)cbImage, 4096); /* Bs3InitHighDlls_rm depend on the 4KiB alignment. */
589 paInputs[i].pbBits = (uint8_t *)RTMemAllocZ(paInputs[i].cbBits);
590 if (!paInputs[i].pbBits)
591 return RTMsgErrorExitFailure("Out of memory (%#x)\n", paInputs[i].cbBits);
592
593 /* Locate the entry for this high dll in the base image. */
594 BS3HIGHDLLENTRY *pHighDllEntry = LocateHighDllEntry(paInputs[1].pbBits, paInputs[1].cbFile,
595 RTPathFilename(paInputs[i].pszFile));
596 AssertReturn(pHighDllEntry, RTEXITCODE_FAILURE);
597 if ( pHighDllEntry->cbLoaded != paInputs[i].cbBits
598 || pHighDllEntry->cbInImage != paInputs[i].cbBits)
599 return RTMsgErrorExitFailure("HighDllEntry fields cbLoaded=%#x and/or cbInImage=%#x differs from cbBits=%#x!",
600 pHighDllEntry->cbLoaded, pHighDllEntry->cbInImage, paInputs[i].cbBits);
601
602 /* Update the segment table with actual selectors. */
603 uint32_t const cSegments = pHighDllEntry->cSegments;
604 BS3HIGHDLLSEGMENT *paSegments = (BS3HIGHDLLSEGMENT *)((uintptr_t)pHighDllEntry + pHighDllEntry->offSegments);
605 if (cSegments < 1 || cSegments > 32)
606 return RTMsgErrorExitFailure("Bogus segment count for '%s': %u", paInputs[i].pszFile, cSegments);
607 for (unsigned iSeg = 0; iSeg < cSegments; iSeg++)
608 {
609 if (paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_16BIT)
610 {
611 if (paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_EXEC)
612 {
613 if (idxHi16BitCs >= BS3_SEL_HIGH16_CS_COUNT)
614 return RTMsgErrorExitFailure("Out of 16-bit CS selectors ('%s')!", paInputs[i].pszFile);
615 paSegments[iSeg].idxSel = BS3_SEL_HIGH16_CS_FIRST + idxHi16BitCs * 8;
616 idxHi16BitCs++;
617 rc = RTLdrLxSetSegmentSelectors(paInputs[i].hLdrMod, iSeg, paSegments[iSeg].idxSel, BS3_SEL_HIGH32_CS);
618 }
619 else
620 {
621 if (idxHi16BitDs >= BS3_SEL_HIGH16_DS_COUNT)
622 return RTMsgErrorExitFailure("Out of 16-bit DS selectors ('%s')!", paInputs[i].pszFile);
623 paSegments[iSeg].idxSel = BS3_SEL_HIGH16_DS_FIRST + idxHi16BitDs * 8;
624 idxHi16BitDs++;
625 rc = RTLdrLxSetSegmentSelectors(paInputs[i].hLdrMod, iSeg, paSegments[iSeg].idxSel, BS3_SEL_HIGH32_DS);
626 }
627 }
628 else
629 {
630 if (paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_32BIT)
631 paSegments[iSeg].idxSel = paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_EXEC
632 ? BS3_SEL_HIGH32_CS : BS3_SEL_HIGH32_DS;
633 else if (paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_64BIT)
634 paSegments[iSeg].idxSel = paSegments[iSeg].fFlags & BS3HIGHDLLSEGMENT_F_EXEC
635 ? BS3_SEL_HIGH64_CS : BS3_SEL_HIGH64_DS;
636 else
637 paSegments[iSeg].idxSel = 0;
638 rc = RTLdrLxSetSegmentSelectors(paInputs[i].hLdrMod, iSeg, 4, paSegments[iSeg].idxSel);
639 }
640 if (RT_FAILURE(rc))
641 return RTMsgErrorExitFailure("RTLdrLxSetSegmentSelectors failed segment #%u in '%s': %Rrc",
642 iSeg, paInputs[i].pszFile, rc);
643 paSegments[iSeg].uAddr += uHiLoadAddr - BS3HIGHDLL_LOAD_ADDRESS; /* Was RVA + BS3HIGHDLL_LOAD_ADDRESS. */
644 }
645
646 /* Update the export addresses. */
647 BS3HIGHDLLEXPORTENTRY *paExports = (BS3HIGHDLLEXPORTENTRY *)((uintptr_t)pHighDllEntry + pHighDllEntry->offExports);
648 const char * const pszzStrings = (const char *)((uintptr_t)pHighDllEntry + pHighDllEntry->offStrings);
649 size_t iExport = pHighDllEntry->cExports;
650 while (iExport-- > 0)
651 {
652 const char * const pszSymbol = (const char *)&pszzStrings[paExports[iExport].offName];
653 uint16_t const idxSeg = paExports[iExport].idxSel;
654 if (idxSeg >= cSegments)
655 return RTMsgErrorExitFailure("Bogus idxSel for '%s' in '%s': %#x(:%#x)",
656 pszSymbol, paInputs[i].pszFile, idxSeg, paExports[iExport].offSeg);
657 RTLDRADDR Value = 0;
658 rc = RTLdrGetSymbolEx(paInputs[i].hLdrMod, paInputs[i].pbBits, uHiLoadAddr, UINT32_MAX, pszSymbol, &Value);
659 if (RT_SUCCESS(rc))
660 {
661 if (Value != paExports[iExport].offSeg + paSegments[idxSeg].uAddr)
662 return RTMsgErrorExitFailure("Bogus value for '%s' in '%s': %#RX64, expected %#RX64",
663 pszSymbol, paInputs[i].pszFile, Value,
664 paExports[iExport].offSeg + paSegments[idxSeg].uAddr);
665 paExports[iExport].offFlat = (uint32_t)Value;
666 paExports[iExport].idxSel = paSegments[idxSeg].idxSel;
667 if (paSegments[idxSeg].fFlags & (BS3HIGHDLLSEGMENT_F_32BIT | BS3HIGHDLLSEGMENT_F_64BIT))
668 paExports[iExport].offSeg = (uint32_t)Value; /* 32-bit and 64-bit uses FLAT selectors, so FLAT addresses too. */
669 }
670 else
671 return RTMsgErrorExitFailure("Failed to resolve '%s' in '%s': %Rrc", pszSymbol, paInputs[i].pszFile, rc);
672 }
673
674 /* Get the fixed up image bits. */
675 rc = RTLdrGetBits(paInputs[i].hLdrMod, paInputs[i].pbBits, uHiLoadAddr, ResolveHighDllImportCallback, pHighDllEntry);
676 if (RT_FAILURE(rc))
677 return RTMsgErrorExitFailure("RTLdrGetBits failed on '%s': %Rrc", paInputs[i].pszFile, rc);
678
679 /* Update the DLL entry with the load address and file address: */
680 pHighDllEntry->offInImage = off;
681 pHighDllEntry->uLoadAddr = uHiLoadAddr;
682 pHighDllEntry->uChecksum = Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, paInputs[i].pbBits, paInputs[i].cbBits);
683 paInputs[i].uLoadAddr = uHiLoadAddr;
684 uHiLoadAddr += paInputs[i].cbBits;
685#if 0
686 RTMsgInfo("offInImage=%#RX32 / sector #%u LB %#x\n", paInputs[i].offInImage, paInputs[i].offInImage / 512, paInputs[i].cbBits);
687 uint32_t uChecksum = BS3_CALC_CHECKSUM_INITIAL_VALUE;
688 for (unsigned j = 0, cSectors = paInputs[i].cbBits / 512; j < cSectors; j++)
689 RTMsgInfo("sector #%u: %#010RX32 %#010RX32\n", j,
690 uChecksum = Bs3CalcChecksum(uChecksum, &paInputs[i].pbBits[j * 512], 512),
691 Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, &paInputs[i].pbBits[j * 512], 512));
692 if (uChecksum != pHighDllEntry->uChecksum)
693 return RTMsgErrorExitFailure("Checksum calculation error: %#x, expected %#x", uChecksum, pHighDllEntry->uChecksum);
694#endif
695 }
696 Assert(!(off & 0x1ff));
697 off += paInputs[i].cbBits;
698 Assert(!(off & 0x1ff));
699 }
700
701 /** @todo image size check. */
702
703 /*
704 * Patch the BPB with base image sector count and image checksum.
705 */
706 PBS3BOOTSECTOR pBs = (PBS3BOOTSECTOR)paInputs[0].pbBits;
707 if ( memcmp(pBs->abLabel, RT_STR_TUPLE(BS3_LABEL)) == 0
708 && memcmp(pBs->abFSType, RT_STR_TUPLE(BS3_FSTYPE)) == 0
709 && memcmp(pBs->abOemId, RT_STR_TUPLE(BS3_OEMID)) == 0)
710 {
711 pBs->cLargeTotalSectors = (paInputs[0].cbBits + paInputs[1].cbBits) / 512;
712 pBs->cTotalSectors = RT_MIN(paInputs[1].cbBits, _1M) / 512;
713 pBs->dwSerialNumber = Bs3CalcChecksum(BS3_CALC_CHECKSUM_INITIAL_VALUE, paInputs[1].pbBits,
714 pBs->cTotalSectors * 512); /* dwSerialNumber is misaligned */
715 }
716 else
717 return RTMsgErrorExitFailure("Didn't find magic strings in the first file (%s).", paInputs[0].pszFile);
718
719 /*
720 * Write out the image.
721 */
722 for (unsigned i = 0; i < cInputs; i++)
723 {
724 Assert(ftell(pOutput) == (int32_t)paInputs[i].offInImage);
725 ssize_t cbWritten = fwrite(paInputs[i].pbBits, sizeof(uint8_t), paInputs[i].cbBits, pOutput);
726 if (cbWritten != (ssize_t)paInputs[i].cbBits)
727 return RTMsgErrorExitFailure("fwrite failed (%zd vs %zu)", cbWritten, paInputs[i].cbBits);
728 }
729
730 /*
731 * Avoid output sizes that makes the FDC code think it's a single sided
732 * floppy. The BIOS always report double sided floppies, and even if we
733 * the bootsector adjust it's bMaxHeads value when getting a 20h error
734 * we end up with a garbaged image (seems somewhere in the BIOS/FDC it is
735 * still treated as a double sided floppy and we get half the data we want
736 * and with gaps).
737 *
738 * Similarly, if the size is 320KB or 360KB the FDC detects it as a double
739 * sided 5.25" floppy with 40 tracks, while the BIOS keeps reporting a
740 * 1.44MB 3.5" floppy. So, just avoid those sizes too.
741 *
742 * There are many problem sizes like 400kB, 820kB; see fd_formats[] in
743 * src/VBox/Devices/Storage/DevFdc.cpp. Every size in the table is an
744 * even number of kB; let's just pad to an odd number number of kB.
745 *
746 * Controller-recognized sizes cause boot failure in bs3-bootsector.asm.
747 * When avoiding 64kB DMA boundaries it tries to read a sector outside the
748 * guessed geometry, receiving a hard I/O error.
749 */
750 uint32_t cbOutput = ftell(pOutput);
751#if 1
752 uint32_t uBoundary = cbOutput % (2 * 1024);
753 if (uBoundary != 1024)
754 {
755 static uint8_t const s_abZeroBytes[2 * 1024] = { 0 };
756 uint32_t cbPaddingNeeded = (uBoundary < 1024) ? 1024 - uBoundary : 3 * 1024 - uBoundary;
757
758 if (fwrite(s_abZeroBytes, sizeof(uint8_t), cbPaddingNeeded, pOutput) != cbPaddingNeeded)
759 return RTMsgErrorExitFailure("fwrite failed (padding)");
760 }
761#else
762 if ( cbOutput == 512 * 8 * 40 * 1 /* 160kB 5"1/4 SS */
763 || cbOutput == 512 * 9 * 40 * 1 /* 180kB 5"1/4 SS */
764 || cbOutput == 512 * 8 * 40 * 2 /* 320kB 5"1/4 DS */
765 || cbOutput == 512 * 9 * 40 * 2 /* 360kB 5"1/4 DS */ )
766 {
767 static uint8_t const s_abZeroSector[512] = { 0 };
768 if (fwrite(s_abZeroSector, sizeof(uint8_t), sizeof(s_abZeroSector), pOutput) != sizeof(s_abZeroSector))
769 return RTMsgErrorExitFailure("fwrite failed (padding)");
770 }
771#endif
772
773 return RTEXITCODE_SUCCESS;
774}
775
776int main(int argc, char **argv)
777{
778 int rc = RTR3InitExe(argc, &argv, 0);
779 if (RT_FAILURE(rc))
780 return RTMsgInitFailure(rc);
781
782 /*
783 * Scan the arguments.
784 */
785 static const RTGETOPTDEF s_aOptions[] =
786 {
787 { "--output", 'o', RTGETOPT_REQ_STRING },
788 { "--generate-high-dll-import-table", 'g', RTGETOPT_REQ_STRING },
789 };
790
791 const char *pszOutput = NULL;
792 const char *pszGenAsmFor = NULL;
793 BS3LNKINPUT aInputs[3]; /* 3 = bootsector, low image, high image */
794 unsigned cInputs = 0;
795 uint32_t cSectors = 0;
796
797 RTGETOPTSTATE GetState;
798 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
799 AssertRCReturn(rc, RTEXITCODE_SYNTAX);
800 RTGETOPTUNION ValueUnion;
801 int ch;
802 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
803 {
804 switch (ch)
805 {
806 case 'o':
807 if (pszOutput)
808 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one output file is allowed. You've specified '%s' and '%s'",
809 pszOutput, ValueUnion.psz);
810 pszOutput = ValueUnion.psz;
811 break;
812
813 case 'g':
814 if (pszGenAsmFor)
815 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-high-dll-import-table can only be used once (first for %s, now for %s)",
816 pszGenAsmFor, ValueUnion.psz);
817 pszGenAsmFor = ValueUnion.psz;
818 break;
819
820 case VINF_GETOPT_NOT_OPTION:
821 {
822 if (pszGenAsmFor)
823 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-high-dll-import-table don't take any non-option arguments!");
824
825 /*
826 * Add to input file collection.
827 */
828 if (cInputs >= RT_ELEMENTS(aInputs))
829 return RTMsgErrorExitFailure("Too many input files!");
830 aInputs[cInputs].pszFile = ValueUnion.psz;
831#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
832 FILE *pFile = fopen(aInputs[cInputs].pszFile, "rb");
833#else
834 FILE *pFile = fopen(aInputs[cInputs].pszFile, "r");
835#endif
836 if (pFile)
837 {
838 if (fseek(pFile, 0, SEEK_END) == 0)
839 {
840 aInputs[cInputs].cbFile = (uint32_t)ftell(pFile);
841 if (fseek(pFile, 0, SEEK_SET) == 0)
842 {
843 if (cInputs != 0 || aInputs[cInputs].cbFile == 512)
844 {
845 cSectors += RT_ALIGN_32(aInputs[cInputs].cbFile, 512) / 512;
846 if (cSectors <= BS3_MAX_SIZE / 512 || cInputs > 0)
847 {
848 if (cSectors > 0)
849 {
850 aInputs[cInputs].pbBits = NULL;
851 aInputs[cInputs].cbBits = 0;
852 aInputs[cInputs].hLdrMod = NIL_RTLDRMOD;
853 aInputs[cInputs++].pFile = pFile;
854 pFile = NULL;
855 break;
856 }
857 RTMsgError("empty input file: '%s'", aInputs[cInputs].pszFile);
858 }
859 else
860 RTMsgError("input is too big: %u bytes, %u sectors (max %u bytes, %u sectors)\n"
861 "detected loading '%s'",
862 cSectors * 512, cSectors, BS3_MAX_SIZE, BS3_MAX_SIZE / 512,
863 aInputs[cInputs].pszFile);
864 }
865 else
866 RTMsgError("first input file (%s) must be exactly 512 bytes", aInputs[cInputs].pszFile);
867 }
868 else
869 RTMsgError("seeking to start of '%s' failed", aInputs[cInputs].pszFile);
870 }
871 else
872 RTMsgError("seeking to end of '%s' failed", aInputs[cInputs].pszFile);
873 }
874 else
875 RTMsgError("Failed to open input file '%s' for reading", aInputs[cInputs].pszFile);
876 if (pFile)
877 fclose(pFile);
878 return RTEXITCODE_FAILURE;
879 }
880
881 case 'V':
882 printf("%s\n", "$Revision: 106061 $");
883 return RTEXITCODE_SUCCESS;
884
885 case 'h':
886 printf("usage: %s --output <output> <basemod> [high-dll1... [high-dllN]]\n"
887 " or: %s --output <high-dll.asm> --generate-high-dll-import-table <some.high-dll>\n"
888 " or: %s --help\n"
889 " or: %s --version\n"
890 , argv[0], argv[0], argv[0], argv[0]);
891 return RTEXITCODE_SUCCESS;
892
893 default:
894 return RTGetOptPrintError(ch, &ValueUnion);
895 }
896 }
897
898 if (!pszOutput)
899 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file was specified (-o or --output).");
900
901 if (cInputs == 0 && !pszGenAsmFor)
902 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No input files was specified.");
903
904 /*
905 * Do the job.
906 */
907 /* Open the output file */
908#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
909 FILE *pOutput = fopen(pszOutput, !pszGenAsmFor ? "wb" : "w");
910#else
911 FILE *pOutput = fopen(pszOutput, "w");
912#endif
913 if (!pOutput)
914 RTMsgErrorExitFailure("Failed to open output file '%s' for writing!", pszOutput);
915
916 RTEXITCODE rcExit;
917 if (pszGenAsmFor)
918 rcExit = GenerateHighDllImportTableAssembly(pOutput, pszGenAsmFor);
919 else
920 rcExit = DoTheLinking(pOutput, aInputs, cInputs);
921
922 /* Finally, close the output file (can fail because of buffered data). */
923 if (fclose(pOutput) != 0)
924 rcExit = RTMsgErrorExitFailure("Error closing '%s'!", pszOutput);
925
926 /* Delete the output file on failure. */
927 if (rcExit != RTEXITCODE_SUCCESS)
928 RTFileDelete(pszOutput);
929
930 /* Close the input files and free memory associated with them. */
931 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
932 {
933 if (aInputs[i].pFile)
934 fclose(aInputs[i].pFile);
935 if (aInputs[i].hLdrMod != NIL_RTLDRMOD)
936 RTLdrClose(aInputs[i].hLdrMod);
937 if (aInputs[i].pbBits)
938 RTMemFree(aInputs[i].pbBits);
939 }
940
941 /* Close stderr to make sure it's flushed properly. */
942 fclose(stderr);
943 return rcExit;
944}
945
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