VirtualBox

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

Last change on this file since 106518 was 106065, checked in by vboxsync, 4 months ago

Manual copyright year updates.

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