1 //===-- LocateSymbolFileMacOSX.cpp ----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Symbol/LocateSymbolFile.h"
10 
11 #include <dirent.h>
12 #include <dlfcn.h>
13 #include <pwd.h>
14 
15 #include <CoreFoundation/CoreFoundation.h>
16 
17 #include "Host/macosx/cfcpp/CFCBundle.h"
18 #include "Host/macosx/cfcpp/CFCData.h"
19 #include "Host/macosx/cfcpp/CFCReleaser.h"
20 #include "Host/macosx/cfcpp/CFCString.h"
21 #include "lldb/Core/ModuleList.h"
22 #include "lldb/Core/ModuleSpec.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Symbol/ObjectFile.h"
25 #include "lldb/Utility/ArchSpec.h"
26 #include "lldb/Utility/DataBuffer.h"
27 #include "lldb/Utility/DataExtractor.h"
28 #include "lldb/Utility/Endian.h"
29 #include "lldb/Utility/LLDBLog.h"
30 #include "lldb/Utility/Log.h"
31 #include "lldb/Utility/ReproducerProvider.h"
32 #include "lldb/Utility/StreamString.h"
33 #include "lldb/Utility/Timer.h"
34 #include "lldb/Utility/UUID.h"
35 #include "mach/machine.h"
36 
37 #include "llvm/ADT/ScopeExit.h"
38 #include "llvm/Support/FileSystem.h"
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 
43 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(CFUUIDRef uuid, CFURLRef exec_url) = nullptr;
44 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = nullptr;
45 
46 int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,
47                                        ModuleSpec &return_module_spec) {
48   Log *log = GetLog(LLDBLog::Host);
49   if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {
50     LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");
51     return 0;
52   }
53 
54   return_module_spec = module_spec;
55   return_module_spec.GetFileSpec().Clear();
56   return_module_spec.GetSymbolFileSpec().Clear();
57 
58   const UUID *uuid = module_spec.GetUUIDPtr();
59   const ArchSpec *arch = module_spec.GetArchitecturePtr();
60 
61   if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) {
62     static repro::SymbolFileLoader symbol_file_loader(l);
63     std::pair<FileSpec, FileSpec> paths = symbol_file_loader.GetPaths(uuid);
64     return_module_spec.GetFileSpec() = paths.first;
65     return_module_spec.GetSymbolFileSpec() = paths.second;
66     return 1;
67   }
68 
69   int items_found = 0;
70 
71   if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
72       g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
73     void *handle = dlopen ("/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", RTLD_LAZY | RTLD_LOCAL);
74     if (handle) {
75       g_dlsym_DBGCopyFullDSYMURLForUUID = (CFURLRef (*)(CFUUIDRef, CFURLRef)) dlsym (handle, "DBGCopyFullDSYMURLForUUID");
76       g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef (*)(CFURLRef)) dlsym (handle, "DBGCopyDSYMPropertyLists");
77     }
78   }
79 
80   if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||
81       g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {
82     return items_found;
83   }
84 
85   if (uuid && uuid->IsValid()) {
86     // Try and locate the dSYM file using DebugSymbols first
87     llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();
88     if (module_uuid.size() == 16) {
89       CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(
90           NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],
91           module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],
92           module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],
93           module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));
94 
95       if (module_uuid_ref.get()) {
96         CFCReleaser<CFURLRef> exec_url;
97         const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();
98         if (exec_fspec) {
99           char exec_cf_path[PATH_MAX];
100           if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))
101             exec_url.reset(::CFURLCreateFromFileSystemRepresentation(
102                 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),
103                 FALSE));
104         }
105 
106         CFCReleaser<CFURLRef> dsym_url(
107             g_dlsym_DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get()));
108         char path[PATH_MAX];
109 
110         if (dsym_url.get()) {
111           if (::CFURLGetFileSystemRepresentation(
112                   dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
113             if (log) {
114               LLDB_LOGF(log,
115                         "DebugSymbols framework returned dSYM path of %s for "
116                         "UUID %s -- looking for the dSYM",
117                         path, uuid->GetAsString().c_str());
118             }
119             FileSpec dsym_filespec(path);
120             if (path[0] == '~')
121               FileSystem::Instance().Resolve(dsym_filespec);
122 
123             if (FileSystem::Instance().IsDirectory(dsym_filespec)) {
124               dsym_filespec =
125                   Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch);
126               ++items_found;
127             } else {
128               ++items_found;
129             }
130             return_module_spec.GetSymbolFileSpec() = dsym_filespec;
131           }
132 
133           bool success = false;
134           if (log) {
135             if (::CFURLGetFileSystemRepresentation(
136                     dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
137               LLDB_LOGF(log,
138                         "DebugSymbols framework returned dSYM path of %s for "
139                         "UUID %s -- looking for an exec file",
140                         path, uuid->GetAsString().c_str());
141             }
142           }
143 
144           CFCReleaser<CFDictionaryRef> dict(
145               g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));
146           CFDictionaryRef uuid_dict = NULL;
147           if (dict.get()) {
148             CFCString uuid_cfstr(uuid->GetAsString().c_str());
149             uuid_dict = static_cast<CFDictionaryRef>(
150                 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));
151           }
152           if (uuid_dict) {
153             CFStringRef exec_cf_path =
154                 static_cast<CFStringRef>(::CFDictionaryGetValue(
155                     uuid_dict, CFSTR("DBGSymbolRichExecutable")));
156             if (exec_cf_path && ::CFStringGetFileSystemRepresentation(
157                                     exec_cf_path, path, sizeof(path))) {
158               if (log) {
159                 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",
160                           path, uuid->GetAsString().c_str());
161               }
162               ++items_found;
163               FileSpec exec_filespec(path);
164               if (path[0] == '~')
165                 FileSystem::Instance().Resolve(exec_filespec);
166               if (FileSystem::Instance().Exists(exec_filespec)) {
167                 success = true;
168                 return_module_spec.GetFileSpec() = exec_filespec;
169               }
170             }
171           }
172 
173           if (!success) {
174             // No dictionary, check near the dSYM bundle for an executable that
175             // matches...
176             if (::CFURLGetFileSystemRepresentation(
177                     dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {
178               char *dsym_extension_pos = ::strstr(path, ".dSYM");
179               if (dsym_extension_pos) {
180                 *dsym_extension_pos = '\0';
181                 if (log) {
182                   LLDB_LOGF(log,
183                             "Looking for executable binary next to dSYM "
184                             "bundle with name with name %s",
185                             path);
186                 }
187                 FileSpec file_spec(path);
188                 FileSystem::Instance().Resolve(file_spec);
189                 ModuleSpecList module_specs;
190                 ModuleSpec matched_module_spec;
191                 using namespace llvm::sys::fs;
192                 switch (get_file_type(file_spec.GetPath())) {
193 
194                 case file_type::directory_file: // Bundle directory?
195                 {
196                   CFCBundle bundle(path);
197                   CFCReleaser<CFURLRef> bundle_exe_url(
198                       bundle.CopyExecutableURL());
199                   if (bundle_exe_url.get()) {
200                     if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),
201                                                            true, (UInt8 *)path,
202                                                            sizeof(path) - 1)) {
203                       FileSpec bundle_exe_file_spec(path);
204                       FileSystem::Instance().Resolve(bundle_exe_file_spec);
205                       if (ObjectFile::GetModuleSpecifications(
206                               bundle_exe_file_spec, 0, 0, module_specs) &&
207                           module_specs.FindMatchingModuleSpec(
208                               module_spec, matched_module_spec))
209 
210                       {
211                         ++items_found;
212                         return_module_spec.GetFileSpec() = bundle_exe_file_spec;
213                         if (log) {
214                           LLDB_LOGF(log,
215                                     "Executable binary %s next to dSYM is "
216                                     "compatible; using",
217                                     path);
218                         }
219                       }
220                     }
221                   }
222                 } break;
223 
224                 case file_type::fifo_file:      // Forget pipes
225                 case file_type::socket_file:    // We can't process socket files
226                 case file_type::file_not_found: // File doesn't exist...
227                 case file_type::status_error:
228                   break;
229 
230                 case file_type::type_unknown:
231                 case file_type::regular_file:
232                 case file_type::symlink_file:
233                 case file_type::block_file:
234                 case file_type::character_file:
235                   if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,
236                                                           module_specs) &&
237                       module_specs.FindMatchingModuleSpec(module_spec,
238                                                           matched_module_spec))
239 
240                   {
241                     ++items_found;
242                     return_module_spec.GetFileSpec() = file_spec;
243                     if (log) {
244                       LLDB_LOGF(log,
245                                 "Executable binary %s next to dSYM is "
246                                 "compatible; using",
247                                 path);
248                     }
249                   }
250                   break;
251                 }
252               }
253             }
254           }
255         }
256       }
257     }
258   }
259 
260   if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
261     g->GetOrCreate<repro::SymbolFileProvider>().AddSymbolFile(
262         uuid, return_module_spec.GetFileSpec(),
263         return_module_spec.GetSymbolFileSpec());
264   }
265 
266   return items_found;
267 }
268 
269 FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec,
270                                          const lldb_private::UUID *uuid,
271                                          const ArchSpec *arch) {
272   std::string dsym_bundle_path = dsym_bundle_fspec.GetPath();
273   llvm::SmallString<128> buffer(dsym_bundle_path);
274   llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF");
275 
276   std::error_code EC;
277   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =
278       FileSystem::Instance().GetVirtualFileSystem();
279   llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC);
280   llvm::vfs::recursive_directory_iterator End;
281   for (; Iter != End && !EC; Iter.increment(EC)) {
282     llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path());
283     if (Status->isDirectory())
284       continue;
285 
286     FileSpec dsym_fspec(Iter->path());
287     ModuleSpecList module_specs;
288     if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {
289       ModuleSpec spec;
290       for (size_t i = 0; i < module_specs.GetSize(); ++i) {
291         bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);
292         assert(got_spec); // The call has side-effects so can't be inlined.
293         UNUSED_IF_ASSERT_DISABLED(got_spec);
294         if ((uuid == nullptr ||
295              (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&
296             (arch == nullptr ||
297              (spec.GetArchitecturePtr() &&
298               spec.GetArchitecture().IsCompatibleMatch(*arch)))) {
299           return dsym_fspec;
300         }
301       }
302     }
303   }
304 
305   return {};
306 }
307 
308 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,
309                                                 ModuleSpec &module_spec) {
310   Log *log = GetLog(LLDBLog::Host);
311   bool success = false;
312   if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {
313     std::string str;
314     CFStringRef cf_str;
315     CFDictionaryRef cf_dict;
316 
317     cf_str = (CFStringRef)CFDictionaryGetValue(
318         (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));
319     if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
320       if (CFCString::FileSystemRepresentation(cf_str, str)) {
321         module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);
322         FileSystem::Instance().Resolve(module_spec.GetFileSpec());
323         if (log) {
324           LLDB_LOGF(log,
325                     "From dsymForUUID plist: Symbol rich executable is at '%s'",
326                     str.c_str());
327         }
328       }
329     }
330 
331     cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
332                                                CFSTR("DBGDSYMPath"));
333     if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
334       if (CFCString::FileSystemRepresentation(cf_str, str)) {
335         module_spec.GetSymbolFileSpec().SetFile(str.c_str(),
336                                                 FileSpec::Style::native);
337         FileSystem::Instance().Resolve(module_spec.GetFileSpec());
338         success = true;
339         if (log) {
340           LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'",
341                     str.c_str());
342         }
343       }
344     }
345 
346     std::string DBGBuildSourcePath;
347     std::string DBGSourcePath;
348 
349     // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.
350     // If DBGVersion 2, strip last two components of path remappings from
351     //                  entries to fix an issue with a specific set of
352     //                  DBGSourcePathRemapping entries that lldb worked
353     //                  with.
354     // If DBGVersion 3, trust & use the source path remappings as-is.
355     //
356     cf_dict = (CFDictionaryRef)CFDictionaryGetValue(
357         (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));
358     if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {
359       // If we see DBGVersion with a value of 2 or higher, this is a new style
360       // DBGSourcePathRemapping dictionary
361       bool new_style_source_remapping_dictionary = false;
362       bool do_truncate_remapping_names = false;
363       std::string original_DBGSourcePath_value = DBGSourcePath;
364       cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
365                                                  CFSTR("DBGVersion"));
366       if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
367         std::string version;
368         CFCString::FileSystemRepresentation(cf_str, version);
369         if (!version.empty() && isdigit(version[0])) {
370           int version_number = atoi(version.c_str());
371           if (version_number > 1) {
372             new_style_source_remapping_dictionary = true;
373           }
374           if (version_number == 2) {
375             do_truncate_remapping_names = true;
376           }
377         }
378       }
379 
380       CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);
381       if (kv_pair_count > 0) {
382         CFStringRef *keys =
383             (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
384         CFStringRef *values =
385             (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));
386         if (keys != nullptr && values != nullptr) {
387           CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,
388                                        (const void **)keys,
389                                        (const void **)values);
390         }
391         for (CFIndex i = 0; i < kv_pair_count; i++) {
392           DBGBuildSourcePath.clear();
393           DBGSourcePath.clear();
394           if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {
395             CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);
396           }
397           if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {
398             CFCString::FileSystemRepresentation(values[i], DBGSourcePath);
399           }
400           if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
401             // In the "old style" DBGSourcePathRemapping dictionary, the
402             // DBGSourcePath values (the "values" half of key-value path pairs)
403             // were wrong.  Ignore them and use the universal DBGSourcePath
404             // string from earlier.
405             if (new_style_source_remapping_dictionary &&
406                 !original_DBGSourcePath_value.empty()) {
407               DBGSourcePath = original_DBGSourcePath_value;
408             }
409             if (DBGSourcePath[0] == '~') {
410               FileSpec resolved_source_path(DBGSourcePath.c_str());
411               FileSystem::Instance().Resolve(resolved_source_path);
412               DBGSourcePath = resolved_source_path.GetPath();
413             }
414             // With version 2 of DBGSourcePathRemapping, we can chop off the
415             // last two filename parts from the source remapping and get a more
416             // general source remapping that still works. Add this as another
417             // option in addition to the full source path remap.
418             module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
419                                                       DBGSourcePath, true);
420             if (do_truncate_remapping_names) {
421               FileSpec build_path(DBGBuildSourcePath.c_str());
422               FileSpec source_path(DBGSourcePath.c_str());
423               build_path.RemoveLastPathComponent();
424               build_path.RemoveLastPathComponent();
425               source_path.RemoveLastPathComponent();
426               source_path.RemoveLastPathComponent();
427               module_spec.GetSourceMappingList().Append(
428                   build_path.GetPath(), source_path.GetPath(), true);
429             }
430           }
431         }
432         if (keys)
433           free(keys);
434         if (values)
435           free(values);
436       }
437     }
438 
439     // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the
440     // source remappings list.
441 
442     cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
443                                                CFSTR("DBGBuildSourcePath"));
444     if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
445       CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);
446     }
447 
448     cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,
449                                                CFSTR("DBGSourcePath"));
450     if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {
451       CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);
452     }
453 
454     if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {
455       if (DBGSourcePath[0] == '~') {
456         FileSpec resolved_source_path(DBGSourcePath.c_str());
457         FileSystem::Instance().Resolve(resolved_source_path);
458         DBGSourcePath = resolved_source_path.GetPath();
459       }
460       module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,
461                                                 DBGSourcePath, true);
462     }
463   }
464   return success;
465 }
466 
467 bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec,
468                                           bool force_lookup) {
469   bool success = false;
470   const UUID *uuid_ptr = module_spec.GetUUIDPtr();
471   const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();
472 
473   if (repro::Loader *l = repro::Reproducer::Instance().GetLoader()) {
474     static repro::SymbolFileLoader symbol_file_loader(l);
475     std::pair<FileSpec, FileSpec> paths = symbol_file_loader.GetPaths(uuid_ptr);
476     if (paths.first)
477       module_spec.GetFileSpec() = paths.first;
478     if (paths.second)
479       module_spec.GetSymbolFileSpec() = paths.second;
480     return true;
481   }
482 
483   // Lambda to capture the state of module_spec before returning from this
484   // function.
485   auto RecordResult = [&]() {
486     if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
487       g->GetOrCreate<repro::SymbolFileProvider>().AddSymbolFile(
488           uuid_ptr, module_spec.GetFileSpec(), module_spec.GetSymbolFileSpec());
489     }
490   };
491 
492   // It's expensive to check for the DBGShellCommands defaults setting, only do
493   // it once per lldb run and cache the result.
494   static bool g_have_checked_for_dbgshell_command = false;
495   static const char *g_dbgshell_command = NULL;
496   if (!g_have_checked_for_dbgshell_command) {
497     g_have_checked_for_dbgshell_command = true;
498     CFTypeRef defaults_setting = CFPreferencesCopyAppValue(
499         CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));
500     if (defaults_setting &&
501         CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {
502       char cstr_buf[PATH_MAX];
503       if (CFStringGetCString((CFStringRef)defaults_setting, cstr_buf,
504                              sizeof(cstr_buf), kCFStringEncodingUTF8)) {
505         g_dbgshell_command =
506             strdup(cstr_buf); // this malloc'ed memory will never be freed
507       }
508     }
509     if (defaults_setting) {
510       CFRelease(defaults_setting);
511     }
512   }
513 
514   // When g_dbgshell_command is NULL, the user has not enabled the use of an
515   // external program to find the symbols, don't run it for them.
516   if (!force_lookup && g_dbgshell_command == NULL) {
517     RecordResult();
518     return false;
519   }
520 
521   if (uuid_ptr ||
522       (file_spec_ptr && FileSystem::Instance().Exists(*file_spec_ptr))) {
523     static bool g_located_dsym_for_uuid_exe = false;
524     static bool g_dsym_for_uuid_exe_exists = false;
525     static char g_dsym_for_uuid_exe_path[PATH_MAX];
526     if (!g_located_dsym_for_uuid_exe) {
527       g_located_dsym_for_uuid_exe = true;
528       const char *dsym_for_uuid_exe_path_cstr =
529           getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE");
530       FileSpec dsym_for_uuid_exe_spec;
531       if (dsym_for_uuid_exe_path_cstr) {
532         dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr,
533                                        FileSpec::Style::native);
534         FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
535         g_dsym_for_uuid_exe_exists =
536             FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
537       }
538 
539       if (!g_dsym_for_uuid_exe_exists) {
540         dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID",
541                                        FileSpec::Style::native);
542         g_dsym_for_uuid_exe_exists =
543             FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
544         if (!g_dsym_for_uuid_exe_exists) {
545           long bufsize;
546           if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) {
547             char buffer[bufsize];
548             struct passwd pwd;
549             struct passwd *tilde_rc = NULL;
550             // we are a library so we need to use the reentrant version of
551             // getpwnam()
552             if (getpwnam_r("rc", &pwd, buffer, bufsize, &tilde_rc) == 0 &&
553                 tilde_rc && tilde_rc->pw_dir) {
554               std::string dsymforuuid_path(tilde_rc->pw_dir);
555               dsymforuuid_path += "/bin/dsymForUUID";
556               dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(),
557                                              FileSpec::Style::native);
558               g_dsym_for_uuid_exe_exists =
559                   FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
560             }
561           }
562         }
563       }
564       if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) {
565         dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command,
566                                        FileSpec::Style::native);
567         FileSystem::Instance().Resolve(dsym_for_uuid_exe_spec);
568         g_dsym_for_uuid_exe_exists =
569             FileSystem::Instance().Exists(dsym_for_uuid_exe_spec);
570       }
571 
572       if (g_dsym_for_uuid_exe_exists)
573         dsym_for_uuid_exe_spec.GetPath(g_dsym_for_uuid_exe_path,
574                                        sizeof(g_dsym_for_uuid_exe_path));
575     }
576     if (g_dsym_for_uuid_exe_exists) {
577       std::string uuid_str;
578       char file_path[PATH_MAX];
579       file_path[0] = '\0';
580 
581       if (uuid_ptr)
582         uuid_str = uuid_ptr->GetAsString();
583 
584       if (file_spec_ptr)
585         file_spec_ptr->GetPath(file_path, sizeof(file_path));
586 
587       StreamString command;
588       if (!uuid_str.empty())
589         command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
590                        g_dsym_for_uuid_exe_path, uuid_str.c_str());
591       else if (file_path[0] != '\0')
592         command.Printf("%s --ignoreNegativeCache --copyExecutable %s",
593                        g_dsym_for_uuid_exe_path, file_path);
594 
595       if (!command.GetString().empty()) {
596         Log *log = GetLog(LLDBLog::Host);
597         int exit_status = -1;
598         int signo = -1;
599         std::string command_output;
600         if (log) {
601           if (!uuid_str.empty())
602             LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM",
603                       g_dsym_for_uuid_exe_path, uuid_str.c_str());
604           else if (file_path[0] != '\0')
605             LLDB_LOGF(log, "Calling %s with file %s to find dSYM",
606                       g_dsym_for_uuid_exe_path, file_path);
607         }
608         Status error = Host::RunShellCommand(
609             command.GetData(),
610             FileSpec(),      // current working directory
611             &exit_status,    // Exit status
612             &signo,          // Signal int *
613             &command_output, // Command output
614             std::chrono::seconds(
615                640), // Large timeout to allow for long dsym download times
616             false);  // Don't run in a shell (we don't need shell expansion)
617         if (error.Success() && exit_status == 0 && !command_output.empty()) {
618           CFCData data(CFDataCreateWithBytesNoCopy(
619               NULL, (const UInt8 *)command_output.data(), command_output.size(),
620               kCFAllocatorNull));
621 
622           CFCReleaser<CFDictionaryRef> plist(
623               (CFDictionaryRef)::CFPropertyListCreateFromXMLData(
624                   NULL, data.get(), kCFPropertyListImmutable, NULL));
625 
626           if (plist.get() &&
627               CFGetTypeID(plist.get()) == CFDictionaryGetTypeID()) {
628             if (!uuid_str.empty()) {
629               CFCString uuid_cfstr(uuid_str.c_str());
630               CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue(
631                   plist.get(), uuid_cfstr.get());
632               success =
633                   GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec);
634             } else {
635               const CFIndex num_values = ::CFDictionaryGetCount(plist.get());
636               if (num_values > 0) {
637                 std::vector<CFStringRef> keys(num_values, NULL);
638                 std::vector<CFDictionaryRef> values(num_values, NULL);
639                 ::CFDictionaryGetKeysAndValues(plist.get(), NULL,
640                                                (const void **)&values[0]);
641                 if (num_values == 1) {
642                   success = GetModuleSpecInfoFromUUIDDictionary(values[0],
643                                                                 module_spec);
644                   RecordResult();
645                   return success;
646                 } else {
647                   for (CFIndex i = 0; i < num_values; ++i) {
648                     ModuleSpec curr_module_spec;
649                     if (GetModuleSpecInfoFromUUIDDictionary(values[i],
650                                                             curr_module_spec)) {
651                       if (module_spec.GetArchitecture().IsCompatibleMatch(
652                               curr_module_spec.GetArchitecture())) {
653                         module_spec = curr_module_spec;
654                         RecordResult();
655                         return true;
656                       }
657                     }
658                   }
659                 }
660               }
661             }
662           }
663         } else {
664           if (log) {
665             if (!uuid_str.empty())
666               LLDB_LOGF(log, "Called %s on %s, no matches",
667                         g_dsym_for_uuid_exe_path, uuid_str.c_str());
668             else if (file_path[0] != '\0')
669               LLDB_LOGF(log, "Called %s on %s, no matches",
670                         g_dsym_for_uuid_exe_path, file_path);
671           }
672         }
673       }
674     }
675   }
676   RecordResult();
677   return success;
678 }
679