brintos

brintos / llvm-project-archived public Read only

0
0
Text · 46.5 KiB · 1e51dda Raw
1160 lines · cpp
1//===-- SymbolLocatorDebugSymbols.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SymbolLocatorDebugSymbols.h"10 11#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"12#include "lldb/Core/Debugger.h"13#include "lldb/Core/Module.h"14#include "lldb/Core/ModuleList.h"15#include "lldb/Core/ModuleSpec.h"16#include "lldb/Core/PluginManager.h"17#include "lldb/Core/Progress.h"18#include "lldb/Core/Section.h"19#include "lldb/Host/FileSystem.h"20#include "lldb/Host/Host.h"21#include "lldb/Host/HostInfo.h"22#include "lldb/Symbol/ObjectFile.h"23#include "lldb/Target/Target.h"24#include "lldb/Utility/ArchSpec.h"25#include "lldb/Utility/DataBuffer.h"26#include "lldb/Utility/DataExtractor.h"27#include "lldb/Utility/LLDBLog.h"28#include "lldb/Utility/Log.h"29#include "lldb/Utility/StreamString.h"30#include "lldb/Utility/Timer.h"31#include "lldb/Utility/UUID.h"32 33#include "llvm/ADT/SmallSet.h"34#include "llvm/Support/FileSystem.h"35#include "llvm/Support/ThreadPool.h"36 37#include "Host/macosx/cfcpp/CFCBundle.h"38#include "Host/macosx/cfcpp/CFCData.h"39#include "Host/macosx/cfcpp/CFCReleaser.h"40#include "Host/macosx/cfcpp/CFCString.h"41 42#include "mach/machine.h"43 44#include <CoreFoundation/CoreFoundation.h>45 46#include <cstring>47#include <dirent.h>48#include <dlfcn.h>49#include <memory>50#include <optional>51#include <pwd.h>52 53using namespace lldb;54using namespace lldb_private;55 56static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)(57    CFUUIDRef uuid, CFURLRef exec_url) = nullptr;58static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) =59    nullptr;60 61LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols)62 63SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {}64 65void SymbolLocatorDebugSymbols::Initialize() {66  PluginManager::RegisterPlugin(67      GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,68      LocateExecutableObjectFile, LocateExecutableSymbolFile,69      DownloadObjectAndSymbolFile, FindSymbolFileInBundle);70}71 72void SymbolLocatorDebugSymbols::Terminate() {73  PluginManager::UnregisterPlugin(CreateInstance);74}75 76llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() {77  return "DebugSymbols symbol locator.";78}79 80SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() {81  return new SymbolLocatorDebugSymbols();82}83 84std::optional<ModuleSpec> SymbolLocatorDebugSymbols::LocateExecutableObjectFile(85    const ModuleSpec &module_spec) {86  Log *log = GetLog(LLDBLog::Host);87  if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {88    LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");89    return {};90  }91  ModuleSpec return_module_spec;92  return_module_spec = module_spec;93  return_module_spec.GetFileSpec().Clear();94  return_module_spec.GetSymbolFileSpec().Clear();95 96  const UUID *uuid = module_spec.GetUUIDPtr();97  const ArchSpec *arch = module_spec.GetArchitecturePtr();98 99  int items_found = 0;100 101  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||102      g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {103    void *handle = dlopen(104        "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",105        RTLD_LAZY | RTLD_LOCAL);106    if (handle) {107      g_dlsym_DBGCopyFullDSYMURLForUUID =108          (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,109                                                  "DBGCopyFullDSYMURLForUUID");110      g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(111          handle, "DBGCopyDSYMPropertyLists");112    }113  }114 115  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||116      g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {117    return {};118  }119 120  if (uuid && uuid->IsValid()) {121    // Try and locate the dSYM file using DebugSymbols first122    llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();123    if (module_uuid.size() == 16) {124      CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(125          NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],126          module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],127          module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],128          module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));129 130      if (module_uuid_ref.get()) {131        CFCReleaser<CFURLRef> exec_url;132        const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();133        if (exec_fspec) {134          char exec_cf_path[PATH_MAX];135          if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))136            exec_url.reset(::CFURLCreateFromFileSystemRepresentation(137                NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),138                FALSE));139        }140 141        CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(142            module_uuid_ref.get(), exec_url.get()));143        char path[PATH_MAX];144 145        if (dsym_url.get()) {146          if (::CFURLGetFileSystemRepresentation(147                  dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {148            LLDB_LOGF(log,149                      "DebugSymbols framework returned dSYM path of %s for "150                      "UUID %s -- looking for the dSYM",151                      path, uuid->GetAsString().c_str());152            FileSpec dsym_filespec(path);153            if (path[0] == '~')154              FileSystem::Instance().Resolve(dsym_filespec);155 156            if (FileSystem::Instance().IsDirectory(dsym_filespec)) {157              dsym_filespec = PluginManager::FindSymbolFileInBundle(158                  dsym_filespec, uuid, arch);159              ++items_found;160            } else {161              ++items_found;162            }163            return_module_spec.GetSymbolFileSpec() = dsym_filespec;164          }165 166          bool success = false;167          if (log) {168            if (::CFURLGetFileSystemRepresentation(169                    dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {170              LLDB_LOGF(log,171                        "DebugSymbols framework returned dSYM path of %s for "172                        "UUID %s -- looking for an exec file",173                        path, uuid->GetAsString().c_str());174            }175          }176 177          CFCReleaser<CFDictionaryRef> dict(178              g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));179          CFDictionaryRef uuid_dict = NULL;180          if (dict.get()) {181            CFCString uuid_cfstr(uuid->GetAsString().c_str());182            uuid_dict = static_cast<CFDictionaryRef>(183                ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));184          }185 186          // Check to see if we have the file on the local filesystem.187          if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {188            ModuleSpec exe_spec;189            exe_spec.GetFileSpec() = module_spec.GetFileSpec();190            exe_spec.GetUUID() = module_spec.GetUUID();191            ModuleSP module_sp;192            module_sp = std::make_shared<Module>(exe_spec);193            if (module_sp && module_sp->GetObjectFile() &&194                module_sp->MatchesModuleSpec(exe_spec)) {195              success = true;196              return_module_spec.GetFileSpec() = module_spec.GetFileSpec();197              LLDB_LOGF(log, "using original binary filepath %s for UUID %s",198                        module_spec.GetFileSpec().GetPath().c_str(),199                        uuid->GetAsString().c_str());200              ++items_found;201            }202          }203 204          // Check if the requested image is in our shared cache.205          if (!success) {206            SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(207                module_spec.GetFileSpec().GetPath());208 209            // If we found it and it has the correct UUID, let's proceed with210            // creating a module from the memory contents.211            if (image_info.uuid && (!module_spec.GetUUID() ||212                                    module_spec.GetUUID() == image_info.uuid)) {213              success = true;214              return_module_spec.GetFileSpec() = module_spec.GetFileSpec();215              LLDB_LOGF(log,216                        "using binary from shared cache for filepath %s for "217                        "UUID %s",218                        module_spec.GetFileSpec().GetPath().c_str(),219                        uuid->GetAsString().c_str());220              ++items_found;221            }222          }223 224          // Use the DBGSymbolRichExecutable filepath if present225          if (!success && uuid_dict) {226            CFStringRef exec_cf_path =227                static_cast<CFStringRef>(::CFDictionaryGetValue(228                    uuid_dict, CFSTR("DBGSymbolRichExecutable")));229            if (exec_cf_path && ::CFStringGetFileSystemRepresentation(230                                    exec_cf_path, path, sizeof(path))) {231              LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",232                        path, uuid->GetAsString().c_str());233              ++items_found;234              FileSpec exec_filespec(path);235              if (path[0] == '~')236                FileSystem::Instance().Resolve(exec_filespec);237              if (FileSystem::Instance().Exists(exec_filespec)) {238                success = true;239                return_module_spec.GetFileSpec() = exec_filespec;240              }241            }242          }243 244          // Look next to the dSYM for the binary file.245          if (!success) {246            if (::CFURLGetFileSystemRepresentation(247                    dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {248              char *dsym_extension_pos = ::strstr(path, ".dSYM");249              if (dsym_extension_pos) {250                *dsym_extension_pos = '\0';251                LLDB_LOGF(log,252                          "Looking for executable binary next to dSYM "253                          "bundle with name with name %s",254                          path);255                FileSpec file_spec(path);256                FileSystem::Instance().Resolve(file_spec);257                ModuleSpecList module_specs;258                ModuleSpec matched_module_spec;259                using namespace llvm::sys::fs;260                switch (get_file_type(file_spec.GetPath())) {261 262                case file_type::directory_file: // Bundle directory?263                {264                  CFCBundle bundle(path);265                  CFCReleaser<CFURLRef> bundle_exe_url(266                      bundle.CopyExecutableURL());267                  if (bundle_exe_url.get()) {268                    if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),269                                                           true, (UInt8 *)path,270                                                           sizeof(path) - 1)) {271                      FileSpec bundle_exe_file_spec(path);272                      FileSystem::Instance().Resolve(bundle_exe_file_spec);273                      if (ObjectFile::GetModuleSpecifications(274                              bundle_exe_file_spec, 0, 0, module_specs) &&275                          module_specs.FindMatchingModuleSpec(276                              module_spec, matched_module_spec))277 278                      {279                        ++items_found;280                        return_module_spec.GetFileSpec() = bundle_exe_file_spec;281                        LLDB_LOGF(log,282                                  "Executable binary %s next to dSYM is "283                                  "compatible; using",284                                  path);285                      }286                    }287                  }288                } break;289 290                case file_type::fifo_file:      // Forget pipes291                case file_type::socket_file:    // We can't process socket files292                case file_type::file_not_found: // File doesn't exist...293                case file_type::status_error:294                  break;295 296                case file_type::type_unknown:297                case file_type::regular_file:298                case file_type::symlink_file:299                case file_type::block_file:300                case file_type::character_file:301                  if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,302                                                          module_specs) &&303                      module_specs.FindMatchingModuleSpec(module_spec,304                                                          matched_module_spec))305 306                  {307                    ++items_found;308                    return_module_spec.GetFileSpec() = file_spec;309                    LLDB_LOGF(log,310                              "Executable binary %s next to dSYM is "311                              "compatible; using",312                              path);313                  }314                  break;315                }316              }317            }318          }319        }320      }321    }322  }323 324  if (items_found)325    return return_module_spec;326 327  return {};328}329 330std::optional<FileSpec> SymbolLocatorDebugSymbols::FindSymbolFileInBundle(331    const FileSpec &dsym_bundle_fspec, const UUID *uuid, const ArchSpec *arch) {332  std::string dsym_bundle_path = dsym_bundle_fspec.GetPath();333  llvm::SmallString<128> buffer(dsym_bundle_path);334  llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF");335 336  std::error_code EC;337  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs =338      FileSystem::Instance().GetVirtualFileSystem();339  llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC);340  llvm::vfs::recursive_directory_iterator End;341  for (; Iter != End && !EC; Iter.increment(EC)) {342    llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path());343    if (Status->isDirectory())344      continue;345 346    FileSpec dsym_fspec(Iter->path());347    ModuleSpecList module_specs;348    if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) {349      ModuleSpec spec;350      for (size_t i = 0; i < module_specs.GetSize(); ++i) {351        bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);352        assert(got_spec); // The call has side-effects so can't be inlined.353        UNUSED_IF_ASSERT_DISABLED(got_spec);354        if ((uuid == nullptr ||355             (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&356            (arch == nullptr ||357             (spec.GetArchitecturePtr() &&358              spec.GetArchitecture().IsCompatibleMatch(*arch)))) {359          return dsym_fspec;360        }361      }362    }363  }364 365  return {};366}367 368static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec,369                                          const ArchSpec *arch,370                                          const lldb_private::UUID *uuid) {371  ModuleSpecList module_specs;372  if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) {373    ModuleSpec spec;374    for (size_t i = 0; i < module_specs.GetSize(); ++i) {375      bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec);376      UNUSED_IF_ASSERT_DISABLED(got_spec);377      assert(got_spec);378      if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) &&379          (arch == nullptr ||380           (spec.GetArchitecturePtr() &&381            spec.GetArchitecture().IsCompatibleMatch(*arch)))) {382        return true;383      }384    }385  }386  return false;387}388 389// Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid,390// return true if there is a matching dSYM bundle next to the exec_fspec,391// and return that value in dsym_fspec.392// If there is a .dSYM.yaa compressed archive next to the exec_fspec,393// call through PluginManager::DownloadObjectAndSymbolFile to download the394// expanded/uncompressed dSYM and return that filepath in dsym_fspec.395static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec,396                                            const FileSpec &exec_fspec,397                                            FileSpec &dsym_fspec) {398  ConstString filename = exec_fspec.GetFilename();399  FileSpec dsym_directory = exec_fspec;400  dsym_directory.RemoveLastPathComponent();401 402  std::string dsym_filename = filename.AsCString();403  dsym_filename += ".dSYM";404  dsym_directory.AppendPathComponent(dsym_filename);405  dsym_directory.AppendPathComponent("Contents");406  dsym_directory.AppendPathComponent("Resources");407  dsym_directory.AppendPathComponent("DWARF");408 409  if (FileSystem::Instance().Exists(dsym_directory)) {410 411    // See if the binary name exists in the dSYM DWARF412    // subdir.413    dsym_fspec = dsym_directory;414    dsym_fspec.AppendPathComponent(filename.AsCString());415    if (FileSystem::Instance().Exists(dsym_fspec) &&416        FileAtPathContainsArchAndUUID(dsym_fspec, mod_spec.GetArchitecturePtr(),417                                      mod_spec.GetUUIDPtr())) {418      return true;419    }420 421    // See if we have "../CF.framework" - so we'll look for422    // CF.framework.dSYM/Contents/Resources/DWARF/CF423    // We need to drop the last suffix after '.' to match424    // 'CF' in the DWARF subdir.425    std::string binary_name(filename.AsCString());426    auto last_dot = binary_name.find_last_of('.');427    if (last_dot != std::string::npos) {428      binary_name.erase(last_dot);429      dsym_fspec = dsym_directory;430      dsym_fspec.AppendPathComponent(binary_name);431      if (FileSystem::Instance().Exists(dsym_fspec) &&432          FileAtPathContainsArchAndUUID(dsym_fspec,433                                        mod_spec.GetArchitecturePtr(),434                                        mod_spec.GetUUIDPtr())) {435        return true;436      }437    }438  }439 440  // See if we have a .dSYM.yaa next to this executable path.441  FileSpec dsym_yaa_fspec = exec_fspec;442  dsym_yaa_fspec.RemoveLastPathComponent();443  std::string dsym_yaa_filename = filename.AsCString();444  dsym_yaa_filename += ".dSYM.yaa";445  dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename);446 447  if (FileSystem::Instance().Exists(dsym_yaa_fspec)) {448    ModuleSpec mutable_mod_spec = mod_spec;449    Status error;450    if (PluginManager::DownloadObjectAndSymbolFile(mutable_mod_spec, error,451                                                   true) &&452        FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) {453      dsym_fspec = mutable_mod_spec.GetSymbolFileSpec();454      return true;455    }456  }457 458  return false;459}460 461// Given a ModuleSpec with a FileSpec and optionally uuid/architecture462// filled in, look for a .dSYM bundle next to that binary.  Returns true463// if a .dSYM bundle is found, and that path is returned in the dsym_fspec464// FileSpec.465//466// This routine looks a few directory layers above the given exec_path -467// exec_path might be /System/Library/Frameworks/CF.framework/CF and the468// dSYM might be /System/Library/Frameworks/CF.framework.dSYM.469//470// If there is a .dSYM.yaa compressed archive found next to the binary,471// we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM472static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec,473                                              FileSpec &dsym_fspec) {474  Log *log = GetLog(LLDBLog::Host);475  const FileSpec &exec_fspec = module_spec.GetFileSpec();476  if (exec_fspec) {477    if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec,478                                          dsym_fspec)) {479      if (log) {480        LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",481                  dsym_fspec.GetPath().c_str());482      }483      return true;484    } else {485      FileSpec parent_dirs = exec_fspec;486 487      // Remove the binary name from the FileSpec488      parent_dirs.RemoveLastPathComponent();489 490      // Add a ".dSYM" name to each directory component of the path,491      // stripping off components.  e.g. we may have a binary like492      // /S/L/F/Foundation.framework/Versions/A/Foundation and493      // /S/L/F/Foundation.framework.dSYM494      //495      // so we'll need to start with496      // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the497      // "A", and if that doesn't exist, strip off the "A" and try it again498      // with "Versions", etc., until we find a dSYM bundle or we've499      // stripped off enough path components that there's no need to500      // continue.501 502      for (int i = 0; i < 4; i++) {503        // Does this part of the path have a "." character - could it be a504        // bundle's top level directory?505        const char *fn = parent_dirs.GetFilename().AsCString();506        if (fn == nullptr)507          break;508        if (::strchr(fn, '.') != nullptr) {509          if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs,510                                                dsym_fspec)) {511            if (log) {512              LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s",513                        dsym_fspec.GetPath().c_str());514            }515            return true;516          }517        }518        parent_dirs.RemoveLastPathComponent();519      }520    }521  }522  dsym_fspec.Clear();523  return false;524}525 526static int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec,527                                              ModuleSpec &return_module_spec) {528  Log *log = GetLog(LLDBLog::Host);529  if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) {530    LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled.");531    return 0;532  }533 534  return_module_spec = module_spec;535  return_module_spec.GetFileSpec().Clear();536  return_module_spec.GetSymbolFileSpec().Clear();537 538  const UUID *uuid = module_spec.GetUUIDPtr();539  const ArchSpec *arch = module_spec.GetArchitecturePtr();540 541  int items_found = 0;542 543  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||544      g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {545    void *handle = dlopen(546        "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols",547        RTLD_LAZY | RTLD_LOCAL);548    if (handle) {549      g_dlsym_DBGCopyFullDSYMURLForUUID =550          (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle,551                                                  "DBGCopyFullDSYMURLForUUID");552      g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym(553          handle, "DBGCopyDSYMPropertyLists");554    }555  }556 557  if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr ||558      g_dlsym_DBGCopyDSYMPropertyLists == nullptr) {559    return items_found;560  }561 562  if (uuid && uuid->IsValid()) {563    // Try and locate the dSYM file using DebugSymbols first564    llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes();565    if (module_uuid.size() == 16) {566      CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes(567          NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3],568          module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7],569          module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11],570          module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15]));571 572      if (module_uuid_ref.get()) {573        CFCReleaser<CFURLRef> exec_url;574        const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();575        if (exec_fspec) {576          char exec_cf_path[PATH_MAX];577          if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path)))578            exec_url.reset(::CFURLCreateFromFileSystemRepresentation(579                NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path),580                FALSE));581        }582 583        CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID(584            module_uuid_ref.get(), exec_url.get()));585        char path[PATH_MAX];586 587        if (dsym_url.get()) {588          if (::CFURLGetFileSystemRepresentation(589                  dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {590            LLDB_LOGF(log,591                      "DebugSymbols framework returned dSYM path of %s for "592                      "UUID %s -- looking for the dSYM",593                      path, uuid->GetAsString().c_str());594            FileSpec dsym_filespec(path);595            if (path[0] == '~')596              FileSystem::Instance().Resolve(dsym_filespec);597 598            if (FileSystem::Instance().IsDirectory(dsym_filespec)) {599              dsym_filespec = PluginManager::FindSymbolFileInBundle(600                  dsym_filespec, uuid, arch);601              ++items_found;602            } else {603              ++items_found;604            }605            return_module_spec.GetSymbolFileSpec() = dsym_filespec;606          }607 608          bool success = false;609          if (log) {610            if (::CFURLGetFileSystemRepresentation(611                    dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {612              LLDB_LOGF(log,613                        "DebugSymbols framework returned dSYM path of %s for "614                        "UUID %s -- looking for an exec file",615                        path, uuid->GetAsString().c_str());616            }617          }618 619          CFCReleaser<CFDictionaryRef> dict(620              g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get()));621          CFDictionaryRef uuid_dict = NULL;622          if (dict.get()) {623            CFCString uuid_cfstr(uuid->GetAsString().c_str());624            uuid_dict = static_cast<CFDictionaryRef>(625                ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get()));626          }627 628          // Check to see if we have the file on the local filesystem.629          if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) {630            ModuleSpec exe_spec;631            exe_spec.GetFileSpec() = module_spec.GetFileSpec();632            exe_spec.GetUUID() = module_spec.GetUUID();633            ModuleSP module_sp;634            module_sp = std::make_shared<Module>(exe_spec);635            if (module_sp && module_sp->GetObjectFile() &&636                module_sp->MatchesModuleSpec(exe_spec)) {637              success = true;638              return_module_spec.GetFileSpec() = module_spec.GetFileSpec();639              LLDB_LOGF(log, "using original binary filepath %s for UUID %s",640                        module_spec.GetFileSpec().GetPath().c_str(),641                        uuid->GetAsString().c_str());642              ++items_found;643            }644          }645 646          // Check if the requested image is in our shared cache.647          if (!success) {648            SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(649                module_spec.GetFileSpec().GetPath());650 651            // If we found it and it has the correct UUID, let's proceed with652            // creating a module from the memory contents.653            if (image_info.uuid && (!module_spec.GetUUID() ||654                                    module_spec.GetUUID() == image_info.uuid)) {655              success = true;656              return_module_spec.GetFileSpec() = module_spec.GetFileSpec();657              LLDB_LOGF(log,658                        "using binary from shared cache for filepath %s for "659                        "UUID %s",660                        module_spec.GetFileSpec().GetPath().c_str(),661                        uuid->GetAsString().c_str());662              ++items_found;663            }664          }665 666          // Use the DBGSymbolRichExecutable filepath if present667          if (!success && uuid_dict) {668            CFStringRef exec_cf_path =669                static_cast<CFStringRef>(::CFDictionaryGetValue(670                    uuid_dict, CFSTR("DBGSymbolRichExecutable")));671            if (exec_cf_path && ::CFStringGetFileSystemRepresentation(672                                    exec_cf_path, path, sizeof(path))) {673              LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s",674                        path, uuid->GetAsString().c_str());675              ++items_found;676              FileSpec exec_filespec(path);677              if (path[0] == '~')678                FileSystem::Instance().Resolve(exec_filespec);679              if (FileSystem::Instance().Exists(exec_filespec)) {680                success = true;681                return_module_spec.GetFileSpec() = exec_filespec;682              }683            }684          }685 686          // Look next to the dSYM for the binary file.687          if (!success) {688            if (::CFURLGetFileSystemRepresentation(689                    dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) {690              char *dsym_extension_pos = ::strstr(path, ".dSYM");691              if (dsym_extension_pos) {692                *dsym_extension_pos = '\0';693                LLDB_LOGF(log,694                          "Looking for executable binary next to dSYM "695                          "bundle with name with name %s",696                          path);697                FileSpec file_spec(path);698                FileSystem::Instance().Resolve(file_spec);699                ModuleSpecList module_specs;700                ModuleSpec matched_module_spec;701                using namespace llvm::sys::fs;702                switch (get_file_type(file_spec.GetPath())) {703 704                case file_type::directory_file: // Bundle directory?705                {706                  CFCBundle bundle(path);707                  CFCReleaser<CFURLRef> bundle_exe_url(708                      bundle.CopyExecutableURL());709                  if (bundle_exe_url.get()) {710                    if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(),711                                                           true, (UInt8 *)path,712                                                           sizeof(path) - 1)) {713                      FileSpec bundle_exe_file_spec(path);714                      FileSystem::Instance().Resolve(bundle_exe_file_spec);715                      if (ObjectFile::GetModuleSpecifications(716                              bundle_exe_file_spec, 0, 0, module_specs) &&717                          module_specs.FindMatchingModuleSpec(718                              module_spec, matched_module_spec))719 720                      {721                        ++items_found;722                        return_module_spec.GetFileSpec() = bundle_exe_file_spec;723                        LLDB_LOGF(log,724                                  "Executable binary %s next to dSYM is "725                                  "compatible; using",726                                  path);727                      }728                    }729                  }730                } break;731 732                case file_type::fifo_file:      // Forget pipes733                case file_type::socket_file:    // We can't process socket files734                case file_type::file_not_found: // File doesn't exist...735                case file_type::status_error:736                  break;737 738                case file_type::type_unknown:739                case file_type::regular_file:740                case file_type::symlink_file:741                case file_type::block_file:742                case file_type::character_file:743                  if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0,744                                                          module_specs) &&745                      module_specs.FindMatchingModuleSpec(module_spec,746                                                          matched_module_spec))747 748                  {749                    ++items_found;750                    return_module_spec.GetFileSpec() = file_spec;751                    LLDB_LOGF(log,752                              "Executable binary %s next to dSYM is "753                              "compatible; using",754                              path);755                  }756                  break;757                }758              }759            }760          }761        }762      }763    }764  }765 766  return items_found;767}768 769std::optional<FileSpec> SymbolLocatorDebugSymbols::LocateExecutableSymbolFile(770    const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {771  const FileSpec *exec_fspec = module_spec.GetFileSpecPtr();772  const ArchSpec *arch = module_spec.GetArchitecturePtr();773  const UUID *uuid = module_spec.GetUUIDPtr();774 775  LLDB_SCOPED_TIMERF(776      "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)",777      exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>",778      arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid);779 780  Progress progress(781      "Locating external symbol file",782      module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"));783 784  FileSpec symbol_fspec;785  ModuleSpec dsym_module_spec;786  // First try and find the dSYM in the same directory as the executable or in787  // an appropriate parent directory788  if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) {789    // We failed to easily find the dSYM above, so use DebugSymbols790    LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec);791  } else {792    dsym_module_spec.GetSymbolFileSpec() = symbol_fspec;793  }794 795  return dsym_module_spec.GetSymbolFileSpec();796}797 798static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict,799                                                ModuleSpec &module_spec,800                                                Status &error,801                                                const std::string &command) {802  Log *log = GetLog(LLDBLog::Host);803  bool success = false;804  if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) {805    std::string str;806    CFStringRef cf_str;807    CFDictionaryRef cf_dict;808 809    cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,810                                               CFSTR("DBGError"));811    if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {812      if (CFCString::FileSystemRepresentation(cf_str, str)) {813        std::string errorstr = command;814        errorstr += ":\n";815        errorstr += str;816        error = Status(errorstr);817      }818    }819 820    cf_str = (CFStringRef)CFDictionaryGetValue(821        (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable"));822    if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {823      if (CFCString::FileSystemRepresentation(cf_str, str)) {824        module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native);825        FileSystem::Instance().Resolve(module_spec.GetFileSpec());826        LLDB_LOGF(log,827                  "From dsymForUUID plist: Symbol rich executable is at '%s'",828                  str.c_str());829      }830    }831 832    cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,833                                               CFSTR("DBGDSYMPath"));834    if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {835      if (CFCString::FileSystemRepresentation(cf_str, str)) {836        module_spec.GetSymbolFileSpec().SetFile(str.c_str(),837                                                FileSpec::Style::native);838        FileSystem::Instance().Resolve(module_spec.GetFileSpec());839        success = true;840        LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str());841      }842    }843 844    std::string DBGBuildSourcePath;845    std::string DBGSourcePath;846 847    // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping.848    // If DBGVersion 2, strip last two components of path remappings from849    //                  entries to fix an issue with a specific set of850    //                  DBGSourcePathRemapping entries that lldb worked851    //                  with.852    // If DBGVersion 3, trust & use the source path remappings as-is.853    //854    cf_dict = (CFDictionaryRef)CFDictionaryGetValue(855        (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping"));856    if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) {857      // If we see DBGVersion with a value of 2 or higher, this is a new style858      // DBGSourcePathRemapping dictionary859      bool new_style_source_remapping_dictionary = false;860      bool do_truncate_remapping_names = false;861      std::string original_DBGSourcePath_value = DBGSourcePath;862      cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,863                                                 CFSTR("DBGVersion"));864      if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {865        std::string version;866        CFCString::FileSystemRepresentation(cf_str, version);867        if (!version.empty() && isdigit(version[0])) {868          int version_number = atoi(version.c_str());869          if (version_number > 1) {870            new_style_source_remapping_dictionary = true;871          }872          if (version_number == 2) {873            do_truncate_remapping_names = true;874          }875        }876      }877 878      CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict);879      if (kv_pair_count > 0) {880        CFStringRef *keys =881            (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));882        CFStringRef *values =883            (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef));884        if (keys != nullptr && values != nullptr) {885          CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict,886                                       (const void **)keys,887                                       (const void **)values);888        }889        for (CFIndex i = 0; i < kv_pair_count; i++) {890          DBGBuildSourcePath.clear();891          DBGSourcePath.clear();892          if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) {893            CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath);894          }895          if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) {896            CFCString::FileSystemRepresentation(values[i], DBGSourcePath);897          }898          if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {899            // In the "old style" DBGSourcePathRemapping dictionary, the900            // DBGSourcePath values (the "values" half of key-value path pairs)901            // were wrong.  Ignore them and use the universal DBGSourcePath902            // string from earlier.903            if (new_style_source_remapping_dictionary &&904                !original_DBGSourcePath_value.empty()) {905              DBGSourcePath = original_DBGSourcePath_value;906            }907            if (DBGSourcePath[0] == '~') {908              FileSpec resolved_source_path(DBGSourcePath.c_str());909              FileSystem::Instance().Resolve(resolved_source_path);910              DBGSourcePath = resolved_source_path.GetPath();911            }912            // With version 2 of DBGSourcePathRemapping, we can chop off the913            // last two filename parts from the source remapping and get a more914            // general source remapping that still works. Add this as another915            // option in addition to the full source path remap.916            module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,917                                                      DBGSourcePath, true);918            if (do_truncate_remapping_names) {919              FileSpec build_path(DBGBuildSourcePath.c_str());920              FileSpec source_path(DBGSourcePath.c_str());921              build_path.RemoveLastPathComponent();922              build_path.RemoveLastPathComponent();923              source_path.RemoveLastPathComponent();924              source_path.RemoveLastPathComponent();925              module_spec.GetSourceMappingList().Append(926                  build_path.GetPath(), source_path.GetPath(), true);927            }928          }929        }930        if (keys)931          free(keys);932        if (values)933          free(values);934      }935    }936 937    // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the938    // source remappings list.939 940    cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,941                                               CFSTR("DBGBuildSourcePath"));942    if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {943      CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath);944    }945 946    cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict,947                                               CFSTR("DBGSourcePath"));948    if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) {949      CFCString::FileSystemRepresentation(cf_str, DBGSourcePath);950    }951 952    if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) {953      if (DBGSourcePath[0] == '~') {954        FileSpec resolved_source_path(DBGSourcePath.c_str());955        FileSystem::Instance().Resolve(resolved_source_path);956        DBGSourcePath = resolved_source_path.GetPath();957      }958      module_spec.GetSourceMappingList().Append(DBGBuildSourcePath,959                                                DBGSourcePath, true);960    }961  }962  return success;963}964 965/// It's expensive to check for the DBGShellCommands defaults setting. Only do966/// it once per lldb run and cache the result.967static llvm::StringRef GetDbgShellCommand() {968  static std::once_flag g_once_flag;969  static std::string g_dbgshell_command;970  std::call_once(g_once_flag, [&]() {971    CFTypeRef defaults_setting = CFPreferencesCopyAppValue(972        CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols"));973    if (defaults_setting &&974        CFGetTypeID(defaults_setting) == CFStringGetTypeID()) {975      char buffer[PATH_MAX];976      if (CFStringGetCString((CFStringRef)defaults_setting, buffer,977                             sizeof(buffer), kCFStringEncodingUTF8)) {978        g_dbgshell_command = buffer;979      }980    }981    if (defaults_setting) {982      CFRelease(defaults_setting);983    }984  });985  return g_dbgshell_command;986}987 988/// Get the dsymForUUID executable and cache the result so we don't end up989/// stat'ing the binary over and over.990static FileSpec GetDsymForUUIDExecutable() {991  // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the992  // test suite to override the dsymForUUID location. Because we must be able993  // to change the value within a single test, don't bother caching it.994  if (const char *dsymForUUID_env =995          getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) {996    FileSpec dsymForUUID_executable(dsymForUUID_env);997    FileSystem::Instance().Resolve(dsymForUUID_executable);998    if (FileSystem::Instance().Exists(dsymForUUID_executable))999      return dsymForUUID_executable;1000  }1001 1002  static std::once_flag g_once_flag;1003  static FileSpec g_dsymForUUID_executable;1004  std::call_once(g_once_flag, [&]() {1005    // Try the DBGShellCommand.1006    llvm::StringRef dbgshell_command = GetDbgShellCommand();1007    if (!dbgshell_command.empty()) {1008      g_dsymForUUID_executable = FileSpec(dbgshell_command);1009      FileSystem::Instance().Resolve(g_dsymForUUID_executable);1010      if (FileSystem::Instance().Exists(g_dsymForUUID_executable))1011        return;1012    }1013 1014    // Try dsymForUUID in /usr/local/bin1015    {1016      g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID");1017      if (FileSystem::Instance().Exists(g_dsymForUUID_executable))1018        return;1019    }1020 1021    // We couldn't find the dsymForUUID binary.1022    g_dsymForUUID_executable = {};1023  });1024  return g_dsymForUUID_executable;1025}1026 1027bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile(1028    ModuleSpec &module_spec, Status &error, bool force_lookup,1029    bool copy_executable) {1030  const UUID *uuid_ptr = module_spec.GetUUIDPtr();1031  const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr();1032 1033  // If \a dbgshell_command is set, the user has specified1034  // forced symbol lookup via that command.  We'll get the1035  // path back from GetDsymForUUIDExecutable() later.1036  llvm::StringRef dbgshell_command = GetDbgShellCommand();1037 1038  // If forced lookup isn't set, by the user's \a dbgshell_command or1039  // by the \a force_lookup argument, exit this method.1040  if (!force_lookup && dbgshell_command.empty())1041    return false;1042 1043  // We need a UUID or valid existing FileSpec.1044  if (!uuid_ptr &&1045      (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr)))1046    return false;1047 1048  // We need a dsymForUUID binary or an equivalent executable/script.1049  FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable();1050  if (!dsymForUUID_exe_spec)1051    return false;1052 1053  // Create the dsymForUUID command.1054  const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath();1055  const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : "";1056 1057  std::string lookup_arg = uuid_str;1058  if (lookup_arg.empty())1059    lookup_arg = file_spec_ptr ? file_spec_ptr->GetPath() : "";1060  if (lookup_arg.empty())1061    return false;1062 1063  StreamString command;1064  command << dsymForUUID_exe_path << " --ignoreNegativeCache ";1065  if (copy_executable)1066    command << "--copyExecutable ";1067  command << lookup_arg;1068 1069  // Log and report progress.1070  std::string lookup_desc;1071  if (uuid_ptr && file_spec_ptr)1072    lookup_desc =1073        llvm::formatv("{0} ({1})", file_spec_ptr->GetFilename().GetString(),1074                      uuid_ptr->GetAsString());1075  else if (uuid_ptr)1076    lookup_desc = uuid_ptr->GetAsString();1077  else if (file_spec_ptr)1078    lookup_desc = file_spec_ptr->GetFilename().GetString();1079 1080  Log *log = GetLog(LLDBLog::Host);1081  LLDB_LOG(log, "Calling {0} for {1} to find dSYM: {2}", dsymForUUID_exe_path,1082           lookup_desc, command.GetString());1083 1084  Progress progress("Downloading symbol file for", lookup_desc);1085 1086  // Invoke dsymForUUID.1087  int exit_status = -1;1088  int signo = -1;1089  std::string command_output;1090  error = Host::RunShellCommand(1091      command.GetData(),1092      FileSpec(),      // current working directory1093      &exit_status,    // Exit status1094      &signo,          // Signal int *1095      &command_output, // Command output1096      std::chrono::seconds(1097          640), // Large timeout to allow for long dsym download times1098      false);   // Don't run in a shell (we don't need shell expansion)1099 1100  if (error.Fail() || exit_status != 0 || command_output.empty()) {1101    LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')",1102              command.GetData(), exit_status, error.AsCString(),1103              command_output.c_str());1104    return false;1105  }1106 1107  CFCData data(1108      CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(),1109                                  command_output.size(), kCFAllocatorNull));1110 1111  CFCReleaser<CFDictionaryRef> plist(1112      (CFDictionaryRef)::CFPropertyListCreateWithData(1113          NULL, data.get(), kCFPropertyListImmutable, NULL, NULL));1114 1115  if (!plist.get()) {1116    LLDB_LOGF(log, "'%s' failed: output is not a valid plist",1117              command.GetData());1118    return false;1119  }1120 1121  if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {1122    LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary",1123              command.GetData());1124    return false;1125  }1126 1127  if (!uuid_str.empty()) {1128    CFCString uuid_cfstr(uuid_str.c_str());1129    CFDictionaryRef uuid_dict =1130        (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get());1131    return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error,1132                                               command.GetData());1133  }1134 1135  if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) {1136    std::vector<CFStringRef> keys(num_values, NULL);1137    std::vector<CFDictionaryRef> values(num_values, NULL);1138    ::CFDictionaryGetKeysAndValues(plist.get(), NULL,1139                                   (const void **)&values[0]);1140    if (num_values == 1) {1141      return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error,1142                                                 command.GetData());1143    }1144 1145    for (CFIndex i = 0; i < num_values; ++i) {1146      ModuleSpec curr_module_spec;1147      if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec,1148                                              error, command.GetData())) {1149        if (module_spec.GetArchitecture().IsCompatibleMatch(1150                curr_module_spec.GetArchitecture())) {1151          module_spec = curr_module_spec;1152          return true;1153        }1154      }1155    }1156  }1157 1158  return false;1159}1160