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