VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp@ 76560

Last change on this file since 76560 was 76560, checked in by vboxsync, 6 years ago

IPRT: Use IPRT_INCLUDED_SRC_ as header guard prefix for files around the source tree.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 KB
Line 
1/* $Id: ntBldSymDb.cpp 76560 2019-01-01 03:01:10Z vboxsync $ */
2/** @file
3 * IPRT - RTDirCreateUniqueNumbered, generic implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/win/windows.h>
32#include <Dbghelp.h>
33
34#include <iprt/alloca.h>
35#include <iprt/dir.h>
36#include <iprt/file.h>
37#include <iprt/err.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/list.h>
41#include <iprt/mem.h>
42#include <iprt/message.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/utf16.h>
47
48#include "r0drv/nt/symdb.h"
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/** A structure member we're interested in. */
55typedef struct MYMEMBER
56{
57 /** The member name. */
58 const char * const pszName;
59 /** Reserved. */
60 uint32_t const fFlags;
61 /** The offset of the member. UINT32_MAX if not found. */
62 uint32_t off;
63 /** The size of the member. */
64 uint32_t cb;
65 /** Alternative names, optional.
66 * This is a string of zero terminated strings, ending with an zero length
67 * string (or double '\\0' if you like). */
68 const char * const pszzAltNames;
69} MYMEMBER;
70/** Pointer to a member we're interested. */
71typedef MYMEMBER *PMYMEMBER;
72
73/** Members we're interested in. */
74typedef struct MYSTRUCT
75{
76 /** The structure name. */
77 const char * const pszName;
78 /** Array of members we're interested in. */
79 MYMEMBER *paMembers;
80 /** The number of members we're interested in. */
81 uint32_t const cMembers;
82 /** Reserved. */
83 uint32_t const fFlags;
84} MYSTRUCT;
85
86/** Architecture. */
87typedef enum MYARCH
88{
89 MYARCH_X86,
90 MYARCH_AMD64,
91 MYARCH_DETECT
92} MYARCH;
93
94/** Set of structures for one kernel. */
95typedef struct MYSET
96{
97 /** The list entry. */
98 RTLISTNODE ListEntry;
99 /** The source PDB. */
100 char *pszPdb;
101 /** The OS version we've harvested structs for */
102 RTNTSDBOSVER OsVerInfo;
103 /** The architecture. */
104 MYARCH enmArch;
105 /** The structures and their member. */
106 MYSTRUCT aStructs[1];
107} MYSET;
108/** Pointer a set of structures for one kernel. */
109typedef MYSET *PMYSET;
110
111
112/*********************************************************************************************************************************
113* Global Variables *
114*********************************************************************************************************************************/
115/** Verbosity level (-v, --verbose). */
116static uint32_t g_iOptVerbose = 1;
117/** Set if we should force ahead despite errors. */
118static bool g_fOptForce = false;
119
120/** The members of the KPRCB structure that we're interested in. */
121static MYMEMBER g_aKprcbMembers[] =
122{
123 { "QuantumEnd", 0, UINT32_MAX, UINT32_MAX, NULL },
124 { "DpcQueueDepth", 0, UINT32_MAX, UINT32_MAX, "DpcData[0].DpcQueueDepth\0" },
125 { "VendorString", 0, UINT32_MAX, UINT32_MAX, NULL },
126};
127
128/** The structures we're interested in. */
129static MYSTRUCT g_aStructs[] =
130{
131 { "_KPRCB", &g_aKprcbMembers[0], RT_ELEMENTS(g_aKprcbMembers), 0 },
132};
133
134/** List of data we've found. This is sorted by version info. */
135static RTLISTANCHOR g_SetList;
136
137
138
139
140
141/**
142 * For debug/verbose output.
143 *
144 * @param pszFormat The format string.
145 * @param ... The arguments referenced in the format string.
146 */
147static void MyDbgPrintf(const char *pszFormat, ...)
148{
149 if (g_iOptVerbose > 1)
150 {
151 va_list va;
152 va_start(va, pszFormat);
153 RTPrintf("debug: ");
154 RTPrintfV(pszFormat, va);
155 va_end(va);
156 }
157}
158
159
160/**
161 * Returns the name we wish to use in the C code.
162 * @returns Structure name.
163 * @param pStruct The structure descriptor.
164 */
165static const char *figureCStructName(MYSTRUCT const *pStruct)
166{
167 const char *psz = pStruct->pszName;
168 while (*psz == '_')
169 psz++;
170 return psz;
171}
172
173
174/**
175 * Returns the name we wish to use in the C code.
176 * @returns Member name.
177 * @param pMember The member descriptor.
178 */
179static const char *figureCMemberName(MYMEMBER const *pMember)
180{
181 return pMember->pszName;
182}
183
184
185/**
186 * Creates a MYSET with copies of all the data and inserts it into the
187 * g_SetList in a orderly fashion.
188 *
189 * @param pOut The output stream.
190 */
191static void generateHeader(PRTSTREAM pOut)
192{
193 RTStrmPrintf(pOut,
194 "/* $" "I" "d" ": $ */\n" /* avoid it being expanded */
195 "/** @file\n"
196 " * IPRT - NT kernel type helpers - Autogenerated, do NOT edit.\n"
197 " */\n"
198 "\n"
199 "/*\n"
200 " * Copyright (C) 2013-2017 Oracle Corporation \n"
201 " *\n"
202 " * This file is part of VirtualBox Open Source Edition (OSE), as\n"
203 " * available from http://www.virtualbox.org. This file is free software;\n"
204 " * you can redistribute it and/or modify it under the terms of the GNU\n"
205 " * General Public License (GPL) as published by the Free Software\n"
206 " * Foundation, in version 2 as it comes in the \"COPYING\" file of the\n"
207 " * VirtualBox OSE distribution. VirtualBox OSE is distributed in the\n"
208 " * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.\n"
209 " *\n"
210 " * The contents of this file may alternatively be used under the terms\n"
211 " * of the Common Development and Distribution License Version 1.0\n"
212 " * (CDDL) only, as it comes in the \"COPYING.CDDL\" file of the\n"
213 " * VirtualBox OSE distribution, in which case the provisions of the\n"
214 " * CDDL are applicable instead of those of the GPL.\n"
215 " *\n"
216 " * You may elect to license modified versions of this file under the\n"
217 " * terms and conditions of either the GPL or the CDDL or both.\n"
218 " */\n"
219 "\n"
220 "\n"
221 "#ifndef IPRT_INCLUDED_SRC_nt_symdbdata_h\n"
222 "#define IPRT_INCLUDED_SRC_nt_symdbdata_h\n"
223 "\n"
224 "#include \"r0drv/nt/symdb.h\"\n"
225 "\n"
226 );
227
228 /*
229 * Generate types.
230 */
231 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
232 {
233 const char *pszStructName = figureCStructName(&g_aStructs[i]);
234
235 RTStrmPrintf(pOut,
236 "typedef struct RTNTSDBTYPE_%s\n"
237 "{\n",
238 pszStructName);
239 PMYMEMBER paMembers = g_aStructs[i].paMembers;
240 for (uint32_t j = 0; j < g_aStructs->cMembers; j++)
241 {
242 const char *pszMemName = figureCMemberName(&paMembers[j]);
243 RTStrmPrintf(pOut,
244 " uint32_t off%s;\n"
245 " uint32_t cb%s;\n",
246 pszMemName, pszMemName);
247 }
248
249 RTStrmPrintf(pOut,
250 "} RTNTSDBTYPE_%s;\n"
251 "\n",
252 pszStructName);
253 }
254
255 RTStrmPrintf(pOut,
256 "\n"
257 "typedef struct RTNTSDBSET\n"
258 "{\n"
259 " RTNTSDBOSVER%-20s OsVerInfo;\n", "");
260 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
261 {
262 const char *pszStructName = figureCStructName(&g_aStructs[i]);
263 RTStrmPrintf(pOut, " RTNTSDBTYPE_%-20s %s;\n", pszStructName, pszStructName);
264 }
265 RTStrmPrintf(pOut,
266 "} RTNTSDBSET;\n"
267 "typedef RTNTSDBSET const *PCRTNTSDBSET;\n"
268 "\n");
269
270 /*
271 * Output the data.
272 */
273 RTStrmPrintf(pOut,
274 "\n"
275 "#ifndef RTNTSDB_NO_DATA\n"
276 "const RTNTSDBSET g_artNtSdbSets[] = \n"
277 "{\n");
278 PMYSET pSet;
279 RTListForEach(&g_SetList, pSet, MYSET, ListEntry)
280 {
281 const char *pszArch = pSet->enmArch == MYARCH_AMD64 ? "AMD64" : "X86";
282 RTStrmPrintf(pOut,
283 "# ifdef RT_ARCH_%s\n"
284 " { /* Source: %s */\n"
285 " /*.OsVerInfo = */\n"
286 " {\n"
287 " /* .uMajorVer = */ %u,\n"
288 " /* .uMinorVer = */ %u,\n"
289 " /* .fChecked = */ %s,\n"
290 " /* .fSmp = */ %s,\n"
291 " /* .uCsdNo = */ %u,\n"
292 " /* .uBuildNo = */ %u,\n"
293 " },\n",
294 pszArch,
295 pSet->pszPdb,
296 pSet->OsVerInfo.uMajorVer,
297 pSet->OsVerInfo.uMinorVer,
298 pSet->OsVerInfo.fChecked ? "true" : "false",
299 pSet->OsVerInfo.fSmp ? "true" : "false",
300 pSet->OsVerInfo.uCsdNo,
301 pSet->OsVerInfo.uBuildNo);
302 for (uint32_t i = 0; i < RT_ELEMENTS(pSet->aStructs); i++)
303 {
304 const char *pszStructName = figureCStructName(&pSet->aStructs[i]);
305 RTStrmPrintf(pOut,
306 " /* .%s = */\n"
307 " {\n", pszStructName);
308 PMYMEMBER paMembers = pSet->aStructs[i].paMembers;
309 for (uint32_t j = 0; j < pSet->aStructs[i].cMembers; j++)
310 {
311 const char *pszMemName = figureCMemberName(&paMembers[j]);
312 RTStrmPrintf(pOut,
313 " /* .off%-25s = */ %#06x,\n"
314 " /* .cb%-26s = */ %#06x,\n",
315 pszMemName, paMembers[j].off,
316 pszMemName, paMembers[j].cb);
317 }
318 RTStrmPrintf(pOut,
319 " },\n");
320 }
321 RTStrmPrintf(pOut,
322 " },\n"
323 "# endif\n"
324 );
325 }
326
327 RTStrmPrintf(pOut,
328 "};\n"
329 "#endif /* !RTNTSDB_NO_DATA */\n"
330 "\n");
331
332 RTStrmPrintf(pOut, "\n#endif\n\n");
333}
334
335
336/**
337 * Creates a MYSET with copies of all the data and inserts it into the
338 * g_SetList in a orderly fashion.
339 *
340 * @returns Fully complained exit code.
341 * @param pOsVerInfo The OS version info.
342 * @param enmArch The NT architecture of the incoming PDB.
343 * @param pszPdb The PDB file name.
344 */
345static RTEXITCODE saveStructures(PRTNTSDBOSVER pOsVerInfo, MYARCH enmArch, const char *pszPdb)
346{
347 /*
348 * Allocate one big chunk, figure it's size once.
349 */
350 static size_t s_cbNeeded = 0;
351 if (s_cbNeeded == 0)
352 {
353 s_cbNeeded = RT_UOFFSETOF(MYSET, aStructs[RT_ELEMENTS(g_aStructs)]);
354 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
355 s_cbNeeded += sizeof(MYMEMBER) * g_aStructs[i].cMembers;
356 }
357
358 size_t cbPdb = strlen(pszPdb) + 1;
359 PMYSET pSet = (PMYSET)RTMemAlloc(s_cbNeeded + cbPdb);
360 if (!pSet)
361 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory!\n");
362
363 /*
364 * Copy over the data.
365 */
366 pSet->enmArch = enmArch;
367 memcpy(&pSet->OsVerInfo, pOsVerInfo, sizeof(pSet->OsVerInfo));
368 memcpy(&pSet->aStructs[0], g_aStructs, sizeof(g_aStructs));
369
370 PMYMEMBER pDst = (PMYMEMBER)&pSet->aStructs[RT_ELEMENTS(g_aStructs)];
371 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
372 {
373 pSet->aStructs[i].paMembers = pDst;
374 memcpy(pDst, g_aStructs[i].paMembers, g_aStructs[i].cMembers * sizeof(*pDst));
375 pDst += g_aStructs[i].cMembers;
376 }
377
378 pSet->pszPdb = (char *)pDst;
379 memcpy(pDst, pszPdb, cbPdb);
380
381 /*
382 * Link it.
383 */
384 PMYSET pInsertBefore;
385 RTListForEach(&g_SetList, pInsertBefore, MYSET, ListEntry)
386 {
387 int iDiff = rtNtOsVerInfoCompare(&pInsertBefore->OsVerInfo, &pSet->OsVerInfo);
388 if (iDiff >= 0)
389 {
390 if (iDiff > 0 || pInsertBefore->enmArch > pSet->enmArch)
391 {
392 RTListNodeInsertBefore(&pInsertBefore->ListEntry, &pSet->ListEntry);
393 return RTEXITCODE_SUCCESS;
394 }
395 }
396 }
397
398 RTListAppend(&g_SetList, &pSet->ListEntry);
399 return RTEXITCODE_SUCCESS;
400}
401
402
403/**
404 * Checks that we found everything.
405 *
406 * @returns Fully complained exit code.
407 */
408static RTEXITCODE checkThatWeFoundEverything(void)
409{
410 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
411 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
412 {
413 PMYMEMBER paMembers = g_aStructs[i].paMembers;
414 uint32_t j = g_aStructs[i].cMembers;
415 while (j-- > 0)
416 {
417 if (paMembers[j].off == UINT32_MAX)
418 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, " Missing %s::%s\n", g_aStructs[i].pszName, paMembers[j].pszName);
419 }
420 }
421 return rcExit;
422}
423
424
425/**
426 * Matches the member against what we're looking for.
427 *
428 * @returns Number of hits.
429 * @param cWantedMembers The number members in paWantedMembers.
430 * @param paWantedMembers The members we're looking for.
431 * @param pszPrefix The member name prefix.
432 * @param pszMember The member name.
433 * @param offMember The member offset.
434 * @param cbMember The member size.
435 */
436static uint32_t matchUpStructMembers(unsigned cWantedMembers, PMYMEMBER paWantedMembers,
437 const char *pszPrefix, const char *pszMember,
438 uint32_t offMember, uint32_t cbMember)
439{
440 size_t cchPrefix = strlen(pszPrefix);
441 uint32_t cHits = 0;
442 uint32_t iMember = cWantedMembers;
443 while (iMember-- > 0)
444 {
445 if ( !strncmp(pszPrefix, paWantedMembers[iMember].pszName, cchPrefix)
446 && !strcmp(pszMember, paWantedMembers[iMember].pszName + cchPrefix))
447 {
448 paWantedMembers[iMember].off = offMember;
449 paWantedMembers[iMember].cb = cbMember;
450 cHits++;
451 }
452 else if (paWantedMembers[iMember].pszzAltNames)
453 {
454 char const *pszCur = paWantedMembers[iMember].pszzAltNames;
455 while (*pszCur)
456 {
457 size_t cchCur = strlen(pszCur);
458 if ( !strncmp(pszPrefix, pszCur, cchPrefix)
459 && !strcmp(pszMember, pszCur + cchPrefix))
460 {
461 paWantedMembers[iMember].off = offMember;
462 paWantedMembers[iMember].cb = cbMember;
463 cHits++;
464 break;
465 }
466 pszCur += cchCur + 1;
467 }
468 }
469 }
470 return cHits;
471}
472
473
474#if 0
475/**
476 * Resets the writable structure members prior to processing a PDB.
477 *
478 * While processing the PDB, will fill in the sizes and offsets of what we find.
479 * Afterwards we'll use look for reset values to see that every structure and
480 * member was located successfully.
481 */
482static void resetMyStructs(void)
483{
484 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
485 {
486 PMYMEMBER paMembers = g_aStructs[i].paMembers;
487 uint32_t j = g_aStructs[i].cMembers;
488 while (j-- > 0)
489 {
490 paMembers[j].off = UINT32_MAX;
491 paMembers[j].cb = UINT32_MAX;
492 }
493 }
494}
495#endif
496
497
498/**
499 * Find members in the specified structure type (@a idxType).
500 *
501 * @returns Fully bitched exit code.
502 * @param hFake Fake process handle.
503 * @param uModAddr The module address.
504 * @param idxType The type index of the structure which members we're
505 * going to process.
506 * @param cWantedMembers The number of wanted members.
507 * @param paWantedMembers The wanted members. This will be modified.
508 * @param offDisp Displacement when calculating member offsets.
509 * @param pszStructNm The top level structure name.
510 * @param pszPrefix The member name prefix.
511 * @param pszLogTag The log tag.
512 */
513static RTEXITCODE findMembers(HANDLE hFake, uint64_t uModAddr, uint32_t idxType,
514 uint32_t cWantedMembers, PMYMEMBER paWantedMembers,
515 uint32_t offDisp, const char *pszStructNm, const char *pszPrefix, const char *pszLogTag)
516{
517 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
518
519 DWORD cChildren = 0;
520 if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_GET_CHILDRENCOUNT, &cChildren))
521 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_GET_CHILDRENCOUNT failed on _KPRCB: %u\n", pszLogTag, GetLastError());
522
523 MyDbgPrintf(" %s: cChildren=%u (%#x)\n", pszStructNm, cChildren);
524 TI_FINDCHILDREN_PARAMS *pChildren;
525 pChildren = (TI_FINDCHILDREN_PARAMS *)alloca(RT_UOFFSETOF_DYN(TI_FINDCHILDREN_PARAMS, ChildId[cChildren]));
526 pChildren->Start = 0;
527 pChildren->Count = cChildren;
528 if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_FINDCHILDREN, pChildren))
529 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_FINDCHILDREN failed on _KPRCB: %u\n", pszLogTag, GetLastError());
530
531 for (uint32_t i = 0; i < cChildren; i++)
532 {
533 //MyDbgPrintf(" %s: child#%u: TypeIndex=%u\n", pszStructNm, i, pChildren->ChildId[i]);
534 IMAGEHLP_SYMBOL_TYPE_INFO enmErr;
535 PWCHAR pwszMember = NULL;
536 uint32_t idxRefType = 0;
537 uint32_t offMember = 0;
538 uint64_t cbMember = 0;
539 uint32_t cMemberChildren = 0;
540 if ( SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_SYMNAME, &pwszMember)
541 && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_OFFSET, &offMember)
542 && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_TYPE, &idxRefType)
543 && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_LENGTH, &cbMember)
544 && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_CHILDRENCOUNT, &cMemberChildren)
545 )
546 {
547 offMember += offDisp;
548
549 char *pszMember;
550 int rc = RTUtf16ToUtf8(pwszMember, &pszMember);
551 if (RT_SUCCESS(rc))
552 {
553 matchUpStructMembers(cWantedMembers, paWantedMembers, pszPrefix, pszMember, offMember, cbMember);
554
555 /*
556 * Gather more info and do some debug printing. We'll use some
557 * of this info below when recursing into sub-structures
558 * and arrays.
559 */
560 uint32_t fNested = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_NESTED, &fNested);
561 uint32_t uDataKind = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_DATAKIND, &uDataKind);
562 uint32_t uBaseType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_BASETYPE, &uBaseType);
563 uint32_t uMembTag = 0; SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], TI_GET_SYMTAG, &uMembTag);
564 uint32_t uBaseTag = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_SYMTAG, &uBaseTag);
565 uint32_t cElements = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_COUNT, &cElements);
566 uint32_t idxArrayType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_ARRAYINDEXTYPEID, &idxArrayType);
567 MyDbgPrintf(" %#06x LB %#06llx %c%c %2d %2d %2d %2d %2d %4d %s::%s%s\n",
568 offMember, cbMember,
569 cMemberChildren > 0 ? 'c' : '-',
570 fNested != 0 ? 'n' : '-',
571 uDataKind,
572 uBaseType,
573 uMembTag,
574 uBaseTag,
575 cElements,
576 idxArrayType,
577 pszStructNm,
578 pszPrefix,
579 pszMember);
580
581 /*
582 * Recurse into children.
583 */
584 if (cMemberChildren > 0)
585 {
586 size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof(".");
587 char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded);
588 if (pszSubPrefix)
589 {
590 strcat(strcat(strcpy(pszSubPrefix, pszPrefix), pszMember), ".");
591 RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxRefType, cWantedMembers,
592 paWantedMembers, offMember,
593 pszStructNm,
594 pszSubPrefix,
595 pszLogTag);
596 if (rcExit2 != RTEXITCODE_SUCCESS)
597 rcExit = rcExit2;
598 RTMemTmpFree(pszSubPrefix);
599 }
600 else
601 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n");
602 }
603 /*
604 * Recurse into arrays too.
605 */
606 else if (cElements > 0 && idxArrayType > 0)
607 {
608 BOOL fRc;
609 uint32_t idxElementRefType = 0;
610 fRc = SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_TYPE, &idxElementRefType); Assert(fRc);
611 uint64_t cbElement = cbMember / cElements;
612 fRc = SymGetTypeInfo(hFake, uModAddr, idxElementRefType, TI_GET_LENGTH, &cbElement); Assert(fRc);
613 MyDbgPrintf("idxArrayType=%u idxElementRefType=%u cbElement=%u\n", idxArrayType, idxElementRefType, cbElement);
614
615 size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof("[xxxxxxxxxxxxxxxx].");
616 char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded);
617 if (pszSubPrefix)
618 {
619 for (uint32_t iElement = 0; iElement < cElements; iElement++)
620 {
621 RTStrPrintf(pszSubPrefix, cbNeeded, "%s%s[%u].", pszPrefix, pszMember, iElement);
622 RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxElementRefType, cWantedMembers,
623 paWantedMembers,
624 offMember + iElement * cbElement,
625 pszStructNm,
626 pszSubPrefix,
627 pszLogTag);
628 if (rcExit2 != RTEXITCODE_SUCCESS)
629 rcExit = rcExit2;
630 }
631 RTMemTmpFree(pszSubPrefix);
632 }
633 else
634 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n");
635 }
636
637 RTStrFree(pszMember);
638 }
639 else
640 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTUtf16ToUtf8 failed on %s child#%u: %Rrc\n",
641 pszLogTag, pszStructNm, i, rc);
642 }
643 /* TI_GET_OFFSET fails on bitfields, so just ignore+skip those. */
644 else if (enmErr != TI_GET_OFFSET || GetLastError() != ERROR_INVALID_FUNCTION)
645 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: SymGetTypeInfo(,,,%d,) failed on %s child#%u: %u\n",
646 pszLogTag, enmErr, pszStructNm, i, GetLastError());
647 LocalFree(pwszMember);
648 } /* For each child. */
649
650 return rcExit;
651}
652
653
654/**
655 * Lookup up structures and members in the given module.
656 *
657 * @returns Fully bitched exit code.
658 * @param hFake Fake process handle.
659 * @param uModAddr The module address.
660 * @param pszLogTag The log tag.
661 * @param pszPdb The full PDB path.
662 * @param pOsVerInfo The OS version info for altering the error handling
663 * for older OSes.
664 */
665static RTEXITCODE findStructures(HANDLE hFake, uint64_t uModAddr, const char *pszLogTag, const char *pszPdb,
666 PCRTNTSDBOSVER pOsVerInfo)
667{
668 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
669 PSYMBOL_INFO pSymInfo = (PSYMBOL_INFO)alloca(sizeof(*pSymInfo));
670 for (uint32_t iStruct = 0; iStruct < RT_ELEMENTS(g_aStructs); iStruct++)
671 {
672 pSymInfo->SizeOfStruct = sizeof(*pSymInfo);
673 pSymInfo->MaxNameLen = 0;
674 if (!SymGetTypeFromName(hFake, uModAddr, g_aStructs[iStruct].pszName, pSymInfo))
675 {
676 if (!(pOsVerInfo->uMajorVer == 5 && pOsVerInfo->uMinorVer == 0) /* w2k */)
677 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to find _KPRCB: %u\n", pszPdb, GetLastError());
678 RTMsgInfo("%s: Skipping - failed to find _KPRCB: %u\n", pszPdb, GetLastError());
679 return RTEXITCODE_SKIPPED;
680 }
681
682 MyDbgPrintf(" %s: TypeIndex=%u\n", g_aStructs[iStruct].pszName, pSymInfo->TypeIndex);
683 MyDbgPrintf(" %s: Size=%u (%#x)\n", g_aStructs[iStruct].pszName, pSymInfo->Size, pSymInfo->Size);
684
685 rcExit = findMembers(hFake, uModAddr, pSymInfo->TypeIndex,
686 g_aStructs[iStruct].cMembers, g_aStructs[iStruct].paMembers, 0 /* offDisp */,
687 g_aStructs[iStruct].pszName, "", pszLogTag);
688 if (rcExit != RTEXITCODE_SUCCESS)
689 return rcExit;
690 } /* for each struct we want */
691 return rcExit;
692}
693
694
695#if 0 /* unused */
696static bool strIEndsWith(const char *pszString, const char *pszSuffix)
697{
698 size_t cchString = strlen(pszString);
699 size_t cchSuffix = strlen(pszSuffix);
700 if (cchString < cchSuffix)
701 return false;
702 return RTStrICmp(pszString + cchString - cchSuffix, pszSuffix) == 0;
703}
704#endif
705
706
707/**
708 * Use various hysterics to figure out the OS version details from the PDB path.
709 *
710 * This ASSUMES quite a bunch of things:
711 * -# Working on unpacked symbol packages. This does not work for
712 * windbg symbol stores/caches.
713 * -# The symbol package has been unpacked into a directory with the same
714 * name as the symbol package (sans suffixes).
715 *
716 * @returns Fully complained exit code.
717 * @param pszPdb The path to the PDB.
718 * @param pVerInfo Where to return the version info.
719 * @param penmArch Where to return the architecture.
720 */
721static RTEXITCODE FigurePdbVersionInfo(const char *pszPdb, PRTNTSDBOSVER pVerInfo, MYARCH *penmArch)
722{
723 /*
724 * Split the path.
725 */
726 union
727 {
728 RTPATHSPLIT Split;
729 uint8_t abPad[RTPATH_MAX + 1024];
730 } u;
731 int rc = RTPathSplit(pszPdb, &u.Split, sizeof(u), 0);
732 if (RT_FAILURE(rc))
733 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathSplit failed on '%s': %Rrc", pszPdb, rc);
734 if (!(u.Split.fProps & RTPATH_PROP_FILENAME))
735 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPATH_PROP_FILENAME not set for: '%s'", pszPdb);
736 const char *pszFilename = u.Split.apszComps[u.Split.cComps - 1];
737
738 /*
739 * SMP or UNI kernel?
740 */
741 if ( !RTStrICmp(pszFilename, "ntkrnlmp.pdb")
742 || !RTStrICmp(pszFilename, "ntkrpamp.pdb")
743 )
744 pVerInfo->fSmp = true;
745 else if ( !RTStrICmp(pszFilename, "ntoskrnl.pdb")
746 || !RTStrICmp(pszFilename, "ntkrnlpa.pdb")
747 )
748 pVerInfo->fSmp = false;
749 else
750 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Doesn't recognize the filename '%s'...", pszFilename);
751
752 /*
753 * Look for symbol pack names in the path. This is stuff like:
754 * - WindowsVista.6002.090410-1830.x86fre
755 * - WindowsVista.6002.090410-1830.amd64chk
756 * - Windows_Win7.7600.16385.090713-1255.X64CHK
757 * - Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE
758 * - Windows_Win8.9200.16384.120725-1247.X86CHK
759 * - en_windows_8_1_symbols_debug_checked_x64_2712568
760 */
761 uint32_t i = u.Split.cComps - 1;
762 while (i-- > 0)
763 {
764 static struct
765 {
766 const char *pszPrefix;
767 size_t cchPrefix;
768 uint8_t uMajorVer;
769 uint8_t uMinorVer;
770 uint8_t uCsdNo;
771 uint32_t uBuildNo; /**< UINT32_MAX means the number immediately after the prefix. */
772 } const s_aSymPacks[] =
773 {
774 { RT_STR_TUPLE("w2kSP1SYM"), 5, 0, 1, 2195 },
775 { RT_STR_TUPLE("w2ksp2srp1"), 5, 0, 2, 2195 },
776 { RT_STR_TUPLE("w2ksp2sym"), 5, 0, 2, 2195 },
777 { RT_STR_TUPLE("w2ksp3sym"), 5, 0, 3, 2195 },
778 { RT_STR_TUPLE("w2ksp4sym"), 5, 0, 4, 2195 },
779 { RT_STR_TUPLE("Windows2000-KB891861"), 5, 0, 4, 2195 },
780 { RT_STR_TUPLE("windowsxp"), 5, 1, 0, 2600 },
781 { RT_STR_TUPLE("xpsp1sym"), 5, 1, 1, 2600 },
782 { RT_STR_TUPLE("WindowsXP-KB835935-SP2-"), 5, 1, 2, 2600 },
783 { RT_STR_TUPLE("WindowsXP-KB936929-SP3-"), 5, 1, 3, 2600 },
784 { RT_STR_TUPLE("Windows2003."), 5, 2, 0, 3790 },
785 { RT_STR_TUPLE("Windows2003_sp1."), 5, 2, 1, 3790 },
786 { RT_STR_TUPLE("WindowsServer2003-KB933548-v1"), 5, 2, 1, 3790 },
787 { RT_STR_TUPLE("WindowsVista.6000."), 6, 0, 0, 6000 },
788 { RT_STR_TUPLE("Windows_Longhorn.6001."), 6, 0, 1, 6001 }, /* incl w2k8 */
789 { RT_STR_TUPLE("WindowsVista.6002."), 6, 0, 2, 6002 }, /* incl w2k8 */
790 { RT_STR_TUPLE("Windows_Winmain.7000"), 6, 1, 0, 7000 }, /* Beta */
791 { RT_STR_TUPLE("Windows_Winmain.7100"), 6, 1, 0, 7100 }, /* RC */
792 { RT_STR_TUPLE("Windows_Win7.7600"), 6, 1, 0, 7600 }, /* RC */
793 { RT_STR_TUPLE("Windows_Win7SP1.7601"), 6, 1, 1, 7601 }, /* RC */
794 { RT_STR_TUPLE("Windows_Winmain.8102"), 6, 2, 0, 8102 }, /* preview */
795 { RT_STR_TUPLE("Windows_Winmain.8250"), 6, 2, 0, 8250 }, /* beta */
796 { RT_STR_TUPLE("Windows_Winmain.8400"), 6, 2, 0, 8400 }, /* RC */
797 { RT_STR_TUPLE("Windows_Win8.9200"), 6, 2, 0, 9200 }, /* RTM */
798 { RT_STR_TUPLE("en_windows_8_1"), 6, 3, 0, 9600 }, /* RTM */
799 { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */
800 { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */
801 { RT_STR_TUPLE("en_windows_10_17134_"), 10, 0, 0,17134 }, /* 1803 */
802 };
803
804 const char *pszComp = u.Split.apszComps[i];
805 uint32_t iSymPack = RT_ELEMENTS(s_aSymPacks);
806 while (iSymPack-- > 0)
807 if (!RTStrNICmp(pszComp, s_aSymPacks[iSymPack].pszPrefix, s_aSymPacks[iSymPack].cchPrefix))
808 break;
809 if (iSymPack >= RT_ELEMENTS(s_aSymPacks))
810 continue;
811
812 pVerInfo->uMajorVer = s_aSymPacks[iSymPack].uMajorVer;
813 pVerInfo->uMinorVer = s_aSymPacks[iSymPack].uMinorVer;
814 pVerInfo->uCsdNo = s_aSymPacks[iSymPack].uCsdNo;
815 pVerInfo->fChecked = false;
816 pVerInfo->uBuildNo = s_aSymPacks[iSymPack].uBuildNo;
817
818 /* Parse build number if necessary. */
819 if (s_aSymPacks[iSymPack].uBuildNo == UINT32_MAX)
820 {
821 char *pszNext;
822 rc = RTStrToUInt32Ex(pszComp + s_aSymPacks[iSymPack].cchPrefix, &pszNext, 10, &pVerInfo->uBuildNo);
823 if (RT_FAILURE(rc))
824 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': %Rrc", pszComp, rc);
825 if (*pszNext != '.' && *pszNext != '_' && *pszNext != '-')
826 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': '%c'", pszComp, *pszNext);
827 }
828
829 /* Look for build arch and checked/free. */
830 if ( RTStrIStr(pszComp, ".x86.chk.")
831 || RTStrIStr(pszComp, ".x86chk.")
832 || RTStrIStr(pszComp, "_x86_chk_")
833 || RTStrIStr(pszComp, "_x86chk_")
834 || RTStrIStr(pszComp, "-x86-DEBUG")
835 || (RTStrIStr(pszComp, "-x86-") && RTStrIStr(pszComp, "-DEBUG"))
836 || RTStrIStr(pszComp, "_debug_checked_x86")
837 )
838 {
839 pVerInfo->fChecked = true;
840 *penmArch = MYARCH_X86;
841 }
842 else if ( RTStrIStr(pszComp, ".amd64.chk.")
843 || RTStrIStr(pszComp, ".amd64chk.")
844 || RTStrIStr(pszComp, ".x64.chk.")
845 || RTStrIStr(pszComp, ".x64chk.")
846 || RTStrIStr(pszComp, "_debug_checked_x64")
847 )
848 {
849 pVerInfo->fChecked = true;
850 *penmArch = MYARCH_AMD64;
851 }
852 else if ( RTStrIStr(pszComp, ".amd64.fre.")
853 || RTStrIStr(pszComp, ".amd64fre.")
854 || RTStrIStr(pszComp, ".x64.fre.")
855 || RTStrIStr(pszComp, ".x64fre.")
856 )
857 {
858 pVerInfo->fChecked = false;
859 *penmArch = MYARCH_AMD64;
860 }
861 else if ( RTStrIStr(pszComp, "DEBUG")
862 || RTStrIStr(pszComp, "_chk")
863 )
864 {
865 pVerInfo->fChecked = true;
866 *penmArch = MYARCH_X86;
867 }
868 else if (RTStrIStr(pszComp, "_x64"))
869 {
870 pVerInfo->fChecked = false;
871 *penmArch = MYARCH_AMD64;
872 }
873 else
874 {
875 pVerInfo->fChecked = false;
876 *penmArch = MYARCH_X86;
877 }
878 return RTEXITCODE_SUCCESS;
879 }
880
881 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Giving up on '%s'...\n", pszPdb);
882}
883
884
885/**
886 * Process one PDB.
887 *
888 * @returns Fully bitched exit code.
889 * @param pszPdb The path to the PDB.
890 */
891static RTEXITCODE processPdb(const char *pszPdb)
892{
893 /*
894 * We need the size later on, so get that now and present proper IPRT error
895 * info if the file is missing or inaccessible.
896 */
897 RTFSOBJINFO ObjInfo;
898 int rc = RTPathQueryInfoEx(pszPdb, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
899 if (RT_FAILURE(rc))
900 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathQueryInfo fail on '%s': %Rrc\n", pszPdb, rc);
901
902 /*
903 * Figure the windows version details for the given PDB.
904 */
905 MYARCH enmArch;
906 RTNTSDBOSVER OsVerInfo;
907 RTEXITCODE rcExit = FigurePdbVersionInfo(pszPdb, &OsVerInfo, &enmArch);
908 if (rcExit != RTEXITCODE_SUCCESS)
909 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to figure the OS version info for '%s'.\n'", pszPdb);
910
911 /*
912 * Create a fake handle and open the PDB.
913 */
914 static uintptr_t s_iHandle = 0;
915 HANDLE hFake = (HANDLE)++s_iHandle;
916 if (!SymInitialize(hFake, NULL, FALSE))
917 return RTMsgErrorExit(RTEXITCODE_FAILURE, "SymInitialied failed: %u\n", GetLastError());
918
919 uint64_t uModAddr = UINT64_C(0x1000000);
920 uModAddr = SymLoadModuleEx(hFake, NULL /*hFile*/, pszPdb, NULL /*pszModuleName*/,
921 uModAddr, ObjInfo.cbObject, NULL /*pData*/, 0 /*fFlags*/);
922 if (uModAddr != 0)
923 {
924 MyDbgPrintf("*** uModAddr=%#llx \"%s\" ***\n", uModAddr, pszPdb);
925
926 char szLogTag[32];
927 RTStrCopy(szLogTag, sizeof(szLogTag), RTPathFilename(pszPdb));
928
929 /*
930 * Find the structures.
931 */
932 rcExit = findStructures(hFake, uModAddr, szLogTag, pszPdb, &OsVerInfo);
933 if (rcExit == RTEXITCODE_SUCCESS)
934 rcExit = checkThatWeFoundEverything();
935 if (rcExit == RTEXITCODE_SUCCESS)
936 {
937 /*
938 * Save the details for later when we produce the header.
939 */
940 rcExit = saveStructures(&OsVerInfo, enmArch, pszPdb);
941 }
942 }
943 else
944 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymLoadModuleEx failed: %u\n", GetLastError());
945
946 if (!SymCleanup(hFake))
947 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymCleanup failed: %u\n", GetLastError());
948
949 if (rcExit == RTEXITCODE_SKIPPED)
950 rcExit = RTEXITCODE_SUCCESS;
951 return rcExit;
952}
953
954
955/** The size of the directory entry buffer we're using. */
956#define MY_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
957
958/**
959 * Checks if the name is of interest to us.
960 *
961 * @returns true/false.
962 * @param pszName The name.
963 * @param cchName The length of the name.
964 */
965static bool isInterestingName(const char *pszName, size_t cchName)
966{
967 static struct { const char *psz; size_t cch; } const s_aNames[] =
968 {
969 RT_STR_TUPLE("ntoskrnl.pdb"),
970 RT_STR_TUPLE("ntkrnlmp.pdb"),
971 RT_STR_TUPLE("ntkrnlpa.pdb"),
972 RT_STR_TUPLE("ntkrpamp.pdb"),
973 };
974
975 if ( cchName == s_aNames[0].cch
976 && (pszName[0] == 'n' || pszName[0] == 'N')
977 && (pszName[1] == 't' || pszName[1] == 'T')
978 )
979 {
980 int i = RT_ELEMENTS(s_aNames);
981 while (i-- > 0)
982 if ( s_aNames[i].cch == cchName
983 && !RTStrICmp(s_aNames[i].psz, pszName))
984 return true;
985 }
986 return false;
987}
988
989
990/**
991 * Recursively processes relevant files in the specified directory.
992 *
993 * @returns Fully complained exit code.
994 * @param pszDir Pointer to the directory buffer.
995 * @param cchDir The length of pszDir in pszDir.
996 * @param pDirEntry Pointer to the directory buffer.
997 * @param iLogDepth The logging depth.
998 */
999static RTEXITCODE processDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry, int iLogDepth)
1000{
1001 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
1002
1003 /* Make sure we've got some room in the path, to save us extra work further down. */
1004 if (cchDir + 3 >= RTPATH_MAX)
1005 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'\n", pszDir);
1006
1007 /* Open directory. */
1008 RTDIR hDir;
1009 int rc = RTDirOpen(&hDir, pszDir);
1010 if (RT_FAILURE(rc))
1011 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirOpen failed on '%s': %Rrc\n", pszDir, rc);
1012
1013 /* Ensure we've got a trailing slash (there is space for it see above). */
1014 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
1015 {
1016 pszDir[cchDir++] = RTPATH_SLASH;
1017 pszDir[cchDir] = '\0';
1018 }
1019
1020 /*
1021 * Process the files and subdirs.
1022 */
1023 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1024 for (;;)
1025 {
1026 /* Get the next directory. */
1027 size_t cbDirEntry = MY_DIRENTRY_BUF_SIZE;
1028 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
1029 if (RT_FAILURE(rc))
1030 break;
1031
1032 /* Skip the dot and dot-dot links. */
1033 if (RTDirEntryExIsStdDotLink(pDirEntry))
1034 continue;
1035
1036 /* Check length. */
1037 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
1038 {
1039 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
1040 break;
1041 }
1042
1043 if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
1044 {
1045 /*
1046 * Process debug info files of interest.
1047 */
1048 if (isInterestingName(pDirEntry->szName, pDirEntry->cbName))
1049 {
1050 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
1051 RTEXITCODE rcExit2 = processPdb(pszDir);
1052 if (rcExit2 != RTEXITCODE_SUCCESS)
1053 rcExit = rcExit2;
1054 }
1055 }
1056 else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
1057 {
1058 /*
1059 * Recurse into the subdirectory. In order to speed up Win7+
1060 * symbol pack traversals, we skip directories with ".pdb" suffixes
1061 * unless they match any of the .pdb files we're looking for.
1062 *
1063 * Note! When we get back pDirEntry will be invalid.
1064 */
1065 if ( pDirEntry->cbName <= 4
1066 || RTStrICmp(&pDirEntry->szName[pDirEntry->cbName - 4], ".pdb")
1067 || isInterestingName(pDirEntry->szName, pDirEntry->cbName))
1068 {
1069 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
1070 if (iLogDepth > 0)
1071 RTMsgInfo("%s%s ...\n", pszDir, RTPATH_SLASH_STR);
1072 RTEXITCODE rcExit2 = processDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, iLogDepth - 1);
1073 if (rcExit2 != RTEXITCODE_SUCCESS)
1074 rcExit = rcExit2;
1075 }
1076 }
1077 }
1078 if (rc != VERR_NO_MORE_FILES)
1079 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
1080
1081 rc = RTDirClose(hDir);
1082 if (RT_FAILURE(rc))
1083 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
1084 return rcExit;
1085}
1086
1087
1088/**
1089 * Recursively processes relevant files in the specified directory.
1090 *
1091 * @returns Fully complained exit code.
1092 * @param pszDir The directory to search.
1093 */
1094static RTEXITCODE processDir(const char *pszDir)
1095{
1096 char szPath[RTPATH_MAX];
1097 int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
1098 if (RT_FAILURE(rc))
1099 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
1100
1101 union
1102 {
1103 uint8_t abPadding[MY_DIRENTRY_BUF_SIZE];
1104 RTDIRENTRYEX DirEntry;
1105 } uBuf;
1106 return processDirSub(szPath, strlen(szPath), &uBuf.DirEntry, g_iOptVerbose);
1107}
1108
1109
1110int main(int argc, char **argv)
1111{
1112 int rc = RTR3InitExe(argc, &argv, 0 /*fFlags*/);
1113 if (RT_FAILURE(rc))
1114 return RTMsgInitFailure(rc);
1115
1116 RTListInit(&g_SetList);
1117
1118 /*
1119 * Parse options.
1120 */
1121 static const RTGETOPTDEF s_aOptions[] =
1122 {
1123 { "--force", 'f', RTGETOPT_REQ_NOTHING },
1124 { "--output", 'o', RTGETOPT_REQ_STRING },
1125 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1126 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1127 };
1128
1129 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1130 const char *pszOutput = "-";
1131
1132 int ch;
1133 RTGETOPTUNION ValueUnion;
1134 RTGETOPTSTATE GetState;
1135 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
1136 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1137 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1138 {
1139 switch (ch)
1140 {
1141 case 'f':
1142 g_fOptForce = true;
1143 break;
1144
1145 case 'v':
1146 g_iOptVerbose++;
1147 break;
1148
1149 case 'q':
1150 g_iOptVerbose++;
1151 break;
1152
1153 case 'o':
1154 pszOutput = ValueUnion.psz;
1155 break;
1156
1157 case 'V':
1158 RTPrintf("$Revision: 76560 $");
1159 break;
1160
1161 case 'h':
1162 RTPrintf("usage: %s [-v|--verbose] [-q|--quiet] [-f|--force] [-o|--output <file.h>] <dir1|pdb1> [...]\n"
1163 " or: %s [-V|--version]\n"
1164 " or: %s [-h|--help]\n",
1165 argv[0], argv[0], argv[0]);
1166 return RTEXITCODE_SUCCESS;
1167
1168 case VINF_GETOPT_NOT_OPTION:
1169 {
1170 RTEXITCODE rcExit2;
1171 if (RTFileExists(ValueUnion.psz))
1172 rcExit2 = processPdb(ValueUnion.psz);
1173 else
1174 rcExit2 = processDir(ValueUnion.psz);
1175 if (rcExit2 != RTEXITCODE_SUCCESS)
1176 {
1177 if (!g_fOptForce)
1178 return rcExit2;
1179 rcExit = rcExit2;
1180 }
1181 break;
1182 }
1183
1184 default:
1185 return RTGetOptPrintError(ch, &ValueUnion);
1186 }
1187 }
1188 if (RTListIsEmpty(&g_SetList))
1189 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No usable debug files found.\n");
1190
1191 /*
1192 * Generate the output.
1193 */
1194 PRTSTREAM pOut = g_pStdOut;
1195 if (strcmp(pszOutput, "-"))
1196 {
1197 rc = RTStrmOpen(pszOutput, "w", &pOut);
1198 if (RT_FAILURE(rc))
1199 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s' for writing: %Rrc\n", pszOutput, rc);
1200 }
1201
1202 generateHeader(pOut);
1203
1204 if (pOut != g_pStdOut)
1205 rc = RTStrmClose(pOut);
1206 else
1207 rc = RTStrmFlush(pOut);
1208 if (RT_FAILURE(rc))
1209 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error %s '%s': %Rrc\n", pszOutput,
1210 pOut != g_pStdOut ? "closing" : "flushing", rc);
1211 return rcExit;
1212}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette