source: trunk/src/kmk/kmkbuiltin/kDepObj.c@ 2263

Last change on this file since 2263 was 2263, checked in by bird, 16 years ago

kDepObj: Initial code that deals with Watcom and MASM OMF files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.7 KB
Line 
1/* $Id: kDepObj.c 2263 2009-01-23 00:22:47Z bird $ */
2/** @file
3 * kDepObj - Extract dependency information from an object file.
4 */
5
6/*
7 * Copyright (c) 2007-2009 knut st. osmundsen <bird-kBuild-spamix@anduin.net>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include "config.h"
30#include <stdio.h>
31#include <stdlib.h>
32#include <stddef.h>
33#include <string.h>
34#include <errno.h>
35#include <ctype.h>
36#ifdef HAVE_ALLOCA_H
37# include <alloca.h>
38#endif
39#if !defined(_MSC_VER)
40# include <unistd.h>
41#else
42# include <io.h>
43#endif
44#include "../../lib/k/kDefs.h"
45#include "../../lib/k/kTypes.h"
46#include "../../lib/kDep.h"
47#include "kmkbuiltin.h"
48
49
50/*******************************************************************************
51* Defined Constants And Macros *
52*******************************************************************************/
53/*#define DEBUG*/
54#define DEBUG
55#ifdef DEBUG
56# define dprintf(a) printf a
57# define dump(pb, cb, offBase) depHexDump(pb,cb,offBase)
58#else
59# define dprintf(a) do {} while (0)
60# define dump(pb, cb, offBase) do {} while (0)
61#endif
62
63/** @name OMF defines
64 * @{ */
65#define KDEPOMF_THEADR 0x80
66#define KDEPOMF_LHEADR 0x82
67#define KDEPOMF_COMMENT 0x88
68#define KDEPOMF_CMTCLS_DEPENDENCY 0xe9
69/** @} */
70
71
72/*******************************************************************************
73* Structures and Typedefs *
74*******************************************************************************/
75/** @name OMF structure
76 * @{ */
77#pragma pack(1)
78/** OMF record header. */
79typedef struct KDEPOMFHDR
80{
81 /** The record type. */
82 KU8 bType;
83 /** The size of the record, excluding this header. */
84 KU16 cbRec;
85} KDEPOMFHDR;
86typedef KDEPOMFHDR *PKDEPOMFHDR;
87typedef const KDEPOMFHDR *PCKDEPOMFHDR;
88
89/** OMF string. */
90typedef struct KDEPOMFSTR
91{
92 KU8 cch;
93 char ach[1];
94} KDEPOMFSTR;
95typedef KDEPOMFSTR *PKDEPOMFSTR;
96typedef const KDEPOMFSTR *PCKDEPOMFSTR;
97
98/** THEADR/LHEADR. */
99typedef struct KDEPOMFTHEADR
100{
101 KDEPOMFHDR Hdr;
102 KDEPOMFSTR Name;
103} KDEPOMFTHEADR;
104typedef KDEPOMFTHEADR *PKDEPOMFTHEADR;
105typedef const KDEPOMFTHEADR *PCKDEPOMFTHEADR;
106
107/** Dependency File. */
108typedef struct KDEPOMFDEPFILE
109{
110 KDEPOMFHDR Hdr;
111 KU8 fType;
112 KU8 bClass;
113 KU16 wDosTime;
114 KU16 wDosDate;
115 KDEPOMFSTR Name;
116} KDEPOMFDEPFILE;
117typedef KDEPOMFDEPFILE *PKDEPOMFDEPFILE;
118typedef const KDEPOMFDEPFILE *PCKDEPOMFDEPFILE;
119
120#pragma pack()
121/** @} */
122
123
124/*******************************************************************************
125* Global Variables *
126*******************************************************************************/
127/** the executable name. */
128static const char *argv0 = "";
129
130
131/**
132 * Parses the OMF file.
133 *
134 * @returns 0 on success, 1 on failure.
135 * @param pbFile The start of the file.
136 * @param cbFile The file size.
137 */
138int kDepObjOMFParse(const KU8 *pbFile, KSIZE cbFile)
139{
140 PCKDEPOMFHDR pHdr = (PCKDEPOMFHDR)pbFile;
141 KSIZE cbLeft = cbFile;
142
143
144 /*
145 * Iterate thru the records until we hit the end or an invalid one.
146 */
147 while ( cbLeft >= sizeof(*pHdr)
148 && cbLeft >= pHdr->cbRec + sizeof(*pHdr))
149 {
150 /* process selected record types. */
151 dprintf(("%#07x: %#04x %#05x\n", (const KU8*)pHdr - pbFile, pHdr->bType, pHdr->cbRec));
152 switch (pHdr->bType)
153 {
154 /*
155 * The T/L Header contains the source name. When emitting CodeView 4
156 * and earlier (like masm and watcom does), all includes used by the
157 * line number tables have their own THEADR record.
158 */
159 case KDEPOMF_THEADR:
160 case KDEPOMF_LHEADR:
161 {
162 PCKDEPOMFTHEADR pTHeadr = (PCKDEPOMFTHEADR)pHdr;
163 if (1 + pTHeadr->Name.cch + 1 != pHdr->cbRec)
164 {
165 fprintf(stderr, "%s: error: %#07x - Bad %cHEADR record, length mismatch.\n",
166 argv0, (const KU8*)pHdr - pbFile, pHdr->bType == KDEPOMF_THEADR ? 'T' : 'L');
167 return 1;
168 }
169 depAdd(pTHeadr->Name.ach, pTHeadr->Name.cch);
170 break;
171 }
172
173 case KDEPOMF_COMMENT:
174 if (pHdr->cbRec < 2)
175 {
176 fprintf(stderr, "%s: error: %#07x - Bad COMMENT record, too small.\n",
177 argv0, (const KU8*)pHdr - pbFile);
178 return 1;
179 }
180 if (((const KU8 *)(pHdr+1))[0] & 0x3f)
181 {
182 fprintf(stderr, "%s: error: %#07x - Bad COMMENT record, reserved flags set.\n",
183 argv0, (const KU8*)pHdr - pbFile);
184 return 1;
185 }
186 switch (((const KU8 *)(pHdr+1))[1])
187 {
188 /*
189 * Borland dependency file comment (famously used by wmake and Watcom).
190 */
191 case KDEPOMF_CMTCLS_DEPENDENCY:
192 {
193 PCKDEPOMFDEPFILE pDep = (PCKDEPOMFDEPFILE)pHdr;
194 if (K_OFFSETOF(KDEPOMFDEPFILE, Name.ach[pDep->Name.cch]) + 1 != pHdr->cbRec + sizeof(*pHdr))
195 {
196 /* Empty record indicates the end of the dependency files,
197 no need to go on. */
198 if (pHdr->cbRec == 2 + 1)
199 return 0;
200
201 fprintf(stderr, "%s: error: %#07x - Bad DEPENDENCY FILE record, length mismatch. (%d/%d)\n",
202 argv0, (const KU8*)pHdr - pbFile,
203 K_OFFSETOF(KDEPOMFDEPFILE, Name.ach[pDep->Name.cch]) + 1,
204 pHdr->cbRec + sizeof(*pHdr));
205 return 1;
206
207 }
208 depAdd(pDep->Name.ach, pDep->Name.cch);
209 break;
210 }
211
212 /** @todo Check for class A1 and pick up the debug info type (HL/CV(/DX)). */
213 }
214 break;
215 /** @todo add support for HLL line number segments and stuff. */
216 }
217
218 /* advance */
219 cbLeft -= pHdr->cbRec + sizeof(*pHdr);
220 pHdr = (PCKDEPOMFHDR)((const KU8 *)(pHdr + 1) + pHdr->cbRec);
221 }
222
223 if (cbLeft)
224 {
225 fprintf(stderr, "%s: error: %#07x - Unexpected EOF. cbLeft=%#x\n",
226 argv0, (const KU8*)pHdr - pbFile, cbLeft);
227 return 1;
228 }
229 return 0;
230}
231
232
233/**
234 * Checks if this file is an OMF file or not.
235 *
236 * @returns K_TRUE if it's OMF, K_FALSE otherwise.
237 *
238 * @param pb The start of the file.
239 * @param cb The file size.
240 */
241KBOOL kDepObjOMFTest(const KU8 *pbFile, KSIZE cbFile)
242{
243 PCKDEPOMFTHEADR pHdr = (PCKDEPOMFTHEADR)pbFile;
244
245 if (cbFile < sizeof(*pHdr))
246 return K_FALSE;
247 if ( pHdr->Hdr.bType != KDEPOMF_THEADR
248 && pHdr->Hdr.bType != KDEPOMF_LHEADR)
249 return K_FALSE;
250 if (pHdr->Hdr.cbRec + sizeof(pHdr->Hdr) >= cbFile)
251 return K_FALSE;
252 if (pHdr->Hdr.cbRec != 1 + pHdr->Name.cch + 1)
253 return K_FALSE;
254
255 return K_TRUE;
256}
257
258
259/**
260 * Read the file into memory and parse it.
261 */
262static int kDepObjProcessFile(FILE *pInput)
263{
264 size_t cbFile;
265 KU8 *pbFile;
266 void *pvOpaque;
267 int rc = 0;
268
269 /*
270 * Read the file into memory.
271 */
272 pbFile = (KU8 *)depReadFileIntoMemory(pInput, &cbFile, &pvOpaque);
273 if (!pbFile)
274 return 1;
275
276 /*
277 * See if it's an OMF file, then process it.
278 */
279 if (kDepObjOMFTest(pbFile, cbFile))
280 rc = kDepObjOMFParse(pbFile, cbFile);
281 else
282 {
283 fprintf(stderr, "%s: error: Doesn't recognize the header of the OMF file.\n", argv0);
284 rc = 1;
285 }
286
287 depFreeFileMemory(pbFile, pvOpaque);
288 return rc;
289}
290
291
292static void usage(const char *a_argv0)
293{
294 printf("usage: %s -o <output> -t <target> [-fqs] <OMF-file>\n"
295 " or: %s --help\n"
296 " or: %s --version\n",
297 a_argv0, a_argv0, a_argv0);
298}
299
300
301int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
302{
303 int i;
304
305 /* Arguments. */
306 FILE *pOutput = NULL;
307 const char *pszOutput = NULL;
308 FILE *pInput = NULL;
309 const char *pszTarget = NULL;
310 int fStubs = 0;
311 int fFixCase = 0;
312 /* Argument parsing. */
313 int fInput = 0; /* set when we've found input argument. */
314 int fQuiet = 0;
315
316 argv0 = argv[0];
317
318 /*
319 * Parse arguments.
320 */
321 if (argc <= 1)
322 {
323 usage(argv[0]);
324 return 1;
325 }
326 for (i = 1; i < argc; i++)
327 {
328 if (argv[i][0] == '-')
329 {
330 const char *psz = &argv[i][1];
331 if (*psz == '-')
332 {
333 if (!strcmp(psz, "-quiet"))
334 psz = "q";
335 else if (!strcmp(psz, "-help"))
336 psz = "?";
337 else if (!strcmp(psz, "-version"))
338 psz = "V";
339 }
340
341 switch (*psz)
342 {
343 /*
344 * Output file.
345 */
346 case 'o':
347 {
348 pszOutput = &argv[i][2];
349 if (pOutput)
350 {
351 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
352 return 1;
353 }
354 if (!*pszOutput)
355 {
356 if (++i >= argc)
357 {
358 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
359 return 1;
360 }
361 pszOutput = argv[i];
362 }
363 if (pszOutput[0] == '-' && !pszOutput[1])
364 pOutput = stdout;
365 else
366 pOutput = fopen(pszOutput, "w");
367 if (!pOutput)
368 {
369 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
370 return 1;
371 }
372 break;
373 }
374
375 /*
376 * Target name.
377 */
378 case 't':
379 {
380 if (pszTarget)
381 {
382 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
383 return 1;
384 }
385 pszTarget = &argv[i][2];
386 if (!*pszTarget)
387 {
388 if (++i >= argc)
389 {
390 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
391 return 1;
392 }
393 pszTarget = argv[i];
394 }
395 break;
396 }
397
398 /*
399 * Fix case.
400 */
401 case 'f':
402 {
403 fFixCase = 1;
404 break;
405 }
406
407 /*
408 * Quiet.
409 */
410 case 'q':
411 {
412 fQuiet = 1;
413 break;
414 }
415
416 /*
417 * Generate stubs.
418 */
419 case 's':
420 {
421 fStubs = 1;
422 break;
423 }
424
425 /*
426 * The mandatory version & help.
427 */
428 case '?':
429 usage(argv[0]);
430 return 0;
431 case 'V':
432 case 'v':
433 return kbuild_version(argv[0]);
434
435 /*
436 * Invalid argument.
437 */
438 default:
439 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
440 usage(argv[0]);
441 return 1;
442 }
443 }
444 else
445 {
446 pInput = fopen(argv[i], "rb");
447 if (!pInput)
448 {
449 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
450 return 1;
451 }
452 fInput = 1;
453 }
454
455 /*
456 * End of the line?
457 */
458 if (fInput)
459 {
460 if (++i < argc)
461 {
462 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
463 return 1;
464 }
465 break;
466 }
467 }
468
469 /*
470 * Got all we require?
471 */
472 if (!pInput)
473 {
474 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
475 return 1;
476 }
477 if (!pOutput)
478 {
479 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
480 return 1;
481 }
482 if (!pszTarget)
483 {
484 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
485 return 1;
486 }
487
488 /*
489 * Do the parsing.
490 */
491 i = kDepObjProcessFile(pInput);
492 fclose(pInput);
493
494 /*
495 * Write the dependecy file.
496 */
497 if (!i)
498 {
499 depOptimize(fFixCase, fQuiet);
500 fprintf(pOutput, "%s:", pszTarget);
501 depPrint(pOutput);
502 if (fStubs)
503 depPrintStubs(pOutput);
504 }
505
506 /*
507 * Close the output, delete output on failure.
508 */
509 if (!i && ferror(pOutput))
510 {
511 i = 1;
512 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
513 }
514 fclose(pOutput);
515 if (i)
516 {
517 if (unlink(pszOutput))
518 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
519 }
520
521 depCleanup();
522 return i;
523}
524
Note: See TracBrowser for help on using the repository browser.