VirtualBox

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

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

Darwin guest OS digger hacking in progress. Adding symbol cache util to iprt and started on the Mach-O code that'll make use of it (RTDbgModCreateFromMachOImage++). Updates kStuff from 53 to 55 for UUID query and 64-bit kext loading.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.2 KB
Line 
1/* $Id: RTDbgSymCache.cpp 49044 2013-10-11 01:06:28Z vboxsync $ */
2/** @file
3 * IPRT - Debug Symbol Cache Utility.
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/zip.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/ldr.h>
38#include <iprt/message.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/vfs.h>
44#include <iprt/zip.h>
45#include <iprt/uuid.h>
46#include <iprt/formats/mach-o.h>
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Cache file type.
54 */
55typedef enum RTDBGSYMCACHEFILETYPE
56{
57 RTDBGSYMCACHEFILETYPE_INVALID,
58 RTDBGSYMCACHEFILETYPE_DIR,
59 RTDBGSYMCACHEFILETYPE_DIR_FILTER,
60 RTDBGSYMCACHEFILETYPE_DEBUG_FILE,
61 RTDBGSYMCACHEFILETYPE_IMAGE_FILE,
62 RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE,
63 RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE,
64 RTDBGSYMCACHEFILETYPE_IGNORE
65} RTDBGSYMCACHEFILETYPE;
66
67
68/**
69 * Configuration for the 'add' command.
70 */
71typedef struct RTDBGSYMCACHEADDCFG
72{
73 bool fRecursive;
74 const char *pszFilter;
75 const char *pszCache;
76} RTDBGSYMCACHEADDCFG;
77/** Pointer to a read only 'add' config. */
78typedef RTDBGSYMCACHEADDCFG const *PCRTDBGSYMCACHEADDCFG;
79
80
81/*******************************************************************************
82* Global Variables *
83*******************************************************************************/
84/** Bundle suffixes. */
85static const char * const g_apszBundleSuffixes[] =
86{
87 ".kext",
88 ".app",
89 ".framework", /** @todo framework is different. */
90 ".component",
91 ".action",
92 ".caction",
93 ".bundle",
94 ".sourcebundle",
95 ".plugin",
96 ".ppp",
97 ".menu",
98 ".monitorpanel",
99 ".scripting",
100 ".prefPane",
101 ".qlgenerator",
102 ".brailledriver",
103 ".saver",
104 ".SpeechVoice",
105 ".SpeechRecognizer",
106 ".SpeechSynthesizer",
107 ".mdimporter",
108 ".spreporter",
109 ".xpc",
110 NULL
111};
112
113/** Debug bundle suffixes. (Same as above + .dSYM) */
114static const char * const g_apszDSymBundleSuffixes[] =
115{
116 ".kext.dSYM",
117 ".app.dSYM",
118 ".framework.dSYM",
119 ".component.dSYM",
120 ".action.dSYM",
121 ".caction.dSYM",
122 ".bundle.dSYM",
123 ".sourcebundle.dSYM",
124 ".menu.dSYM",
125 ".plugin.dSYM",
126 ".ppp.dSYM",
127 ".monitorpanel.dSYM",
128 ".scripting.dSYM",
129 ".prefPane.dSYM",
130 ".qlgenerator.dSYM",
131 ".brailledriver.dSYM",
132 ".saver.dSYM",
133 ".SpeechVoice.dSYM",
134 ".SpeechRecognizer.dSYM",
135 ".SpeechSynthesizer.dSYM",
136 ".mdimporter.dSYM",
137 ".spreporter.dSYM",
138 ".xpc.dSYM",
139 ".dSYM",
140 NULL
141};
142
143
144
145/**
146 * Display the version of the cache program.
147 *
148 * @returns exit code.
149 */
150static RTEXITCODE rtDbgSymCacheVersion(void)
151{
152 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
153 return RTEXITCODE_SUCCESS;
154}
155
156
157/**
158 * Shows the usage of the cache program.
159 *
160 * @returns Exit code.
161 * @param pszArg0 Program name.
162 * @param pszCommand Command selector, NULL if all.
163 */
164static RTEXITCODE rtDbgSymCacheUsage(const char *pszArg0, const char *pszCommand)
165{
166 if (!pszCommand || !strcmp(pszCommand, "add"))
167 RTPrintf("Usage: %s add [-rn] <cache-root-dir> <file1> [fileN..]\n", pszArg0);
168 return RTEXITCODE_SUCCESS;
169}
170
171
172/**
173 * Creates a UUID mapping for the file.
174 *
175 * @returns IPRT status code.
176 * @param pszCacheFile The path to the file in the cache.
177 * @param pFileUuid The UUID of the file.
178 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
179 * wanted, otherwise NULL.
180 * @param pCfg The configuration.
181 */
182static int rtDbgSymCacheAddCreateUuidMapping(const char *pszCacheFile, PRTUUID pFileUuid,
183 const char *pszUuidMapDir, PCRTDBGSYMCACHEADDCFG pCfg)
184{
185 /*
186 * Create the UUID map entry first, deep.
187 */
188 char szMapPath[RTPATH_MAX];
189 int rc = RTPathJoin(szMapPath, sizeof(szMapPath) - sizeof("/xxxx/yyyy/xxxx/yyyy/xxxx/zzzzzzzzzzzz") + 1,
190 pCfg->pszCache, pszUuidMapDir);
191 if (RT_FAILURE(rc))
192 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTPathJoin): %Rrc", rc);
193
194 size_t cch = strlen(szMapPath);
195 szMapPath[cch] = '-';
196
197 rc = RTUuidToStr(pFileUuid, &szMapPath[cch + 2], sizeof(szMapPath) - cch);
198 if (RT_FAILURE(rc))
199 return RTMsgErrorRc(rc, "Error constructing UUID map path (RTUuidToStr): %Rrc", rc);
200
201 /* Uppercase the whole lot. */
202 RTStrToUpper(&szMapPath[cch + 2]);
203
204 /* Split the first dword in two. */
205 szMapPath[cch + 1] = szMapPath[cch + 2];
206 szMapPath[cch + 2] = szMapPath[cch + 3];
207 szMapPath[cch + 3] = szMapPath[cch + 4];
208 szMapPath[cch + 4] = szMapPath[cch + 5];
209 szMapPath[cch + 5] = '-';
210
211 /*
212 * Create the directories in the path.
213 */
214 char chSaved = RTPATH_SLASH;
215 for (unsigned i = 0; i < 6; i++, cch += 5)
216 {
217 Assert(szMapPath[cch] == '-');
218 szMapPath[cch] = '\0';
219 if (!RTDirExists(szMapPath))
220 {
221 rc = RTDirCreate(szMapPath, 0755, 0/*fFlags*/);
222 if (RT_FAILURE(rc))
223 return RTMsgErrorRc(rc, "RTDirCreate failed on '%s' (UUID map path): %Rrc", szMapPath, rc);
224 }
225 szMapPath[cch] = RTPATH_SLASH;
226 }
227 cch -= 5;
228
229 /*
230 * Calculate a relative path from there to the actual file.
231 */
232 char szLinkTarget[RTPATH_MAX];
233 //szMapPath[cch] = '\0';
234 rc = RTPathCalcRelative(szLinkTarget, sizeof(szLinkTarget), szMapPath, pszCacheFile);
235 //szMapPath[cch] = RTPATH_SLASH;
236 if (RT_FAILURE(rc))
237 return RTMsgErrorRc(rc, "Failed to calculate relative path from '%s' to '%s': %Rrc", szMapPath, pszCacheFile, rc);
238
239 /*
240 * If there is already a link there, check if it matches or whether
241 * perhaps it's target doesn't exist.
242 */
243 RTFSOBJINFO ObjInfo;
244 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
245 if (RT_SUCCESS(rc))
246 {
247 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
248 {
249 rc = RTPathQueryInfoEx(szMapPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
250 if (RT_SUCCESS(rc))
251 {
252 char *pszCurTarget = NULL;
253 rc = RTSymlinkReadA(szMapPath, &pszCurTarget);
254 if (RT_FAILURE(rc))
255 return RTMsgErrorRc(rc, "UUID map: failed to read existing symlink '%s': %Rrc", szMapPath, rc);
256 if (RTPathCompare(pszCurTarget, szLinkTarget) == 0)
257 RTMsgInfo("UUID map: existing link '%s' has the same target ('%s').", szMapPath, pszCurTarget);
258 else
259 {
260 RTMsgError("UUID map: Existing mapping '%s' pointing to '%s' insted of '%s'",
261 szMapPath, pszCurTarget, szLinkTarget);
262 rc = VERR_ALREADY_EXISTS;
263 }
264 RTStrFree(pszCurTarget);
265 return rc;
266 }
267 else
268 RTMsgInfo("UUID map: replacing dangling link '%s'", szMapPath);
269 RTSymlinkDelete(szMapPath, 0 /*fFlags*/);
270 }
271 else if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
272 return RTMsgErrorRc(VERR_IS_A_FILE,
273 "UUID map: found file at '%s', expect symbolic link or nothing.", szMapPath);
274 else if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
275 return RTMsgErrorRc(VERR_IS_A_DIRECTORY,
276 "UUID map: found directory at '%s', expect symbolic link or nothing.", szMapPath);
277 else
278 return RTMsgErrorRc(VERR_NOT_SYMLINK,
279 "UUID map: Expected symbolic link or nothing at '%s', found: fMode=%#x",
280 szMapPath, ObjInfo.Attr.fMode);
281 }
282
283 /*
284 * Create the symbolic link.
285 */
286 rc = RTSymlinkCreate(szMapPath, szLinkTarget, RTSYMLINKTYPE_FILE, 0);
287 if (RT_FAILURE(rc))
288 return RTMsgErrorRc(rc, "Failed to create UUID map symlink '%s' to '%s': %Rrc", szMapPath, szLinkTarget, rc);
289 RTMsgInfo("UUID map: %s => %s", szMapPath, szLinkTarget);
290 return VINF_SUCCESS;
291}
292
293
294/**
295 * Adds a file to the cache.
296 *
297 * @returns IPRT status code.
298 * @param pszSrcPath Path to the source file.
299 * @param pszDstName The name of the destionation file (no path stuff).
300 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
301 * @param pszDstSubDir The subdirectory to file it under. This is the
302 * stringification of a relatively unique identifier of
303 * the file in question.
304 * @param pAddToUuidMap Optional file UUID that is used to create a UUID map
305 * entry.
306 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
307 * wanted, otherwise NULL.
308 * @param pCfg The configuration.
309 */
310static int rtDbgSymCacheAddOneFile(const char *pszSrcPath, const char *pszDstName, const char *pszExtraStuff,
311 const char *pszDstSubDir, PRTUUID pAddToUuidMap, const char *pszUuidMapDir,
312 PCRTDBGSYMCACHEADDCFG pCfg)
313{
314 /*
315 * Build and create the destination path, step by step.
316 */
317 char szDstPath[RTPATH_MAX];
318 int rc = RTPathJoin(szDstPath, sizeof(szDstPath), pCfg->pszCache, pszDstName);
319 if (RT_FAILURE(rc))
320 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
321
322 if (!RTDirExists(szDstPath))
323 {
324 rc = RTDirCreate(szDstPath, 0755, 0 /*fFlags*/);
325 if (RT_FAILURE(rc))
326 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
327 }
328
329 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstSubDir);
330 if (RT_FAILURE(rc))
331 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
332
333 if (!RTDirExists(szDstPath))
334 {
335 rc = RTDirCreate(szDstPath, 0755, 0 /*fFlags*/);
336 if (RT_FAILURE(rc))
337 return RTMsgErrorRc(rc, "Error creating '%s': %Rrc", szDstPath, rc);
338 }
339
340 rc = RTPathAppend(szDstPath, sizeof(szDstPath), pszDstName);
341 if (RT_FAILURE(rc))
342 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
343 if (pszExtraStuff)
344 {
345 rc = RTStrCat(szDstPath, sizeof(szDstPath), pszExtraStuff);
346 if (RT_FAILURE(rc))
347 return RTMsgErrorRc(rc, "Error constructing cache path for '%s': %Rrc", pszSrcPath, rc);
348 }
349
350 /*
351 * If the file exists, we compare the two and throws an error if the doesn't match.
352 */
353 if (RTPathExists(szDstPath))
354 {
355 rc = RTFileCompare(pszSrcPath, szDstPath);
356 if (RT_SUCCESS(rc))
357 {
358 RTMsgInfo("%s is already in the cache.", pszSrcPath);
359 if (pAddToUuidMap && pszUuidMapDir)
360 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
361 return VINF_SUCCESS;
362 }
363 if (rc == VERR_NOT_EQUAL)
364 RTMsgInfo("Cache conflict with existing entry '%s' when inserting '%s'.", szDstPath, pszSrcPath);
365 else
366 RTMsgInfo("Error comparing '%s' with '%s': %Rrc", pszSrcPath, szDstPath, rc);
367 return rc;
368 }
369
370 /*
371 * The file doesn't exist or we should overwrite it,
372 */
373 RTMsgInfo("Copying '%s' to '%s'...", pszSrcPath, szDstPath);
374 rc = RTFileCopy(pszSrcPath, szDstPath);
375 if (RT_FAILURE(rc))
376 return RTMsgErrorRc(rc, "Error copying '%s' to '%s': %Rrc", pszSrcPath, szDstPath, rc);
377 if (pAddToUuidMap && pszUuidMapDir)
378 return rtDbgSymCacheAddCreateUuidMapping(szDstPath, pAddToUuidMap, pszUuidMapDir, pCfg);
379 return VINF_SUCCESS;
380}
381
382
383/**
384 * Worker that add the image file to the right place.
385 *
386 * @returns IPRT status code.
387 * @param pszPath Path to the image file.
388 * @param pCfg Configuration data.
389 * @param hLdrMod Image handle.
390 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
391 * @param pszUuidMapDir Optional UUID map cache directory if the image
392 * should be mapped by UUID.
393 * The map is a Mac OS X debug feature supported by
394 * the two native debuggers gdb and lldb. Look for
395 * descriptions of DBGFileMappedPaths in the
396 * com.apple.DebugSymbols in the user defaults.
397 */
398static int rtDbgSymCacheAddImageFileWorker(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg, RTLDRMOD hLdrMod,
399 const char *pszExtrSuff, const char *pszUuidMapDir)
400{
401 /*
402 * Determine which subdirectory to put the files in.
403 */
404 RTUUID Uuid;
405 PRTUUID pUuid = NULL;
406 int rc;
407 char szSubDir[48];
408 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod);
409 switch (enmFmt)
410 {
411 case RTLDRFMT_MACHO:
412 {
413 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_UUID, &Uuid, sizeof(Uuid));
414 if (RT_FAILURE(rc))
415 return RTMsgErrorRc(rc, "Error quering image UUID from image '%s': %Rrc", pszPath, rc);
416
417 rc = RTUuidToStr(&Uuid, szSubDir, sizeof(szSubDir));
418 if (RT_FAILURE(rc))
419 return RTMsgErrorRc(rc, "Error convering UUID for image '%s' to string: %Rrc", pszPath, rc);
420 pUuid = &Uuid;
421 break;
422 }
423
424 case RTLDRFMT_PE:
425 {
426 uint32_t uTimestamp;
427 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp, sizeof(uTimestamp));
428 if (RT_FAILURE(rc))
429 return RTMsgErrorRc(rc, "Error quering timestamp from image '%s': %Rrc", pszPath, rc);
430
431 size_t cbImage = RTLdrSize(hLdrMod);
432 if (cbImage == ~(size_t)0)
433 return RTMsgErrorRc(rc, "Error quering size of image '%s': %Rrc", pszPath, rc);
434
435 RTStrPrintf(szSubDir, sizeof(szSubDir), "%08X%x", uTimestamp, cbImage);
436 break;
437 }
438
439 case RTLDRFMT_AOUT:
440 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of a.out image has not yet been implemented: %s", pszPath);
441 case RTLDRFMT_ELF:
442 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of ELF image has not yet been implemented: %s", pszPath);
443 case RTLDRFMT_LX:
444 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Caching of LX image has not yet been implemented: %s", pszPath);
445 default:
446 return RTMsgErrorRc(VERR_NOT_SUPPORTED, "Unknown loader format for '%s': %d", pszPath, enmFmt);
447 }
448
449 /*
450 * Now add it.
451 */
452 return rtDbgSymCacheAddOneFile(pszPath, RTPathFilename(pszPath), pszExtrSuff,
453 szSubDir, pUuid, pszUuidMapDir, pCfg);
454}
455
456
457/**
458 * Adds what we think is an image file to the cache.
459 *
460 * @returns IPRT status code.
461 * @param pszPath Path to the image file.
462 * @param pszExtraSuff Optional extra suffix. Mach-O dSYM hack.
463 * @param pszUuidMapDir The UUID map subdirectory in the cache, if this is
464 * wanted, otherwise NULL.
465 * @param pCfg Configuration data.
466 */
467static int rtDbgSymCacheAddImageFile(const char *pszPath, const char *pszExtraSuff, const char *pszUuidMapDir,
468 PCRTDBGSYMCACHEADDCFG pCfg)
469{
470 /*
471 * Use the loader to open the alleged image file. We need to open it with
472 * arch set to amd64 and x86_32 in order to handle FAT images from the mac
473 * guys (we should actually enumerate archs, but that's currently not
474 * implemented nor necessary for our current use).
475 */
476 /* Open it as AMD64. */
477 RTLDRMOD hLdrMod64;
478 int rc = RTLdrOpen(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_AMD64, &hLdrMod64);
479 if (RT_FAILURE(rc))
480 {
481 if (rc != VERR_LDR_ARCH_MISMATCH)
482 {
483 if (rc != VERR_INVALID_EXE_SIGNATURE)
484 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=amd64]: %Rrc", pszPath, rc);
485 RTMsgInfo("Skipping '%s', no a recognizable image file...", pszPath);
486 return VINF_SUCCESS;
487 }
488 hLdrMod64 = NIL_RTLDRMOD;
489 }
490
491 /* Open it as X86. */
492 RTLDRMOD hLdrMod32;
493 rc = RTLdrOpen(pszPath, RTLDR_O_FOR_DEBUG, RTLDRARCH_X86_32, &hLdrMod32);
494 if (RT_FAILURE(rc))
495 {
496 if (rc != VERR_LDR_ARCH_MISMATCH)
497 {
498 RTLdrClose(hLdrMod32);
499 return RTMsgErrorRc(rc, "RTLdrOpen failed opening '%s' [arch=x86]: %Rrc", pszPath, rc);
500 }
501 hLdrMod32 = NIL_RTLDRMOD;
502 }
503
504 /*
505 * Add the file.
506 */
507 if (hLdrMod32 == NIL_RTLDRMOD)
508 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
509 else if (hLdrMod64 == NIL_RTLDRMOD)
510 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
511 else
512 {
513 /*
514 * Do we need to add it once or twice?
515 */
516 RTLDRFMT enmFmt = RTLdrGetFormat(hLdrMod32);
517 bool fSame = enmFmt == RTLdrGetFormat(hLdrMod64);
518 if (fSame && enmFmt == RTLDRFMT_MACHO)
519 {
520 RTUUID Uuid32, Uuid64;
521 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_UUID, &Uuid32, sizeof(Uuid32));
522 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_UUID, &Uuid64, sizeof(Uuid64));
523 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
524 if (fSame && RT_SUCCESS(rc32))
525 fSame = RTUuidCompare(&Uuid32, &Uuid64) == 0;
526 }
527 else if (fSame && enmFmt == RTLDRFMT_PE)
528 {
529 fSame = RTLdrSize(hLdrMod32) == RTLdrSize(hLdrMod64);
530 if (fSame)
531 {
532 uint32_t uTimestamp32, uTimestamp64;
533 int rc32 = RTLdrQueryProp(hLdrMod32, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp32, sizeof(uTimestamp32));
534 int rc64 = RTLdrQueryProp(hLdrMod64, RTLDRPROP_TIMESTAMP_SECONDS, &uTimestamp64, sizeof(uTimestamp64));
535 fSame = RT_SUCCESS(rc32) == RT_SUCCESS(rc64);
536 if (fSame && RT_SUCCESS(rc32))
537 fSame = uTimestamp32 == uTimestamp64;
538 }
539 }
540
541 rc = rtDbgSymCacheAddImageFileWorker(pszPath, pCfg, hLdrMod64, pszExtraSuff, pszUuidMapDir);
542 if (!fSame)
543 {
544 /** @todo should symlink or hardlink this second copy. */
545 int rc2 = rtDbgSymCacheAddImageFileWorker(pszPath, pCfg, hLdrMod32, pszExtraSuff, pszUuidMapDir);
546 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
547 rc = rc2;
548 }
549 }
550
551 RTLdrClose(hLdrMod32);
552 RTLdrClose(hLdrMod64);
553 return VINF_SUCCESS;
554}
555
556
557/**
558 * Worker for rtDbgSymCacheAddDebugFile that adds a Mach-O debug file to the
559 * cache.
560 *
561 * @returns IPRT status code
562 * @param pszPath The path to the PDB file.
563 * @param pCfg The configuration.
564 * @param hFile Handle to the file.
565 */
566static int rtDbgSymCacheAddDebugMachO(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
567{
568 /* This shouldn't happen, figure out what to do if it does. */
569 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED,
570 "'%s' is an OS X image file, did you point me to a file inside a .dSYM or .sym file?",
571 pszPath);
572}
573
574
575/**
576 * Worker for rtDbgSymCacheAddDebugFile that adds PDBs to the cace.
577 *
578 * @returns IPRT status code
579 * @param pszPath The path to the PDB file.
580 * @param pCfg The configuration.
581 * @param hFile Handle to the file.
582 */
583static int rtDbgSymCacheAddDebugPdb(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg, RTFILE hFile)
584{
585 return RTMsgErrorRc(VERR_NOT_IMPLEMENTED, "PDB support not implemented: '%s'");
586}
587
588
589/**
590 * Adds a debug file to the cache.
591 *
592 * @returns IPRT status code
593 * @param pszPath The path to the debug file in question.
594 * @param pCfg The configuration.
595 */
596static int rtDbgSymCacheAddDebugFile(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
597{
598 /*
599 * Need to extract an identifier of sorts here in order to put them in
600 * the right place in the cache. Currently only implemnted for Mach-O
601 * files since these use executable containers.
602 *
603 * We take a look at the file header in hope to figure out what to do
604 * with the file.
605 */
606 RTFILE hFile;
607 int rc = RTFileOpen(&hFile, pszPath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
608 if (RT_FAILURE(rc))
609 return RTMsgErrorRc(rc, "Error opening '%s': %Rrc", pszPath, rc);
610
611 union
612 {
613 uint64_t au64[16];
614 uint32_t au32[16];
615 uint16_t au16[32];
616 uint8_t ab[64];
617 } uBuf;
618 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), NULL);
619 if (RT_SUCCESS(rc))
620 {
621 /*
622 * Look for magics and call workers.
623 */
624 if (!memcmp(uBuf.ab, RT_STR_TUPLE("Microsoft C/C++ MSF 7.00")))
625 rc = rtDbgSymCacheAddDebugPdb(pszPath, pCfg, hFile);
626 else if ( uBuf.au32[0] == IMAGE_FAT_SIGNATURE
627 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
628 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
629 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
630 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
631 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
632 rc = rtDbgSymCacheAddDebugMachO(pszPath, pCfg);
633 else
634 rc = RTMsgErrorRc(VERR_INVALID_MAGIC, "Unsupported debug file '%s' magic: %#010x", pszPath, uBuf.au32[0]);
635 }
636 else
637 rc = RTMsgErrorRc(rc, "Error reading '%s': %Rrc", pszPath, rc);
638
639 /* close the file. */
640 int rc2 = RTFileClose(hFile);
641 if (RT_FAILURE(rc2))
642 {
643 RTMsgError("Error closing '%s': %Rrc", pszPath, rc2);
644 if (RT_SUCCESS(rc))
645 rc = rc2;
646 }
647 return rc;
648}
649
650
651/**
652 * Constructs the path to the file instide the bundle that we're keen on.
653 *
654 * @returns IPRT status code.
655 * @param pszPath Path to the bundle on input, on successful
656 * return it's the path to the desired file. This
657 * a RTPATH_MAX size buffer.
658 * @param cchPath The length of the path up to the bundle name.
659 * @param cchName The length of the bundle name.
660 * @param pszSubDir The bundle subdirectory the file lives in.
661 * @param papszSuffixes Pointer to an array of bundle suffixes.
662 */
663static int rtDbgSymCacheConstructBundlePath(char *pszPath, size_t cchPath, size_t cchName, const char *pszSubDir,
664 const char * const *papszSuffixes)
665{
666 int rc = RTPathAppend(pszPath, RTPATH_MAX, pszSubDir);
667 if (RT_SUCCESS(rc))
668 {
669 /* Strip off the bundle suffix. */
670 const char *pszEnd = &pszPath[cchPath + cchName];
671 for (unsigned i = 0; papszSuffixes[i]; i++)
672 {
673 Assert(papszSuffixes[i][0] == '.');
674 size_t cchSuff = strlen(papszSuffixes[i]);
675 if ( cchSuff < cchName
676 && !memcmp(&pszEnd[-(ssize_t)cchSuff], papszSuffixes[i], cchSuff))
677 {
678 cchName -= cchSuff;
679 break;
680 }
681 }
682
683 /* Add the name. */
684 rc = RTPathAppendEx(pszPath, RTPATH_MAX, &pszPath[cchPath], cchName);
685 }
686 if (RT_FAILURE(rc))
687 {
688 pszPath[cchPath + cchName] = '\0';
689 return RTMsgErrorRc(rc, "Error constructing image bundle path for '%s': %Rrc", pszPath, rc);
690 }
691 return VINF_SUCCESS;
692}
693
694
695/**
696 * Adds a image bundle of some sort.
697 *
698 * @returns IPRT status code.
699 * @param pszPath Path to the bundle. This a RTPATH_MAX size
700 * buffer that we can write to when creating the
701 * path to the file inside the bundle that we're
702 * interested in.
703 * @param cchPath The length of the path up to the bundle name.
704 * @param cchName The length of the bundle name.
705 * @param pCfg The configuration.
706 */
707static int rtDbgSymCacheAddImageBundle(char *pszPath, size_t cchPath, size_t cchName, PCRTDBGSYMCACHEADDCFG pCfg)
708{
709 /* Assuming these are kexts or simple applications, we only add the image
710 file itself to the cache. No Info.plist or other files. */
711 /** @todo consider looking for Frameworks and handling framework bundles. */
712 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/MacOS/", g_apszBundleSuffixes);
713 if (RT_SUCCESS(rc))
714 rc = rtDbgSymCacheAddImageFile(pszPath, NULL, "image-uuids", pCfg);
715 return rc;
716}
717
718
719/**
720 * Adds a debug bundle.
721 *
722 * @returns IPRT status code.
723 * @param pszPath Path to the bundle. This a RTPATH_MAX size
724 * buffer that we can write to when creating the
725 * path to the file inside the bundle that we're
726 * interested in.
727 * @param cchPath The length of the path up to the bundle name.
728 * @param cchName The length of the bundle name.
729 * @param pCfg The configuration.
730 */
731static int rtDbgSymCacheAddDebugBundle(char *pszPath, size_t cchPath, size_t cchName, PCRTDBGSYMCACHEADDCFG pCfg)
732{
733 /*
734 * The current policy is not to add the whole .dSYM (or .sym) bundle, but
735 * rather just the dwarf image instide it. The <UUID>.plist and Info.plist
736 * files generally doesn't contain much extra information that's really
737 * necessary, I hope. At least this is what the uuidmap example in the
738 * lldb hints at (it links to the dwarf file, not the .dSYM dir).
739 *
740 * To avoid confusion with a .dSYM bundle, as well as collision with the
741 * image file, we use .dwarf suffix for the file.
742 *
743 * For details on the uuid map see rtDbgSymCacheAddImageFile as well as
744 * http://lldb.llvm.org/symbols.html .
745 *
746 * ASSUMES bundles contains Mach-O DWARF files.
747 */
748 int rc = rtDbgSymCacheConstructBundlePath(pszPath, cchPath, cchName, "Contents/Resources/DWARF/", g_apszDSymBundleSuffixes);
749 if (RT_SUCCESS(rc))
750 rc = rtDbgSymCacheAddImageFile(pszPath, ".dwarf", "dsym-uuids", pCfg);
751 return rc;
752}
753
754
755/**
756 * Figure the type of a file/dir based on path and FS object info.
757 *
758 * @returns The type.
759 * @param pszPath The path to the file/dir.
760 * @param pObjInfo The object information, symlinks followed.
761 */
762static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType2(const char *pszPath, PCRTFSOBJINFO pObjInfo)
763{
764 const char *pszName = RTPathFilename(pszPath);
765 const char *pszExt = RTPathSuffix(pszName);
766 if (pszExt)
767 pszExt++;
768 else
769 pszExt = "";
770
771 if ( RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode)
772 || (pObjInfo->Attr.fMode & RTFS_DOS_DIRECTORY)) /** @todo OS X samba reports reparse points in /Volumes/ that we cannot resolve. */
773 {
774 /* Skip directories shouldn't bother with. */
775 if ( !RTStrICmp(pszName, ".Trashes")
776 || !RTStrICmp(pszName, ".$RESCYCLE.BIN")
777 || !RTStrICmp(pszName, "System.kext") /* Usually only plugins here, so skip it. */
778 )
779 return RTDBGSYMCACHEFILETYPE_IGNORE;
780
781 /* Directories can also be bundles on the mac. */
782 if (!RTStrICmp(pszExt, "dSYM"))
783 return RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE;
784
785 for (unsigned i = 0; i < RT_ELEMENTS(g_apszBundleSuffixes) - 1; i++)
786 if (!RTStrICmp(pszExt, &g_apszBundleSuffixes[i][1]))
787 return RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE;
788
789 return RTDBGSYMCACHEFILETYPE_DIR;
790 }
791
792 if (!RTFS_IS_FILE(pObjInfo->Attr.fMode))
793 return RTDBGSYMCACHEFILETYPE_INVALID;
794
795 /* Select image vs debug info based on extension. */
796 if ( !RTStrICmp(pszExt, "pdb")
797 || !RTStrICmp(pszExt, "dbg")
798 || !RTStrICmp(pszExt, "sym")
799 || !RTStrICmp(pszExt, "dwo")
800 || !RTStrICmp(pszExt, "dwp")
801 || !RTStrICmp(pszExt, "debug")
802 || !RTStrICmp(pszExt, "dsym")
803 || !RTStrICmp(pszExt, "dwarf")
804 || !RTStrICmp(pszExt, "map")
805 || !RTStrICmp(pszExt, "cv"))
806 return RTDBGSYMCACHEFILETYPE_DEBUG_FILE;
807
808 /* Filter out a bunch of files which obviously shouldn't be images. */
809 if ( !RTStrICmp(pszExt, "txt")
810 || !RTStrICmp(pszExt, "html")
811 || !RTStrICmp(pszExt, "htm")
812 || !RTStrICmp(pszExt, "rtf")
813 || !RTStrICmp(pszExt, "zip")
814 || !RTStrICmp(pszExt, "doc")
815 || !RTStrICmp(pszExt, "gz")
816 || !RTStrICmp(pszExt, "bz2")
817 || !RTStrICmp(pszExt, "xz")
818 || !RTStrICmp(pszExt, "kmk")
819 || !RTStrICmp(pszExt, "c")
820 || !RTStrICmp(pszExt, "cpp")
821 || !RTStrICmp(pszExt, "h")
822 || !RTStrICmp(pszExt, "m")
823 || !RTStrICmp(pszExt, "mm")
824 || !RTStrICmp(pszExt, "asm")
825 || !RTStrICmp(pszExt, "S")
826 || !RTStrICmp(pszExt, "inc")
827 || !RTStrICmp(pszExt, "sh")
828 )
829 return RTDBGSYMCACHEFILETYPE_IGNORE;
830 if ( !RTStrICmp(pszName, "Makefile")
831 || !RTStrICmp(pszName, "GNUmakefile")
832 || !RTStrICmp(pszName, "createsymbolfiles")
833 || !RTStrICmp(pszName, "kgmacros")
834 )
835 return RTDBGSYMCACHEFILETYPE_IGNORE;
836
837 return RTDBGSYMCACHEFILETYPE_IMAGE_FILE;
838}
839
840
841/**
842 * Figure file type based on name, will stat the file/dir.
843 *
844 * @returns File type.
845 * @param pszPath The path to the file/dir to figure.
846 */
847static RTDBGSYMCACHEFILETYPE rtDbgSymCacheFigureType(const char *pszPath)
848{
849 const char *pszName = RTPathFilename(pszPath);
850
851 /* Trailing slash. */
852 if (!pszName)
853 return RTDBGSYMCACHEFILETYPE_DIR;
854
855 /* Wildcard means listing directory and filtering. */
856 if (strpbrk(pszName, "?*"))
857 return RTDBGSYMCACHEFILETYPE_DIR_FILTER;
858
859 /* Get object info, following links. */
860 RTFSOBJINFO ObjInfo;
861 int rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
862 if (RT_FAILURE(rc))
863 return RTDBGSYMCACHEFILETYPE_INVALID;
864 return rtDbgSymCacheFigureType2(pszPath, &ObjInfo);
865}
866
867
868/**
869 * Recursive worker for rtDbgSymCacheAddDir, for minimal stack wasting.
870 *
871 * @returns IPRT status code (fully bitched).
872 * @param pszPath Pointer to a RTPATH_MAX size buffer containing
873 * the path to the current directory ending with a
874 * slash.
875 * @param cchPath The size of the current directory path.
876 * @param pDirEntry Pointer to the RTDIRENTRYEX structure to use.
877 * @param pCfg The configuration.
878 */
879static int rtDbgSymCacheAddDirWorker(char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry, PCRTDBGSYMCACHEADDCFG pCfg)
880{
881 /*
882 * Open the directory.
883 */
884 PRTDIR pDir;
885 int rc, rc2;
886 if (pCfg->pszFilter)
887 {
888 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX - cchPath, pCfg->pszFilter);
889 if (RT_FAILURE(rc))
890 {
891 pszPath[cchPath] = '\0';
892 return RTMsgErrorRc(rc, "Filename too long (%Rrc): '%s" RTPATH_SLASH_STR "%s'", rc, pszPath, pCfg->pszFilter);
893 }
894 rc = RTDirOpenFiltered(&pDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
895 }
896 else
897 rc = RTDirOpen(&pDir, pszPath);
898 if (RT_FAILURE(rc))
899 return RTMsgErrorRc(rc, "RTDirOpen%s failed on '%s': %Rrc", pCfg->pszFilter ? "Filtered" : "", pszPath, rc);
900
901 /*
902 * Enumerate the files.
903 */
904 for (;;)
905 {
906 rc2 = RTDirReadEx(pDir, pDirEntry, NULL, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
907 if (RT_FAILURE(rc2))
908 {
909 pszPath[cchPath] = '\0';
910 if (rc2 != VERR_NO_MORE_FILES)
911 {
912 RTMsgError("RTDirReadEx failed in '%s': %Rrc\n", pszPath, rc2);
913 rc = rc2;
914 }
915 break;
916 }
917
918 /* Skip dot and dot-dot. */
919 if (RTDirEntryExIsStdDotLink(pDirEntry))
920 continue;
921
922 /* Construct a full path. */
923 rc = RTStrCopy(&pszPath[cchPath], RTPATH_MAX, pDirEntry->szName);
924 if (RT_FAILURE(rc))
925 {
926 pszPath[cchPath] = '\0';
927 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
928 break;
929 }
930
931 switch (rtDbgSymCacheFigureType2(pszPath, &pDirEntry->Info))
932 {
933 case RTDBGSYMCACHEFILETYPE_DIR:
934 if (!pCfg->fRecursive)
935 RTMsgInfo("Skipping directory '%s'...", pszPath);
936 else
937 {
938 if (cchPath + pDirEntry->cbName + 3 <= RTPATH_MAX)
939 {
940 pszPath[cchPath + pDirEntry->cbName] = RTPATH_SLASH;
941 pszPath[cchPath + pDirEntry->cbName + 1] = '\0';
942 rc2 = rtDbgSymCacheAddDirWorker(pszPath, cchPath + pDirEntry->cbName + 1, pDirEntry, pCfg);
943 }
944 else
945 {
946 RTMsgError("File name too long in '%s': '%s' (%Rrc)", pszPath, pDirEntry->szName, rc);
947 rc2 = VERR_FILENAME_TOO_LONG;
948 }
949 }
950 break;
951
952 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
953 rc2 = rtDbgSymCacheAddDebugFile(pszPath, pCfg);
954 break;
955
956 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
957 rc2 = rtDbgSymCacheAddImageFile(pszPath, NULL /*pszExtraSuff*/, "image-uuids", pCfg);
958 break;
959
960 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
961 rc2 = rtDbgSymCacheAddDebugBundle(pszPath, cchPath, pDirEntry->cbName, pCfg);
962 break;
963
964 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
965 rc2 = rtDbgSymCacheAddImageBundle(pszPath, cchPath, pDirEntry->cbName, pCfg);
966 break;
967
968 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
969 case RTDBGSYMCACHEFILETYPE_INVALID:
970 rc2 = RTMsgErrorRc(VERR_INTERNAL_ERROR_2, "Invalid: '%s'", pszPath);
971 break;
972
973 case RTDBGSYMCACHEFILETYPE_IGNORE:
974 rc2 = VINF_SUCCESS;
975 break;
976 }
977
978 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
979 rc = rc2;
980 }
981
982 /*
983 * Clean up.
984 */
985 rc2 = RTDirClose(pDir);
986 if (RT_FAILURE(rc2))
987 {
988 RTMsgError("RTDirClose failed in '%s': %Rrc", pszPath, rc);
989 rc = rc2;
990 }
991 return rc;
992}
993
994
995/**
996 * Adds a directory.
997 *
998 * @returns IPRT status code (fully bitched).
999 * @param pszPath The directory path.
1000 * @param pCfg The configuration.
1001 */
1002static int rtDbgSymCacheAddDir(const char *pszPath, PCRTDBGSYMCACHEADDCFG pCfg)
1003{
1004 /*
1005 * Set up the path buffer, stripping any filter.
1006 */
1007 char szPath[RTPATH_MAX];
1008 int rc = RTStrCopy(szPath, sizeof(szPath) - 2, pszPath);
1009 if (RT_FAILURE(rc))
1010 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'", pszPath);
1011
1012 size_t cchPath = strlen(pszPath);
1013 if (!cchPath)
1014 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path empty: '%s'", pszPath);
1015
1016 if (pCfg->pszFilter)
1017 szPath[cchPath - strlen(pCfg->pszFilter)] = '\0';
1018 cchPath = RTPathStripTrailingSlash(szPath);
1019 if (!RTPATH_IS_SEP(pszPath[cchPath - 1]))
1020 {
1021 szPath[cchPath++] = RTPATH_SLASH;
1022 szPath[cchPath] = '\0';
1023 }
1024
1025 /*
1026 * Let the worker do the rest.
1027 */
1028 RTDIRENTRYEX DirEntry;
1029 return rtDbgSymCacheAddDirWorker(szPath, cchPath, &DirEntry, pCfg);
1030}
1031
1032
1033/**
1034 * Adds a file or directory.
1035 *
1036 * @returns Program exit code.
1037 * @param pszPath The user supplied path to the file or directory.
1038 * @param pszCache The path to the cache.
1039 * @param fRecursive Whether to process directories recursively.
1040 */
1041static RTEXITCODE rtDbgSymCacheAddFileOrDir(const char *pszPath, const char *pszCache, bool fRecursive)
1042{
1043 RTDBGSYMCACHEADDCFG Cfg;
1044 Cfg.fRecursive = fRecursive;
1045 Cfg.pszCache = pszCache;
1046 Cfg.pszFilter = NULL;
1047
1048 int rc;
1049 RTDBGSYMCACHEFILETYPE enmType = rtDbgSymCacheFigureType(pszPath);
1050 switch (enmType)
1051 {
1052 default:
1053 case RTDBGSYMCACHEFILETYPE_INVALID:
1054 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid: '%s'", pszPath);
1055
1056 case RTDBGSYMCACHEFILETYPE_DIR_FILTER:
1057 Cfg.pszFilter = RTPathFilename(pszPath);
1058 /* fall thru */
1059 case RTDBGSYMCACHEFILETYPE_DIR:
1060 rc = rtDbgSymCacheAddDir(pszPath, &Cfg);
1061 break;
1062
1063 case RTDBGSYMCACHEFILETYPE_DEBUG_FILE:
1064 rc = rtDbgSymCacheAddDebugFile(pszPath, &Cfg);
1065 break;
1066
1067 case RTDBGSYMCACHEFILETYPE_IMAGE_FILE:
1068 rc = rtDbgSymCacheAddImageFile(pszPath, NULL /*pszExtraSuff*/, "image-uuids", &Cfg);
1069 break;
1070
1071 case RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE:
1072 case RTDBGSYMCACHEFILETYPE_IMAGE_BUNDLE:
1073 {
1074 size_t cchPath = strlen(pszPath);
1075 size_t cchFilename = strlen(RTPathFilename(pszPath));
1076 char szPathBuf[RTPATH_MAX];
1077 if (cchPath < sizeof(szPathBuf))
1078 {
1079 memcpy(szPathBuf, pszPath, cchPath + 1);
1080 if (enmType == RTDBGSYMCACHEFILETYPE_DEBUG_BUNDLE)
1081 rc = rtDbgSymCacheAddDebugBundle(szPathBuf, cchPath - cchFilename, cchFilename, &Cfg);
1082 else
1083 rc = rtDbgSymCacheAddImageBundle(szPathBuf, cchPath - cchFilename, cchFilename, &Cfg);
1084 }
1085 else
1086 rc = RTMsgErrorRc(VERR_FILENAME_TOO_LONG, "Filename too long: '%s'", pszPath);
1087 break;
1088 }
1089
1090 case RTDBGSYMCACHEFILETYPE_IGNORE:
1091 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "Invalid file: '%s'");
1092 break;
1093 }
1094 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1095}
1096
1097
1098/**
1099 * Handles the 'add' command.
1100 *
1101 * @returns Program exit code.
1102 * @param pszArg0 The program name.
1103 * @param cArgs The number of arguments to the 'add' command.
1104 * @param papszArgs The argument vector, starting after 'add'.
1105 */
1106static RTEXITCODE rtDbgSymCacheCmdAdd(const char *pszArg0, int cArgs, char **papszArgs)
1107{
1108 /*
1109 * Parse the command line.
1110 */
1111 static RTGETOPTDEF const s_aOptions[] =
1112 {
1113 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
1114 { "--no-recursive", 'n', RTGETOPT_REQ_NOTHING },
1115 };
1116
1117 const char *pszCache = NULL;
1118 bool fRecursive = false;
1119
1120 RTGETOPTSTATE State;
1121 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1122 if (RT_FAILURE(rc))
1123 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
1124
1125 uint32_t cAdded = 0;
1126 RTGETOPTUNION ValueUnion;
1127 int chOpt;
1128 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
1129 {
1130 switch (chOpt)
1131 {
1132 case 'R':
1133 fRecursive = true;
1134 break;
1135
1136 case 'n':
1137 fRecursive = false;
1138 break;
1139
1140 case VINF_GETOPT_NOT_OPTION:
1141 /* The first non-option is a cache directory. */
1142 if (!pszCache)
1143 {
1144 pszCache = ValueUnion.psz;
1145 if (!RTPathExists(pszCache))
1146 {
1147 rc = RTDirCreate(pszCache, 0755, 0 /*fFlags*/);
1148 if (RT_FAILURE(rc))
1149 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Error creating cache directory '%s': %Rrc", pszCache, rc);
1150 }
1151 else if (!RTDirExists(pszCache))
1152 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specified cache directory is not a directory: '%s'", pszCache);
1153 }
1154 /* Subsequent non-options are files to be added to the cache. */
1155 else
1156 {
1157 RTEXITCODE rcExit = rtDbgSymCacheAddFileOrDir(ValueUnion.psz, pszCache, fRecursive);
1158 if (rcExit != RTEXITCODE_FAILURE)
1159 return rcExit;
1160 }
1161 break;
1162
1163 case 'h':
1164 return rtDbgSymCacheUsage(pszArg0, "add");
1165 case 'V':
1166 return rtDbgSymCacheVersion();
1167 default:
1168 return RTGetOptPrintError(chOpt, &ValueUnion);
1169 }
1170 }
1171
1172 if (!pszCache)
1173 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No cache directory or files to add were specified.");
1174 return RTEXITCODE_SUCCESS;
1175}
1176
1177
1178int main(int argc, char **argv)
1179{
1180 int rc = RTR3InitExe(argc, &argv, 0);
1181 if (RT_FAILURE(rc))
1182 return RTMsgInitFailure(rc);
1183
1184 /*
1185 * Switch on the command.
1186 */
1187 RTEXITCODE rcExit = RTEXITCODE_SYNTAX;
1188 if (argc < 2)
1189 rtDbgSymCacheUsage(argv[0], NULL);
1190 else if (!strcmp(argv[1], "add"))
1191 rcExit = rtDbgSymCacheCmdAdd(argv[0], argc - 2, argv + 2);
1192 else if ( !strcmp(argv[1], "-h")
1193 || !strcmp(argv[1], "-?")
1194 || !strcmp(argv[1], "--help"))
1195 rcExit = rtDbgSymCacheUsage(argv[0], NULL);
1196 else if ( !strcmp(argv[1], "-V")
1197 || !strcmp(argv[1], "--version"))
1198 rcExit = rtDbgSymCacheVersion();
1199 else
1200 RTMsgError("Unknown command: '%s'", argv[1]);
1201
1202 return rcExit;
1203}
1204
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette