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