VirtualBox

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

Last change on this file since 45405 was 45405, checked in by vboxsync, 12 years ago

ntBldSymDb.cpp: back to the os version info detection code.

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