VirtualBox

source: vbox/trunk/src/bldprogs/VBoxEditCoffLib.cpp@ 107044

Last change on this file since 107044 was 106398, checked in by vboxsync, 5 weeks ago

bldprogs: Add support for arm64 for VBoxEditCoffLib and VBoxPeSetVersion, bugref:10734

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: VBoxEditCoffLib.cpp 106398 2024-10-16 20:53:42Z vboxsync $ */
2/** @file
3 * VBoxEditCoffLib - Simple COFF editor for library files.
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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <stdio.h>
33#include <string.h>
34#include <stdlib.h>
35
36#include <iprt/assertcompile.h>
37#include <iprt/types.h>
38#include <iprt/ctype.h>
39#include <iprt/formats/pecoff.h>
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45typedef struct ARHDR
46{
47 char achName[16];
48 char achDate[12];
49 char achUid[6];
50 char achGid[6];
51 char achMode[8];
52 char achSize[10];
53 char achMagic[2];
54} ARHDR;
55AssertCompileSize(ARHDR, 16+12+6+6+8+10+2);
56
57
58
59/*********************************************************************************************************************************
60* Global Variables *
61*********************************************************************************************************************************/
62/** Verbosity level. */
63static int g_cVerbosity = 0;
64
65/** The binary size. */
66static unsigned g_cbBinary = 0;
67/** The binary data we're editing. */
68static uint8_t *g_pbBinary = NULL;
69
70/** Size of the currently selected member. */
71static unsigned g_cbMember = 0;
72/** Pointer to the data for the currently selected member. */
73static uint8_t *g_pbMember = NULL;
74
75
76/**
77 * File size.
78 *
79 * @returns file size in bytes.
80 * @returns 0 on failure.
81 * @param pFile File to size.
82 */
83static unsigned fsize(FILE *pFile)
84{
85 long cbFile;
86 off_t Pos = ftell(pFile);
87 if ( Pos >= 0
88 && !fseek(pFile, 0, SEEK_END))
89 {
90 cbFile = ftell(pFile);
91 if ( cbFile >= 0
92 && !fseek(pFile, 0, SEEK_SET))
93 return cbFile;
94 }
95 return 0;
96}
97
98
99/**
100 * Reports a problem.
101 *
102 * @returns RTEXITCODE_FAILURE
103 */
104static int error(const char *pszFormat, ...)
105{
106 fprintf(stderr, "error: ");
107 va_list va;
108 va_start(va, pszFormat);
109 vfprintf(stderr, pszFormat, va);
110 va_end(va);
111 return RTEXITCODE_FAILURE;
112}
113
114
115/**
116 * Reports a syntax problem.
117 *
118 * @returns RTEXITCODE_SYNTAX
119 */
120static int syntax(const char *pszFormat, ...)
121{
122 fprintf(stderr, "syntax error: ");
123 va_list va;
124 va_start(va, pszFormat);
125 vfprintf(stderr, pszFormat, va);
126 va_end(va);
127 return RTEXITCODE_FAILURE;
128}
129
130
131/**
132 * Display usage
133 *
134 * @returns success if stdout, syntax error if stderr.
135 */
136static int usage(FILE *pOut, const char *argv0)
137{
138 fprintf(pOut,
139 "usage: %s --input <in.lib> --output <out.lib> [options and operations]\n"
140 "\n"
141 "Operations and Options (processed in place):\n"
142 " --verbose Noisier.\n"
143 " --quiet Quiet execution.\n"
144 " --select <member>\n"
145 " Selects archive member which name ends in the given string.\n"
146 " --redefine-sym <old>=<new>\n"
147 " Redefine the symbol <old> to <new>.\n"
148 " Note! the length must be the same!\n"
149 , argv0);
150 return pOut == stdout ? RTEXITCODE_SUCCESS : RTEXITCODE_SYNTAX;
151}
152
153
154/**
155 * Helper for SelectMember.
156 */
157static bool AreAllDigits(const char *pch, size_t cch, size_t *puValue)
158{
159 *puValue = 0;
160 do
161 {
162 if (!RT_C_IS_DIGIT(*pch))
163 return false;
164 *puValue = *puValue * 10 + *pch - '0';
165 pch++;
166 cch--;
167 } while (cch > 0);
168 return true;
169}
170
171
172/**
173 * Selects archive member ending with the given name.
174 *
175 * Updates g_cbMember and g_pbMember.
176 */
177static int SelectMember(const char *pszEndsWith)
178{
179 size_t const cchEndsWith = strlen(pszEndsWith);
180
181 /*
182 * Check the header.
183 */
184 if (memcmp(g_pbBinary, RT_STR_TUPLE("!<arch>\n")))
185 return error("Not an AR library!\n");
186
187 /*
188 * Work the members.
189 */
190 const char *pszStringTab = NULL;
191 size_t cbStringTab = 0;
192 for (size_t off = sizeof("!<arch>\n") - 1; off < g_cbBinary;)
193 {
194 ARHDR *pHdr = (ARHDR *)&g_pbBinary[off];
195 char szTmp[16 + 8];
196 size_t uValue;
197 char *pszIgn;
198#define COPY_AND_TRIM(a_pchSrc, a_cbSrc) do { \
199 memcpy(szTmp, (a_pchSrc), (a_cbSrc)); \
200 size_t offCopy = (a_cbSrc); \
201 while (offCopy > 0 && (szTmp[offCopy - 1] == ' ' || szTmp[offCopy - 1] == '\0')) \
202 offCopy--; \
203 szTmp[offCopy] = '\0'; \
204 } while (0)
205
206 /*
207 * Parse the header.
208 */
209
210 /* The size: */
211 COPY_AND_TRIM(pHdr->achSize, sizeof(pHdr->achSize));
212 size_t cbFile = strtol(szTmp, &pszIgn, 10);
213
214 /* The name: */
215 size_t cbExtra = 0;
216 size_t cchName = sizeof(pHdr->achName);
217 const char *pchName = pHdr->achName;
218 if ( pchName[0] == '#'
219 && pchName[1] == '1'
220 && pchName[2] == '/')
221 {
222 COPY_AND_TRIM(&pchName[3], cchName - 3);
223 cchName = cbExtra = strtol(szTmp, &pszIgn, 10);
224 pchName = (char *)(pHdr + 1);
225 }
226 else
227 {
228 while (cchName > 0 && (pchName[cchName - 1] == ' ' || pchName[cchName - 1] == '\0'))
229 cchName--;
230
231 /* Long filename string table? */
232 if ( (cchName == 2 && pchName[0] == '/' && pchName[1] == '/')
233 || (cchName == sizeof("ARFILENAMES/") - 1 && memcmp(pchName, RT_STR_TUPLE("ARFILENAMES/")) == 0))
234 {
235 pszStringTab = (char *)(pHdr + 1);
236 cbStringTab = cbFile;
237 }
238 /* Long filename string table reference? */
239 else if ( cchName >= 2
240 && ( pchName[0] == '/' /* System V */
241 || pchName[0] == ' ' /* Other */)
242 && AreAllDigits(&pchName[1], cchName - 1, &uValue) && uValue < cbStringTab)
243 {
244 pchName = &pszStringTab[uValue];
245 cchName = strlen(pchName); /** @todo unsafe! */
246 }
247 /* Drop trailing slash in case of System V filename: */
248 else if (cchName > 1 && pchName[cchName - 1] == '/')
249 cchName -= 1;
250 }
251
252 if (g_cVerbosity > 2)
253 fprintf(stderr, "debug: %#08x: %#010x %*.*s\n",
254 (unsigned)off, (unsigned)(cbFile - cbExtra), (int)cchName, (int)cchName, pchName);
255
256 /*
257 * Do matching.
258 */
259 if ( cchName >= cchEndsWith
260 && strncmp(&pchName[cchName - cchEndsWith], pszEndsWith, cchEndsWith) == 0)
261 {
262 g_pbMember = (uint8_t *)(pHdr + 1) + cbExtra;
263 g_cbMember = (unsigned)(cbFile - cbExtra);
264 if (g_cVerbosity > 1)
265 fprintf(stderr, "debug: selected '%*.*s': %#x LB %#x\n",
266 (int)cchName, (int)cchName, pchName, (unsigned)(off + sizeof(*pHdr) + cbExtra), g_cbMember);
267 return 0;
268 }
269
270 /*
271 * Advance.
272 */
273 off += sizeof(ARHDR) + cbFile + (cbFile & 1);
274 }
275
276 return error("No member ending with '%s' was found!\n", pszEndsWith);
277}
278
279
280/**
281 * @note Borrowed from VBoxBs3objConverter.cpp
282 */
283static const char *coffGetSymbolName(PCIMAGE_SYMBOL pSym, const char *pchStrTab, uint32_t cbStrTab, char pszShortName[16])
284{
285 if (pSym->N.Name.Short != 0)
286 {
287 memcpy(pszShortName, pSym->N.ShortName, 8);
288 pszShortName[8] = '\0';
289 return pszShortName;
290 }
291 if (pSym->N.Name.Long < cbStrTab)
292 {
293 uint32_t const cbLeft = cbStrTab - pSym->N.Name.Long;
294 const char *pszRet = pchStrTab + pSym->N.Name.Long;
295 if (memchr(pszRet, '\0', cbLeft) != NULL)
296 return pszRet;
297 }
298 error("Invalid string table index %#x!\n", pSym->N.Name.Long);
299 return "Invalid Symbol Table Entry";
300}
301
302
303/**
304 * Redefine a symbol with a different name.
305 */
306static int RedefineSymbol(const char *pszOldEqualNew)
307{
308 /*
309 * Check state and split up the input.
310 */
311 if (!g_pbMember)
312 return error("No selected archive member!\n");
313
314 const char *pszNew = strchr(pszOldEqualNew, '=');
315 if (!pszNew || pszNew[1] == '\0')
316 return error("Malformed 'old=new' argument: %s\n", pszOldEqualNew);
317 const char *pszOld = pszOldEqualNew;
318 size_t const cchOld = pszNew - pszOldEqualNew;
319 pszNew += 1;
320 size_t const cchNew = strlen(pszNew);
321 if (cchNew > cchOld)
322 return error("The new symbol must not be longer than the old symbol: %#x vs %#x (%s)\n", cchNew, cchOld, pszOldEqualNew);
323
324 if (g_cVerbosity > 2)
325 fprintf(stderr, "debug: redefining symbol '%*.*s' to '%*.*s'...\n",
326 (int)cchOld, (int)cchOld, pszOld, (int)cchNew, (int)cchNew, pszNew);
327
328 /*
329 * Parse COFF header.
330 */
331 const IMAGE_FILE_HEADER *pHdr = (const IMAGE_FILE_HEADER *)g_pbMember;
332 if (sizeof(*pHdr) >= g_cbMember)
333 return error("member too small for COFF\n");
334 if ( pHdr->Machine != IMAGE_FILE_MACHINE_AMD64
335 && pHdr->Machine != IMAGE_FILE_MACHINE_I386
336 && pHdr->Machine != IMAGE_FILE_MACHINE_ARM64
337 && pHdr->Machine != IMAGE_FILE_MACHINE_ARM)
338 return error("Unsupported COFF machine: %#x\n", pHdr->Machine);
339 if ( pHdr->PointerToSymbolTable >= g_cbMember
340 || pHdr->PointerToSymbolTable < sizeof(*pHdr))
341 return error("PointerToSymbolTable is out of bounds: %#x, max %#x\n", pHdr->PointerToSymbolTable, g_cbMember);
342 unsigned const cSymbols = pHdr->NumberOfSymbols;
343 if ( cSymbols >= g_cbMember - pHdr->PointerToSymbolTable
344 || cSymbols * sizeof(IMAGE_SYMBOL) > g_cbMember - pHdr->PointerToSymbolTable)
345 return error("PointerToSymbolTable + NumberOfSymbols is out of bounds: %#x + %#x * %#x (%#x), max %#x\n",
346 pHdr->PointerToSymbolTable, cSymbols, sizeof(IMAGE_SYMBOL),
347 pHdr->PointerToSymbolTable + cSymbols * sizeof(IMAGE_SYMBOL), g_cbMember);
348
349 /*
350 * Work the symbol table.
351 */
352 unsigned cRenames = 0;
353 PIMAGE_SYMBOL const paSymTab = (PIMAGE_SYMBOL)&g_pbMember[pHdr->PointerToSymbolTable];
354 const char * const pchStrTab = (const char *)&paSymTab[pHdr->NumberOfSymbols];
355 uint32_t const cbStrTab = (uint32_t)((uintptr_t)&g_pbMember[g_cbMember] - (uintptr_t)pchStrTab);
356 for (unsigned iSym = 0; iSym < cSymbols; iSym++)
357 {
358 char szShort[16];
359 const char *pszSymName = coffGetSymbolName(&paSymTab[iSym], pchStrTab, cbStrTab, szShort);
360 size_t cchSymName = strlen(pszSymName);
361 if (g_cVerbosity > 3 && cchSymName > 0)
362 fprintf(stderr, "debug: symbol %u: %s\n", iSym, pszSymName);
363 if ( cchSymName == cchOld
364 && memcmp(pszSymName, pszOld, cchSymName) == 0)
365 {
366 size_t const offStrTab = (size_t)(pszSymName - pchStrTab);
367 if (offStrTab < cbStrTab)
368 {
369 if (g_cVerbosity > 1)
370 fprintf(stderr, "debug: Found symbol '%s' in at string table offset %#x, renaming to '%s'.\n",
371 pszSymName, (uint32_t)offStrTab, pszNew);
372 if (offStrTab > 0 && pchStrTab[offStrTab - 1] != '\0')
373 return error("Cannot rename sub-string!\n");
374 memset((char *)pszSymName, 0, cchOld);
375 memcpy((char *)pszSymName, pszNew, cchNew);
376 }
377 else
378 {
379 if (g_cVerbosity > 1)
380 fprintf(stderr, "debug: Found symbol '%s' in symbol table, renaming to '%s'.\n", pszSymName, pszNew);
381 memset(paSymTab[iSym].N.ShortName, 0, sizeof(paSymTab[iSym].N.ShortName));
382 memcpy(paSymTab[iSym].N.ShortName, pszNew, cchNew);
383 }
384 cRenames++;
385 }
386
387 /* Skip AUX symbols. */
388 uint8_t cAuxSyms = paSymTab[iSym].NumberOfAuxSymbols;
389 while (cAuxSyms-- > 0)
390 iSym++;
391 }
392
393 if (cRenames > 0)
394 return RTEXITCODE_SUCCESS;
395 return error("Symbol '%*.*s' was not found!\n", cchOld, cchOld, pszOld);
396}
397
398
399int main(int argc, char **argv)
400{
401 /*
402 * Parse arguments.
403 */
404 const char *pszIn = NULL;
405 const char *pszOut = NULL;
406 for (int i = 1; i < argc; i++)
407 {
408 const char *pszArg = argv[i];
409
410 /* Options without values first: */
411 if ( strcmp(pszArg, "--verbose") == 0
412 || strcmp(pszArg, "-v") == 0)
413 g_cVerbosity += 1;
414 else if ( strcmp(pszArg, "--quiet") == 0
415 || strcmp(pszArg, "--q") == 0)
416 g_cVerbosity = 0;
417 else if ( strcmp(pszArg, "--help") == 0
418 || strcmp(pszArg, "-h") == 0
419 || strcmp(pszArg, "-?") == 0)
420 return usage(stdout, argv[0]);
421 else if (i + 1 >= argc)
422 return syntax("Missing argument value or unknown option '%s'!\n", pszArg);
423 else
424 {
425 i++;
426 const char *pszValue = argv[i];
427 int rc = 0;
428 if (strcmp(pszArg, "--input") == 0)
429 {
430 if (pszIn)
431 return syntax("--input can only be specified once!\n");
432 pszIn = pszValue;
433
434 /* Load it into memory: */
435 FILE *pIn = fopen(pszIn, "rb");
436 if (!pIn)
437 return error("Failed to open '%s' for reading!\n", pszIn);
438 g_cbBinary = fsize(pIn);
439 if (!g_cbBinary)
440 return error("Failed to determin the size of '%s'!\n", pszIn);
441 if (g_cbBinary > _128M)
442 return error("'%s' is too large: %x, max %x\n", g_cbBinary, (size_t)_128M);
443 g_pbBinary = (uint8_t *)calloc(1, g_cbBinary + 4096);
444 if (!g_pbBinary)
445 return error("Out of memory!\n");
446 if (fread(g_pbBinary, g_cbBinary, 1, pIn) != 1)
447 return error("Failed to read '%s' into memory!\n", pszIn);
448 fclose(pIn);
449 }
450 else if (strcmp(pszArg, "--output") == 0)
451 pszOut = pszValue;
452 else if (strcmp(pszArg, "--select") == 0)
453 rc = SelectMember(pszValue);
454 else if (strcmp(pszArg, "--redefine-sym") == 0)
455 rc = RedefineSymbol(pszValue);
456 else
457 return syntax("Unknown option: %s\n", pszArg);
458 if (rc != RTEXITCODE_SUCCESS)
459 return rc;
460 }
461 }
462
463 if (!pszIn || !pszOut)
464 return syntax("No %s specified!\n", pszIn ? "output file" : "intput library file");
465
466 /*
467 * Write out the result.
468 */
469 FILE *pOut = fopen(pszOut, "wb");
470 if (!pOut)
471 return error("Failed to open '%s' for writing!\n", pszOut);
472 if (fwrite(g_pbBinary, g_cbBinary, 1, pOut) != 1)
473 return error("Error writing %#x bytes to '%s'!\n", g_cbBinary, pszOut);
474 if (fclose(pOut) != 0)
475 return error("Error closing '%s'!\n", pszOut);
476 return RTEXITCODE_SUCCESS;
477}
478
Note: See TracBrowser for help on using the repository browser.

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