VirtualBox

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

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

More ntBldSymDb.cpp work.

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