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 | *********************************************************************************************************************************/
|
---|
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 | uint8_t *pbBits;
|
---|
70 | RTLDRMOD hLdrMod;
|
---|
71 | } BS3LNKINPUT;
|
---|
72 |
|
---|
73 |
|
---|
74 | #define BS3LNK_MAX_SEGMENTS 24
|
---|
75 |
|
---|
76 | typedef 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 |
|
---|
89 | typedef 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 | */
|
---|
103 | static 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 | */
|
---|
170 | static 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 | */
|
---|
234 | static 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 | */
|
---|
254 | static 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 | */
|
---|
272 | static 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 | */
|
---|
291 | static 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 | */
|
---|
321 | static 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 | */
|
---|
336 | static 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 |
|
---|
469 | static 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 | */
|
---|
511 | static 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 | */
|
---|
540 | static 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 |
|
---|
776 | int 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 |
|
---|