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