VirtualBox

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

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

bs3kit: Speed up high DLL loading by using a larger buffer. Extended the linker to process the segment table as well and output more appropriate address symbols in the base module assembly file. TODO: missing 16-bit selector setup in the bs3kit loader code. bugref:10371

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

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