| //===-- SymbolLocatorDebugSymbols.cpp -------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SymbolLocatorDebugSymbols.h" |
| |
| #include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ModuleList.h" |
| #include "lldb/Core/ModuleSpec.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Progress.h" |
| #include "lldb/Core/Section.h" |
| #include "lldb/Host/FileSystem.h" |
| #include "lldb/Host/Host.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/ArchSpec.h" |
| #include "lldb/Utility/DataBuffer.h" |
| #include "lldb/Utility/DataExtractor.h" |
| #include "lldb/Utility/LLDBLog.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/StreamString.h" |
| #include "lldb/Utility/Timer.h" |
| #include "lldb/Utility/UUID.h" |
| |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/ThreadPool.h" |
| |
| #include "Host/macosx/cfcpp/CFCBundle.h" |
| #include "Host/macosx/cfcpp/CFCData.h" |
| #include "Host/macosx/cfcpp/CFCReleaser.h" |
| #include "Host/macosx/cfcpp/CFCString.h" |
| |
| #include "mach/machine.h" |
| |
| #include <CoreFoundation/CoreFoundation.h> |
| |
| #include <cstring> |
| #include <dirent.h> |
| #include <dlfcn.h> |
| #include <optional> |
| #include <pwd.h> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)( |
| CFUUIDRef uuid, CFURLRef exec_url) = nullptr; |
| static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = |
| nullptr; |
| |
| LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols) |
| |
| SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {} |
| |
| void SymbolLocatorDebugSymbols::Initialize() { |
| PluginManager::RegisterPlugin( |
| GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, |
| LocateExecutableObjectFile, LocateExecutableSymbolFile, |
| DownloadObjectAndSymbolFile, FindSymbolFileInBundle); |
| } |
| |
| void SymbolLocatorDebugSymbols::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() { |
| return "DebugSymbols symbol locator."; |
| } |
| |
| SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() { |
| return new SymbolLocatorDebugSymbols(); |
| } |
| |
| std::optional<ModuleSpec> SymbolLocatorDebugSymbols::LocateExecutableObjectFile( |
| const ModuleSpec &module_spec) { |
| Log *log = GetLog(LLDBLog::Host); |
| if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) { |
| LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled."); |
| return {}; |
| } |
| ModuleSpec return_module_spec; |
| return_module_spec = module_spec; |
| return_module_spec.GetFileSpec().Clear(); |
| return_module_spec.GetSymbolFileSpec().Clear(); |
| |
| const UUID *uuid = module_spec.GetUUIDPtr(); |
| const ArchSpec *arch = module_spec.GetArchitecturePtr(); |
| |
| int items_found = 0; |
| |
| if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || |
| g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { |
| void *handle = dlopen( |
| "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", |
| RTLD_LAZY | RTLD_LOCAL); |
| if (handle) { |
| g_dlsym_DBGCopyFullDSYMURLForUUID = |
| (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle, |
| "DBGCopyFullDSYMURLForUUID"); |
| g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym( |
| handle, "DBGCopyDSYMPropertyLists"); |
| } |
| } |
| |
| if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || |
| g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { |
| return {}; |
| } |
| |
| if (uuid && uuid->IsValid()) { |
| // Try and locate the dSYM file using DebugSymbols first |
| llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes(); |
| if (module_uuid.size() == 16) { |
| CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes( |
| NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3], |
| module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7], |
| module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11], |
| module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15])); |
| |
| if (module_uuid_ref.get()) { |
| CFCReleaser<CFURLRef> exec_url; |
| const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); |
| if (exec_fspec) { |
| char exec_cf_path[PATH_MAX]; |
| if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) |
| exec_url.reset(::CFURLCreateFromFileSystemRepresentation( |
| NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path), |
| FALSE)); |
| } |
| |
| CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID( |
| module_uuid_ref.get(), exec_url.get())); |
| char path[PATH_MAX]; |
| |
| if (dsym_url.get()) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| LLDB_LOGF(log, |
| "DebugSymbols framework returned dSYM path of %s for " |
| "UUID %s -- looking for the dSYM", |
| path, uuid->GetAsString().c_str()); |
| FileSpec dsym_filespec(path); |
| if (path[0] == '~') |
| FileSystem::Instance().Resolve(dsym_filespec); |
| |
| if (FileSystem::Instance().IsDirectory(dsym_filespec)) { |
| dsym_filespec = PluginManager::FindSymbolFileInBundle( |
| dsym_filespec, uuid, arch); |
| ++items_found; |
| } else { |
| ++items_found; |
| } |
| return_module_spec.GetSymbolFileSpec() = dsym_filespec; |
| } |
| |
| bool success = false; |
| if (log) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| LLDB_LOGF(log, |
| "DebugSymbols framework returned dSYM path of %s for " |
| "UUID %s -- looking for an exec file", |
| path, uuid->GetAsString().c_str()); |
| } |
| } |
| |
| CFCReleaser<CFDictionaryRef> dict( |
| g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get())); |
| CFDictionaryRef uuid_dict = NULL; |
| if (dict.get()) { |
| CFCString uuid_cfstr(uuid->GetAsString().c_str()); |
| uuid_dict = static_cast<CFDictionaryRef>( |
| ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get())); |
| } |
| |
| // Check to see if we have the file on the local filesystem. |
| if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { |
| ModuleSpec exe_spec; |
| exe_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| exe_spec.GetUUID() = module_spec.GetUUID(); |
| ModuleSP module_sp; |
| module_sp.reset(new Module(exe_spec)); |
| if (module_sp && module_sp->GetObjectFile() && |
| module_sp->MatchesModuleSpec(exe_spec)) { |
| success = true; |
| return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| LLDB_LOGF(log, "using original binary filepath %s for UUID %s", |
| module_spec.GetFileSpec().GetPath().c_str(), |
| uuid->GetAsString().c_str()); |
| ++items_found; |
| } |
| } |
| |
| // Check if the requested image is in our shared cache. |
| if (!success) { |
| SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo( |
| module_spec.GetFileSpec().GetPath()); |
| |
| // If we found it and it has the correct UUID, let's proceed with |
| // creating a module from the memory contents. |
| if (image_info.uuid && (!module_spec.GetUUID() || |
| module_spec.GetUUID() == image_info.uuid)) { |
| success = true; |
| return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| LLDB_LOGF(log, |
| "using binary from shared cache for filepath %s for " |
| "UUID %s", |
| module_spec.GetFileSpec().GetPath().c_str(), |
| uuid->GetAsString().c_str()); |
| ++items_found; |
| } |
| } |
| |
| // Use the DBGSymbolRichExecutable filepath if present |
| if (!success && uuid_dict) { |
| CFStringRef exec_cf_path = |
| static_cast<CFStringRef>(::CFDictionaryGetValue( |
| uuid_dict, CFSTR("DBGSymbolRichExecutable"))); |
| if (exec_cf_path && ::CFStringGetFileSystemRepresentation( |
| exec_cf_path, path, sizeof(path))) { |
| LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s", |
| path, uuid->GetAsString().c_str()); |
| ++items_found; |
| FileSpec exec_filespec(path); |
| if (path[0] == '~') |
| FileSystem::Instance().Resolve(exec_filespec); |
| if (FileSystem::Instance().Exists(exec_filespec)) { |
| success = true; |
| return_module_spec.GetFileSpec() = exec_filespec; |
| } |
| } |
| } |
| |
| // Look next to the dSYM for the binary file. |
| if (!success) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| char *dsym_extension_pos = ::strstr(path, ".dSYM"); |
| if (dsym_extension_pos) { |
| *dsym_extension_pos = '\0'; |
| LLDB_LOGF(log, |
| "Looking for executable binary next to dSYM " |
| "bundle with name with name %s", |
| path); |
| FileSpec file_spec(path); |
| FileSystem::Instance().Resolve(file_spec); |
| ModuleSpecList module_specs; |
| ModuleSpec matched_module_spec; |
| using namespace llvm::sys::fs; |
| switch (get_file_type(file_spec.GetPath())) { |
| |
| case file_type::directory_file: // Bundle directory? |
| { |
| CFCBundle bundle(path); |
| CFCReleaser<CFURLRef> bundle_exe_url( |
| bundle.CopyExecutableURL()); |
| if (bundle_exe_url.get()) { |
| if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(), |
| true, (UInt8 *)path, |
| sizeof(path) - 1)) { |
| FileSpec bundle_exe_file_spec(path); |
| FileSystem::Instance().Resolve(bundle_exe_file_spec); |
| if (ObjectFile::GetModuleSpecifications( |
| bundle_exe_file_spec, 0, 0, module_specs) && |
| module_specs.FindMatchingModuleSpec( |
| module_spec, matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = bundle_exe_file_spec; |
| LLDB_LOGF(log, |
| "Executable binary %s next to dSYM is " |
| "compatible; using", |
| path); |
| } |
| } |
| } |
| } break; |
| |
| case file_type::fifo_file: // Forget pipes |
| case file_type::socket_file: // We can't process socket files |
| case file_type::file_not_found: // File doesn't exist... |
| case file_type::status_error: |
| break; |
| |
| case file_type::type_unknown: |
| case file_type::regular_file: |
| case file_type::symlink_file: |
| case file_type::block_file: |
| case file_type::character_file: |
| if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, |
| module_specs) && |
| module_specs.FindMatchingModuleSpec(module_spec, |
| matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = file_spec; |
| LLDB_LOGF(log, |
| "Executable binary %s next to dSYM is " |
| "compatible; using", |
| path); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (items_found) |
| return return_module_spec; |
| |
| return {}; |
| } |
| |
| std::optional<FileSpec> SymbolLocatorDebugSymbols::FindSymbolFileInBundle( |
| const FileSpec &dsym_bundle_fspec, const UUID *uuid, const ArchSpec *arch) { |
| std::string dsym_bundle_path = dsym_bundle_fspec.GetPath(); |
| llvm::SmallString<128> buffer(dsym_bundle_path); |
| llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF"); |
| |
| std::error_code EC; |
| llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = |
| FileSystem::Instance().GetVirtualFileSystem(); |
| llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC); |
| llvm::vfs::recursive_directory_iterator End; |
| for (; Iter != End && !EC; Iter.increment(EC)) { |
| llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path()); |
| if (Status->isDirectory()) |
| continue; |
| |
| FileSpec dsym_fspec(Iter->path()); |
| ModuleSpecList module_specs; |
| if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) { |
| ModuleSpec spec; |
| for (size_t i = 0; i < module_specs.GetSize(); ++i) { |
| bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec); |
| assert(got_spec); // The call has side-effects so can't be inlined. |
| UNUSED_IF_ASSERT_DISABLED(got_spec); |
| if ((uuid == nullptr || |
| (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && |
| (arch == nullptr || |
| (spec.GetArchitecturePtr() && |
| spec.GetArchitecture().IsCompatibleMatch(*arch)))) { |
| return dsym_fspec; |
| } |
| } |
| } |
| } |
| |
| return {}; |
| } |
| |
| static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec, |
| const ArchSpec *arch, |
| const lldb_private::UUID *uuid) { |
| ModuleSpecList module_specs; |
| if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) { |
| ModuleSpec spec; |
| for (size_t i = 0; i < module_specs.GetSize(); ++i) { |
| bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec); |
| UNUSED_IF_ASSERT_DISABLED(got_spec); |
| assert(got_spec); |
| if ((uuid == nullptr || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && |
| (arch == nullptr || |
| (spec.GetArchitecturePtr() && |
| spec.GetArchitecture().IsCompatibleMatch(*arch)))) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Given a binary exec_fspec, and a ModuleSpec with an architecture/uuid, |
| // return true if there is a matching dSYM bundle next to the exec_fspec, |
| // and return that value in dsym_fspec. |
| // If there is a .dSYM.yaa compressed archive next to the exec_fspec, |
| // call through PluginManager::DownloadObjectAndSymbolFile to download the |
| // expanded/uncompressed dSYM and return that filepath in dsym_fspec. |
| static bool LookForDsymNextToExecutablePath(const ModuleSpec &mod_spec, |
| const FileSpec &exec_fspec, |
| FileSpec &dsym_fspec) { |
| ConstString filename = exec_fspec.GetFilename(); |
| FileSpec dsym_directory = exec_fspec; |
| dsym_directory.RemoveLastPathComponent(); |
| |
| std::string dsym_filename = filename.AsCString(); |
| dsym_filename += ".dSYM"; |
| dsym_directory.AppendPathComponent(dsym_filename); |
| dsym_directory.AppendPathComponent("Contents"); |
| dsym_directory.AppendPathComponent("Resources"); |
| dsym_directory.AppendPathComponent("DWARF"); |
| |
| if (FileSystem::Instance().Exists(dsym_directory)) { |
| |
| // See if the binary name exists in the dSYM DWARF |
| // subdir. |
| dsym_fspec = dsym_directory; |
| dsym_fspec.AppendPathComponent(filename.AsCString()); |
| if (FileSystem::Instance().Exists(dsym_fspec) && |
| FileAtPathContainsArchAndUUID(dsym_fspec, mod_spec.GetArchitecturePtr(), |
| mod_spec.GetUUIDPtr())) { |
| return true; |
| } |
| |
| // See if we have "../CF.framework" - so we'll look for |
| // CF.framework.dSYM/Contents/Resources/DWARF/CF |
| // We need to drop the last suffix after '.' to match |
| // 'CF' in the DWARF subdir. |
| std::string binary_name(filename.AsCString()); |
| auto last_dot = binary_name.find_last_of('.'); |
| if (last_dot != std::string::npos) { |
| binary_name.erase(last_dot); |
| dsym_fspec = dsym_directory; |
| dsym_fspec.AppendPathComponent(binary_name); |
| if (FileSystem::Instance().Exists(dsym_fspec) && |
| FileAtPathContainsArchAndUUID(dsym_fspec, |
| mod_spec.GetArchitecturePtr(), |
| mod_spec.GetUUIDPtr())) { |
| return true; |
| } |
| } |
| } |
| |
| // See if we have a .dSYM.yaa next to this executable path. |
| FileSpec dsym_yaa_fspec = exec_fspec; |
| dsym_yaa_fspec.RemoveLastPathComponent(); |
| std::string dsym_yaa_filename = filename.AsCString(); |
| dsym_yaa_filename += ".dSYM.yaa"; |
| dsym_yaa_fspec.AppendPathComponent(dsym_yaa_filename); |
| |
| if (FileSystem::Instance().Exists(dsym_yaa_fspec)) { |
| ModuleSpec mutable_mod_spec = mod_spec; |
| Status error; |
| if (PluginManager::DownloadObjectAndSymbolFile(mutable_mod_spec, error, |
| true) && |
| FileSystem::Instance().Exists(mutable_mod_spec.GetSymbolFileSpec())) { |
| dsym_fspec = mutable_mod_spec.GetSymbolFileSpec(); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| // Given a ModuleSpec with a FileSpec and optionally uuid/architecture |
| // filled in, look for a .dSYM bundle next to that binary. Returns true |
| // if a .dSYM bundle is found, and that path is returned in the dsym_fspec |
| // FileSpec. |
| // |
| // This routine looks a few directory layers above the given exec_path - |
| // exec_path might be /System/Library/Frameworks/CF.framework/CF and the |
| // dSYM might be /System/Library/Frameworks/CF.framework.dSYM. |
| // |
| // If there is a .dSYM.yaa compressed archive found next to the binary, |
| // we'll call DownloadObjectAndSymbolFile to expand it into a plain .dSYM |
| static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec, |
| FileSpec &dsym_fspec) { |
| Log *log = GetLog(LLDBLog::Host); |
| const FileSpec &exec_fspec = module_spec.GetFileSpec(); |
| if (exec_fspec) { |
| if (::LookForDsymNextToExecutablePath(module_spec, exec_fspec, |
| dsym_fspec)) { |
| if (log) { |
| LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s", |
| dsym_fspec.GetPath().c_str()); |
| } |
| return true; |
| } else { |
| FileSpec parent_dirs = exec_fspec; |
| |
| // Remove the binary name from the FileSpec |
| parent_dirs.RemoveLastPathComponent(); |
| |
| // Add a ".dSYM" name to each directory component of the path, |
| // stripping off components. e.g. we may have a binary like |
| // /S/L/F/Foundation.framework/Versions/A/Foundation and |
| // /S/L/F/Foundation.framework.dSYM |
| // |
| // so we'll need to start with |
| // /S/L/F/Foundation.framework/Versions/A, add the .dSYM part to the |
| // "A", and if that doesn't exist, strip off the "A" and try it again |
| // with "Versions", etc., until we find a dSYM bundle or we've |
| // stripped off enough path components that there's no need to |
| // continue. |
| |
| for (int i = 0; i < 4; i++) { |
| // Does this part of the path have a "." character - could it be a |
| // bundle's top level directory? |
| const char *fn = parent_dirs.GetFilename().AsCString(); |
| if (fn == nullptr) |
| break; |
| if (::strchr(fn, '.') != nullptr) { |
| if (::LookForDsymNextToExecutablePath(module_spec, parent_dirs, |
| dsym_fspec)) { |
| if (log) { |
| LLDB_LOGF(log, "dSYM with matching UUID & arch found at %s", |
| dsym_fspec.GetPath().c_str()); |
| } |
| return true; |
| } |
| } |
| parent_dirs.RemoveLastPathComponent(); |
| } |
| } |
| } |
| dsym_fspec.Clear(); |
| return false; |
| } |
| |
| static int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, |
| ModuleSpec &return_module_spec) { |
| Log *log = GetLog(LLDBLog::Host); |
| if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) { |
| LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled."); |
| return 0; |
| } |
| |
| return_module_spec = module_spec; |
| return_module_spec.GetFileSpec().Clear(); |
| return_module_spec.GetSymbolFileSpec().Clear(); |
| |
| const UUID *uuid = module_spec.GetUUIDPtr(); |
| const ArchSpec *arch = module_spec.GetArchitecturePtr(); |
| |
| int items_found = 0; |
| |
| if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || |
| g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { |
| void *handle = dlopen( |
| "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", |
| RTLD_LAZY | RTLD_LOCAL); |
| if (handle) { |
| g_dlsym_DBGCopyFullDSYMURLForUUID = |
| (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle, |
| "DBGCopyFullDSYMURLForUUID"); |
| g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym( |
| handle, "DBGCopyDSYMPropertyLists"); |
| } |
| } |
| |
| if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || |
| g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { |
| return items_found; |
| } |
| |
| if (uuid && uuid->IsValid()) { |
| // Try and locate the dSYM file using DebugSymbols first |
| llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes(); |
| if (module_uuid.size() == 16) { |
| CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes( |
| NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3], |
| module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7], |
| module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11], |
| module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15])); |
| |
| if (module_uuid_ref.get()) { |
| CFCReleaser<CFURLRef> exec_url; |
| const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); |
| if (exec_fspec) { |
| char exec_cf_path[PATH_MAX]; |
| if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) |
| exec_url.reset(::CFURLCreateFromFileSystemRepresentation( |
| NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path), |
| FALSE)); |
| } |
| |
| CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID( |
| module_uuid_ref.get(), exec_url.get())); |
| char path[PATH_MAX]; |
| |
| if (dsym_url.get()) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| LLDB_LOGF(log, |
| "DebugSymbols framework returned dSYM path of %s for " |
| "UUID %s -- looking for the dSYM", |
| path, uuid->GetAsString().c_str()); |
| FileSpec dsym_filespec(path); |
| if (path[0] == '~') |
| FileSystem::Instance().Resolve(dsym_filespec); |
| |
| if (FileSystem::Instance().IsDirectory(dsym_filespec)) { |
| dsym_filespec = PluginManager::FindSymbolFileInBundle( |
| dsym_filespec, uuid, arch); |
| ++items_found; |
| } else { |
| ++items_found; |
| } |
| return_module_spec.GetSymbolFileSpec() = dsym_filespec; |
| } |
| |
| bool success = false; |
| if (log) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| LLDB_LOGF(log, |
| "DebugSymbols framework returned dSYM path of %s for " |
| "UUID %s -- looking for an exec file", |
| path, uuid->GetAsString().c_str()); |
| } |
| } |
| |
| CFCReleaser<CFDictionaryRef> dict( |
| g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get())); |
| CFDictionaryRef uuid_dict = NULL; |
| if (dict.get()) { |
| CFCString uuid_cfstr(uuid->GetAsString().c_str()); |
| uuid_dict = static_cast<CFDictionaryRef>( |
| ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get())); |
| } |
| |
| // Check to see if we have the file on the local filesystem. |
| if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { |
| ModuleSpec exe_spec; |
| exe_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| exe_spec.GetUUID() = module_spec.GetUUID(); |
| ModuleSP module_sp; |
| module_sp.reset(new Module(exe_spec)); |
| if (module_sp && module_sp->GetObjectFile() && |
| module_sp->MatchesModuleSpec(exe_spec)) { |
| success = true; |
| return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| LLDB_LOGF(log, "using original binary filepath %s for UUID %s", |
| module_spec.GetFileSpec().GetPath().c_str(), |
| uuid->GetAsString().c_str()); |
| ++items_found; |
| } |
| } |
| |
| // Check if the requested image is in our shared cache. |
| if (!success) { |
| SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo( |
| module_spec.GetFileSpec().GetPath()); |
| |
| // If we found it and it has the correct UUID, let's proceed with |
| // creating a module from the memory contents. |
| if (image_info.uuid && (!module_spec.GetUUID() || |
| module_spec.GetUUID() == image_info.uuid)) { |
| success = true; |
| return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); |
| LLDB_LOGF(log, |
| "using binary from shared cache for filepath %s for " |
| "UUID %s", |
| module_spec.GetFileSpec().GetPath().c_str(), |
| uuid->GetAsString().c_str()); |
| ++items_found; |
| } |
| } |
| |
| // Use the DBGSymbolRichExecutable filepath if present |
| if (!success && uuid_dict) { |
| CFStringRef exec_cf_path = |
| static_cast<CFStringRef>(::CFDictionaryGetValue( |
| uuid_dict, CFSTR("DBGSymbolRichExecutable"))); |
| if (exec_cf_path && ::CFStringGetFileSystemRepresentation( |
| exec_cf_path, path, sizeof(path))) { |
| LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s", |
| path, uuid->GetAsString().c_str()); |
| ++items_found; |
| FileSpec exec_filespec(path); |
| if (path[0] == '~') |
| FileSystem::Instance().Resolve(exec_filespec); |
| if (FileSystem::Instance().Exists(exec_filespec)) { |
| success = true; |
| return_module_spec.GetFileSpec() = exec_filespec; |
| } |
| } |
| } |
| |
| // Look next to the dSYM for the binary file. |
| if (!success) { |
| if (::CFURLGetFileSystemRepresentation( |
| dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { |
| char *dsym_extension_pos = ::strstr(path, ".dSYM"); |
| if (dsym_extension_pos) { |
| *dsym_extension_pos = '\0'; |
| LLDB_LOGF(log, |
| "Looking for executable binary next to dSYM " |
| "bundle with name with name %s", |
| path); |
| FileSpec file_spec(path); |
| FileSystem::Instance().Resolve(file_spec); |
| ModuleSpecList module_specs; |
| ModuleSpec matched_module_spec; |
| using namespace llvm::sys::fs; |
| switch (get_file_type(file_spec.GetPath())) { |
| |
| case file_type::directory_file: // Bundle directory? |
| { |
| CFCBundle bundle(path); |
| CFCReleaser<CFURLRef> bundle_exe_url( |
| bundle.CopyExecutableURL()); |
| if (bundle_exe_url.get()) { |
| if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(), |
| true, (UInt8 *)path, |
| sizeof(path) - 1)) { |
| FileSpec bundle_exe_file_spec(path); |
| FileSystem::Instance().Resolve(bundle_exe_file_spec); |
| if (ObjectFile::GetModuleSpecifications( |
| bundle_exe_file_spec, 0, 0, module_specs) && |
| module_specs.FindMatchingModuleSpec( |
| module_spec, matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = bundle_exe_file_spec; |
| LLDB_LOGF(log, |
| "Executable binary %s next to dSYM is " |
| "compatible; using", |
| path); |
| } |
| } |
| } |
| } break; |
| |
| case file_type::fifo_file: // Forget pipes |
| case file_type::socket_file: // We can't process socket files |
| case file_type::file_not_found: // File doesn't exist... |
| case file_type::status_error: |
| break; |
| |
| case file_type::type_unknown: |
| case file_type::regular_file: |
| case file_type::symlink_file: |
| case file_type::block_file: |
| case file_type::character_file: |
| if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, |
| module_specs) && |
| module_specs.FindMatchingModuleSpec(module_spec, |
| matched_module_spec)) |
| |
| { |
| ++items_found; |
| return_module_spec.GetFileSpec() = file_spec; |
| LLDB_LOGF(log, |
| "Executable binary %s next to dSYM is " |
| "compatible; using", |
| path); |
| } |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return items_found; |
| } |
| |
| std::optional<FileSpec> SymbolLocatorDebugSymbols::LocateExecutableSymbolFile( |
| const ModuleSpec &module_spec, const FileSpecList &default_search_paths) { |
| const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); |
| const ArchSpec *arch = module_spec.GetArchitecturePtr(); |
| const UUID *uuid = module_spec.GetUUIDPtr(); |
| |
| LLDB_SCOPED_TIMERF( |
| "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)", |
| exec_fspec ? exec_fspec->GetFilename().AsCString("<NULL>") : "<NULL>", |
| arch ? arch->GetArchitectureName() : "<NULL>", (const void *)uuid); |
| |
| Progress progress( |
| "Locating external symbol file", |
| module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>")); |
| |
| FileSpec symbol_fspec; |
| ModuleSpec dsym_module_spec; |
| // First try and find the dSYM in the same directory as the executable or in |
| // an appropriate parent directory |
| if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) { |
| // We failed to easily find the dSYM above, so use DebugSymbols |
| LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec); |
| } else { |
| dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; |
| } |
| |
| return dsym_module_spec.GetSymbolFileSpec(); |
| } |
| |
| static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, |
| ModuleSpec &module_spec, |
| Status &error, |
| const std::string &command) { |
| Log *log = GetLog(LLDBLog::Host); |
| bool success = false; |
| if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) { |
| std::string str; |
| CFStringRef cf_str; |
| CFDictionaryRef cf_dict; |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, |
| CFSTR("DBGError")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) { |
| std::string errorstr = command; |
| errorstr += ":\n"; |
| errorstr += str; |
| error.SetErrorString(errorstr); |
| } |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue( |
| (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) { |
| module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native); |
| FileSystem::Instance().Resolve(module_spec.GetFileSpec()); |
| LLDB_LOGF(log, |
| "From dsymForUUID plist: Symbol rich executable is at '%s'", |
| str.c_str()); |
| } |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, |
| CFSTR("DBGDSYMPath")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| if (CFCString::FileSystemRepresentation(cf_str, str)) { |
| module_spec.GetSymbolFileSpec().SetFile(str.c_str(), |
| FileSpec::Style::native); |
| FileSystem::Instance().Resolve(module_spec.GetFileSpec()); |
| success = true; |
| LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str()); |
| } |
| } |
| |
| std::string DBGBuildSourcePath; |
| std::string DBGSourcePath; |
| |
| // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping. |
| // If DBGVersion 2, strip last two components of path remappings from |
| // entries to fix an issue with a specific set of |
| // DBGSourcePathRemapping entries that lldb worked |
| // with. |
| // If DBGVersion 3, trust & use the source path remappings as-is. |
| // |
| cf_dict = (CFDictionaryRef)CFDictionaryGetValue( |
| (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping")); |
| if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) { |
| // If we see DBGVersion with a value of 2 or higher, this is a new style |
| // DBGSourcePathRemapping dictionary |
| bool new_style_source_remapping_dictionary = false; |
| bool do_truncate_remapping_names = false; |
| std::string original_DBGSourcePath_value = DBGSourcePath; |
| cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, |
| CFSTR("DBGVersion")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| std::string version; |
| CFCString::FileSystemRepresentation(cf_str, version); |
| if (!version.empty() && isdigit(version[0])) { |
| int version_number = atoi(version.c_str()); |
| if (version_number > 1) { |
| new_style_source_remapping_dictionary = true; |
| } |
| if (version_number == 2) { |
| do_truncate_remapping_names = true; |
| } |
| } |
| } |
| |
| CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict); |
| if (kv_pair_count > 0) { |
| CFStringRef *keys = |
| (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); |
| CFStringRef *values = |
| (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); |
| if (keys != nullptr && values != nullptr) { |
| CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict, |
| (const void **)keys, |
| (const void **)values); |
| } |
| for (CFIndex i = 0; i < kv_pair_count; i++) { |
| DBGBuildSourcePath.clear(); |
| DBGSourcePath.clear(); |
| if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) { |
| CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath); |
| } |
| if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) { |
| CFCString::FileSystemRepresentation(values[i], DBGSourcePath); |
| } |
| if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { |
| // In the "old style" DBGSourcePathRemapping dictionary, the |
| // DBGSourcePath values (the "values" half of key-value path pairs) |
| // were wrong. Ignore them and use the universal DBGSourcePath |
| // string from earlier. |
| if (new_style_source_remapping_dictionary && |
| !original_DBGSourcePath_value.empty()) { |
| DBGSourcePath = original_DBGSourcePath_value; |
| } |
| if (DBGSourcePath[0] == '~') { |
| FileSpec resolved_source_path(DBGSourcePath.c_str()); |
| FileSystem::Instance().Resolve(resolved_source_path); |
| DBGSourcePath = resolved_source_path.GetPath(); |
| } |
| // With version 2 of DBGSourcePathRemapping, we can chop off the |
| // last two filename parts from the source remapping and get a more |
| // general source remapping that still works. Add this as another |
| // option in addition to the full source path remap. |
| module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, |
| DBGSourcePath, true); |
| if (do_truncate_remapping_names) { |
| FileSpec build_path(DBGBuildSourcePath.c_str()); |
| FileSpec source_path(DBGSourcePath.c_str()); |
| build_path.RemoveLastPathComponent(); |
| build_path.RemoveLastPathComponent(); |
| source_path.RemoveLastPathComponent(); |
| source_path.RemoveLastPathComponent(); |
| module_spec.GetSourceMappingList().Append( |
| build_path.GetPath(), source_path.GetPath(), true); |
| } |
| } |
| } |
| if (keys) |
| free(keys); |
| if (values) |
| free(values); |
| } |
| } |
| |
| // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the |
| // source remappings list. |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, |
| CFSTR("DBGBuildSourcePath")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); |
| } |
| |
| cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, |
| CFSTR("DBGSourcePath")); |
| if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { |
| CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); |
| } |
| |
| if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { |
| if (DBGSourcePath[0] == '~') { |
| FileSpec resolved_source_path(DBGSourcePath.c_str()); |
| FileSystem::Instance().Resolve(resolved_source_path); |
| DBGSourcePath = resolved_source_path.GetPath(); |
| } |
| module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, |
| DBGSourcePath, true); |
| } |
| } |
| return success; |
| } |
| |
| /// It's expensive to check for the DBGShellCommands defaults setting. Only do |
| /// it once per lldb run and cache the result. |
| static llvm::StringRef GetDbgShellCommand() { |
| static std::once_flag g_once_flag; |
| static std::string g_dbgshell_command; |
| std::call_once(g_once_flag, [&]() { |
| CFTypeRef defaults_setting = CFPreferencesCopyAppValue( |
| CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols")); |
| if (defaults_setting && |
| CFGetTypeID(defaults_setting) == CFStringGetTypeID()) { |
| char buffer[PATH_MAX]; |
| if (CFStringGetCString((CFStringRef)defaults_setting, buffer, |
| sizeof(buffer), kCFStringEncodingUTF8)) { |
| g_dbgshell_command = buffer; |
| } |
| } |
| if (defaults_setting) { |
| CFRelease(defaults_setting); |
| } |
| }); |
| return g_dbgshell_command; |
| } |
| |
| /// Get the dsymForUUID executable and cache the result so we don't end up |
| /// stat'ing the binary over and over. |
| static FileSpec GetDsymForUUIDExecutable() { |
| // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the |
| // test suite to override the dsymForUUID location. Because we must be able |
| // to change the value within a single test, don't bother caching it. |
| if (const char *dsymForUUID_env = |
| getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) { |
| FileSpec dsymForUUID_executable(dsymForUUID_env); |
| FileSystem::Instance().Resolve(dsymForUUID_executable); |
| if (FileSystem::Instance().Exists(dsymForUUID_executable)) |
| return dsymForUUID_executable; |
| } |
| |
| static std::once_flag g_once_flag; |
| static FileSpec g_dsymForUUID_executable; |
| std::call_once(g_once_flag, [&]() { |
| // Try the DBGShellCommand. |
| llvm::StringRef dbgshell_command = GetDbgShellCommand(); |
| if (!dbgshell_command.empty()) { |
| g_dsymForUUID_executable = FileSpec(dbgshell_command); |
| FileSystem::Instance().Resolve(g_dsymForUUID_executable); |
| if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) |
| return; |
| } |
| |
| // Try dsymForUUID in /usr/local/bin |
| { |
| g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID"); |
| if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) |
| return; |
| } |
| |
| // We couldn't find the dsymForUUID binary. |
| g_dsymForUUID_executable = {}; |
| }); |
| return g_dsymForUUID_executable; |
| } |
| |
| bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile( |
| ModuleSpec &module_spec, Status &error, bool force_lookup, |
| bool copy_executable) { |
| const UUID *uuid_ptr = module_spec.GetUUIDPtr(); |
| const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); |
| |
| // If \a dbgshell_command is set, the user has specified |
| // forced symbol lookup via that command. We'll get the |
| // path back from GetDsymForUUIDExecutable() later. |
| llvm::StringRef dbgshell_command = GetDbgShellCommand(); |
| |
| // If forced lookup isn't set, by the user's \a dbgshell_command or |
| // by the \a force_lookup argument, exit this method. |
| if (!force_lookup && dbgshell_command.empty()) |
| return false; |
| |
| // We need a UUID or valid existing FileSpec. |
| if (!uuid_ptr && |
| (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr))) |
| return false; |
| |
| // We need a dsymForUUID binary or an equivalent executable/script. |
| FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable(); |
| if (!dsymForUUID_exe_spec) |
| return false; |
| |
| // Create the dsymForUUID command. |
| const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath(); |
| const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : ""; |
| |
| std::string lookup_arg = uuid_str; |
| if (lookup_arg.empty()) |
| lookup_arg = file_spec_ptr ? file_spec_ptr->GetPath() : ""; |
| if (lookup_arg.empty()) |
| return false; |
| |
| StreamString command; |
| command << dsymForUUID_exe_path << " --ignoreNegativeCache "; |
| if (copy_executable) |
| command << "--copyExecutable "; |
| command << lookup_arg; |
| |
| // Log and report progress. |
| std::string lookup_desc; |
| if (uuid_ptr && file_spec_ptr) |
| lookup_desc = |
| llvm::formatv("{0} ({1})", file_spec_ptr->GetFilename().GetString(), |
| uuid_ptr->GetAsString()); |
| else if (uuid_ptr) |
| lookup_desc = uuid_ptr->GetAsString(); |
| else if (file_spec_ptr) |
| lookup_desc = file_spec_ptr->GetFilename().GetString(); |
| |
| Log *log = GetLog(LLDBLog::Host); |
| LLDB_LOG(log, "Calling {0} for {1} to find dSYM: {2}", dsymForUUID_exe_path, |
| lookup_desc, command.GetString()); |
| |
| Progress progress("Downloading symbol file for", lookup_desc); |
| |
| // Invoke dsymForUUID. |
| int exit_status = -1; |
| int signo = -1; |
| std::string command_output; |
| error = Host::RunShellCommand( |
| command.GetData(), |
| FileSpec(), // current working directory |
| &exit_status, // Exit status |
| &signo, // Signal int * |
| &command_output, // Command output |
| std::chrono::seconds( |
| 640), // Large timeout to allow for long dsym download times |
| false); // Don't run in a shell (we don't need shell expansion) |
| |
| if (error.Fail() || exit_status != 0 || command_output.empty()) { |
| LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')", |
| command.GetData(), exit_status, error.AsCString(), |
| command_output.c_str()); |
| return false; |
| } |
| |
| CFCData data( |
| CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(), |
| command_output.size(), kCFAllocatorNull)); |
| |
| CFCReleaser<CFDictionaryRef> plist( |
| (CFDictionaryRef)::CFPropertyListCreateWithData( |
| NULL, data.get(), kCFPropertyListImmutable, NULL, NULL)); |
| |
| if (!plist.get()) { |
| LLDB_LOGF(log, "'%s' failed: output is not a valid plist", |
| command.GetData()); |
| return false; |
| } |
| |
| if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) { |
| LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary", |
| command.GetData()); |
| return false; |
| } |
| |
| if (!uuid_str.empty()) { |
| CFCString uuid_cfstr(uuid_str.c_str()); |
| CFDictionaryRef uuid_dict = |
| (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get()); |
| return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error, |
| command.GetData()); |
| } |
| |
| if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) { |
| std::vector<CFStringRef> keys(num_values, NULL); |
| std::vector<CFDictionaryRef> values(num_values, NULL); |
| ::CFDictionaryGetKeysAndValues(plist.get(), NULL, |
| (const void **)&values[0]); |
| if (num_values == 1) { |
| return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error, |
| command.GetData()); |
| } |
| |
| for (CFIndex i = 0; i < num_values; ++i) { |
| ModuleSpec curr_module_spec; |
| if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec, |
| error, command.GetData())) { |
| if (module_spec.GetArchitecture().IsCompatibleMatch( |
| curr_module_spec.GetArchitecture())) { |
| module_spec = curr_module_spec; |
| return true; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |