VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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