source: trunk/src/kmk/kmkbuiltin/kDepIDB.c@ 1165

Last change on this file since 1165 was 1165, checked in by bird, 18 years ago

Optimized kDebIDB a bit for Windows; use nt_fullpath and map the IDB file into memory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: kDepIDB.c 1165 2007-09-30 07:36:23Z bird $ */
2/** @file
3 *
4 * kDepIDB - Extract dependency information from a MS Visual C++ .idb file.
5 *
6 * Copyright (c) 2006-2007 knut st. osmundsen <bird-kBuild-spam@anduin.net>
7 *
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 2 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, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <stdio.h>
32#include <stdlib.h>
33#include <stddef.h>
34#include <string.h>
35#include <errno.h>
36#include <ctype.h>
37#if !defined(_MSC_VER)
38# include <stdint.h>
39#else
40# define USE_WIN_MMAP
41# include <io.h>
42# include <Windows.h>
43 typedef unsigned char uint8_t;
44 typedef unsigned short uint16_t;
45 typedef unsigned int uint32_t;
46#endif
47/*#include "kDep.h"*/
48#include "../../lib/kDep.h"
49#include "kmkbuiltin.h"
50
51#define OFFSETOF(type, member) ( (int)(size_t)(void *)&( ((type *)(void *)0)->member) )
52
53/*#define DEBUG*/
54#ifdef DEBUG
55# define dprintf(a) printf a
56# define dump(pb, cb, offBase) hexdump(pb,cb,offBase)
57#else
58# define dprintf(a) do {} while (0)
59# define dump(pb, cb, offBase) do {} while (0)
60#endif
61
62
63/*******************************************************************************
64* Global Variables *
65*******************************************************************************/
66/** the executable name. */
67static const char *argv0 = "";
68
69#ifdef DEBUG
70/**
71 * Performs a hexdump.
72 */
73static void hexdump(const uint8_t *pb, size_t cb, size_t offBase)
74{
75 static const char szHex[16] = "0123456789abcdef";
76
77 const unsigned cchWidth = 16;
78 size_t off = 0;
79 while (off < cb)
80 {
81 unsigned i;
82 printf("%s%0*x %04x:", off ? "\n" : "", sizeof(pb) * 2, offBase + off, off);
83 for (i = 0; i < cchWidth && off + i < cb ; i++)
84 printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
85
86 while (i++ < cchWidth)
87 printf(" ");
88 printf(" ");
89
90 for (i = 0; i < cchWidth && off + i < cb; i++)
91 {
92 const uint8_t u8 = pb[i];
93 printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.', 1);
94 }
95 off += cchWidth;
96 pb += cchWidth;
97 }
98 printf("\n");
99}
100#endif
101
102/**
103 * Scans a stream (chunk of data really) for dependencies.
104 *
105 * @returns 0 on success.
106 * @returns !0 on failure.
107 * @param pbStream The stream bits.
108 * @param cbStream The size of the stream.
109 * @param pszPrefix The dependency prefix.
110 * @param cchPrefix The size of the prefix.
111 */
112static int ScanStream(uint8_t *pbStream, size_t cbStream, const char *pszPrefix, size_t cchPrefix)
113{
114 const uint8_t *pbCur = pbStream;
115 size_t cbLeft = cbStream;
116 register char chFirst = *pszPrefix;
117 while (cbLeft > cchPrefix + 2)
118 {
119 if ( *pbCur != chFirst
120 || memcmp(pbCur, pszPrefix, cchPrefix))
121 {
122 pbCur++;
123 cbLeft--;
124 }
125 else
126 {
127 size_t cchDep;
128 pbCur += cchPrefix;
129 cchDep = strlen(pbCur);
130 depAdd(pbCur, cchDep);
131 dprintf(("%05x: '%s'\n", pbCur - pbStream, pbCur));
132
133 pbCur += cchDep;
134 cbLeft -= cchDep + cchPrefix;
135 }
136 }
137
138 return 0;
139}
140
141
142#ifdef USE_WIN_MMAP
143/** Handle to the current file mapping object. */
144static HANDLE g_hMapObj = NULL;
145#endif
146
147
148/**
149 * Reads the file specified by the pInput file stream into memory.
150 * The size of the file is returned in *pcbFile if specified.
151 * The returned pointer should be freed by FreeFileMemory().
152 */
153void *ReadFileIntoMemory(FILE *pInput, size_t *pcbFile)
154{
155 void *pvFile;
156 long cbFile;
157 int rc = 0;
158
159 /*
160 * Figure out file size.
161 */
162#if defined(_MSC_VER)
163 cbFile = _filelength(fileno(pInput));
164 if (cbFile < 0)
165#else
166 if ( fseek(pInput, 0, SEEK_END) < 0
167 || (cbFile = ftell(pInput)) < 0
168 || fseek(pInput, 0, SEEK_SET))
169#endif
170 {
171 fprintf(stderr, "%s: error: Failed to determin file size.\n", argv0);
172 return NULL;
173 }
174 if (pcbFile)
175 *pcbFile = cbFile;
176
177 /*
178 * Try mmap first.
179 */
180#ifdef USE_WIN_MMAP
181 {
182 HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
183 NULL, PAGE_READONLY, 0, cbFile, NULL);
184 if (hMapObj != NULL)
185 {
186 pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
187 if (pvFile)
188 {
189 g_hMapObj = hMapObj;
190 return pvFile;
191 }
192 fprintf(stderr, "%s: warning: MapViewOfFile failed, %d.\n", argv0, GetLastError());
193 CloseHandle(hMapObj);
194 }
195 else
196 fprintf(stderr, "%s: warning: CreateFileMapping failed, %d.\n", argv0, GetLastError());
197 }
198
199#endif
200
201 /*
202 * Allocate memory and read the file.
203 */
204 pvFile = malloc(cbFile + 1);
205 if (pvFile)
206 {
207#if 1
208 long cbRead = (long)read(fileno(pInput), pvFile, cbFile);
209 if (cbRead == cbFile)
210#else
211 if (fread(pvFile, cbFile, 1, pInput))
212#endif
213 {
214 ((uint8_t *)pvFile)[cbFile] = '\0';
215 return pvFile;
216 }
217 fprintf(stderr, "%s: error: Failed to read %ld bytes.\n", argv0, cbFile);
218 free(pvFile);
219 }
220 else
221 fprintf(stderr, "%s: error: Failed to allocate %ld bytes (file mapping).\n", argv0, cbFile);
222 return NULL;
223}
224
225
226static void FreeFileMemory(void *pvFile)
227{
228#if defined(USE_WIN_MMAP)
229 if (g_hMapObj)
230 {
231 UnmapViewOfFile(pvFile);
232 CloseHandle(g_hMapObj);
233 return;
234 }
235#endif
236 free(pvFile);
237}
238
239
240///////////////////////////////////////////////////////////////////////////////
241//
242//
243// P D B 7 . 0
244//
245//
246///////////////////////////////////////////////////////////////////////////////
247
248/** A PDB 7.0 Page number. */
249typedef uint32_t PDB70PAGE;
250/** Pointer to a PDB 7.0 Page number. */
251typedef PDB70PAGE *PPDB70PAGE;
252
253/**
254 * A PDB 7.0 stream.
255 */
256typedef struct PDB70STREAM
257{
258 /** The size of the stream. */
259 uint32_t cbStream;
260} PDB70STREAM, *PPDB70STREAM;
261
262
263/** The PDB 7.00 signature. */
264#define PDB_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\x1A" "DS\0\0"
265/**
266 * The PDB 7.0 header.
267 */
268typedef struct PDB70HDR
269{
270 /** The signature string. */
271 uint8_t szSignature[sizeof(PDB_SIGNATURE_700)];
272 /** The page size. */
273 uint32_t cbPage;
274 /** The start page. */
275 PDB70PAGE iStartPage;
276 /** The number of pages in the file. */
277 PDB70PAGE cPages;
278 /** The root stream directory. */
279 uint32_t cbRoot;
280 /** Unknown function, always 0. */
281 uint32_t u32Reserved;
282 /** The page index of the root page table. */
283 PDB70PAGE iRootPages;
284} PDB70HDR, *PPDB70HDR;
285
286/**
287 * The PDB 7.0 root directory.
288 */
289typedef struct PDB70ROOT
290{
291 /** The number of streams */
292 uint32_t cStreams;
293 /** Array of streams. */
294 PDB70STREAM aStreams[1];
295 /* uint32_t aiPages[] */
296} PDB70ROOT, *PPDB70ROOT;
297
298/**
299 * The PDB 7.0 name stream (#1) header.
300 */
301typedef struct PDB70NAMES
302{
303 /** The structure version. */
304 uint32_t Version;
305 /** Timestamp. */
306 uint32_t TimeStamp;
307 /** Unknown. */
308 uint32_t Unknown1;
309 /** GUID. */
310 uint32_t u32Guid[4];
311 /** The size of the following name table. */
312 uint32_t cbNames;
313 /** The name table. */
314 char szzNames[1];
315} PDB70NAMES, *PPDB70NAMES;
316
317/** The version / magic of the names structure. */
318#define PDB70NAMES_VERSION 20000404
319
320
321static int Pdb70ValidateHeader(PPDB70HDR pHdr, size_t cbFile)
322{
323 if (pHdr->cbPage * pHdr->cPages != cbFile)
324 {
325 fprintf(stderr, "%s: error: Bad PDB 2.0 header - cbPage * cPages != cbFile.\n", argv0);
326 return 1;
327 }
328 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
329 {
330 fprintf(stderr, "%s: error: Bad PDB 2.0 header - iStartPage=%u cPages=%u.\n", argv0,
331 pHdr->iStartPage, pHdr->cPages);
332 return 1;
333 }
334 if (pHdr->iRootPages >= pHdr->cPages && pHdr->iRootPages <= 0)
335 {
336 fprintf(stderr, "%s: error: Bad PDB 2.0 header - iRootPages=%u cPage=%u.\n", argv0,
337 pHdr->iStartPage, pHdr->cPages);
338 return 1;
339 }
340 return 0;
341}
342
343static size_t Pdb70Align(PPDB70HDR pHdr, size_t cb)
344{
345 if (cb == ~(uint32_t)0 || !cb)
346 return 0;
347 return ((cb + pHdr->cbPage - 1) / pHdr->cbPage) * pHdr->cbPage;
348}
349
350static size_t Pdb70Pages(PPDB70HDR pHdr, size_t cb)
351{
352 if (cb == ~(uint32_t)0 || !cb)
353 return 0;
354 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
355}
356
357static void *Pdb70AllocAndRead(PPDB70HDR pHdr, size_t cb, PPDB70PAGE paiPageMap)
358{
359 const size_t cbPage = pHdr->cbPage;
360 size_t cPages = Pdb70Pages(pHdr, cb);
361 uint8_t *pbBuf = malloc(cPages * cbPage + 1);
362 if (pbBuf)
363 {
364 size_t iPage = 0;
365 while (iPage < cPages)
366 {
367 size_t off = paiPageMap[iPage];
368 if (off < pHdr->cPages)
369 {
370 off *= cbPage;
371 memcpy(pbBuf + iPage * cbPage, (uint8_t *)pHdr + off, cbPage);
372 dump(pbBuf + iPage * cbPage, iPage + 1 < cPages ? cbPage : cb % cbPage, off);
373 }
374 else
375 {
376 fprintf(stderr, "%s: warning: Invalid page index %u (max %u)!\n", argv0,
377 (unsigned)off, pHdr->cPages);
378 memset(pbBuf + iPage * cbPage, 0, cbPage);
379 }
380
381 iPage++;
382 }
383 pbBuf[cPages * cbPage] = '\0';
384 }
385 else
386 fprintf(stderr, "%s: error: failed to allocate %u bytes\n", argv0, cPages * cbPage + 1);
387 return pbBuf;
388}
389
390static PPDB70ROOT Pdb70AllocAndReadRoot(PPDB70HDR pHdr)
391{
392 /*
393 * The tricky bit here is to find the right length. Really?
394 * (Todo: Check if we can just use the stream #0 size..)
395 */
396 PPDB70PAGE piPageMap = (uint32_t *)((uint8_t *)pHdr + pHdr->iRootPages * pHdr->cbPage);
397 PPDB70ROOT pRoot = Pdb70AllocAndRead(pHdr, pHdr->cbRoot, piPageMap);
398 if (pRoot)
399 {
400#if 1
401 /* This stuff is probably unnecessary: */
402 /* size = stream header + array of stream. */
403 size_t cb = OFFSETOF(PDB70ROOT, aStreams[pRoot->cStreams]);
404 free(pRoot);
405 pRoot = Pdb70AllocAndRead(pHdr, cb, piPageMap);
406 if (pRoot)
407 {
408 /* size += page tables. */
409 unsigned iStream = pRoot->cStreams;
410 while (iStream-- > 0)
411 if (pRoot->aStreams[iStream].cbStream != ~(uint32_t)0)
412 cb += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB70PAGE);
413 free(pRoot);
414 pRoot = Pdb70AllocAndRead(pHdr, cb, piPageMap);
415 if (pRoot)
416 {
417 /* validate? */
418 return pRoot;
419 }
420 }
421#else
422 /* validate? */
423 return pRoot;
424#endif
425 }
426 return NULL;
427}
428
429static void *Pdb70AllocAndReadStream(PPDB70HDR pHdr, PPDB70ROOT pRoot, unsigned iStream, size_t *pcbStream)
430{
431 const size_t cbStream = pRoot->aStreams[iStream].cbStream;
432 PPDB70PAGE paiPageMap;
433 if ( iStream >= pRoot->cStreams
434 || cbStream == ~(uint32_t)0)
435 {
436 fprintf(stderr, "%s: error: Invalid stream %d\n", iStream);
437 return NULL;
438 }
439
440 paiPageMap = (PPDB70PAGE)&pRoot->aStreams[pRoot->cStreams];
441 while (iStream-- > 0)
442 if (pRoot->aStreams[iStream].cbStream != ~(uint32_t)0)
443 paiPageMap += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream);
444
445 if (pcbStream)
446 *pcbStream = cbStream;
447 return Pdb70AllocAndRead(pHdr, cbStream, paiPageMap);
448}
449
450static int Pdb70Process(uint8_t *pbFile, size_t cbFile)
451{
452 PPDB70HDR pHdr = (PPDB70HDR)pbFile;
453 PPDB70ROOT pRoot;
454 PPDB70NAMES pNames;
455 size_t cbStream;
456 unsigned fDone = 0;
457 unsigned iStream;
458 int rc = 0;
459 dprintf(("pdb70\n"));
460
461 /*
462 * Validate the header and read the root stream.
463 */
464 if (Pdb70ValidateHeader(pHdr, cbFile))
465 return 1;
466 pRoot = Pdb70AllocAndReadRoot(pHdr);
467 if (!pRoot)
468 return 1;
469
470 /*
471 * The names we want are usually all found in the 'Names' stream, that is #1.
472 */
473 dprintf(("Reading the names stream....\n"));
474 pNames = Pdb70AllocAndReadStream(pHdr, pRoot, 1, &cbStream);
475 if (pNames)
476 {
477 dprintf(("Names: Version=%u cbNames=%u (%#x)\n", pNames->Version, pNames->cbNames, pNames->cbNames));
478 if ( pNames->Version == PDB70NAMES_VERSION
479 && pNames->cbNames > 32
480 && pNames->cbNames + offsetof(PDB70NAMES, szzNames) <= pRoot->aStreams[1].cbStream)
481 {
482 /*
483 * Iterate the names and add the /mr/inversedeps/ ones to the dependency list.
484 */
485 const char *psz = &pNames->szzNames[0];
486 size_t cb = pNames->cbNames;
487 size_t off = 0;
488 dprintf(("0x0000 #0: %6d bytes [root / toc]\n", pRoot->aStreams[0].cbStream));
489 for (iStream = 1; cb > 0; iStream++)
490 {
491 int fAdded = 0;
492 size_t cch = strlen(psz);
493 if ( cch >= sizeof("/mr/inversedeps/")
494 && !memcmp(psz, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1))
495 {
496 depAdd(psz + sizeof("/mr/inversedeps/") - 1, cch - (sizeof("/mr/inversedeps/") - 1));
497 fAdded = 1;
498 }
499 dprintf(("%#06x #%d: %6d bytes %s%s\n", off, iStream,
500 iStream < pRoot->cStreams ? pRoot->aStreams[iStream].cbStream : -1,
501 psz, fAdded ? " [dep]" : ""));
502 (void)fAdded;
503
504 /* next */
505 if (cch >= cb)
506 {
507 dprintf(("warning! cch=%d cb=%d\n", cch, cb));
508 cch = cb - 1;
509 }
510 cb -= cch + 1;
511 psz += cch + 1;
512 off += cch + 1;
513 }
514 rc = 0;
515 fDone = 1;
516 }
517 else
518 dprintf(("Unknown version or bad size: Version=%u cbNames=%d cbStream=%d\n",
519 pNames->Version, pNames->cbNames, cbStream));
520 free(pNames);
521 }
522
523 if (!fDone)
524 {
525 /*
526 * Iterate the streams in the root and scan their content for
527 * dependencies.
528 */
529 rc = 0;
530 for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
531 {
532 uint8_t *pbStream;
533 if ( pRoot->aStreams[iStream].cbStream == ~(uint32_t)0
534 || !pRoot->aStreams[iStream].cbStream)
535 continue;
536 dprintf(("Stream #%d: %#x bytes (%#x aligned)\n", iStream, pRoot->aStreams[iStream].cbStream,
537 Pdb70Align(pHdr, pRoot->aStreams[iStream].cbStream)));
538 pbStream = (uint8_t *)Pdb70AllocAndReadStream(pHdr, pRoot, iStream, &cbStream);
539 if (pbStream)
540 {
541 rc = ScanStream(pbStream, cbStream, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1);
542 free(pbStream);
543 }
544 else
545 rc = 1;
546 }
547 }
548
549 free(pRoot);
550 return rc;
551}
552
553
554
555///////////////////////////////////////////////////////////////////////////////
556//
557//
558// P D B 2 . 0
559//
560//
561///////////////////////////////////////////////////////////////////////////////
562
563
564/** A PDB 2.0 Page number. */
565typedef uint16_t PDB20PAGE;
566/** Pointer to a PDB 2.0 Page number. */
567typedef PDB20PAGE *PPDB20PAGE;
568
569/**
570 * A PDB 2.0 stream.
571 */
572typedef struct PDB20STREAM
573{
574 /** The size of the stream. */
575 uint32_t cbStream;
576 /** Some unknown value. */
577 uint32_t u32Unknown;
578} PDB20STREAM, *PPDB20STREAM;
579
580/** The PDB 2.00 signature. */
581#define PDB_SIGNATURE_200 "Microsoft C/C++ program database 2.00\r\n\x1A" "JG\0"
582/**
583 * The PDB 2.0 header.
584 */
585typedef struct PDB20HDR
586{
587 /** The signature string. */
588 uint8_t szSignature[sizeof(PDB_SIGNATURE_200)];
589 /** The page size. */
590 uint32_t cbPage;
591 /** The start page - whatever that is... */
592 PDB20PAGE iStartPage;
593 /** The number of pages in the file. */
594 PDB20PAGE cPages;
595 /** The root stream directory. */
596 PDB20STREAM RootStream;
597 /** The root page table. */
598 PDB20PAGE aiRootPageMap[1];
599} PDB20HDR, *PPDB20HDR;
600
601/**
602 * The PDB 2.0 root directory.
603 */
604typedef struct PDB20ROOT
605{
606 /** The number of streams */
607 uint16_t cStreams;
608 /** Reserved or high part of cStreams. */
609 uint16_t u16Reserved;
610 /** Array of streams. */
611 PDB20STREAM aStreams[1];
612} PDB20ROOT, *PPDB20ROOT;
613
614
615static int Pdb20ValidateHeader(PPDB20HDR pHdr, size_t cbFile)
616{
617 if (pHdr->cbPage * pHdr->cPages != cbFile)
618 {
619 fprintf(stderr, "%s: error: Bad PDB 2.0 header - cbPage * cPages != cbFile.\n", argv0);
620 return 1;
621 }
622 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
623 {
624 fprintf(stderr, "%s: error: Bad PDB 2.0 header - cbPage * cPages != cbFile.\n", argv0);
625 return 1;
626 }
627 return 0;
628}
629
630static size_t Pdb20Pages(PPDB20HDR pHdr, size_t cb)
631{
632 if (cb == ~(uint32_t)0 || !cb)
633 return 0;
634 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
635}
636
637static void *Pdb20AllocAndRead(PPDB20HDR pHdr, size_t cb, PPDB20PAGE paiPageMap)
638{
639 size_t cPages = Pdb20Pages(pHdr, cb);
640 uint8_t *pbBuf = malloc(cPages * pHdr->cbPage + 1);
641 if (pbBuf)
642 {
643 size_t iPage = 0;
644 while (iPage < cPages)
645 {
646 size_t off = paiPageMap[iPage];
647 off *= pHdr->cbPage;
648 memcpy(pbBuf + iPage * pHdr->cbPage, (uint8_t *)pHdr + off, pHdr->cbPage);
649 iPage++;
650 }
651 pbBuf[cPages * pHdr->cbPage] = '\0';
652 }
653 else
654 fprintf(stderr, "%s: error: failed to allocate %d bytes\n", argv0, cPages * pHdr->cbPage + 1);
655 return pbBuf;
656}
657
658static PPDB20ROOT Pdb20AllocAndReadRoot(PPDB20HDR pHdr)
659{
660 /*
661 * The tricky bit here is to find the right length.
662 * (Todo: Check if we can just use the stream size..)
663 */
664 PPDB20ROOT pRoot = Pdb20AllocAndRead(pHdr, sizeof(*pRoot), &pHdr->aiRootPageMap[0]);
665 if (pRoot)
666 {
667 /* size = stream header + array of stream. */
668 size_t cb = OFFSETOF(PDB20ROOT, aStreams[pRoot->cStreams]);
669 free(pRoot);
670 pRoot = Pdb20AllocAndRead(pHdr, cb, &pHdr->aiRootPageMap[0]);
671 if (pRoot)
672 {
673 /* size += page tables. */
674 unsigned iStream = pRoot->cStreams;
675 while (iStream-- > 0)
676 if (pRoot->aStreams[iStream].cbStream != ~(uint32_t)0)
677 cb += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB20PAGE);
678 free(pRoot);
679 pRoot = Pdb20AllocAndRead(pHdr, cb, &pHdr->aiRootPageMap[0]);
680 if (pRoot)
681 {
682 /* validate? */
683 return pRoot;
684 }
685 }
686 }
687 return NULL;
688
689}
690
691static void *Pdb20AllocAndReadStream(PPDB20HDR pHdr, PPDB20ROOT pRoot, unsigned iStream, size_t *pcbStream)
692{
693 size_t cbStream = pRoot->aStreams[iStream].cbStream;
694 PPDB20PAGE paiPageMap;
695 if ( iStream >= pRoot->cStreams
696 || cbStream == ~(uint32_t)0)
697 {
698 fprintf(stderr, "%s: error: Invalid stream %d\n", iStream);
699 return NULL;
700 }
701
702 paiPageMap = (PPDB20PAGE)&pRoot->aStreams[pRoot->cStreams];
703 while (iStream-- > 0)
704 if (pRoot->aStreams[iStream].cbStream != ~(uint32_t)0)
705 paiPageMap += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream);
706
707 if (pcbStream)
708 *pcbStream = cbStream;
709 return Pdb20AllocAndRead(pHdr, cbStream, paiPageMap);
710}
711
712static int Pdb20Process(uint8_t *pbFile, size_t cbFile)
713{
714 PPDB20HDR pHdr = (PPDB20HDR)pbFile;
715 PPDB20ROOT pRoot;
716 unsigned iStream;
717 int rc = 0;
718
719 /*
720 * Validate the header and read the root stream.
721 */
722 if (Pdb20ValidateHeader(pHdr, cbFile))
723 return 1;
724 pRoot = Pdb20AllocAndReadRoot(pHdr);
725 if (!pRoot)
726 return 1;
727
728 /*
729 * Iterate the streams in the root and scan their content for
730 * dependencies.
731 */
732 rc = 0;
733 for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
734 {
735 uint8_t *pbStream;
736 if (pRoot->aStreams[iStream].cbStream == ~(uint32_t)0)
737 continue;
738 pbStream = (uint8_t *)Pdb20AllocAndReadStream(pHdr, pRoot, iStream, NULL);
739 if (pbStream)
740 {
741 rc = ScanStream(pbStream, pRoot->aStreams[iStream].cbStream, "/ipm/header/", sizeof("/ipm/header/") - 1);
742 free(pbStream);
743 }
744 else
745 rc = 1;
746 }
747
748 free(pRoot);
749 return rc;
750}
751
752
753/**
754 * Make an attempt at parsing a Visual C++ IDB file.
755 */
756static int ProcessIDB(FILE *pInput)
757{
758 size_t cbFile;
759 char *pbFile;
760 char *pbHdr = PDB_SIGNATURE_200;
761 int rc = 0;
762
763 /*
764 * Read the file into memory.
765 */
766 pbFile = (char *)ReadFileIntoMemory(pInput, &cbFile);
767 if (!pbFile)
768 return 1;
769
770 /*
771 * Figure out which parser to use.
772 */
773 if (!memcmp(pbFile, PDB_SIGNATURE_700, sizeof(PDB_SIGNATURE_700)))
774 rc = Pdb70Process(pbFile, cbFile);
775 else if (!memcmp(pbFile, PDB_SIGNATURE_200, sizeof(PDB_SIGNATURE_200)))
776 rc = Pdb20Process(pbFile, cbFile);
777 else
778 {
779 fprintf(stderr, "%s: error: Doesn't recognize the header of the Visual C++ IDB file.\n", argv0);
780 rc = 1;
781 }
782
783 FreeFileMemory(pbFile);
784 return rc;
785}
786
787
788static void usage(const char *argv0)
789{
790 printf("syntax: %s -o <output> -t <target> [-f] [-s] <vc idb-file>\n", argv0);
791}
792
793
794int kmk_builtin_kDepIDB(int argc, char *argv[], char **envp)
795{
796 int i;
797
798 /* Arguments. */
799 FILE *pOutput = NULL;
800 const char *pszOutput = NULL;
801 FILE *pInput = NULL;
802 const char *pszTarget = NULL;
803 int fStubs = 0;
804 int fFixCase = 0;
805 /* Argument parsing. */
806 int fInput = 0; /* set when we've found input argument. */
807
808 argv0 = argv[0];
809
810 /*
811 * Parse arguments.
812 */
813 if (argc <= 1)
814 {
815 usage(argv[0]);
816 return 1;
817 }
818 for (i = 1; i < argc; i++)
819 {
820 if (argv[i][0] == '-')
821 {
822 switch (argv[i][1])
823 {
824 /*
825 * Output file.
826 */
827 case 'o':
828 {
829 pszOutput = &argv[i][2];
830 if (pOutput)
831 {
832 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
833 return 1;
834 }
835 if (!*pszOutput)
836 {
837 if (++i >= argc)
838 {
839 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
840 return 1;
841 }
842 pszOutput = argv[i];
843 }
844 if (pszOutput[0] == '-' && !pszOutput[1])
845 pOutput = stdout;
846 else
847 pOutput = fopen(pszOutput, "w");
848 if (!pOutput)
849 {
850 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
851 return 1;
852 }
853 break;
854 }
855
856 /*
857 * Target name.
858 */
859 case 't':
860 {
861 if (pszTarget)
862 {
863 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
864 return 1;
865 }
866 pszTarget = &argv[i][2];
867 if (!*pszTarget)
868 {
869 if (++i >= argc)
870 {
871 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
872 return 1;
873 }
874 pszTarget = argv[i];
875 }
876 break;
877 }
878
879 /*
880 * Fix case.
881 */
882 case 'f':
883 {
884 fFixCase = 1;
885 break;
886 }
887
888 /*
889 * Generate stubs.
890 */
891 case 's':
892 {
893 fStubs = 1;
894 break;
895 }
896
897 /*
898 * Invalid argument.
899 */
900 default:
901 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
902 usage(argv[0]);
903 return 1;
904 }
905 }
906 else
907 {
908 pInput = fopen(argv[i], "rb");
909 if (!pInput)
910 {
911 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
912 return 1;
913 }
914 fInput = 1;
915 }
916
917 /*
918 * End of the line?
919 */
920 if (fInput)
921 {
922 if (++i < argc)
923 {
924 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
925 return 1;
926 }
927 break;
928 }
929 }
930
931 /*
932 * Got all we require?
933 */
934 if (!pInput)
935 {
936 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
937 return 1;
938 }
939 if (!pOutput)
940 {
941 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
942 return 1;
943 }
944 if (!pszTarget)
945 {
946 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
947 return 1;
948 }
949
950 /*
951 * Do the parsing.
952 */
953 i = ProcessIDB(pInput);
954
955 /*
956 * Write the dependecy file.
957 */
958 if (!i)
959 {
960 depOptimize(fFixCase);
961 fprintf(pOutput, "%s:", pszTarget);
962 depPrint(pOutput);
963 if (fStubs)
964 depPrintStubs(pOutput);
965 }
966
967 /*
968 * Close the output, delete output on failure.
969 */
970 if (!i && ferror(pOutput))
971 {
972 i = 1;
973 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
974 }
975 fclose(pOutput);
976 if (i)
977 {
978 if (unlink(pszOutput))
979 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
980 }
981
982 depCleanup();
983 return i;
984}
985
Note: See TracBrowser for help on using the repository browser.