VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTDbgSymCache.cpp@ 96609

Last change on this file since 96609 was 96609, checked in by vboxsync, 2 years ago

IPRT/path: Added fFlags parameter to RTPathAppendEx and RTPathJoinEx to make it possible to select the path style. bugref:10286

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.1 KB
Line 
1/* $Id: RTDbgSymCache.cpp 96609 2022-09-06 14:13:23Z vboxsync $ */
2/** @file
3 * IPRT - Debug Symbol Cache Utility.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.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/zip.h>
42
43#include <iprt/buildconfig.h>
44#include <iprt/dbg.h>
45#include <iprt/err.h>
46#include <iprt/file.h>
47#include <iprt/formats/mach-o.h>
48#include <iprt/getopt.h>
49#include <iprt/initterm.h>
50#include <iprt/ldr.h>
51#include <iprt/message.h>
52#include <iprt/param.h>
53#include <iprt/path.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/uuid.h>
57#include <iprt/vfs.h>
58#include <iprt/zip.h>
59
60
61/*********************************************************************************************************************************
62* Structures and Typedefs *
63*********************************************************************************************************************************/
64/**
65 * Cache file type.
66 */
67typedef enum RTDBGSYMCACHEFILETYPE
68{
69 RTDBGSYMCACHEFILETYPE_INVALID,
70 RTDBGSYMCACHEFILETYPE_DIR,
71 RTDBGSYMCACHEFILETYPE_DIR_FILTER,
72 RTDBGSYMCACHEFILETYPE_DEBUG_FILE,
73 RTDBGSYMCACHEFILETYPE_IMAGE_FILE,
74 RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE,
75 RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE,
76 RTDBGSYMCACHEFILETYPE_IGNORE
77} RTDBGSYMCACHEFILETYPE;
78
79
80/**
81 * Configuration for the 'add' command.
82 */
83typedef struct RTDBGSYMCACHEADDCFG
84{
85 bool fRecursive;
86 bool fOverwriteOnConflict;
87 const char *pszFilter;
88 const char *pszCache;
89} RTDBGSYMCACHEADDCFG;
90/** Pointer to a read only 'add' config. */
91typedef RTDBGSYMCACHEADDCFG const *PCRTDBGSYMCACHEADDCFG;
92
93
94/*********************************************************************************************************************************
95* Global Variables *
96*********************************************************************************************************************************/
97/** Bundle suffixes. */
98static const char * const g_apszBundleSuffixes[] =
99{
100 ".kext",
101 ".app",
102 ".framework", /** @todo framework is different. */
103 ".component",
104 ".action",
105 ".caction",
106 ".bundle",
107 ".sourcebundle",
108 ".plugin",
109 ".ppp",
110 ".menu",
111 ".monitorpanel",
112 ".scripting",
113 ".prefPane",
114 ".qlgenerator",
115 ".brailledriver",
116 ".saver",
117 ".SpeechVoice",
118 ".SpeechRecognizer",
119 ".SpeechSynthesizer",
120 ".mdimporter",
121 ".spreporter",
122 ".xpc",
123 NULL
124};
125
126/** Debug bundle suffixes. (Same as above + .dSYM) */
127static const char * const g_apszDSymBundleSuffixes[] =
128{
129 ".kext.dSYM",
130 ".app.dSYM",
131 ".framework.dSYM",
132 ".component.dSYM",
133 ".action.dSYM",
134 ".caction.dSYM",
135 ".bundle.dSYM",
136 ".sourcebundle.dSYM",
137 ".menu.dSYM",
138 ".plugin.dSYM",
139 ".ppp.dSYM",
140 ".monitorpanel.dSYM",
141 ".scripting.dSYM",
142 ".prefPane.dSYM",
143 ".qlgenerator.dSYM",
144 ".brailledriver.dSYM",
145 ".saver.dSYM",
146 ".SpeechVoice.dSYM",
147 ".SpeechRecognizer.dSYM",
148 ".SpeechSynthesizer.dSYM",
149 ".mdimporter.dSYM",
150 ".spreporter.dSYM",
151 ".xpc.dSYM",
152 ".dSYM",
153 NULL
154};
155
156
157/*********************************************************************************************************************************
158* Internal Functions *
159*********************************************************************************************************************************/
160static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg);
161
162
163/*********************************************************************************************************************************
164* Global Variables *
165*********************************************************************************************************************************/
166/** Verbositity level. */
167static uint32_t g_iLogLevel = 99;
168
169
170/**
171 * Display the version of the cache program.
172 *
173 * @returns exit code.
174 */
175static RTEXITCODE rtDbgSymCacheVersion(void)
176{
177 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
178 return RTEXITCODE_SUCCESS;
179}
180
181
182/**
183 * Shows the usage of the cache program.
184 *
185 * @returns Exit code.
186 * @param pszArg0 Program name.
187 * @param pszCommand Command selector, NULL if all.
188 */
189static RTEXITCODE rtDbgSymCacheUsage(const char *pszArg0, const char *pszCommand)
190{
191 if (!pszCommand || !strcmp(pszCommand, "add"))
192 RTPrintf("Usage: %s add [-Rno] <cache-root-dir> <file1[=cache-name]> [fileN..]\n"
193 "\n"
194 "Options:\n"
195 " -R, --recursive\n"
196 " Process directory arguments recursively.\n"
197 " -n, --no-recursive\n"
198 " No recursion. (default)\n"
199 " -o, --overwrite-on-conflict\n"
200 " Overwrite existing cache entry.\n"
201 , RTPathFilename(pszArg0));
202
203
204 if (!pszCommand || !strcmp(pszCommand, "get"))
205 RTPrintf("Usage: %s get <query-options> <cache-options> [--output|-o <path>]\n"
206 "\n"
207 "Query Options:\n"
208 " --for-exe[cutable] <path>\n"
209 " Get debug file for the given executable.\n"
210 " --dwo, --dwarf, --dwarf-external\n"
211 " Get external DWARF debug file. Needs --name and --dwo-crc32.\n"
212 " --dsym\n"
213 " Get DWARF debug file from .dSYM bundle. Needs --uuid or --name.\n"
214 " --dbg\n"
215 " Get NT DBG debug file. Needs --name, --timestamp and --size.\n"
216 " --pdb20\n"
217 " Get PDB 2.0 debug file. Needs --name, --timestamp, --size\n"
218 " and --pdb-age (if non-zero).\n"
219 " --pdb70\n"
220 " Get PDB 7.0 debug file. Needs --name, --uuid, and --pdb-age\n"
221 " (if non-zero).\n"
222 " --macho\n"
223 " Get Mach-O image file. Needs --uuid or --name.\n"
224 " --pe\n"
225 " Get PE image file. Needs --name, --timestamp and --size.\n"
226 " --timestamp, --ts, -t <timestamp>\n"
227 " The timestamp (32-bit) for the file to get. Used with --dbg, --pdb20\n"
228 " and --pe.\n"
229 " --uuid, -u, <uuid>\n"
230 " The UUID for the file to get. Used with --dsym, --pdb70 and --macho\n"
231 " --image-size, --size, -z <size>\n"
232 " The image size (32-bit) for the file to get. Used with --dbg,\n"
233 " --pdb20, --pdb70 and --pe.\n"
234 " --pdb-age, -a <age>\n"
235 " The PDB age (32-bit) for the file to get. Used with --pdb20 and --pdb70.\n"
236 " --dwo-crc32, -c <crc32>\n"
237 " The CRC32 for the file to get. Used with --dwo.\n"
238 " --name, -n <name>\n"
239 " The name (in the cache) of the file to get.\n"
240 "\n"
241 "Debug Cache Options:\n"
242 " --sym-path, -s <path>\n"
243 " Adds the path to the debug configuration, NT style with 'srv*' and\n"
244 " 'cache*' prefixes as well as our own 'rec*' and 'norec*' recursion\n"
245 " prefixes.\n"
246 " --env-prefix, -p <prefix>\n"
247 " The enviornment variable prefix, default is 'IPRT_' making the\n"
248 " symbol path variable 'IPRT_PATH'.\n"
249 " --use-native-paths (default), --no-native-paths\n"
250 " Pick up native symbol paths from the environment.\n"
251 "\n"
252 "Output Options:\n"
253 " --output, -o <path>\n"
254 " The output filename or directory. Directories must end with a\n"
255 " path separator. The default filename that in the cache.\n"
256 "\n"
257 "This is handy for triggering downloading of symbol files from a server. Say\n"
258 "you have the executable but want the corrsponding PDB or .dSYM file:\n"
259 " %s get --for-executable VBoxRT.dll\n"
260 " %s get --for-executable VBoxRT.dylib\n"
261 " "
262 , RTPathFilename(pszArg0), RTPathFilename(pszArg0), RTPathFilename(pszArg0));
263
264 return RTEXITCODE_SUCCESS;
265}
266
267
268/**
269 * @callback_method_impl{FNRTDBGCFGLOG}
270 */
271static DECLCALLBACK(void) rtDbgSymCacheLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
272{
273 RT_NOREF(hDbgCfg, pvUser);
274 if (iLevel <= g_iLogLevel)
275 {
276 size_t cchMsg = strlen(pszMsg);
277 if (cchMsg > 0 && pszMsg[cchMsg - 1] == '\n')
278 RTMsgInfo("[%u] %s", iLevel, pszMsg);
279 else if (cchMsg > 0)
280 RTMsgInfo("[%u] %s\n", iLevel, pszMsg);
281 }
282}
283
284
285/**
286 * Creates a UUID mapping for the file.
287 *
288 * @returns IPRT status code.
289 * @param pszCacheFile The path to the file in the cache.
290 * @param pFileUuid The UUID of the file.
291 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
292 * wanted, otherwise NULL.
293 * @param pCfg The configuration.
294 */
295static int rtDbgSymCacheAddCreateUuidMapping(const char *pszCacheFile, PRTUUID pFileUuid,
296 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
297{
298 /*
299 * Create the UUID map entry first, deep.
300 */
301 char szMapPath[RTPATH_MAX];
302 int rc = RTPathJoin(szMapPath, sizeof(szMapPath) - sizeof("/xxxx/yyyy/xxxx/yyyy/xxxx/zzzzzzzzzzzz") + 1,
303 pCfg->pszCache, pszUuidMapDir);
304 if (RT_FAILURE(rc))
305 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTPathJoin): %Rrc", rc);
306
307 size_t cch = strlen(szMapPath);
308 szMapPath[cch] = '-';
309
310 rc = RTUuidToStr(pFileUuid, &szMapPath[cch + 2], sizeof(szMapPath) - cch);
311 if (RT_FAILURE(rc))
312 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTUuidToStr): %Rrc", rc);
313
314 /* Uppercase the whole lot. */
315 RTStrToUpper(&szMapPath[cch + 2]);
316
317 /* Split the first dword in two. */
318 szMapPath[cch + 1] = szMapPath[cch + 2];
319 szMapPath[cch + 2] = szMapPath[cch + 3];
320 szMapPath[cch + 3] = szMapPath[cch + 4];
321 szMapPath[cch + 4] = szMapPath[cch + 5];
322 szMapPath[cch + 5] = '-';
323
324 /*
325 * Create the directories in the path.
326 */
327 for (unsigned i = 0; i < 6; i++, cch += 5)
328 {
329 Assert(szMapPath[cch] == '-');
330 szMapPath[cch] = '\0';
331 if (!RTDirExists(szMapPath))
332 {
333 rc = RTDirCreate(szMapPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
334 if (RT_FAILURE(rc))
335 return RTMsgErrorRc(rc, "RTDirCreate failed on '%s' (UUID map path): %Rrc", szMapPath, rc);
336 }
337 szMapPath[cch] = RTPATH_SLASH;
338 }
339 cch -= 5;
340
341 /*
342 * Calculate a relative path from there to the actual file.
343 */
344 char szLinkTarget[RTPATH_MAX];
345 szMapPath[cch] = '\0';
346 rc = RTPathCalcRelative(szLinkTarget, sizeof(szLinkTarget), szMapPath, false /*fFromFile*/, pszCacheFile);
347 szMapPath[cch] = RTPATH_SLASH;
348 if (RT_FAILURE(rc))
349 return RTMsgErrorRc(rc, "Failed to calculate relative path from '%s' to '%s': %Rrc", szMapPath, pszCacheFile, rc);
350
351 /*
352 * If there is already a link there, check if it matches or whether
353 * perhaps it's target doesn't exist.
354 */
355 RTFSOBJINFO ObjInfo;
356 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
357 if (RT_SUCCESS(rc))
358 {
359 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
360 {
361 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
362 if (RT_SUCCESS(rc))
363 {
364 char *pszCurTarget = NULL;
365 rc = RTSymlinkReadA(szMapPath, &pszCurTarget);
366 if (RT_FAILURE(rc))
367 return RTMsgErrorRc(rc, "UUID map: failed to read existing symlink '%s': %Rrc", szMapPath, rc);
368 if (RTPathCompare(pszCurTarget, szLinkTarget) == 0)
369 RTMsgInfo("UUID map: existing link '%s' has the same target ('%s').", szMapPath, pszCurTarget);
370 else
371 {
372 RTMsgError("UUID map: Existing mapping '%s' pointing to '%s' insted of '%s'",
373 szMapPath, pszCurTarget, szLinkTarget);
374 rc = VERR_ALREADY_EXISTS;
375 }
376 RTStrFree(pszCurTarget);
377 return rc;
378 }
379 else
380 RTMsgInfo("UUID map: replacing dangling link '%s'", szMapPath);
381 RTSymlinkDelete(szMapPath, 0 /*fFlags*/);
382 }
383 else if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
384 return RTMsgErrorRc(VERR_IS_A_FILE,
385 "UUID map: found file at '%s', expect symbolic link or nothing.", szMapPath);
386 else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
387 return RTMsgErrorRc(VERR_IS_A_DIRECTORY,
388 "UUID map: found directory at '%s', expect symbolic link or nothing.", szMapPath);
389 else
390 return RTMsgErrorRc(VERR_NOT_SYMLINK,
391 "UUID map: Expected symbolic link or nothing at '%s', found: fMode=%#x",
392 szMapPath, ObjInfo.Attr.fMode);
393 }
394
395 /*
396 * Create the symbolic link.
397 */
398 rc = RTSymlinkCreate(szMapPath, szLinkTarget, RTSYMLINKTYPE_FILE, 0);
399 if (RT_FAILURE(rc))
400 return RTMsgErrorRc(rc, "Failed to create UUID map symlink '%s' to '%s': %Rrc", szMapPath, szLinkTarget, rc);
401 RTMsgInfo("UUID map: %s => %s", szMapPath, szLinkTarget);
402 return VINF_SUCCESS;
403}
404
405
406/**
407 * Adds a file to the cache.
408 *
409 * @returns IPRT status code.
410 * @param pszSrcPath Path to the source file.
411 * @param pszDstName The name of the destionation file (no path stuff).
412 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
413 * @param pszDstSubDir The subdirectory to file it under. This is the
414 * stringification of a relatively unique identifier of
415 * the file in question.
416 * @param pAddToUuidMap Optional file UUID that is used to create a UUID map
417 * entry.
418 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
419 * wanted, otherwise NULL.
420 * @param pCfg The configuration.
421 */
422static int rtDbgSymCacheAddOneFile(const char *pszSrcPath, const char *pszDstName, const char *pszExtraStuff,
423 const char *pszDstSubDir, PRTUUID pAddToUuidMap, const char *pszUuidMapDir,
424 PCRTDBGSYMCACHEADDCFG pCfg)
425{
426 /*
427 * Build and create the destination path, step by step.
428 */
429 char szDstPath[RTPATH_MAX];
430 int rc = RTPathJoin(szDstPath, sizeof(szDstPath), pCfg->pszCache, pszDstName);
431 if (RT_FAILURE(rc))
432 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
433
434 if (!RTDirExists(szDstPath))
435 {
436 rc = RTDirCreate(szDstPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
437 if (RT_FAILURE(rc))
438 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
439 }
440
441 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstSubDir);
442 if (RT_FAILURE(rc))
443 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
444
445 if (!RTDirExists(szDstPath))
446 {
447 rc = RTDirCreate(szDstPath, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
448 if (RT_FAILURE(rc))
449 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
450 }
451
452 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstName);
453 if (RT_FAILURE(rc))
454 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
455 if (pszExtraStuff)
456 {
457 rc = RTStrCat(szDstPath, sizeof(szDstPath), pszExtraStuff);
458 if (RT_FAILURE(rc))
459 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
460 }
461
462 /*
463 * If the file exists, we compare the two and throws an error if the doesn't match.
464 */
465 if (RTPathExists(szDstPath))
466 {
467 rc = RTFileCompare(pszSrcPath, szDstPath);
468 if (RT_SUCCESS(rc))
469 {
470 RTMsgInfo("%s is already in the cache.", pszSrcPath);
471 if (pAddToUuidMap && pszUuidMapDir)
472 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
473 return VINF_SUCCESS;
474 }
475 if (rc == VERR_NOT_EQUAL)
476 RTMsgInfo("Cache conflict with existing entry '%s' when inserting '%s'.", szDstPath, pszSrcPath);
477 else
478 RTMsgInfo("Error comparing '%s' with '%s': %Rrc", pszSrcPath, szDstPath, rc);
479 if (!pCfg->fOverwriteOnConflict)
480 return rc;
481 }
482
483 /*
484 * The file doesn't exist or we should overwrite it,
485 */
486 RTMsgInfo("Copying '%s' to '%s'...", pszSrcPath, szDstPath);
487 rc = RTFileCopy(pszSrcPath, szDstPath);
488 if (RT_FAILURE(rc))
489 return RTMsgErrorRc(rc, "Error copying '%s' to '%s': %Rrc", pszSrcPath, szDstPath, rc);
490 if (pAddToUuidMap && pszUuidMapDir)
491 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
492 return VINF_SUCCESS;
493}
494
495
496/**
497 * Worker that add the image file to the right place.
498 *
499 * @returns IPRT status code.
500 * @param pszPath Path to the image file.
501 * @param pszDstName Add to the cache under this name. Typically the
502 * filename part of @a pszPath.
503 * @param pCfg Configuration data.
504 * @param hLdrMod Image handle.
505 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
506 * @param pszUuidMapDir Optional UUID map cache directory if the image
507 * should be mapped by UUID.
508 * The map is a Mac OS X debug feature supported by
509 * the two native debuggers gdb and lldb. Look for
510 * descriptions of DBGFileMappedPaths in the
511 * com.apple.DebugSymbols in the user defaults.
512 */
513static int rtDbgSymCacheAddImageFileWorker(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg,
514 RTLDRMOD hLdrMod, const char *pszExtrSuff, const char *pszUuidMapDir)
515{
516 /*
517 * Determine which subdirectory to put the files in.
518 */
519 RTUUID Uuid;
520 PRTUUID pUuid = NULL;
521 int rc;
522 char szSubDir[48];
523 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod);
524 switch (enmFmt)
525 {
526 case RTLDRFMT_MACHO:
527 {
528 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid));
529 if (RT_FAILURE(rc))
530 return RTMsgErrorRc(rc, "Error quering image UUID from image '%s': %Rrc", pszPath, rc);
531
532 rc = RTUuidToStr(&Uuid, szSubDir, sizeof(szSubDir));
533 if (RT_FAILURE(rc))
534 return RTMsgErrorRc(rc, "Error convering UUID for image '%s' to string: %Rrc", pszPath, rc);
535 pUuid = &Uuid;
536 break;
537 }
538
539 case RTLDRFMT_PE:
540 {
541 uint32_t uTimestamp;
542 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uTimestamp));
543 if (RT_FAILURE(rc))
544 return RTMsgErrorRc(rc, "Error quering timestamp from image '%s': %Rrc", pszPath, rc);
545
546 size_t cbImage = RTLdrSize(hLdrMod);
547 if (cbImage == ~(size_t)0)
548 return RTMsgErrorRc(rc, "Error quering size of image '%s': %Rrc", pszPath, rc);
549
550 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
551 break;
552 }
553
554 case RTLDRFMT_AOUT:
555 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of a.out image has not yet been implemented: %s", pszPath);
556 case RTLDRFMT_ELF:
557 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of ELF image has not yet been implemented: %s", pszPath);
558 case RTLDRFMT_LX:
559 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of LX image has not yet been implemented: %s", pszPath);
560 default:
561 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Unknown loader format for '%s': %d", pszPath, enmFmt);
562 }
563
564 /*
565 * Now add it.
566 */
567 return rtDbgSymCacheAddOneFile(pszPath, pszDstName, pszExtrSuff, szSubDir, pUuid, pszUuidMapDir, pCfg);
568}
569
570
571/**
572 * Adds what we think is an image file to the cache.
573 *
574 * @returns IPRT status code.
575 * @param pszPath Path to the image file.
576 * @param pszDstName Add to the cache under this name. Typically the
577 * filename part of @a pszPath.
578 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
579 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
580 * wanted, otherwise NULL.
581 * @param pCfg Configuration data.
582 */
583static int rtDbgSymCacheAddImageFile(const char *pszPath, const char *pszDstName, const char *pszExtraSuff,
584 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
585{
586 RTERRINFOSTATIC ErrInfo;
587
588 /*
589 * Use the loader to open the alleged image file. We need to open it with
590 * arch set to amd64 and x86_32 in order to handle FAT images from the mac
591 * guys (we should actually enumerate archs, but that's currently not
592 * implemented nor necessary for our current use).
593 */
594 /* Open it as AMD64. */
595 RTLDRMOD hLdrMod64;
596 int rc = RTLdrOpenEx(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_AMD64, &hLdrMod64, RTErrInfoInitStatic(&ErrInfo));
597 if (RT_FAILURE(rc))
598 {
599 if (rc != VERR_LDR_ARCH_MISMATCH)
600 {
601 if (rc != VERR_INVALID_EXE_SIGNATURE)
602 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=amd64]: %Rrc%s%s", pszPath, rc,
603 RTErrInfoIsSet(&ErrInfo.Core) ? " - " : "",
604 RTErrInfoIsSet(&ErrInfo.Core) ? ErrInfo.Core.pszMsg : "");
605
606 RTMsgInfo("Skipping '%s', no a recognizable image file...", pszPath);
607 return VINF_SUCCESS;
608 }
609 hLdrMod64 = NIL_RTLDRMOD;
610 }
611
612 /* Open it as X86. */
613 RTLDRMOD hLdrMod32;
614 rc = RTLdrOpenEx(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_X86_32, &hLdrMod32, RTErrInfoInitStatic(&ErrInfo));
615 if (RT_FAILURE(rc))
616 {
617 if (rc != VERR_LDR_ARCH_MISMATCH)
618 {
619 RTLdrClose(hLdrMod64);
620 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=x86]: %Rrc%s%s", pszPath, rc,
621 RTErrInfoIsSet(&ErrInfo.Core) ? " - " : "",
622 RTErrInfoIsSet(&ErrInfo.Core) ? ErrInfo.Core.pszMsg : "");
623 }
624 hLdrMod32 = NIL_RTLDRMOD;
625 }
626
627 /*
628 * Add the file.
629 */
630 if (hLdrMod32 == NIL_RTLDRMOD)
631 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
632 else if (hLdrMod64 == NIL_RTLDRMOD)
633 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
634 else
635 {
636 /*
637 * Do we need to add it once or twice?
638 */
639 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod32);
640 bool fSame = enmFmt == RTLdrGetFormat(hLdrMod64);
641 if (fSame && enmFmt == RTLDRFMT_MACHO)
642 {
643 RTUUID Uuid32, Uuid64;
644 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_UUID, &Uuid32, sizeof(Uuid32));
645 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_UUID, &Uuid64, sizeof(Uuid64));
646 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
647 if (fSame && RT_SUCCESS(rc32))
648 fSame = RTUuidCompare(&Uuid32, &Uuid64) == 0;
649 }
650 else if (fSame && enmFmt == RTLDRFMT_PE)
651 {
652 fSame = RTLdrSize(hLdrMod32) == RTLdrSize(hLdrMod64);
653 if (fSame)
654 {
655 uint32_t uTimestamp32, uTimestamp64;
656 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp32, sizeof(uTimestamp32));
657 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp64, sizeof(uTimestamp64));
658 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
659 if (fSame && RT_SUCCESS(rc32))
660 fSame = uTimestamp32 == uTimestamp64;
661 }
662 }
663
664 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
665 if (!fSame)
666 {
667 /** @todo should symlink or hardlink this second copy. */
668 int rc2 = rtDbgSymCacheAddImageFileWorker(pszPath, pszDstName, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
669 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
670 rc = rc2;
671 }
672 }
673
674 RTLdrClose(hLdrMod32);
675 RTLdrClose(hLdrMod64);
676 return VINF_SUCCESS;
677}
678
679
680/**
681 * Worker for rtDbgSymCacheAddDebugFile that adds a Mach-O debug file to the
682 * cache.
683 *
684 * @returns IPRT status code
685 * @param pszPath The path to the PDB file.
686 * @param pszDstName Add to the cache under this name. Typically the
687 * filename part of @a pszPath.
688 * @param pCfg The configuration.
689 * @param hFile Handle to the file.
690 */
691static int rtDbgSymCacheAddDebugMachO(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg)
692{
693 /* This shouldn't happen, figure out what to do if it does. */
694 RT_NOREF(pCfg, pszDstName);
695 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED,
696 "'%s' is an OS X image file, did you point me to a file inside a .dSYM or .sym file?",
697 pszPath);
698}
699
700
701/**
702 * Worker for rtDbgSymCacheAddDebugFile that adds PDBs to the cace.
703 *
704 * @returns IPRT status code
705 * @param pszPath The path to the PDB file.
706 * @param pszDstName Add to the cache under this name. Typically the
707 * filename part of @a pszPath.
708 * @param pCfg The configuration.
709 * @param hFile Handle to the file.
710 */
711static int rtDbgSymCacheAddDebugPdb(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg, RTFILE hFile)
712{
713 RT_NOREF(pCfg, hFile, pszDstName);
714 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "PDB support not implemented: '%s'", pszPath);
715}
716
717
718/**
719 * Adds a debug file to the cache.
720 *
721 * @returns IPRT status code
722 * @param pszPath The path to the debug file in question.
723 * @param pszDstName Add to the cache under this name. Typically the
724 * filename part of @a pszPath.
725 * @param pCfg The configuration.
726 */
727static int rtDbgSymCacheAddDebugFile(const char *pszPath, const char *pszDstName, PCRTDBGSYMCACHEADDCFG pCfg)
728{
729 /*
730 * Need to extract an identifier of sorts here in order to put them in
731 * the right place in the cache. Currently only implemnted for Mach-O
732 * files since these use executable containers.
733 *
734 * We take a look at the file header in hope to figure out what to do
735 * with the file.
736 */
737 RTFILE hFile;
738 int rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
739 if (RT_FAILURE(rc))
740 return RTMsgErrorRc(rc, "Error opening '%s': %Rrc", pszPath, rc);
741
742 union
743 {
744 uint64_t au64[16];
745 uint32_t au32[16];
746 uint16_t au16[32];
747 uint8_t ab[64];
748 } uBuf;
749 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), NULL);
750 if (RT_SUCCESS(rc))
751 {
752 /*
753 * Look for magics and call workers.
754 */
755 if (!memcmp(uBuf.ab, RT_STR_TUPLE("Microsoft C/C++ MSF 7.00")))
756 rc = rtDbgSymCacheAddDebugPdb(pszPath, pszDstName, pCfg, hFile);
757 else if ( uBuf.au32[0] == IMAGE_FAT_SIGNATURE
758 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
759 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
760 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
761 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
762 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
763 rc = rtDbgSymCacheAddDebugMachO(pszPath, pszDstName, pCfg);
764 else
765 rc = RTMsgErrorRc(VERR_INVALID_MAGIC, "Unsupported debug file '%s' magic: %#010x", pszPath, uBuf.au32[0]);
766 }
767 else
768 rc = RTMsgErrorRc(rc, "Error reading '%s': %Rrc", pszPath, rc);
769
770 /* close the file. */
771 int rc2 = RTFileClose(hFile);
772 if (RT_FAILURE(rc2))
773 {
774 RTMsgError("Error closing '%s': %Rrc", pszPath, rc2);
775 if (RT_SUCCESS(rc))
776 rc = rc2;
777 }
778 return rc;
779}
780
781
782/**
783 * Constructs the path to the file instide the bundle that we're keen on.
784 *
785 * @returns IPRT status code.
786 * @param pszPath Path to the bundle on input, on successful
787 * return it's the path to the desired file. This
788 * a RTPATH_MAX size buffer.
789 * @param cchPath The length of the path up to the bundle name.
790 * @param cchName The length of the bundle name.
791 * @param pszSubDir The bundle subdirectory the file lives in.
792 * @param papszSuffixes Pointer to an array of bundle suffixes.
793 */
794static int rtDbgSymCacheConstructBundlePath(char *pszPath, size_t cchPath, size_t cchName, const char *pszSubDir,
795 const char * const *papszSuffixes)
796{
797 /*
798 * Calc the name without the bundle extension.
799 */
800 size_t const cchOrgName = cchName;
801 const char *pszEnd = &pszPath[cchPath + cchName];
802 for (unsigned i = 0; papszSuffixes[i]; i++)
803 {
804 Assert(papszSuffixes[i][0] == '.');
805 size_t cchSuff = strlen(papszSuffixes[i]);
806 if ( cchSuff < cchName
807 && !memcmp(&pszEnd[-(ssize_t)cchSuff], papszSuffixes[i], cchSuff))
808 {
809 cchName -= cchSuff;
810 break;
811 }
812 }
813
814 /*
815 * Check the immediate directory first, in case it's layed out like
816 * IOPCIFamily.kext.
817 */
818 int rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName, RTPATH_STR_F_STYLE_HOST);
819 if (RT_FAILURE(rc) || !RTFileExists(pszPath))
820 {
821 /*
822 * Not there, ok then try the given subdirectory + name.
823 */
824 pszPath[cchPath + cchOrgName] = '\0';
825 rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
826 if (RT_SUCCESS(rc))
827 rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName, RTPATH_STR_F_STYLE_HOST);
828 if (RT_FAILURE(rc))
829 {
830 pszPath[cchPath + cchOrgName] = '\0';
831 return RTMsgErrorRc(rc, "Error constructing image bundle path for '%s': %Rrc", pszPath, rc);
832 }
833 }
834
835 return VINF_SUCCESS;
836}
837
838
839/**
840 * Adds a image bundle of some sort.
841 *
842 * @returns IPRT status code.
843 * @param pszPath Path to the bundle. This a RTPATH_MAX size
844 * buffer that we can write to when creating the
845 * path to the file inside the bundle that we're
846 * interested in.
847 * @param cchPath The length of the path up to the bundle name.
848 * @param cchName The length of the bundle name.
849 * @param pszDstName Add to the cache under this name, NULL if not
850 * specified.
851 * @param pDirEntry The directory entry buffer, for handling bundle
852 * within bundle recursion.
853 * @param pCfg The configuration.
854 */
855static int rtDbgSymCacheAddImageBundle(char *pszPath, size_t cchPath, size_t cchName, const char *pszDstName,
856 PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
857{
858 /*
859 * Assuming these are kexts or simple applications, we only add the image
860 * file itself to the cache. No Info.plist or other files.
861 */
862 /** @todo consider looking for Frameworks and handling framework bundles. */
863 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/MacOS/", g_apszBundleSuffixes);
864 if (RT_SUCCESS(rc))
865 {
866 if (!pszDstName)
867 pszDstName = RTPathFilename(pszPath);
868 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, NULL, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
869 }
870
871 /*
872 * Look for plugins and other sub-bundles.
873 */
874 if (pCfg->fRecursive)
875 {
876 static char const * const s_apszSubBundleDirs[] =
877 {
878 "Contents/Plugins/",
879 /** @todo Frameworks ++ */
880 };
881 for (uint32_t i = 0; i < RT_ELEMENTS(s_apszSubBundleDirs); i++)
882 {
883 pszPath[cchPath + cchName] = '\0';
884 int rc2 = RTPathAppend(pszPath, RTPATH_MAX - 1, s_apszSubBundleDirs[i]);
885 if (RT_SUCCESS(rc2))
886 {
887 if (RTDirExists(pszPath))
888 {
889 size_t cchPath2 = strlen(pszPath);
890 if (!RTPATH_IS_SLASH(pszPath[cchPath2 - 1]))
891 {
892 pszPath[cchPath2++] = RTPATH_SLASH;
893 pszPath[cchPath2] = '\0';
894 }
895 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath2, pDirEntry, pCfg);
896 }
897 }
898 else
899 {
900 pszPath[cchPath + cchName] = '\0';
901 RTMsgError("Error constructing bundle subdir path for '%s' + '%s': %Rrc", pszPath, s_apszSubBundleDirs[i], rc);
902 }
903 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
904 rc = rc2;
905 }
906 }
907
908 return rc;
909}
910
911
912/**
913 * Adds a debug bundle.
914 *
915 * @returns IPRT status code.
916 * @param pszPath Path to the bundle. This a RTPATH_MAX size
917 * buffer that we can write to when creating the
918 * path to the file inside the bundle that we're
919 * interested in.
920 * @param cchPath The length of the path up to the bundle name.
921 * @param cchName The length of the bundle name.
922 * @param pszDstName Add to the cache under this name, NULL if not
923 * specified.
924 * @param pCfg The configuration.
925 */
926static int rtDbgSymCacheAddDebugBundle(char *pszPath, size_t cchPath, size_t cchName, const char *pszDstName,
927 PCRTDBGSYMCACHEADDCFG pCfg)
928{
929 /*
930 * The current policy is not to add the whole .dSYM (or .sym) bundle, but
931 * rather just the dwarf image instide it. The <UUID>.plist and Info.plist
932 * files generally doesn't contain much extra information that's really
933 * necessary, I hope. At least this is what the uuidmap example in the
934 * lldb hints at (it links to the dwarf file, not the .dSYM dir).
935 *
936 * To avoid confusion with a .dSYM bundle, as well as collision with the
937 * image file, we use .dwarf suffix for the file.
938 *
939 * For details on the uuid map see rtDbgSymCacheAddImageFile as well as
940 * http://lldb.llvm.org/symbols.html .
941 *
942 * ASSUMES bundles contains Mach-O DWARF files.
943 */
944 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/Resources/DWARF/", g_apszDSymBundleSuffixes);
945 if (RT_SUCCESS(rc))
946 {
947 if (!pszDstName)
948 pszDstName = RTPathFilename(pszPath);
949 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, RTDBG_CACHE_DSYM_FILE_SUFFIX, RTDBG_CACHE_UUID_MAP_DIR_DSYMS, pCfg);
950 }
951 return rc;
952}
953
954
955/**
956 * Figure the type of a file/dir based on path and FS object info.
957 *
958 * @returns The type.
959 * @param pszPath The path to the file/dir.
960 * @param pObjInfo The object information, symlinks followed.
961 */
962static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType2(const char *pszPath, PCRTFSOBJINFO pObjInfo)
963{
964 const char *pszName = RTPathFilename(pszPath);
965 const char *pszExt = RTPathSuffix(pszName);
966 if (pszExt)
967 pszExt++;
968 else
969 pszExt = "";
970
971 if ( RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode)
972 || (pObjInfo->Attr.fMode & RTFS_DOS_DIRECTORY)) /** @todo OS X samba reports reparse points in /Volumes/ that we cannot resolve. */
973 {
974 /* Skip directories shouldn't bother with. */
975 if ( !RTStrICmp(pszName, ".Trashes")
976 || !RTStrICmp(pszName, ".$RESCYCLE.BIN")
977 || !RTStrICmp(pszName, "System.kext") /* Usually only plugins here, so skip it. */
978 )
979 return RTDBGSYMCACHEFILETYPE_IGNORE;
980
981 /* Directories can also be bundles on the mac. */
982 if (!RTStrICmp(pszExt, "dSYM"))
983 return RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE;
984
985 for (unsigned i = 0; i < RT_ELEMENTS(g_apszBundleSuffixes) - 1; i++)
986 if (!RTStrICmp(pszExt, &g_apszBundleSuffixes[i][1]))
987 return RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE;
988
989 return RTDBGSYMCACHEFILETYPE_DIR;
990 }
991
992 if (!RTFS_IS_FILE(pObjInfo->Attr.fMode))
993 return RTDBGSYMCACHEFILETYPE_INVALID;
994
995 /* Select image vs debug info based on extension. */
996 if ( !RTStrICmp(pszExt, "pdb")
997 || !RTStrICmp(pszExt, "dbg")
998 || !RTStrICmp(pszExt, "sym")
999 || !RTStrICmp(pszExt, "dwo")
1000 || !RTStrICmp(pszExt, "dwp")
1001 || !RTStrICmp(pszExt, "debug")
1002 || !RTStrICmp(pszExt, "dsym")
1003 || !RTStrICmp(pszExt, "dwarf")
1004 || !RTStrICmp(pszExt, "map")
1005 || !RTStrICmp(pszExt, "cv"))
1006 return RTDBGSYMCACHEFILETYPE_DEBUG_FILE;
1007
1008 /* Filter out a bunch of files which obviously shouldn't be images. */
1009 if ( !RTStrICmp(pszExt, "txt")
1010 || !RTStrICmp(pszExt, "html")
1011 || !RTStrICmp(pszExt, "htm")
1012 || !RTStrICmp(pszExt, "rtf")
1013 || !RTStrICmp(pszExt, "zip")
1014 || !RTStrICmp(pszExt, "doc")
1015 || !RTStrICmp(pszExt, "gz")
1016 || !RTStrICmp(pszExt, "bz2")
1017 || !RTStrICmp(pszExt, "xz")
1018 || !RTStrICmp(pszExt, "kmk")
1019 || !RTStrICmp(pszExt, "c")
1020 || !RTStrICmp(pszExt, "cpp")
1021 || !RTStrICmp(pszExt, "h")
1022 || !RTStrICmp(pszExt, "m")
1023 || !RTStrICmp(pszExt, "mm")
1024 || !RTStrICmp(pszExt, "asm")
1025 || !RTStrICmp(pszExt, "S")
1026 || !RTStrICmp(pszExt, "inc")
1027 || !RTStrICmp(pszExt, "sh")
1028 )
1029 return RTDBGSYMCACHEFILETYPE_IGNORE;
1030 if ( !RTStrICmp(pszName, "Makefile")
1031 || !RTStrICmp(pszName, "GNUmakefile")
1032 || !RTStrICmp(pszName, "createsymbolfiles")
1033 || !RTStrICmp(pszName, "kgmacros")
1034 )
1035 return RTDBGSYMCACHEFILETYPE_IGNORE;
1036
1037 return RTDBGSYMCACHEFILETYPE_IMAGE_FILE;
1038}
1039
1040
1041/**
1042 * Figure file type based on name, will stat the file/dir.
1043 *
1044 * @returns File type.
1045 * @param pszPath The path to the file/dir to figure.
1046 */
1047static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType(const char *pszPath)
1048{
1049 const char *pszName = RTPathFilename(pszPath);
1050
1051 /* Trailing slash. */
1052 if (!pszName)
1053 return RTDBGSYMCACHEFILETYPE_DIR;
1054
1055 /* Wildcard means listing directory and filtering. */
1056 if (strpbrk(pszName, "?*"))
1057 return RTDBGSYMCACHEFILETYPE_DIR_FILTER;
1058
1059 /* Get object info, following links. */
1060 RTFSOBJINFO ObjInfo;
1061 int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
1062 if (RT_FAILURE(rc))
1063 return RTDBGSYMCACHEFILETYPE_INVALID;
1064 return rtDbgSymCacheFigureType2(pszPath, &ObjInfo);
1065}
1066
1067
1068/**
1069 * Recursive worker for rtDbgSymCacheAddDir, for minimal stack wasting.
1070 *
1071 * @returns IPRT status code (fully bitched).
1072 * @param pszPath Pointer to a RTPATH_MAX size buffer containing
1073 * the path to the current directory ending with a
1074 * slash.
1075 * @param cchPath The size of the current directory path.
1076 * @param pDirEntry Pointer to the RTDIRENTRYEX structure to use.
1077 * @param pCfg The configuration.
1078 */
1079static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
1080{
1081 /*
1082 * Open the directory.
1083 */
1084 RTDIR hDir;
1085 int rc, rc2;
1086 if (pCfg->pszFilter)
1087 {
1088 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX - cchPath, pCfg->pszFilter);
1089 if (RT_FAILURE(rc))
1090 {
1091 pszPath[cchPath] = '\0';
1092 return RTMsgErrorRc(rc, "Filename too long (%Rrc): '%s" RTPATH_SLASH_STR "%s'", rc, pszPath, pCfg->pszFilter);
1093 }
1094 rc = RTDirOpenFiltered(&hDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
1095 }
1096 else
1097 rc = RTDirOpen(&hDir, pszPath);
1098 if (RT_FAILURE(rc))
1099 return RTMsgErrorRc(rc, "RTDirOpen%s failed on '%s': %Rrc", pCfg->pszFilter ? "Filtered" : "", pszPath, rc);
1100
1101 /*
1102 * Enumerate the files.
1103 */
1104 for (;;)
1105 {
1106 rc2 = RTDirReadEx(hDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
1107 if (RT_FAILURE(rc2))
1108 {
1109 pszPath[cchPath] = '\0';
1110 if (rc2 != VERR_NO_MORE_FILES)
1111 {
1112 RTMsgError("RTDirReadEx failed in '%s': %Rrc\n", pszPath, rc2);
1113 rc = rc2;
1114 }
1115 break;
1116 }
1117
1118 /* Skip dot and dot-dot. */
1119 if (RTDirEntryExIsStdDotLink(pDirEntry))
1120 continue;
1121
1122 /* Construct a full path. */
1123 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX, pDirEntry->szName);
1124 if (RT_FAILURE(rc))
1125 {
1126 pszPath[cchPath] = '\0';
1127 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
1128 break;
1129 }
1130
1131 switch (rtDbgSymCacheFigureType2(pszPath, &pDirEntry->Info))
1132 {
1133 case RTDBGSYMCACHEFILETYPE_DIR:
1134 if (!pCfg->fRecursive)
1135 RTMsgInfo("Skipping directory '%s'...", pszPath);
1136 else
1137 {
1138 if (cchPath + pDirEntry->cbName + 3 <= RTPATH_MAX)
1139 {
1140 pszPath[cchPath + pDirEntry->cbName] = RTPATH_SLASH;
1141 pszPath[cchPath + pDirEntry->cbName + 1] = '\0';
1142 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath + pDirEntry->cbName + 1, pDirEntry, pCfg);
1143 }
1144 else
1145 {
1146 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
1147 rc2 = VERR_FILENAME_TOO_LONG;
1148 }
1149 }
1150 break;
1151
1152 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1153 rc2 = rtDbgSymCacheAddDebugFile(pszPath, pDirEntry->szName, pCfg);
1154 break;
1155
1156 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1157 rc2 = rtDbgSymCacheAddImageFile(pszPath, pDirEntry->szName, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, pCfg);
1158 break;
1159
1160 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1161 rc2 = rtDbgSymCacheAddDebugBundle(pszPath, cchPath, pDirEntry->cbName, NULL /*pszDstName*/, pCfg);
1162 break;
1163
1164 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1165 rc2 = rtDbgSymCacheAddImageBundle(pszPath, cchPath, pDirEntry->cbName, NULL /*pszDstName*/, pDirEntry, pCfg);
1166 break;
1167
1168 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1169 case RTDBGSYMCACHEFILETYPE_INVALID:
1170 rc2 = RTMsgErrorRc(VERR_INTERNAL_ERROR_2, "Invalid: '%s'", pszPath);
1171 break;
1172
1173 case RTDBGSYMCACHEFILETYPE_IGNORE:
1174 rc2 = VINF_SUCCESS;
1175 break;
1176 }
1177
1178 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1179 rc = rc2;
1180 }
1181
1182 /*
1183 * Clean up.
1184 */
1185 rc2 = RTDirClose(hDir);
1186 if (RT_FAILURE(rc2))
1187 {
1188 RTMsgError("RTDirClose failed in '%s': %Rrc", pszPath, rc);
1189 rc = rc2;
1190 }
1191 return rc;
1192}
1193
1194
1195/**
1196 * Adds a directory.
1197 *
1198 * @returns IPRT status code (fully bitched).
1199 * @param pszPath The directory path.
1200 * @param pCfg The configuration.
1201 */
1202static int rtDbgSymCacheAddDir(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
1203{
1204 /*
1205 * Set up the path buffer, stripping any filter.
1206 */
1207 char szPath[RTPATH_MAX];
1208 int rc = RTStrCopy(szPath, sizeof(szPath) - 2, pszPath);
1209 if (RT_FAILURE(rc))
1210 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'", pszPath);
1211
1212 size_t cchPath = strlen(pszPath);
1213 if (!cchPath)
1214 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path empty: '%s'", pszPath);
1215
1216 if (pCfg->pszFilter)
1217 szPath[cchPath - strlen(pCfg->pszFilter)] = '\0';
1218 cchPath = RTPathStripTrailingSlash(szPath);
1219 if (!RTPATH_IS_SEP(pszPath[cchPath - 1]))
1220 {
1221 szPath[cchPath++] = RTPATH_SLASH;
1222 szPath[cchPath] = '\0';
1223 }
1224
1225 /*
1226 * Let the worker do the rest.
1227 */
1228 RTDIRENTRYEX DirEntry;
1229 return rtDbgSymCacheAddDirWorker(szPath, cchPath, &DirEntry, pCfg);
1230}
1231
1232
1233/**
1234 * Adds a file or directory.
1235 *
1236 * @returns Program exit code.
1237 * @param pszPath The user supplied path to the file or directory.
1238 * @param pszCache The path to the cache.
1239 * @param fRecursive Whether to process directories recursively.
1240 * @param fOverwriteOnConflict Whether to overwrite existing cache entry on
1241 * conflict, or just leave it.
1242 */
1243static RTEXITCODE rtDbgSymCacheAddFileOrDir(const char *pszPath, const char *pszCache, bool fRecursive,
1244 bool fOverwriteOnConflict)
1245{
1246 RT_NOREF1(fOverwriteOnConflict);
1247 RTDBGSYMCACHEADDCFG Cfg;
1248 Cfg.fRecursive = fRecursive;
1249 Cfg.pszCache = pszCache;
1250 Cfg.pszFilter = NULL;
1251
1252 /* If the filename contains an equal ('=') char, treat the left as the file
1253 to add tne right part as the name to add it under (handy for kernels). */
1254 char *pszFree = NULL;
1255 const char *pszDstName = RTPathFilename(pszPath);
1256 const char *pszEqual = pszDstName ? strchr(pszDstName, '=') : NULL;
1257 if (pszEqual)
1258 {
1259 pszPath = pszFree = RTStrDupN(pszPath, pszEqual - pszPath);
1260 if (!pszFree)
1261 return RTMsgErrorExitFailure("out of memory!\n");
1262 pszDstName = pszEqual + 1;
1263 if (!*pszDstName)
1264 return RTMsgErrorExitFailure("add-as filename is empty!\n");
1265 }
1266
1267 int rc;
1268 RTDBGSYMCACHEFILETYPE enmType = rtDbgSymCacheFigureType(pszPath);
1269 switch (enmType)
1270 {
1271 default:
1272 case RTDBGSYMCACHEFILETYPE_INVALID:
1273 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid: '%s'", pszPath);
1274 break;
1275
1276 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1277 Cfg.pszFilter = RTPathFilename(pszPath);
1278 RT_FALL_THRU();
1279 case RTDBGSYMCACHEFILETYPE_DIR:
1280 if (!pszEqual)
1281 rc = rtDbgSymCacheAddDir(pszPath, &Cfg);
1282 else
1283 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Add-as filename is not applicable to directories!");
1284 break;
1285
1286 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1287 rc = rtDbgSymCacheAddDebugFile(pszPath, pszDstName, &Cfg);
1288 break;
1289
1290 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1291 rc = rtDbgSymCacheAddImageFile(pszPath, pszDstName, NULL /*pszExtraSuff*/, RTDBG_CACHE_UUID_MAP_DIR_IMAGES, &Cfg);
1292 break;
1293
1294 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1295 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1296 {
1297 size_t cchPath = strlen(pszPath);
1298 size_t cchFilename = strlen(RTPathFilename(pszPath));
1299 char szPathBuf[RTPATH_MAX];
1300 if (cchPath < sizeof(szPathBuf))
1301 {
1302 memcpy(szPathBuf, pszPath, cchPath + 1);
1303 if (enmType == RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE)
1304 rc = rtDbgSymCacheAddDebugBundle(szPathBuf, cchPath - cchFilename, cchFilename,
1305 pszEqual ? pszDstName : NULL, &Cfg);
1306 else
1307 {
1308 RTDIRENTRYEX DirEntry;
1309 rc = rtDbgSymCacheAddImageBundle(szPathBuf, cchPath - cchFilename, cchFilename,
1310 pszEqual ? pszDstName : NULL, &DirEntry, &Cfg);
1311 }
1312 }
1313 else
1314 rc = RTMsgErrorRc(VERR_FILENAME_TOO_LONG, "Filename too long: '%s'", pszPath);
1315 break;
1316 }
1317
1318 case RTDBGSYMCACHEFILETYPE_IGNORE:
1319 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid file: '%s'", pszPath);
1320 break;
1321 }
1322
1323 RTStrFree(pszFree);
1324 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1325}
1326
1327
1328/**
1329 * Handles the 'add' command.
1330 *
1331 * @returns Program exit code.
1332 * @param pszArg0 The program name.
1333 * @param cArgs The number of arguments to the 'add' command.
1334 * @param papszArgs The argument vector, starting after 'add'.
1335 */
1336static RTEXITCODE rtDbgSymCacheCmdAdd(const char *pszArg0, int cArgs, char **papszArgs)
1337{
1338 /*
1339 * Parse the command line.
1340 */
1341 static RTGETOPTDEF const s_aOptions[] =
1342 {
1343 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1344 { "--no-recursive", 'n', RTGETOPT_REQ_NOTHING },
1345 { "--overwrite-on-conflict", 'o', RTGETOPT_REQ_NOTHING },
1346 };
1347
1348 const char *pszCache = NULL;
1349 bool fRecursive = false;
1350 bool fOverwriteOnConflict = false;
1351
1352 RTGETOPTSTATE State;
1353 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1354 if (RT_FAILURE(rc))
1355 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
1356
1357 //uint32_t cAdded = 0;
1358 RTGETOPTUNION ValueUnion;
1359 int chOpt;
1360 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
1361 {
1362 switch (chOpt)
1363 {
1364 case 'R':
1365 fRecursive = true;
1366 break;
1367
1368 case 'n':
1369 fRecursive = false;
1370 break;
1371
1372 case 'o':
1373 fOverwriteOnConflict = true;
1374 break;
1375
1376 case VINF_GETOPT_NOT_OPTION:
1377 /* The first non-option is a cache directory. */
1378 if (!pszCache)
1379 {
1380 pszCache = ValueUnion.psz;
1381 if (!RTPathExists(pszCache))
1382 {
1383 rc = RTDirCreate(pszCache, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1384 if (RT_FAILURE(rc))
1385 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Error creating cache directory '%s': %Rrc", pszCache, rc);
1386 }
1387 else if (!RTDirExists(pszCache))
1388 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specified cache directory is not a directory: '%s'", pszCache);
1389 }
1390 /* Subsequent non-options are files to be added to the cache. */
1391 else
1392 {
1393 RTEXITCODE rcExit = rtDbgSymCacheAddFileOrDir(ValueUnion.psz, pszCache, fRecursive, fOverwriteOnConflict);
1394 if (rcExit != RTEXITCODE_FAILURE)
1395 return rcExit;
1396 }
1397 break;
1398
1399 case 'h':
1400 return rtDbgSymCacheUsage(pszArg0, "add");
1401 case 'V':
1402 return rtDbgSymCacheVersion();
1403 default:
1404 return RTGetOptPrintError(chOpt, &ValueUnion);
1405 }
1406 }
1407
1408 if (!pszCache)
1409 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No cache directory or files to add were specified.");
1410 return RTEXITCODE_SUCCESS;
1411}
1412
1413
1414/**
1415 * Debug info + external path for the 'get' command.
1416 */
1417typedef struct MYDBGINFO
1418{
1419 /** The kind of debug info. */
1420 RTLDRDBGINFOTYPE enmType;
1421 /** The CRC32 of the external file (RTLDRDBGINFOTYPE_DWARF_DWO). */
1422 uint32_t uDwoCrc32;
1423 /** The PE image size (RTLDRDBGINFOTYPE_CODEVIEW_DBG,
1424 * RTLDRDBGINFOTYPE_CODEVIEW_PDB20, RTLDRDBGINFOTYPE_CODEVIEW_PDB70 (,
1425 * RTLDRDBGINFOTYPE_CODEVIEW, RTLDRDBGINFOTYPE_COFF)). */
1426 uint32_t cbImage;
1427 /** Timestamp in seconds since unix epoch (RTLDRDBGINFOTYPE_CODEVIEW_DBG,
1428 * RTLDRDBGINFOTYPE_CODEVIEW_PDB20 (, RTLDRDBGINFOTYPE_CODEVIEW,
1429 * RTLDRDBGINFOTYPE_COFF)). */
1430 uint32_t uTimestamp;
1431 /** The PDB age (RTLDRDBGINFOTYPE_CODEVIEW_PDB20, RTLDRDBGINFOTYPE_CODEVIEW_PDB70). */
1432 uint32_t uPdbAge;
1433 /** The UUID of the PDB or mach-o image (RTLDRDBGINFOTYPE_CODEVIEW_PDB70, +). */
1434 RTUUID Uuid;
1435 /** External path (can be empty). */
1436 char szExtFile[RTPATH_MAX];
1437} MYDBGINFO;
1438
1439/**
1440 * @callback_method_impl{FNRTLDRENUMDBG, For the 'get' command.}
1441 */
1442static DECLCALLBACK(int) rtDbgSymCacheCmdGetForExeDbgInfoCallback(RTLDRMOD hLdrMod, PCRTLDRDBGINFO pDbgInfo, void *pvUser)
1443{
1444 RT_NOREF(hLdrMod);
1445 if (!pDbgInfo->pszExtFile)
1446 switch (pDbgInfo->enmType)
1447 {
1448 case RTLDRDBGINFOTYPE_CODEVIEW_PDB20:
1449 case RTLDRDBGINFOTYPE_CODEVIEW_PDB70:
1450 case RTLDRDBGINFOTYPE_CODEVIEW_DBG:
1451 break;
1452 default:
1453 return VINF_SUCCESS;
1454 }
1455
1456 /* Copy the info: */
1457 MYDBGINFO *pMyInfo = (MYDBGINFO *)pvUser;
1458 RT_ZERO(*pMyInfo);
1459 pMyInfo->enmType = pDbgInfo->enmType;
1460 int rc = VINF_SUCCESS;
1461 if (pDbgInfo->pszExtFile)
1462 rc = RTStrCopy(pMyInfo->szExtFile, sizeof(pMyInfo->szExtFile), pDbgInfo->pszExtFile);
1463
1464 switch (pDbgInfo->enmType)
1465 {
1466 case RTLDRDBGINFOTYPE_DWARF_DWO:
1467 pMyInfo->uDwoCrc32 = pDbgInfo->u.Dwo.uCrc32;
1468 break;
1469
1470 case RTLDRDBGINFOTYPE_CODEVIEW:
1471 case RTLDRDBGINFOTYPE_COFF:
1472 pMyInfo->cbImage = pDbgInfo->u.Cv.cbImage;
1473 pMyInfo->uTimestamp = pDbgInfo->u.Cv.uTimestamp;
1474 break;
1475
1476 case RTLDRDBGINFOTYPE_CODEVIEW_DBG:
1477 pMyInfo->cbImage = pDbgInfo->u.Dbg.cbImage;
1478 pMyInfo->uTimestamp = pDbgInfo->u.Dbg.uTimestamp;
1479 break;
1480
1481 case RTLDRDBGINFOTYPE_CODEVIEW_PDB20:
1482 pMyInfo->cbImage = pDbgInfo->u.Pdb20.cbImage;
1483 pMyInfo->uTimestamp = pDbgInfo->u.Pdb20.uTimestamp;
1484 pMyInfo->uPdbAge = pDbgInfo->u.Pdb20.uAge;
1485 break;
1486
1487 case RTLDRDBGINFOTYPE_CODEVIEW_PDB70:
1488 pMyInfo->cbImage = pDbgInfo->u.Pdb70.cbImage;
1489 pMyInfo->Uuid = pDbgInfo->u.Pdb70.Uuid;
1490 pMyInfo->uPdbAge = pDbgInfo->u.Pdb70.uAge;
1491 break;
1492
1493 default:
1494 return VINF_SUCCESS;
1495 }
1496
1497 return rc;
1498}
1499
1500
1501/**
1502 * @callback_method_impl{FNRTDBGCFGOPEN}
1503 */
1504static DECLCALLBACK(int) rtDbgSymCacheCmdGetCallback(RTDBGCFG hDbgCfg, const char *pszFilename, void *pvUser1, void *pvUser2)
1505{
1506 RT_NOREF(hDbgCfg, pvUser2);
1507
1508 char *pszJoined = NULL;
1509 const char *pszOutput = (const char *)pvUser1;
1510 if (!pszOutput || *pszOutput == '\0')
1511 pszOutput = RTPathFilename(pszFilename);
1512 else if (RTPathFilename(pszOutput) == NULL)
1513 pszOutput = pszJoined = RTPathJoinA(pszOutput, RTPathFilename(pszFilename));
1514
1515 if (g_iLogLevel > 0) // --pe --name wintypes.dll --image-size 1388544 --timestamp 0x57F8D9F0
1516 RTMsgInfo("Copying '%s' to '%s...", pszFilename, pszOutput);
1517 int rc = RTFileCopy(pszFilename, pszOutput);
1518 if (RT_FAILURE(rc))
1519 {
1520 if (rc == VERR_ALREADY_EXISTS)
1521 {
1522 rc = RTFileCompare(pszFilename, pszOutput);
1523 if (RT_SUCCESS(rc))
1524 RTMsgInfo("Output '%s' exists and matches '%s'.", pszOutput, pszFilename);
1525 else
1526 RTMsgError("Output '%s' already exists (does not match '%s')", pszOutput, pszFilename);
1527 }
1528 else
1529 RTMsgError("Copying '%s' to '%s failed: %Rrc", pszFilename, pszOutput, rc);
1530 }
1531 RTStrFree(pszJoined);
1532 if (RT_SUCCESS(rc))
1533 return VINF_CALLBACK_RETURN;
1534 return rc;
1535}
1536
1537
1538/**
1539 * Handles the 'get' command.
1540 *
1541 * @returns Program exit code.
1542 * @param pszArg0 The program name.
1543 * @param cArgs The number of arguments to the 'add' command.
1544 * @param papszArgs The argument vector, starting after 'add'.
1545 */
1546static RTEXITCODE rtDbgSymCacheCmdGet(const char *pszArg0, int cArgs, char **papszArgs)
1547{
1548 RTERRINFOSTATIC ErrInfo;
1549
1550 /*
1551 * Parse the command line.
1552 */
1553 static RTGETOPTDEF const s_aOptions[] =
1554 {
1555 { "--output", 'o', RTGETOPT_REQ_STRING },
1556
1557 /* Query: */
1558 { "--for-exe", 'e', RTGETOPT_REQ_STRING },
1559 { "--for-executable", 'e', RTGETOPT_REQ_STRING },
1560 { "--uuid", 'u', RTGETOPT_REQ_UUID },
1561 { "--ts", 't', RTGETOPT_REQ_UINT32 },
1562 { "--timestamp", 't', RTGETOPT_REQ_UINT32 },
1563 { "--size", 'z', RTGETOPT_REQ_UINT32 },
1564 { "--image-size", 'z', RTGETOPT_REQ_UINT32 },
1565 { "--pdb-age", 'a', RTGETOPT_REQ_UINT32 },
1566 { "--dwo-crc32", 'c', RTGETOPT_REQ_UINT32 },
1567 { "--name", 'n', RTGETOPT_REQ_STRING },
1568
1569 { "--dwo", 'd', RTGETOPT_REQ_NOTHING },
1570 { "--dwarf", 'd', RTGETOPT_REQ_NOTHING },
1571 { "--dwarf-external", 'd', RTGETOPT_REQ_NOTHING },
1572 { "--dsym", 'D', RTGETOPT_REQ_NOTHING },
1573 { "--dbg", '0', RTGETOPT_REQ_NOTHING },
1574 { "--pdb20", '2', RTGETOPT_REQ_NOTHING },
1575 { "--pdb70", '7', RTGETOPT_REQ_NOTHING },
1576
1577 { "--pe", 'P', RTGETOPT_REQ_NOTHING },
1578 { "--macho", 'M', RTGETOPT_REQ_NOTHING },
1579 { "--elf", 'E', RTGETOPT_REQ_NOTHING },
1580
1581 /* RTDbgCfg: */
1582 { "--env-prefix", 'p', RTGETOPT_REQ_STRING },
1583 { "--sym-path", 's', RTGETOPT_REQ_STRING },
1584 { "--use-native-paths", 1000, RTGETOPT_REQ_NOTHING },
1585 { "--no-native-paths", 1001, RTGETOPT_REQ_NOTHING },
1586 };
1587
1588 RTGETOPTSTATE State;
1589 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1590 if (RT_FAILURE(rc))
1591 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
1592
1593 const char *pszOutput = NULL;
1594
1595 bool fGetExeImage = true;
1596 const char *pszForExec = NULL;
1597 const char *pszName = NULL;
1598 RTLDRARCH enmImageArch = RTLDRARCH_WHATEVER;
1599 RTLDRFMT enmImageFmt = RTLDRFMT_INVALID;
1600 MYDBGINFO DbgInfo;
1601 RT_ZERO(DbgInfo);
1602
1603 const char *pszEnvPrefix = "IPRT_";
1604 bool fNativePaths = true;
1605 const char *apszSymPaths[12] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
1606 unsigned cSymPaths = 0;
1607
1608 RTGETOPTUNION ValueUnion;
1609 int chOpt;
1610 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
1611 {
1612 switch (chOpt)
1613 {
1614 case 'o':
1615 pszOutput = ValueUnion.psz;
1616 break;
1617
1618 /*
1619 * Query elements:
1620 */
1621 case 'z':
1622 DbgInfo.cbImage = ValueUnion.u32;
1623 break;
1624
1625 case 't':
1626 DbgInfo.uTimestamp = ValueUnion.u32;
1627 enmImageFmt = RTLDRFMT_PE;
1628 break;
1629
1630 case 'u':
1631 DbgInfo.Uuid = ValueUnion.Uuid;
1632 enmImageFmt = RTLDRFMT_MACHO;
1633 break;
1634
1635 case 'a':
1636 DbgInfo.uPdbAge = ValueUnion.u32;
1637 if (DbgInfo.enmType != RTLDRDBGINFOTYPE_CODEVIEW_PDB20)
1638 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
1639 break;
1640
1641 case 'c':
1642 DbgInfo.uDwoCrc32 = ValueUnion.u32;
1643 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF_DWO;
1644 break;
1645
1646 case 'n':
1647 pszName = ValueUnion.psz;
1648 DbgInfo.szExtFile[0] = '\0';
1649 break;
1650
1651 case 'd':
1652 fGetExeImage = false;
1653 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF_DWO;
1654 break;
1655
1656 case 'D':
1657 fGetExeImage = false;
1658 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF; /* == dSYM */
1659 break;
1660
1661 case '0':
1662 fGetExeImage = false;
1663 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG;
1664 break;
1665
1666 case '2':
1667 fGetExeImage = false;
1668 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20;
1669 break;
1670
1671 case '7':
1672 fGetExeImage = false;
1673 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
1674 break;
1675
1676 case 'E':
1677 fGetExeImage = true;
1678 enmImageFmt = RTLDRFMT_ELF;
1679 break;
1680
1681 case 'M':
1682 fGetExeImage = true;
1683 enmImageFmt = RTLDRFMT_MACHO;
1684 break;
1685
1686 case 'P':
1687 fGetExeImage = true;
1688 enmImageFmt = RTLDRFMT_PE;
1689 break;
1690
1691 case 'e':
1692 {
1693 /* Open the executable and retrieve the query parameters from it: */
1694 fGetExeImage = false;
1695 pszForExec = ValueUnion.psz;
1696 if (!pszName)
1697 pszName = RTPathFilename(pszForExec);
1698
1699 RTLDRMOD hLdrMod;
1700 rc = RTLdrOpenEx(pszForExec, RTLDR_O_FOR_DEBUG, enmImageArch, &hLdrMod, RTErrInfoInitStatic(&ErrInfo));
1701 if (RT_FAILURE(rc))
1702 return RTMsgErrorExitFailure("Failed to open image '%s': %Rrc%#RTeim", pszForExec, rc, &ErrInfo);
1703
1704 DbgInfo.cbImage = (uint32_t)RTLdrSize(hLdrMod);
1705 enmImageFmt = RTLdrGetFormat(hLdrMod);
1706 if (enmImageFmt == RTLDRFMT_MACHO)
1707 {
1708 DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF; /* .dSYM */
1709 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_UUID, &DbgInfo.Uuid, sizeof(DbgInfo.Uuid));
1710 if (RT_FAILURE(rc))
1711 RTMsgError("Failed to query image UUID from '%s': %Rrc", pszForExec, rc);
1712 }
1713 else
1714 {
1715 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &DbgInfo.uTimestamp, sizeof(DbgInfo.uTimestamp));
1716 if (RT_SUCCESS(rc) || (rc == VERR_NOT_FOUND && enmImageFmt != RTLDRFMT_PE))
1717 {
1718 RT_ZERO(DbgInfo);
1719 rc = RTLdrEnumDbgInfo(hLdrMod, NULL, rtDbgSymCacheCmdGetForExeDbgInfoCallback, &DbgInfo);
1720 if (RT_FAILURE(rc))
1721 RTMsgError("RTLdrEnumDbgInfo failed on '%s': %Rrc", pszForExec, rc);
1722 }
1723 else if (RT_FAILURE(rc))
1724 RTMsgError("Failed to query image timestamp from '%s': %Rrc", pszForExec, rc);
1725 }
1726
1727 RTLdrClose(hLdrMod);
1728 if (RT_FAILURE(rc))
1729 return RTEXITCODE_FAILURE;
1730 break;
1731 }
1732
1733 /*
1734 * RTDbgCfg setup:
1735 */
1736 case 'p':
1737 pszEnvPrefix = ValueUnion.psz;
1738 break;
1739
1740 case 's':
1741 if (cSymPaths < RT_ELEMENTS(apszSymPaths))
1742 apszSymPaths[cSymPaths++] = ValueUnion.psz;
1743 else
1744 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --sym-paths arguments: max %u", RT_ELEMENTS(apszSymPaths));
1745 break;
1746
1747 case 1000:
1748 fNativePaths = true;
1749 break;
1750
1751 case 1001:
1752 fNativePaths = false;
1753 break;
1754
1755 case 'h':
1756 return rtDbgSymCacheUsage(pszArg0, "get");
1757 case 'V':
1758 return rtDbgSymCacheVersion();
1759 default:
1760 return RTGetOptPrintError(chOpt, &ValueUnion);
1761 }
1762 }
1763
1764 /*
1765 * Instantiate the debug config we'll be querying.
1766 */
1767 RTDBGCFG hDbgCfg;
1768 rc = RTDbgCfgCreate(&hDbgCfg, pszEnvPrefix, fNativePaths);
1769 if (RT_FAILURE(rc))
1770 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgCfgCreate failed: %Rrc", rc);
1771
1772 rc = RTDbgCfgSetLogCallback(hDbgCfg, rtDbgSymCacheLogCallback, NULL);
1773 AssertRCStmt(rc, RTMsgError("RTDbgCfgSetLogCallback failed: %Rrc", rc));
1774
1775 for (unsigned i = 0; i < cSymPaths && RT_SUCCESS(rc); i++)
1776 {
1777 rc = RTDbgCfgChangeString(hDbgCfg, RTDBGCFGPROP_PATH, RTDBGCFGOP_APPEND, apszSymPaths[i]);
1778 if (RT_FAILURE(rc))
1779 RTMsgError("Failed to append symbol path '%s': %Rrc", apszSymPaths[i], rc);
1780 }
1781 if (RT_SUCCESS(rc))
1782 {
1783 /*
1784 * Do the getting.
1785 */
1786 if (fGetExeImage)
1787 {
1788 if (enmImageFmt == RTLDRFMT_INVALID)
1789 {
1790 if (!RTUuidIsNull(&DbgInfo.Uuid))
1791 enmImageFmt = RTLDRFMT_MACHO;
1792 else if (DbgInfo.cbImage && DbgInfo.uTimestamp)
1793 enmImageFmt = RTLDRFMT_PE;
1794 else
1795 rc = RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "Not enough to go on to find executable");
1796 }
1797 if (enmImageFmt == RTLDRFMT_PE)
1798 rc = RTDbgCfgOpenPeImage(hDbgCfg, pszName, DbgInfo.cbImage, DbgInfo.uTimestamp,
1799 rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1800 else if (enmImageFmt == RTLDRFMT_MACHO)
1801 rc = RTDbgCfgOpenMachOImage(hDbgCfg, pszName, &DbgInfo.Uuid,
1802 rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1803 else if (enmImageFmt != RTLDRFMT_INVALID)
1804 rc = RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "Format not implemented: %s", RTLdrGetFormat);
1805 }
1806 else if (DbgInfo.enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB70)
1807 rc = RTDbgCfgOpenPdb70(hDbgCfg, DbgInfo.szExtFile[0] ? DbgInfo.szExtFile : pszName, &DbgInfo.Uuid, DbgInfo.uPdbAge,
1808 rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1809 else if (DbgInfo.enmType == RTLDRDBGINFOTYPE_CODEVIEW_PDB20)
1810 rc = RTDbgCfgOpenPdb20(hDbgCfg, DbgInfo.szExtFile[0] ? DbgInfo.szExtFile : pszName, DbgInfo.cbImage,
1811 DbgInfo.uTimestamp, DbgInfo.uPdbAge, rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1812 else if (DbgInfo.enmType == RTLDRDBGINFOTYPE_CODEVIEW_DBG)
1813 rc = RTDbgCfgOpenDbg(hDbgCfg, DbgInfo.szExtFile[0] ? DbgInfo.szExtFile : pszName, DbgInfo.cbImage,
1814 DbgInfo.uTimestamp, rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1815 else if (DbgInfo.enmType == RTLDRDBGINFOTYPE_DWARF_DWO)
1816 rc = RTDbgCfgOpenDwo(hDbgCfg, DbgInfo.szExtFile[0] ? DbgInfo.szExtFile : pszName, DbgInfo.uDwoCrc32,
1817 rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1818 else if (DbgInfo.enmType == RTLDRDBGINFOTYPE_DWARF)
1819 rc = RTDbgCfgOpenDsymBundle(hDbgCfg, DbgInfo.szExtFile[0] ? DbgInfo.szExtFile : pszName, &DbgInfo.Uuid,
1820 rtDbgSymCacheCmdGetCallback, (void *)pszOutput, NULL);
1821 else
1822 rc = RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "Format not implemented");
1823 }
1824
1825 RTDbgCfgRelease(hDbgCfg);
1826 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1827}
1828
1829
1830int main(int argc, char **argv)
1831{
1832 int rc = RTR3InitExe(argc, &argv, 0);
1833 if (RT_FAILURE(rc))
1834 return RTMsgInitFailure(rc);
1835
1836 /*
1837 * Switch on the command.
1838 */
1839 RTEXITCODE rcExit = RTEXITCODE_SYNTAX;
1840 if (argc < 2)
1841 rtDbgSymCacheUsage(argv[0], NULL);
1842 else if (!strcmp(argv[1], "add"))
1843 rcExit = rtDbgSymCacheCmdAdd(argv[0], argc - 2, argv + 2);
1844 else if (!strcmp(argv[1], "get"))
1845 rcExit = rtDbgSymCacheCmdGet(argv[0], argc - 2, argv + 2);
1846 else if ( !strcmp(argv[1], "-h")
1847 || !strcmp(argv[1], "-?")
1848 || !strcmp(argv[1], "--help"))
1849 rcExit = rtDbgSymCacheUsage(argv[0], NULL);
1850 else if ( !strcmp(argv[1], "-V")
1851 || !strcmp(argv[1], "--version"))
1852 rcExit = rtDbgSymCacheVersion();
1853 else
1854 RTMsgError("Unknown command: '%s'", argv[1]);
1855
1856 return rcExit;
1857}
1858
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