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

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

fixed file header.

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