VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/* $Id: VBoxEditCoffLib.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBoxEditCoffLib - Simple COFF editor for library files.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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", off, cbFile - cbExtra, cchName, cchName, pchName);
244
245 /*
246 * Do matching.
247 */
248 if ( cchName >= cchEndsWith
249 && strncmp(&pchName[cchName - cchEndsWith], pszEndsWith, cchEndsWith) == 0)
250 {
251 g_pbMember = (uint8_t *)(pHdr + 1) + cbExtra;
252 g_cbMember = (unsigned)(cbFile - cbExtra);
253 if (g_cVerbosity > 1)
254 fprintf(stderr, "debug: selected '%*.*s': %#x LB %#x\n",
255 cchName, cchName, pchName, off + sizeof(*pHdr) + cbExtra, g_cbMember);
256 return 0;
257 }
258
259 /*
260 * Advance.
261 */
262 off += sizeof(ARHDR) + cbFile + (cbFile & 1);
263 }
264
265 return error("No member ending with '%s' was found!\n", pszEndsWith);
266}
267
268
269/**
270 * @note Borrowed from VBoxBs3objConverter.cpp
271 */
272static const char *coffGetSymbolName(PCIMAGE_SYMBOL pSym, const char *pchStrTab, uint32_t cbStrTab, char pszShortName[16])
273{
274 if (pSym->N.Name.Short != 0)
275 {
276 memcpy(pszShortName, pSym->N.ShortName, 8);
277 pszShortName[8] = '\0';
278 return pszShortName;
279 }
280 if (pSym->N.Name.Long < cbStrTab)
281 {
282 uint32_t const cbLeft = cbStrTab - pSym->N.Name.Long;
283 const char *pszRet = pchStrTab + pSym->N.Name.Long;
284 if (memchr(pszRet, '\0', cbLeft) != NULL)
285 return pszRet;
286 }
287 error("Invalid string table index %#x!\n", pSym->N.Name.Long);
288 return "Invalid Symbol Table Entry";
289}
290
291
292/**
293 * Redefine a symbol with a different name.
294 */
295static int RedefineSymbol(const char *pszOldEqualNew)
296{
297 /*
298 * Check state and split up the input.
299 */
300 if (!g_pbMember)
301 return error("No selected archive member!\n");
302
303 const char *pszNew = strchr(pszOldEqualNew, '=');
304 if (!pszNew || pszNew[1] == '\0')
305 return error("Malformed 'old=new' argument: %s\n", pszOldEqualNew);
306 const char *pszOld = pszOldEqualNew;
307 size_t const cchOld = pszNew - pszOldEqualNew;
308 pszNew += 1;
309 size_t const cchNew = strlen(pszNew);
310 if (cchNew > cchOld)
311 return error("The new symbol must not be longer than the old symbol: %#x vs %#x (%s)\n", cchNew, cchOld, pszOldEqualNew);
312
313 if (g_cVerbosity > 2)
314 fprintf(stderr, "debug: redefining symbol '%*.*s' to '%*.*s'...\n",
315 cchOld, cchOld, pszOld, cchNew, cchNew, pszNew);
316
317 /*
318 * Parse COFF header.
319 */
320 const IMAGE_FILE_HEADER *pHdr = (const IMAGE_FILE_HEADER *)g_pbMember;
321 if (sizeof(*pHdr) >= g_cbMember)
322 return error("member too small for COFF\n");
323 if ( pHdr->Machine != IMAGE_FILE_MACHINE_AMD64
324 && pHdr->Machine != IMAGE_FILE_MACHINE_I386)
325 return error("Unsupported COFF machine: %#x\n", pHdr->Machine);
326 if ( pHdr->PointerToSymbolTable >= g_cbMember
327 || pHdr->PointerToSymbolTable < sizeof(*pHdr))
328 return error("PointerToSymbolTable is out of bounds: %#x, max %#x\n", pHdr->PointerToSymbolTable, g_cbMember);
329 unsigned const cSymbols = pHdr->NumberOfSymbols;
330 if ( cSymbols >= g_cbMember - pHdr->PointerToSymbolTable
331 || cSymbols * sizeof(IMAGE_SYMBOL) > g_cbMember - pHdr->PointerToSymbolTable)
332 return error("PointerToSymbolTable + NumberOfSymbols is out of bounds: %#x + %#x * %#x (%#x), max %#x\n",
333 pHdr->PointerToSymbolTable, cSymbols, sizeof(IMAGE_SYMBOL),
334 pHdr->PointerToSymbolTable + cSymbols * sizeof(IMAGE_SYMBOL), g_cbMember);
335
336 /*
337 * Work the symbol table.
338 */
339 unsigned cRenames = 0;
340 PIMAGE_SYMBOL const paSymTab = (PIMAGE_SYMBOL)&g_pbMember[pHdr->PointerToSymbolTable];
341 const char * const pchStrTab = (const char *)&paSymTab[pHdr->NumberOfSymbols];
342 uint32_t const cbStrTab = (uint32_t)((uintptr_t)&g_pbMember[g_cbMember] - (uintptr_t)pchStrTab);
343 for (unsigned iSym = 0; iSym < cSymbols; iSym++)
344 {
345 char szShort[16];
346 const char *pszSymName = coffGetSymbolName(&paSymTab[iSym], pchStrTab, cbStrTab, szShort);
347 size_t cchSymName = strlen(pszSymName);
348 if (g_cVerbosity > 3 && cchSymName > 0)
349 fprintf(stderr, "debug: symbol %u: %s\n", iSym, pszSymName);
350 if ( cchSymName == cchOld
351 && memcmp(pszSymName, pszOld, cchSymName) == 0)
352 {
353 size_t const offStrTab = (size_t)(pszSymName - pchStrTab);
354 if (offStrTab < cbStrTab)
355 {
356 if (g_cVerbosity > 1)
357 fprintf(stderr, "debug: Found symbol '%s' in at string table offset %#x, renaming to '%s'.\n",
358 pszSymName, (uint32_t)offStrTab, pszNew);
359 if (offStrTab > 0 && pchStrTab[offStrTab - 1] != '\0')
360 return error("Cannot rename sub-string!\n");
361 memset((char *)pszSymName, 0, cchOld);
362 memcpy((char *)pszSymName, pszNew, cchNew);
363 }
364 else
365 {
366 if (g_cVerbosity > 1)
367 fprintf(stderr, "debug: Found symbol '%s' in symbol table, renaming to '%s'.\n", pszSymName, pszNew);
368 memset(paSymTab[iSym].N.ShortName, 0, sizeof(paSymTab[iSym].N.ShortName));
369 memcpy(paSymTab[iSym].N.ShortName, pszNew, cchNew);
370 }
371 cRenames++;
372 }
373
374 /* Skip AUX symbols. */
375 uint8_t cAuxSyms = paSymTab[iSym].NumberOfAuxSymbols;
376 while (cAuxSyms-- > 0)
377 iSym++;
378 }
379
380 if (cRenames > 0)
381 return RTEXITCODE_SUCCESS;
382 return error("Symbol '%*.*s' was not found!\n", cchOld, cchOld, pszOld);
383}
384
385
386int main(int argc, char **argv)
387{
388 /*
389 * Parse arguments.
390 */
391 const char *pszIn = NULL;
392 const char *pszOut = NULL;
393 for (int i = 1; i < argc; i++)
394 {
395 const char *pszArg = argv[i];
396
397 /* Options without values first: */
398 if ( strcmp(pszArg, "--verbose") == 0
399 || strcmp(pszArg, "-v") == 0)
400 g_cVerbosity += 1;
401 else if ( strcmp(pszArg, "--quiet") == 0
402 || strcmp(pszArg, "--q") == 0)
403 g_cVerbosity = 0;
404 else if ( strcmp(pszArg, "--help") == 0
405 || strcmp(pszArg, "-h") == 0
406 || strcmp(pszArg, "-?") == 0)
407 return usage(stdout, argv[0]);
408 else if (i + 1 >= argc)
409 return syntax("Missing argument value or unknown option '%s'!\n", pszArg);
410 else
411 {
412 i++;
413 const char *pszValue = argv[i];
414 int rc = 0;
415 if (strcmp(pszArg, "--input") == 0)
416 {
417 if (pszIn)
418 return syntax("--input can only be specified once!\n");
419 pszIn = pszValue;
420
421 /* Load it into memory: */
422 FILE *pIn = fopen(pszIn, "rb");
423 if (!pIn)
424 return error("Failed to open '%s' for reading!\n", pszIn);
425 g_cbBinary = fsize(pIn);
426 if (!g_cbBinary)
427 return error("Failed to determin the size of '%s'!\n", pszIn);
428 if (g_cbBinary > _128M)
429 return error("'%s' is too large: %x, max %x\n", g_cbBinary, (size_t)_128M);
430 g_pbBinary = (uint8_t *)calloc(1, g_cbBinary + 4096);
431 if (!g_pbBinary)
432 return error("Out of memory!\n");
433 if (fread(g_pbBinary, g_cbBinary, 1, pIn) != 1)
434 return error("Failed to read '%s' into memory!\n", pszIn);
435 fclose(pIn);
436 }
437 else if (strcmp(pszArg, "--output") == 0)
438 pszOut = pszValue;
439 else if (strcmp(pszArg, "--select") == 0)
440 rc = SelectMember(pszValue);
441 else if (strcmp(pszArg, "--redefine-sym") == 0)
442 rc = RedefineSymbol(pszValue);
443 else
444 return syntax("Unknown option: %s\n", pszArg);
445 if (rc != RTEXITCODE_SUCCESS)
446 return rc;
447 }
448 }
449
450 if (!pszIn || !pszOut)
451 return syntax("No %s specified!\n", pszIn ? "output file" : "intput library file");
452
453 /*
454 * Write out the result.
455 */
456 FILE *pOut = fopen(pszOut, "wb");
457 if (!pOut)
458 return error("Failed to open '%s' for writing!\n", pszOut);
459 if (fwrite(g_pbBinary, g_cbBinary, 1, pOut) != 1)
460 return error("Error writing %#x bytes to '%s'!\n", g_cbBinary, pszOut);
461 if (fclose(pOut) != 0)
462 return error("Error closing '%s'!\n", pszOut);
463 return RTEXITCODE_SUCCESS;
464}
465
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