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