VirtualBox

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

Last change on this file since 94187 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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