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 | *********************************************************************************************************************************/
|
---|
61 | typedef 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 |
|
---|
74 | typedef struct BS3LNKIMPORTSTATE
|
---|
75 | {
|
---|
76 | FILE *pOutput;
|
---|
77 | RTSTRSPACE hImportNames;
|
---|
78 | unsigned cImports;
|
---|
79 | unsigned cExports;
|
---|
80 | size_t cbStrings;
|
---|
81 | } BS3LNKIMPORTSTATE;
|
---|
82 |
|
---|
83 | typedef 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 | */
|
---|
96 | static 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 | */
|
---|
119 | static 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 | */
|
---|
138 | static 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 | */
|
---|
156 | static 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 | */
|
---|
172 | static 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 |
|
---|
199 | static 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 |
|
---|
309 | static 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 | */
|
---|
351 | static 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 |
|
---|
375 | static 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 |
|
---|
508 | int 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 |
|
---|