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

Last change on this file since 2275 was 2267, checked in by bird, 17 years ago

kDepObj: Taught it how to parse .debug$S section and find the source file info. CV8 and Visual C++ 2005 and later (+YASM). Not quite polished up yet, work in progress. References #82.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.8 KB
Line 
1/* $Id: kDepObj.c 2267 2009-01-24 02:00:40Z 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/k/kLdrFmts/pe.h"
47#include "../../lib/kDep.h"
48#include "kmkbuiltin.h"
49
50
51/*******************************************************************************
52* Defined Constants And Macros *
53*******************************************************************************/
54/*#define DEBUG*/
55#define DEBUG
56#ifdef DEBUG
57# define dprintf(a) printf a
58# define dump(pb, cb, offBase) depHexDump(pb,cb,offBase)
59#else
60# define dprintf(a) do {} while (0)
61# define dump(pb, cb, offBase) do {} while (0)
62#endif
63
64/** @name OMF defines
65 * @{ */
66#define KDEPOMF_THEADR 0x80
67#define KDEPOMF_LHEADR 0x82
68#define KDEPOMF_COMMENT 0x88
69#define KDEPOMF_CMTCLS_DEPENDENCY 0xe9
70/** @} */
71
72
73/*******************************************************************************
74* Structures and Typedefs *
75*******************************************************************************/
76/** @name OMF Structures
77 * @{ */
78#pragma pack(1)
79/** OMF record header. */
80typedef struct KDEPOMFHDR
81{
82 /** The record type. */
83 KU8 bType;
84 /** The size of the record, excluding this header. */
85 KU16 cbRec;
86} KDEPOMFHDR;
87typedef KDEPOMFHDR *PKDEPOMFHDR;
88typedef const KDEPOMFHDR *PCKDEPOMFHDR;
89
90/** OMF string. */
91typedef struct KDEPOMFSTR
92{
93 KU8 cch;
94 char ach[1];
95} KDEPOMFSTR;
96typedef KDEPOMFSTR *PKDEPOMFSTR;
97typedef const KDEPOMFSTR *PCKDEPOMFSTR;
98
99/** THEADR/LHEADR. */
100typedef struct KDEPOMFTHEADR
101{
102 KDEPOMFHDR Hdr;
103 KDEPOMFSTR Name;
104} KDEPOMFTHEADR;
105typedef KDEPOMFTHEADR *PKDEPOMFTHEADR;
106typedef const KDEPOMFTHEADR *PCKDEPOMFTHEADR;
107
108/** Dependency File. */
109typedef struct KDEPOMFDEPFILE
110{
111 KDEPOMFHDR Hdr;
112 KU8 fType;
113 KU8 bClass;
114 KU16 wDosTime;
115 KU16 wDosDate;
116 KDEPOMFSTR Name;
117} KDEPOMFDEPFILE;
118typedef KDEPOMFDEPFILE *PKDEPOMFDEPFILE;
119typedef const KDEPOMFDEPFILE *PCKDEPOMFDEPFILE;
120
121#pragma pack()
122/** @} */
123
124
125/** @name COFF Structures
126 * @{ */
127#pragma pack(1)
128
129typedef struct KDEPCVSYMHDR
130{
131 /** The record size minus the size field. */
132 KU16 cb;
133 /** The record type. */
134 KU16 uType;
135} KDEPCVSYMHDR;
136typedef KDEPCVSYMHDR *PKDEPCVSYMHDR;
137typedef const KDEPCVSYMHDR *PCKDEPCVSYMHDR;
138
139/** @name Selection of KDEPCVSYMHDR::uType values.
140 * @{ */
141#define K_CV8_S_MSTOOL KU16_C(0x1116)
142/** @} */
143
144typedef struct KDEPCV8SYMHDR
145{
146 /** The record type. */
147 KU32 uType;
148 /** The record size minus the size field. */
149 KU32 cb;
150} KDEPCV8SYMHDR;
151typedef KDEPCV8SYMHDR *PKDEPCV8SYMHDR;
152typedef const KDEPCV8SYMHDR *PCKDEPCV8SYMHDR;
153
154/** @name Known KDEPCV8SYMHDR::uType Values.
155 * @{ */
156#define K_CV8_SYMBOL_INFO KU32_C(0x000000f1)
157#define K_CV8_LINE_NUMBERS KU32_C(0x000000f2)
158#define K_CV8_STRING_TABLE KU32_C(0x000000f3)
159#define K_CV8_SOURCE_FILES KU32_C(0x000000f4)
160#define K_CV8_COMDAT_XXXXX KU32_C(0x000000f5) /**< no idea about the format... */
161/** @} */
162
163#pragma pack()
164/** @} */
165
166
167/*******************************************************************************
168* Global Variables *
169*******************************************************************************/
170/** the executable name. */
171static const char *argv0 = "";
172
173
174/**
175 * Parses the OMF file.
176 *
177 * @returns 0 on success, 1 on failure.
178 * @param pbFile The start of the file.
179 * @param cbFile The file size.
180 */
181int kDepObjOMFParse(const KU8 *pbFile, KSIZE cbFile)
182{
183 PCKDEPOMFHDR pHdr = (PCKDEPOMFHDR)pbFile;
184 KSIZE cbLeft = cbFile;
185
186
187 /*
188 * Iterate thru the records until we hit the end or an invalid one.
189 */
190 while ( cbLeft >= sizeof(*pHdr)
191 && cbLeft >= pHdr->cbRec + sizeof(*pHdr))
192 {
193 /* process selected record types. */
194 dprintf(("%#07x: %#04x %#05x\n", (const KU8*)pHdr - pbFile, pHdr->bType, pHdr->cbRec));
195 switch (pHdr->bType)
196 {
197 /*
198 * The T/L Header contains the source name. When emitting CodeView 4
199 * and earlier (like masm and watcom does), all includes used by the
200 * line number tables have their own THEADR record.
201 */
202 case KDEPOMF_THEADR:
203 case KDEPOMF_LHEADR:
204 {
205 PCKDEPOMFTHEADR pTHeadr = (PCKDEPOMFTHEADR)pHdr;
206 if (1 + pTHeadr->Name.cch + 1 != pHdr->cbRec)
207 {
208 fprintf(stderr, "%s: error: %#07x - Bad %cHEADR record, length mismatch.\n",
209 argv0, (const KU8*)pHdr - pbFile, pHdr->bType == KDEPOMF_THEADR ? 'T' : 'L');
210 return 1;
211 }
212 depAdd(pTHeadr->Name.ach, pTHeadr->Name.cch);
213 break;
214 }
215
216 case KDEPOMF_COMMENT:
217 if (pHdr->cbRec < 2)
218 {
219 fprintf(stderr, "%s: error: %#07x - Bad COMMENT record, too small.\n",
220 argv0, (const KU8*)pHdr - pbFile);
221 return 1;
222 }
223 if (((const KU8 *)(pHdr+1))[0] & 0x3f)
224 {
225 fprintf(stderr, "%s: error: %#07x - Bad COMMENT record, reserved flags set.\n",
226 argv0, (const KU8*)pHdr - pbFile);
227 return 1;
228 }
229 switch (((const KU8 *)(pHdr+1))[1])
230 {
231 /*
232 * Borland dependency file comment (famously used by wmake and Watcom).
233 */
234 case KDEPOMF_CMTCLS_DEPENDENCY:
235 {
236 PCKDEPOMFDEPFILE pDep = (PCKDEPOMFDEPFILE)pHdr;
237 if (K_OFFSETOF(KDEPOMFDEPFILE, Name.ach[pDep->Name.cch]) + 1 != pHdr->cbRec + sizeof(*pHdr))
238 {
239 /* Empty record indicates the end of the dependency files,
240 no need to go on. */
241 if (pHdr->cbRec == 2 + 1)
242 return 0;
243
244 fprintf(stderr, "%s: error: %#07x - Bad DEPENDENCY FILE record, length mismatch. (%d/%d)\n",
245 argv0, (const KU8*)pHdr - pbFile,
246 K_OFFSETOF(KDEPOMFDEPFILE, Name.ach[pDep->Name.cch]) + 1,
247 pHdr->cbRec + sizeof(*pHdr));
248 return 1;
249
250 }
251 depAdd(pDep->Name.ach, pDep->Name.cch);
252 break;
253 }
254
255 /** @todo Check for class A1 and pick up the debug info type (HL/CV(/DX)). */
256 }
257 break;
258 /** @todo add support for HLL line number segments and stuff. */
259 }
260
261 /* advance */
262 cbLeft -= pHdr->cbRec + sizeof(*pHdr);
263 pHdr = (PCKDEPOMFHDR)((const KU8 *)(pHdr + 1) + pHdr->cbRec);
264 }
265
266 if (cbLeft)
267 {
268 fprintf(stderr, "%s: error: %#07x - Unexpected EOF. cbLeft=%#x\n",
269 argv0, (const KU8*)pHdr - pbFile, cbLeft);
270 return 1;
271 }
272 return 0;
273}
274
275
276/**
277 * Checks if this file is an OMF file or not.
278 *
279 * @returns K_TRUE if it's OMF, K_FALSE otherwise.
280 *
281 * @param pb The start of the file.
282 * @param cb The file size.
283 */
284KBOOL kDepObjOMFTest(const KU8 *pbFile, KSIZE cbFile)
285{
286 PCKDEPOMFTHEADR pHdr = (PCKDEPOMFTHEADR)pbFile;
287
288 if (cbFile <= sizeof(*pHdr))
289 return K_FALSE;
290 if ( pHdr->Hdr.bType != KDEPOMF_THEADR
291 && pHdr->Hdr.bType != KDEPOMF_LHEADR)
292 return K_FALSE;
293 if (pHdr->Hdr.cbRec + sizeof(pHdr->Hdr) >= cbFile)
294 return K_FALSE;
295 if (pHdr->Hdr.cbRec != 1 + pHdr->Name.cch + 1)
296 return K_FALSE;
297
298 return K_TRUE;
299}
300
301
302/**
303 * Parses a CodeView 8 symbol section.
304 *
305 * @returns 0 on success, 1 on failure.
306 * @param pbSyms Pointer to the start of the symbol section.
307 * @param cbSyms Size of the symbol section.
308 */
309int kDepObjCOFFParseCV8SymbolSection(const KU8 *pbSyms, KSIZE cbSyms)
310{
311 char const * pchStrTab = NULL;
312 KU32 cbStrTab = 0;
313 KPCUINT uSrcFiles = {0};
314 KU32 cbSrcFiles = 0;
315 KU32 off = 4;
316 KU32 iSrc = 0;
317
318 if (cbSyms < 16)
319 return 1;
320
321 /*
322 * The parsing loop.
323 */
324 while (off < cbSyms)
325 {
326 PCKDEPCV8SYMHDR pHdr = (PCKDEPCV8SYMHDR)(pbSyms + off);
327 KPCUINT uData;
328 KU32 cbData;
329 uData.pv = pHdr + 1;
330
331 if (off + sizeof(*pHdr) >= cbSyms)
332 {
333 fprintf(stderr, "%s: CV symbol table entry at %08" KX32_PRI " is too long; cbSyms=%#" KSIZE_PRI "\n",
334 argv0, off, cbSyms);
335 return 1; /* FIXME */
336 }
337
338 cbData = pHdr->cb;
339 if (off + cbData + sizeof(*pHdr) > cbSyms)
340 {
341 fprintf(stderr, "%s: CV symbol table entry at %08" KX32_PRI " is too long; cbData=%#" KX32_PRI " cbSyms=%#" KSIZE_PRI "\n",
342 argv0, off, cbData, cbSyms);
343 return 1; /* FIXME */
344 }
345
346 /* If the size is 0, assume it covers the rest of the section. VC++ 2003 has
347 been observed doing thing. */
348 if (!cbData)
349 cbData = cbSyms - off;
350
351 switch (pHdr->uType)
352 {
353 case K_CV8_SYMBOL_INFO:
354 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": Symbol Info\n", off, cbData));
355 /*dump(uData.pb, cbData, 0);*/
356 break;
357
358 case K_CV8_LINE_NUMBERS:
359 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": Line numbers\n", off, cbData));
360 /*dump(uData.pb, cbData, 0);*/
361 break;
362
363 case K_CV8_STRING_TABLE:
364 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": String table\n", off, cbData));
365 if (pchStrTab)
366 fprintf(stderr, "%s: warning: Found yet another string table!\n");
367 pchStrTab = uData.pch;
368 cbStrTab = cbData;
369 /*dump(uData.pb, cbData, 0);*/
370 break;
371
372 case K_CV8_SOURCE_FILES:
373 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": Source files\n", off, cbData));
374 if (uSrcFiles.pb)
375 fprintf(stderr, "%s: warning: Found yet another source files table!\n");
376 uSrcFiles = uData;
377 cbSrcFiles = cbData;
378 /*dump(uData.pb, cbData, 0);*/
379 break;
380
381 case K_CV8_COMDAT_XXXXX:
382 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": 0xf5 Unknown COMDAT stuff\n", off, cbData));
383 /*dump(uData.pb, cbData, 0);*/
384 break;
385
386 default:
387 dprintf(("%06" KX32_PRI " %06" KX32_PRI ": Unknown type %#" KX32_PRI "\n",
388 off, cbData, pHdr->uType));
389 dump(uData.pb, cbData, 0);
390 break;
391 }
392
393 /* next */
394 cbData = (cbData + 3) & ~KU32_C(3);
395 off += cbData + sizeof(*pHdr);
396 }
397
398 /*
399 * Did we find any source files and strings?
400 */
401 if (!pchStrTab || !uSrcFiles.pv)
402 {
403 dprintf(("No cylindrical smoking thing: pchStrTab=%p uSrcFiles.pv=%p\n", pchStrTab, uSrcFiles.pv));
404 return 2;
405 }
406
407 /*
408 * Iterate the source file table.
409 */
410 off = 0;
411 while (off < cbSrcFiles)
412 {
413 KU32 offFile;
414 const char *pszFile;
415 KSIZE cchFile;
416 KU16 u16Type;
417 KPCUINT uSrc;
418 KU32 cbSrc;
419
420 /*
421 * Validate and parse the entry (variable length record are fun).
422 */
423 if (off + 8 > cbSrcFiles)
424 {
425 fprintf(stderr, "%s: CV source file entry at %08" KX32_PRI " is too long; cbSrcFiles=%#" KSIZE_PRI "\n",
426 argv0, off, cbSrcFiles);
427 return 1;
428 }
429 uSrc.pb = uSrcFiles.pb + off;
430 u16Type = uSrc.pu16[2];
431 cbSrc = u16Type == 0x0110 ? 6 + 16 + 2 : 6 + 2;
432 if (off + cbSrc > cbSrcFiles)
433 {
434 fprintf(stderr, "%s: CV source file entry at %08" KX32_PRI " is too long; cbSrc=%#" KX32_PRI " cbSrcFiles=%#" KSIZE_PRI "\n",
435 argv0, off, cbSrc, cbSrcFiles);
436 return 1;
437 }
438
439 offFile = *uSrc.pu32;
440 if (offFile > cbStrTab)
441 {
442 fprintf(stderr, "%s: CV source file entry at %08" KX32_PRI " is out side the string table; offFile=%#" KX32_PRI " cbStrTab=%#" KX32_PRI "\n",
443 argv0, off, offFile, cbStrTab);
444 return 1;
445 }
446 pszFile = pchStrTab + offFile;
447 cchFile = strlen(pszFile);
448 if (cchFile == 0)
449 {
450 fprintf(stderr, "%s: CV source file entry at %08" KX32_PRI " has an empty file name; offFile=%#x" KX32_PRI "\n",
451 argv0, off, offFile);
452 return 1;
453 }
454
455 /*
456 * Display the result and add it to the dependency database.
457 */
458 depAdd(pszFile, cchFile);
459 if (u16Type == 0x0110)
460 dprintf(("#%03" KU32_PRI ": {todo-md5-todo} '%s'\n",
461 iSrc, pszFile));
462 else
463 dprintf(("#%03" KU32_PRI ": type=%#06" KX16_PRI " '%s'\n", iSrc, u16Type, pszFile));
464
465
466 /* next */
467 iSrc++;
468 off += cbSrc;
469 }
470
471 if (iSrc == 0)
472 {
473 dprintf(("No cylindrical smoking thing: iSrc=0\n"));
474 return 2;
475 }
476 dprintf(("iSrc=%" KU32_PRI "\n", iSrc));
477 return 0;
478}
479
480
481/**
482 * Parses the OMF file.
483 *
484 * @returns 0 on success, 1 on failure, 2 if no dependencies was found.
485 * @param pbFile The start of the file.
486 * @param cbFile The file size.
487 */
488int kDepObjCOFFParse(const KU8 *pbFile, KSIZE cbFile)
489{
490 IMAGE_FILE_HEADER const *pFileHdr = (IMAGE_FILE_HEADER const *)pbFile;
491 IMAGE_SECTION_HEADER const *paSHdrs = (IMAGE_SECTION_HEADER const *)((KU8 const *)(pFileHdr + 1) + pFileHdr->SizeOfOptionalHeader);
492 unsigned cSHdrs = pFileHdr->NumberOfSections;
493 unsigned iSHdr;
494 KPCUINT u;
495 int rcRet = 2;
496 int rc;
497
498 printf("COFF file!\n");
499
500 for (iSHdr = 0; iSHdr < cSHdrs; iSHdr++)
501 {
502 if ( !memcmp(paSHdrs[iSHdr].Name, ".debug$S", sizeof(".debug$S") - 1)
503 && paSHdrs[iSHdr].SizeOfRawData > 4)
504 {
505 u.pb = pbFile + paSHdrs[iSHdr].PointerToRawData;
506 dprintf(("CV symbol table: version=%x\n", *u.pu32));
507 if (*u.pu32 == 0x000000004)
508 rc = kDepObjCOFFParseCV8SymbolSection(u.pb, paSHdrs[iSHdr].SizeOfRawData);
509 else
510 rc = 2;
511 dprintf(("rc=%d\n", rc));
512 if (rcRet == 2)
513 rcRet = rc;
514 if (rcRet != 2)
515 return rc;
516 }
517 printf("#%d: %.8s\n", iSHdr, paSHdrs[iSHdr].Name);
518 }
519 return rcRet;
520}
521
522
523/**
524 * Checks if this file is a COFF file or not.
525 *
526 * @returns K_TRUE if it's COFF, K_FALSE otherwise.
527 *
528 * @param pb The start of the file.
529 * @param cb The file size.
530 */
531KBOOL kDepObjCOFFTest(const KU8 *pbFile, KSIZE cbFile)
532{
533 IMAGE_FILE_HEADER const *pFileHdr = (IMAGE_FILE_HEADER const *)pbFile;
534 IMAGE_SECTION_HEADER const *paSHdrs = (IMAGE_SECTION_HEADER const *)((KU8 const *)(pFileHdr + 1) + pFileHdr->SizeOfOptionalHeader);
535 unsigned cSHdrs = pFileHdr->NumberOfSections;
536 unsigned iSHdr;
537 KSIZE cbHdrs = (const KU8 *)&paSHdrs[cSHdrs] - (const KU8 *)pbFile;
538
539 if (cbFile <= sizeof(*pFileHdr))
540 return K_FALSE;
541 if ( pFileHdr->Machine != IMAGE_FILE_MACHINE_I386
542 && pFileHdr->Machine != IMAGE_FILE_MACHINE_AMD64)
543 return K_FALSE;
544 if (pFileHdr->SizeOfOptionalHeader != 0)
545 return K_FALSE; /* COFF files doesn't have an optional header */
546
547 if ( pFileHdr->NumberOfSections <= 1
548 || pFileHdr->NumberOfSections > cbFile)
549 return K_FALSE;
550
551 if (cbHdrs >= cbFile)
552 return K_FALSE;
553
554 if ( pFileHdr->PointerToSymbolTable != 0
555 && ( pFileHdr->PointerToSymbolTable < cbHdrs
556 || pFileHdr->PointerToSymbolTable > cbFile))
557 return K_FALSE;
558 if ( pFileHdr->PointerToSymbolTable == 0
559 && pFileHdr->NumberOfSymbols != 0)
560 return K_FALSE;
561 if ( pFileHdr->Characteristics
562 & ( IMAGE_FILE_DLL
563 | IMAGE_FILE_SYSTEM
564 | IMAGE_FILE_UP_SYSTEM_ONLY
565 | IMAGE_FILE_NET_RUN_FROM_SWAP
566 | IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP
567 | IMAGE_FILE_EXECUTABLE_IMAGE
568 | IMAGE_FILE_RELOCS_STRIPPED))
569 return K_FALSE;
570
571 for (iSHdr = 0; iSHdr < cSHdrs; iSHdr++)
572 {
573 if ( paSHdrs[iSHdr].PointerToRawData != 0
574 && ( paSHdrs[iSHdr].PointerToRawData < cbHdrs
575 || paSHdrs[iSHdr].PointerToRawData >= cbFile
576 || paSHdrs[iSHdr].PointerToRawData + paSHdrs[iSHdr].SizeOfRawData > cbFile))
577 return K_FALSE;
578 if ( paSHdrs[iSHdr].PointerToRelocations != 0
579 && ( paSHdrs[iSHdr].PointerToRelocations < cbHdrs
580 || paSHdrs[iSHdr].PointerToRelocations >= cbFile
581 || paSHdrs[iSHdr].PointerToRelocations + paSHdrs[iSHdr].NumberOfRelocations * 10 > cbFile)) /* IMAGE_RELOCATION */
582 return K_FALSE;
583 if ( paSHdrs[iSHdr].PointerToLinenumbers != 0
584 && ( paSHdrs[iSHdr].PointerToLinenumbers < cbHdrs
585 || paSHdrs[iSHdr].PointerToLinenumbers >= cbFile
586 || paSHdrs[iSHdr].PointerToLinenumbers + paSHdrs[iSHdr].NumberOfLinenumbers * 6 > cbFile)) /* IMAGE_LINENUMBER */
587 return K_FALSE;
588 }
589
590 return K_TRUE;
591}
592
593
594/**
595 * Read the file into memory and parse it.
596 */
597static int kDepObjProcessFile(FILE *pInput)
598{
599 size_t cbFile;
600 KU8 *pbFile;
601 void *pvOpaque;
602 int rc = 0;
603
604 /*
605 * Read the file into memory.
606 */
607 pbFile = (KU8 *)depReadFileIntoMemory(pInput, &cbFile, &pvOpaque);
608 if (!pbFile)
609 return 1;
610
611 /*
612 * See if it's an OMF file, then process it.
613 */
614 if (kDepObjOMFTest(pbFile, cbFile))
615 rc = kDepObjOMFParse(pbFile, cbFile);
616 else if (kDepObjCOFFTest(pbFile, cbFile))
617 rc = kDepObjCOFFParse(pbFile, cbFile);
618 else
619 {
620 fprintf(stderr, "%s: error: Doesn't recognize the header of the OMF file.\n", argv0);
621 rc = 1;
622 }
623
624 depFreeFileMemory(pbFile, pvOpaque);
625 return rc;
626}
627
628
629static void usage(const char *a_argv0)
630{
631 printf("usage: %s -o <output> -t <target> [-fqs] <OMF-file>\n"
632 " or: %s --help\n"
633 " or: %s --version\n",
634 a_argv0, a_argv0, a_argv0);
635}
636
637
638int kmk_builtin_kDepObj(int argc, char *argv[], char **envp)
639{
640 int i;
641
642 /* Arguments. */
643 FILE *pOutput = NULL;
644 const char *pszOutput = NULL;
645 FILE *pInput = NULL;
646 const char *pszTarget = NULL;
647 int fStubs = 0;
648 int fFixCase = 0;
649 /* Argument parsing. */
650 int fInput = 0; /* set when we've found input argument. */
651 int fQuiet = 0;
652
653 argv0 = argv[0];
654
655 /*
656 * Parse arguments.
657 */
658 if (argc <= 1)
659 {
660 usage(argv[0]);
661 return 1;
662 }
663 for (i = 1; i < argc; i++)
664 {
665 if (argv[i][0] == '-')
666 {
667 const char *psz = &argv[i][1];
668 if (*psz == '-')
669 {
670 if (!strcmp(psz, "-quiet"))
671 psz = "q";
672 else if (!strcmp(psz, "-help"))
673 psz = "?";
674 else if (!strcmp(psz, "-version"))
675 psz = "V";
676 }
677
678 switch (*psz)
679 {
680 /*
681 * Output file.
682 */
683 case 'o':
684 {
685 pszOutput = &argv[i][2];
686 if (pOutput)
687 {
688 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
689 return 1;
690 }
691 if (!*pszOutput)
692 {
693 if (++i >= argc)
694 {
695 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
696 return 1;
697 }
698 pszOutput = argv[i];
699 }
700 if (pszOutput[0] == '-' && !pszOutput[1])
701 pOutput = stdout;
702 else
703 pOutput = fopen(pszOutput, "w");
704 if (!pOutput)
705 {
706 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
707 return 1;
708 }
709 break;
710 }
711
712 /*
713 * Target name.
714 */
715 case 't':
716 {
717 if (pszTarget)
718 {
719 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
720 return 1;
721 }
722 pszTarget = &argv[i][2];
723 if (!*pszTarget)
724 {
725 if (++i >= argc)
726 {
727 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
728 return 1;
729 }
730 pszTarget = argv[i];
731 }
732 break;
733 }
734
735 /*
736 * Fix case.
737 */
738 case 'f':
739 {
740 fFixCase = 1;
741 break;
742 }
743
744 /*
745 * Quiet.
746 */
747 case 'q':
748 {
749 fQuiet = 1;
750 break;
751 }
752
753 /*
754 * Generate stubs.
755 */
756 case 's':
757 {
758 fStubs = 1;
759 break;
760 }
761
762 /*
763 * The mandatory version & help.
764 */
765 case '?':
766 usage(argv[0]);
767 return 0;
768 case 'V':
769 case 'v':
770 return kbuild_version(argv[0]);
771
772 /*
773 * Invalid argument.
774 */
775 default:
776 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
777 usage(argv[0]);
778 return 1;
779 }
780 }
781 else
782 {
783 pInput = fopen(argv[i], "rb");
784 if (!pInput)
785 {
786 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
787 return 1;
788 }
789 fInput = 1;
790 }
791
792 /*
793 * End of the line?
794 */
795 if (fInput)
796 {
797 if (++i < argc)
798 {
799 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
800 return 1;
801 }
802 break;
803 }
804 }
805
806 /*
807 * Got all we require?
808 */
809 if (!pInput)
810 {
811 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
812 return 1;
813 }
814 if (!pOutput)
815 {
816 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
817 return 1;
818 }
819 if (!pszTarget)
820 {
821 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
822 return 1;
823 }
824
825 /*
826 * Do the parsing.
827 */
828 i = kDepObjProcessFile(pInput);
829 fclose(pInput);
830
831 /*
832 * Write the dependecy file.
833 */
834 if (!i)
835 {
836 depOptimize(fFixCase, fQuiet);
837 fprintf(pOutput, "%s:", pszTarget);
838 depPrint(pOutput);
839 if (fStubs)
840 depPrintStubs(pOutput);
841 }
842
843 /*
844 * Close the output, delete output on failure.
845 */
846 if (!i && ferror(pOutput))
847 {
848 i = 1;
849 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
850 }
851 fclose(pOutput);
852 if (i)
853 {
854 if (unlink(pszOutput))
855 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
856 }
857
858 depCleanup();
859 return i;
860}
861
Note: See TracBrowser for help on using the repository browser.