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