VirtualBox

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

最後變更 在這個檔案從96407是 96407,由 vboxsync 提交於 3 年 前

scm copyright and license note update

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

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