VirtualBox

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

Last change on this file since 85561 was 83167, checked in by vboxsync, 5 years ago

RTDbgSymCache: Added a 'get' command for getting image and debug files from the cache - can also download stuff.

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