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