VirtualBox

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

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

ntBldSymDb.cpp: -> weinstadt

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