source: trunk/emx/src/emxomf/emxomfstrip.c@ 3940

Last change on this file since 3940 was 3940, checked in by bird, 11 years ago

emxomfstrip: Debugged it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.4 KB
Line 
1/* $Id: emxomfstrip.c 3940 2014-11-10 20:16:06Z bird $ */
2/** @file
3 * emxstrip - Simple LX debug info stripping tool.
4 *
5 * @copyright Copyright (C) 2014 knut st. osmundsen <bird-klibc-spam-xiv@anduin.net>
6 * @licenses MIT, BSD2, BSD3, BSD4, LGPLv2.1, LGPLv3, LGPLvFuture.
7 */
8
9
10/*******************************************************************************
11* Header Files *
12*******************************************************************************/
13#include "defs.h"
14#include <errno.h>
15#include <getopt.h>
16#include <stdarg.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <sys/omflib.h>
22#if defined(_MSC_VER) || defined(__WATCOMC__)
23# include <io.h>
24# define ftruncate _chsize
25#endif
26
27
28
29/*******************************************************************************
30* Structures and Typedefs *
31*******************************************************************************/
32typedef struct EMXOMFSTRIPOPTS
33{
34 const char *pszInFile;
35 const char *pszDbgFile;
36 const char *pszOutFile;
37 int cVerbose;
38} EMXOMFSTRIPOPTS;
39
40
41static int error(const char *pszMsg, ...)
42{
43 va_list va;
44 va_start(va, pszMsg);
45 fprintf(stderr, "emxomfstrip: error: ");
46 vfprintf(stderr, pszMsg, va);
47 va_end(va);
48 return 1;
49}
50
51
52static int copyFileBytes(FILE *pDst, const char *pszDstName, FILE *pSrc, const char *pszSrcName,
53 unsigned long offSrc, unsigned long cb)
54{
55 if (fseek(pDst, 0, SEEK_SET) != 0)
56 return error("Seek error: %s\n", strerror(errno));
57 if (fseek(pSrc, offSrc, SEEK_SET) != 0)
58 return error("Seek error: %s\n", strerror(errno));
59 while (cb > 0)
60 {
61 byte abBuf[0x8000];
62 unsigned cbThis = cb < sizeof(abBuf) ? (unsigned)cb : sizeof(abBuf);
63 if (fread(abBuf, cbThis, 1, pSrc) != 1)
64 return error("fread (@%#lx) failed on '%s': %s\n", offSrc, pszSrcName, strerror(errno));
65 if (fwrite(abBuf, cbThis, 1, pDst) != 1)
66 return error("fwrite failed on '%s': %s\n", pszDstName, strerror(errno));
67 cb -= cbThis;
68 offSrc += cbThis;
69 }
70 return 0;
71}
72
73
74static int isNbSignature(byte const *pbMagic)
75{
76 return pbMagic[0] == 'N'
77 && pbMagic[1] == 'B'
78 && pbMagic[2] >= '0' && pbMagic[2] <= '1'
79 && pbMagic[3] >= '0' && pbMagic[3] <= '9';
80}
81
82
83static int isLxDebugInfoSignature(byte const *pbMagic)
84{
85 return isNbSignature(pbMagic);
86}
87
88
89/**
90 * Strips a file.
91 *
92 * @returns 0 on success, 1 on failure, -1 if no debug info.
93 * @param pOpts Options.
94 * @param pInFile The input file.
95 * @param pOutFile The output file. Can be the same as @a pInFile, can
96 * also be NULL (no stripping).
97 * @param pDbgFile Where to write the raw debug.
98 */
99static int stripFile(EMXOMFSTRIPOPTS *pOpts, FILE *pInFile, FILE *pOutFile, FILE *pDbgFile)
100{
101 struct stat StIn;
102 unsigned long offLX = 0;
103 unsigned long offDbg = 0;
104 unsigned long cbDbg = 0;
105 int rc;
106 union
107 {
108 struct exe1p2_header mz;
109 struct os2_header lx;
110 byte abBuf[32];
111 } uHdr;
112 union
113 {
114 byte abBuf[32];
115 word awBuf[32/2];
116 dword adwBuf[32/4];
117 } uBuf;
118
119 if (fstat(fileno(pInFile), &StIn) != 0)
120 return error("fstat failed on '%s': %s\n", pOpts->pszInFile, strerror(errno));
121
122 /*
123 * Parse the executable header.
124 */
125 if (fread(&uHdr.mz, sizeof(uHdr.mz), 1, pInFile) != 1)
126 return error("Failed to read the header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
127 if (uHdr.mz.e_magic == EXE_MAGIC_MZ)
128 {
129 offLX = uHdr.mz.e_lfanew;
130 if (fseek(pInFile, offLX, SEEK_SET) != 0)
131 return error("Failed to seek to 2nd header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
132 if (fread(&uHdr.lx, sizeof(uHdr.lx), 1, pInFile) != 1)
133 return error("Failed to read the LX header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
134 if (uHdr.lx.magic != EXE_MAGIC_LX)
135 return error("Header of '%s' is not recognized (%#x)\n", pOpts->pszInFile, uHdr.lx.magic);
136 }
137 else if (uHdr.lx.magic == EXE_MAGIC_LX)
138 {
139 if (fread(&uHdr.mz + 1, sizeof(uHdr.lx) - sizeof(uHdr.mz), 1, pInFile) != 1)
140 return error("Failed to read the header of '%s': %s\n", pOpts->pszInFile, strerror(errno));
141 }
142 else if ( uHdr.abBuf[0] == THEADR
143 && (uHdr.abBuf[1] > 3 || uHdr.abBuf[2]) )
144 return error("%s: Stripping OMF object files is not currently supported!\n", pOpts->pszInFile);
145 else if ( uHdr.abBuf[0] == LIBHDR
146 && (uHdr.abBuf[1] > 3 || uHdr.abBuf[2]) )
147 return error("%s: Stripping OMF library files is not currently supported!\n", pOpts->pszInFile);
148 else
149 return error("Header of '%s' is not recognized (%#x)\n", pOpts->pszInFile, uHdr.mz.e_magic);
150
151 /*
152 * Is there a pointer to the debug info in the header? If so, check that
153 * it is at the end of the file.
154 */
155 if ( uHdr.lx.debug_offset != 0
156 && uHdr.lx.debug_size != 0 && 0)
157 {
158 offDbg = uHdr.lx.debug_offset;
159 cbDbg = uHdr.lx.debug_size;
160 if ( offDbg > StIn.st_size
161 || cbDbg > StIn.st_size
162 || (off_t)offDbg + cbDbg > StIn.st_size)
163 return error("Debug info indicated by the LX header of '%s' makes no sense! (%#lx + %#lx = %#lx (file: %#x)\n",
164 pOpts->pszInFile, offDbg, cbDbg, offDbg + cbDbg, (unsigned long)StIn.st_size);
165
166 if ( fseek(pInFile, offDbg, SEEK_SET) != 0
167 || fread(&uBuf, 4, 1, pInFile) != 1)
168 return error("Error seeking/reading debug info header (@%#lx) in '%s': %s\n",
169 offDbg, pOpts->pszInFile, strerror(errno));
170 if (!isLxDebugInfoSignature(uBuf.abBuf))
171 {
172 /* Try add the LX offset. */
173 if ( fseek(pInFile, offDbg + offLX, SEEK_SET) == 0
174 && fread(&uBuf.abBuf[4], 4, 1, pInFile) == 0
175 && isLxDebugInfoSignature(&uBuf.abBuf[4]))
176 offDbg += offLX;
177 else
178 return error("Debug info indicated by the LX header of '%s' is not recognized! (magic: %02x %02x %02x %02x)\n",
179 pOpts->pszInFile, uBuf.abBuf[0], uBuf.abBuf[1], uBuf.abBuf[2], uBuf.abBuf[3]);
180 }
181
182 if (offDbg + cbDbg != StIn.st_size) /* (This can be relaxed if we wish.) */
183 return error("Debug info indicated by the LX header is not at the end of '%s'! (%#lx + %#lx = %#lx (file: %#x)\n",
184 pOpts->pszInFile, offDbg, cbDbg, offDbg + cbDbg, (unsigned long)StIn.st_size);
185 }
186 /*
187 * Check for debug info at the end of the file.
188 */
189 else
190 {
191 if (fseek(pInFile, -8, SEEK_END) != 0)
192 return error("Failed seeking in '%s': %s\n", pOpts->pszInFile, strerror(errno));
193 if (fread(&uBuf, 8, 1, pInFile) != 1)
194 return error("Error reading last 8 bytes of '%s': %s\n", pOpts->pszInFile, strerror(errno));
195 if (isNbSignature(uBuf.abBuf))
196 {
197 int iVer = (uBuf.abBuf[2] - '0') * 10 + (uBuf.abBuf[3] - '0');
198 if (iVer != 4 && iVer != 2)
199 fprintf(stderr, "emxomfstrip: warning: Unknown debug info signature '%4.4s' in '%s'.\n",
200 uBuf.abBuf, pOpts->pszInFile);
201 cbDbg = uBuf.adwBuf[1];
202 if ( cbDbg >= (unsigned long)StIn.st_size
203 || cbDbg <= 16)
204 return error("Bad size in NB trailer debug of '%s': %#x (file size %#lx)\n",
205 pOpts->pszInFile, cbDbg, (unsigned long)StIn.st_size);
206 offDbg = (unsigned long)StIn.st_size - cbDbg;
207
208 if ( fseek(pInFile, offDbg, SEEK_SET) != 0
209 || fread(&uBuf.abBuf[4], 4, 1, pInFile) != 1)
210 return error("Error seeking/reading debug info header (@%#lx) in '%s': %s\n",
211 offDbg, pOpts->pszInFile, strerror(errno));
212 if (uBuf.adwBuf[0] != uBuf.adwBuf[1])
213 return error("Debug header does not match trailer in '%s': %02x %02x %02x %02x != %02x %02x %02x %02x\n",
214 pOpts->pszInFile,
215 uBuf.abBuf[0], uBuf.abBuf[1], uBuf.abBuf[2], uBuf.abBuf[3],
216 uBuf.abBuf[4+0], uBuf.abBuf[4+1], uBuf.abBuf[4+2], uBuf.abBuf[4+3]);
217 }
218 }
219
220 /*
221 * Did we find anything to strip? If not return immediately indicating this.
222 */
223 if (cbDbg == 0)
224 return -1;
225
226 /*
227 * Do we copy the debug info into a separate file first?
228 */
229 rc = 0;
230 if (pDbgFile)
231 rc = copyFileBytes(pDbgFile, pOpts->pszDbgFile, pInFile, pOpts->pszInFile, offDbg, cbDbg);
232
233 /*
234 * If pOutFile is NULL, we're only supposed to extract the debug info.
235 */
236 if (rc == 0 && pOutFile != NULL)
237 {
238 /*
239 * If there is a separate output file, copy over the bytes that we should keep.
240 */
241 if (pOutFile != pInFile)
242 rc = copyFileBytes(pOutFile, pOpts->pszOutFile, pInFile, pOpts->pszInFile, 0, offDbg);
243
244 /*
245 * Update the LX header in the output(/input) file.
246 */
247 if ( rc == 0
248 && ( uHdr.lx.debug_offset != 0
249 || uHdr.lx.debug_size != 0))
250 {
251 uHdr.lx.debug_offset = 0;
252 uHdr.lx.debug_size = 0;
253 uHdr.lx.loader_checksum = 0;
254 if (fseek(pOutFile, offLX, SEEK_SET) != 0)
255 return error("Error seeking to LX header in output file: %s\n", strerror(errno));
256 if (fwrite(&uHdr.lx, sizeof(uHdr.lx), 1, pOutFile) != 1)
257 return error("Error writing LX header of output file: %s\n", strerror(errno));
258 }
259
260 /*
261 * Truncate the output file if it's the same as the input.
262 */
263 if (rc == 0 && pOutFile == pInFile)
264 {
265 if (ftruncate(fileno(pOutFile), offDbg) != 0)
266 return error("Error truncating '%s': %s\n", pOpts->pszInFile, strerror(errno));
267 }
268 }
269
270 return rc;
271}
272
273
274static int usage(FILE *pOut, int rc)
275{
276 fprintf(pOut, "emxomfstrip [-dgsSxXvV] [-D dbginfo.file] [-o output.file] <file1> [file2...]\n");
277 return rc;
278}
279
280
281int main(int argc, char **argv)
282{
283 int ch;
284 int rcExit = 0;
285 EMXOMFSTRIPOPTS Opts = { NULL, NULL, NULL, 0 } ;
286
287#ifdef __EMX__
288 /* Preload the executable. */
289 _emxload_env("GCCLOAD");
290#endif
291
292 /* Get more arguments from EMXOMFSTRIPOPT and load reponse files and expand wildcards. */
293#ifdef __EMX__
294 _envargs(&argc, &argv, "EMXOMFSTRIPOPT");
295 _response(&argc, &argv);
296 _wildcard(&argc, &argv);
297#endif
298
299 /*
300 * Parse the options.
301 */
302 while ((ch = getopt_long(argc, argv, "dD:go:sSxXvV", NULL, NULL)) != EOF)
303 {
304 switch (ch)
305 {
306 case 'd':
307 case 'g':
308 case 's':
309 case 'S':
310 case 'x':
311 case 'X':
312 break;
313
314 case 'D':
315 Opts.pszDbgFile = optarg;
316 break;
317
318 case 'o':
319 Opts.pszOutFile = optarg;
320 break;
321
322 case 'v':
323 Opts.cVerbose++;
324 break;
325
326 case 'V':
327 printf(VERSION INNOTEK_VERSION "\n");
328 return 0;
329
330 default:
331 return usage(stderr, 1);
332 }
333 }
334
335 if (argc - optind == 0)
336 return usage(stderr, 1);
337 if ( argc - optind != 1
338 && (Opts.pszDbgFile || Opts.pszOutFile))
339 return error("exactly one file must be specified if -D or -o is used\n");
340
341 /*
342 * Process the files.
343 */
344 while ( optind < argc
345 && rcExit == 0)
346 {
347 /* Open the input and maybe output file. */
348 FILE *pInFile;
349 Opts.pszInFile = argv[optind];
350 if (Opts.pszOutFile)
351 pInFile = fopen(Opts.pszInFile, "rb");
352 else
353 pInFile = fopen(Opts.pszInFile, "r+b");
354 if (pInFile)
355 {
356 /* Open the output file if it differs. Special handling of /dev/null
357 for facilitaing only extracting the debug info. */
358 int fNull = 0;
359 FILE *pOutFile;
360 if (!Opts.pszOutFile)
361 pOutFile = pInFile;
362 else
363 {
364 if (strcmp(Opts.pszOutFile, "/dev/null"))
365 pOutFile = fopen(Opts.pszOutFile, "wb");
366 else
367 {
368 fNull = 1;
369 pOutFile = NULL;
370 }
371 }
372 if (pOutFile || fNull)
373 {
374 /* Open the debug file. */
375 FILE *pDbgFile = NULL;
376 if (Opts.pszDbgFile)
377 pDbgFile = fopen(Opts.pszDbgFile, "wb");
378 if (pDbgFile || !Opts.pszDbgFile)
379 {
380 /*
381 * All the files have been opened, get down to business.
382 */
383 int rc = stripFile(&Opts, pInFile, pOutFile, pDbgFile);
384 if (rc != 0 && rc != -1)
385 rcExit = rc;
386
387 /*
388 * Close the files. The user can clean up files on
389 * failure, at least for now.
390 */
391 if (pDbgFile && fclose(pDbgFile) != 0)
392 rcExit = error("fclose failed on '%s': %s\n", Opts.pszDbgFile, strerror(errno));
393 else if (rc == -1 && Opts.pszDbgFile != NULL && unlink(Opts.pszDbgFile) != 0)
394 rcExit = error("Failed to unlink debug info file '%s': %s\n", Opts.pszDbgFile, strerror(errno));
395 }
396 else
397 rcExit = error("Failed opening debug info file '%s': %s\n", Opts.pszDbgFile, strerror(errno));
398 if (pOutFile && pOutFile != pInFile && fclose(pOutFile) != 0)
399 rcExit = error("fclose failed on '%s': %s\n", Opts.pszOutFile, strerror(errno));
400 }
401 else
402 rcExit = error("Failed opening output file '%s': %s\n", Opts.pszOutFile, strerror(errno));
403 if (fclose(pInFile) != 0)
404 rcExit = error("fclose failed on '%s': %s\n", Opts.pszInFile, strerror(errno));
405 }
406 else
407 rcExit = error("Failed opening '%s': %s\n", Opts.pszInFile, strerror(errno));
408 optind++;
409 }
410
411 return rcExit;
412}
413
Note: See TracBrowser for help on using the repository browser.