VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 7 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: VBoxEditCoffLib.cpp 106061 2024-09-16 14:03:52Z 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 return error("Unsupported COFF machine: %#x\n", pHdr->Machine);
337 if ( pHdr->PointerToSymbolTable >= g_cbMember
338 || pHdr->PointerToSymbolTable < sizeof(*pHdr))
339 return error("PointerToSymbolTable is out of bounds: %#x, max %#x\n", pHdr->PointerToSymbolTable, g_cbMember);
340 unsigned const cSymbols = pHdr->NumberOfSymbols;
341 if ( cSymbols >= g_cbMember - pHdr->PointerToSymbolTable
342 || cSymbols * sizeof(IMAGE_SYMBOL) > g_cbMember - pHdr->PointerToSymbolTable)
343 return error("PointerToSymbolTable + NumberOfSymbols is out of bounds: %#x + %#x * %#x (%#x), max %#x\n",
344 pHdr->PointerToSymbolTable, cSymbols, sizeof(IMAGE_SYMBOL),
345 pHdr->PointerToSymbolTable + cSymbols * sizeof(IMAGE_SYMBOL), g_cbMember);
346
347 /*
348 * Work the symbol table.
349 */
350 unsigned cRenames = 0;
351 PIMAGE_SYMBOL const paSymTab = (PIMAGE_SYMBOL)&g_pbMember[pHdr->PointerToSymbolTable];
352 const char * const pchStrTab = (const char *)&paSymTab[pHdr->NumberOfSymbols];
353 uint32_t const cbStrTab = (uint32_t)((uintptr_t)&g_pbMember[g_cbMember] - (uintptr_t)pchStrTab);
354 for (unsigned iSym = 0; iSym < cSymbols; iSym++)
355 {
356 char szShort[16];
357 const char *pszSymName = coffGetSymbolName(&paSymTab[iSym], pchStrTab, cbStrTab, szShort);
358 size_t cchSymName = strlen(pszSymName);
359 if (g_cVerbosity > 3 && cchSymName > 0)
360 fprintf(stderr, "debug: symbol %u: %s\n", iSym, pszSymName);
361 if ( cchSymName == cchOld
362 && memcmp(pszSymName, pszOld, cchSymName) == 0)
363 {
364 size_t const offStrTab = (size_t)(pszSymName - pchStrTab);
365 if (offStrTab < cbStrTab)
366 {
367 if (g_cVerbosity > 1)
368 fprintf(stderr, "debug: Found symbol '%s' in at string table offset %#x, renaming to '%s'.\n",
369 pszSymName, (uint32_t)offStrTab, pszNew);
370 if (offStrTab > 0 && pchStrTab[offStrTab - 1] != '\0')
371 return error("Cannot rename sub-string!\n");
372 memset((char *)pszSymName, 0, cchOld);
373 memcpy((char *)pszSymName, pszNew, cchNew);
374 }
375 else
376 {
377 if (g_cVerbosity > 1)
378 fprintf(stderr, "debug: Found symbol '%s' in symbol table, renaming to '%s'.\n", pszSymName, pszNew);
379 memset(paSymTab[iSym].N.ShortName, 0, sizeof(paSymTab[iSym].N.ShortName));
380 memcpy(paSymTab[iSym].N.ShortName, pszNew, cchNew);
381 }
382 cRenames++;
383 }
384
385 /* Skip AUX symbols. */
386 uint8_t cAuxSyms = paSymTab[iSym].NumberOfAuxSymbols;
387 while (cAuxSyms-- > 0)
388 iSym++;
389 }
390
391 if (cRenames > 0)
392 return RTEXITCODE_SUCCESS;
393 return error("Symbol '%*.*s' was not found!\n", cchOld, cchOld, pszOld);
394}
395
396
397int main(int argc, char **argv)
398{
399 /*
400 * Parse arguments.
401 */
402 const char *pszIn = NULL;
403 const char *pszOut = NULL;
404 for (int i = 1; i < argc; i++)
405 {
406 const char *pszArg = argv[i];
407
408 /* Options without values first: */
409 if ( strcmp(pszArg, "--verbose") == 0
410 || strcmp(pszArg, "-v") == 0)
411 g_cVerbosity += 1;
412 else if ( strcmp(pszArg, "--quiet") == 0
413 || strcmp(pszArg, "--q") == 0)
414 g_cVerbosity = 0;
415 else if ( strcmp(pszArg, "--help") == 0
416 || strcmp(pszArg, "-h") == 0
417 || strcmp(pszArg, "-?") == 0)
418 return usage(stdout, argv[0]);
419 else if (i + 1 >= argc)
420 return syntax("Missing argument value or unknown option '%s'!\n", pszArg);
421 else
422 {
423 i++;
424 const char *pszValue = argv[i];
425 int rc = 0;
426 if (strcmp(pszArg, "--input") == 0)
427 {
428 if (pszIn)
429 return syntax("--input can only be specified once!\n");
430 pszIn = pszValue;
431
432 /* Load it into memory: */
433 FILE *pIn = fopen(pszIn, "rb");
434 if (!pIn)
435 return error("Failed to open '%s' for reading!\n", pszIn);
436 g_cbBinary = fsize(pIn);
437 if (!g_cbBinary)
438 return error("Failed to determin the size of '%s'!\n", pszIn);
439 if (g_cbBinary > _128M)
440 return error("'%s' is too large: %x, max %x\n", g_cbBinary, (size_t)_128M);
441 g_pbBinary = (uint8_t *)calloc(1, g_cbBinary + 4096);
442 if (!g_pbBinary)
443 return error("Out of memory!\n");
444 if (fread(g_pbBinary, g_cbBinary, 1, pIn) != 1)
445 return error("Failed to read '%s' into memory!\n", pszIn);
446 fclose(pIn);
447 }
448 else if (strcmp(pszArg, "--output") == 0)
449 pszOut = pszValue;
450 else if (strcmp(pszArg, "--select") == 0)
451 rc = SelectMember(pszValue);
452 else if (strcmp(pszArg, "--redefine-sym") == 0)
453 rc = RedefineSymbol(pszValue);
454 else
455 return syntax("Unknown option: %s\n", pszArg);
456 if (rc != RTEXITCODE_SUCCESS)
457 return rc;
458 }
459 }
460
461 if (!pszIn || !pszOut)
462 return syntax("No %s specified!\n", pszIn ? "output file" : "intput library file");
463
464 /*
465 * Write out the result.
466 */
467 FILE *pOut = fopen(pszOut, "wb");
468 if (!pOut)
469 return error("Failed to open '%s' for writing!\n", pszOut);
470 if (fwrite(g_pbBinary, g_cbBinary, 1, pOut) != 1)
471 return error("Error writing %#x bytes to '%s'!\n", g_cbBinary, pszOut);
472 if (fclose(pOut) != 0)
473 return error("Error closing '%s'!\n", pszOut);
474 return RTEXITCODE_SUCCESS;
475}
476
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