VirtualBox

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

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

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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