VirtualBox

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

Last change on this file since 80431 was 78608, checked in by vboxsync, 6 years ago

winnt/vboxsf: Don't flush and purge the cache twice on newer systems, instead do a library trick to redirect relevant imports from write.obj and read.obj to our wrappers that uses CcCoherencyFlushAndPurgeCache when possible to get better coherency between mmap regions and file content when writing and reading normally. This comes at a cost when the file has been mmapped at some point previously (or currently) and we may need to purge stuff. bugref:9172

  • 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 78608 2019-05-20 23:04:08Z vboxsync $ */
2/** @file
3 * VBoxEditCoffLib - Simple COFF editor for library files.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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