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 52 using namespace lldb; 53 using namespace lldb_private; 54 55 static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)( 56 CFUUIDRef uuid, CFURLRef exec_url) = nullptr; 57 static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = 58 nullptr; 59 60 LLDB_PLUGIN_DEFINE(SymbolLocatorDebugSymbols) 61 62 SymbolLocatorDebugSymbols::SymbolLocatorDebugSymbols() : SymbolLocator() {} 63 64 void SymbolLocatorDebugSymbols::Initialize() { 65 PluginManager::RegisterPlugin( 66 GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, 67 LocateExecutableObjectFile, LocateExecutableSymbolFile, 68 DownloadObjectAndSymbolFile, FindSymbolFileInBundle); 69 } 70 71 void SymbolLocatorDebugSymbols::Terminate() { 72 PluginManager::UnregisterPlugin(CreateInstance); 73 } 74 75 llvm::StringRef SymbolLocatorDebugSymbols::GetPluginDescriptionStatic() { 76 return "DebugSymbols symbol locator."; 77 } 78 79 SymbolLocator *SymbolLocatorDebugSymbols::CreateInstance() { 80 return new SymbolLocatorDebugSymbols(); 81 } 82 83 std::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 329 std::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 367 static 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. 394 static 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 471 static 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 525 static 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 768 std::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 Progress progress( 780 "Locating external symbol file", 781 module_spec.GetFileSpec().GetFilename().AsCString("<Unknown>")); 782 783 FileSpec symbol_fspec; 784 ModuleSpec dsym_module_spec; 785 // First try and find the dSYM in the same directory as the executable or in 786 // an appropriate parent directory 787 if (!LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec)) { 788 // We failed to easily find the dSYM above, so use DebugSymbols 789 LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec); 790 } else { 791 dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; 792 } 793 794 return dsym_module_spec.GetSymbolFileSpec(); 795 } 796 797 static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, 798 ModuleSpec &module_spec, 799 Status &error, 800 const std::string &command) { 801 Log *log = GetLog(LLDBLog::Host); 802 bool success = false; 803 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) { 804 std::string str; 805 CFStringRef cf_str; 806 CFDictionaryRef cf_dict; 807 808 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 809 CFSTR("DBGError")); 810 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 811 if (CFCString::FileSystemRepresentation(cf_str, str)) { 812 std::string errorstr = command; 813 errorstr += ":\n"; 814 errorstr += str; 815 error.SetErrorString(errorstr); 816 } 817 } 818 819 cf_str = (CFStringRef)CFDictionaryGetValue( 820 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable")); 821 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 822 if (CFCString::FileSystemRepresentation(cf_str, str)) { 823 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native); 824 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 825 LLDB_LOGF(log, 826 "From dsymForUUID plist: Symbol rich executable is at '%s'", 827 str.c_str()); 828 } 829 } 830 831 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 832 CFSTR("DBGDSYMPath")); 833 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 834 if (CFCString::FileSystemRepresentation(cf_str, str)) { 835 module_spec.GetSymbolFileSpec().SetFile(str.c_str(), 836 FileSpec::Style::native); 837 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 838 success = true; 839 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str()); 840 } 841 } 842 843 std::string DBGBuildSourcePath; 844 std::string DBGSourcePath; 845 846 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping. 847 // If DBGVersion 2, strip last two components of path remappings from 848 // entries to fix an issue with a specific set of 849 // DBGSourcePathRemapping entries that lldb worked 850 // with. 851 // If DBGVersion 3, trust & use the source path remappings as-is. 852 // 853 cf_dict = (CFDictionaryRef)CFDictionaryGetValue( 854 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping")); 855 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) { 856 // If we see DBGVersion with a value of 2 or higher, this is a new style 857 // DBGSourcePathRemapping dictionary 858 bool new_style_source_remapping_dictionary = false; 859 bool do_truncate_remapping_names = false; 860 std::string original_DBGSourcePath_value = DBGSourcePath; 861 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 862 CFSTR("DBGVersion")); 863 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 864 std::string version; 865 CFCString::FileSystemRepresentation(cf_str, version); 866 if (!version.empty() && isdigit(version[0])) { 867 int version_number = atoi(version.c_str()); 868 if (version_number > 1) { 869 new_style_source_remapping_dictionary = true; 870 } 871 if (version_number == 2) { 872 do_truncate_remapping_names = true; 873 } 874 } 875 } 876 877 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict); 878 if (kv_pair_count > 0) { 879 CFStringRef *keys = 880 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 881 CFStringRef *values = 882 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 883 if (keys != nullptr && values != nullptr) { 884 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict, 885 (const void **)keys, 886 (const void **)values); 887 } 888 for (CFIndex i = 0; i < kv_pair_count; i++) { 889 DBGBuildSourcePath.clear(); 890 DBGSourcePath.clear(); 891 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) { 892 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath); 893 } 894 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) { 895 CFCString::FileSystemRepresentation(values[i], DBGSourcePath); 896 } 897 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 898 // In the "old style" DBGSourcePathRemapping dictionary, the 899 // DBGSourcePath values (the "values" half of key-value path pairs) 900 // were wrong. Ignore them and use the universal DBGSourcePath 901 // string from earlier. 902 if (new_style_source_remapping_dictionary && 903 !original_DBGSourcePath_value.empty()) { 904 DBGSourcePath = original_DBGSourcePath_value; 905 } 906 if (DBGSourcePath[0] == '~') { 907 FileSpec resolved_source_path(DBGSourcePath.c_str()); 908 FileSystem::Instance().Resolve(resolved_source_path); 909 DBGSourcePath = resolved_source_path.GetPath(); 910 } 911 // With version 2 of DBGSourcePathRemapping, we can chop off the 912 // last two filename parts from the source remapping and get a more 913 // general source remapping that still works. Add this as another 914 // option in addition to the full source path remap. 915 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, 916 DBGSourcePath, true); 917 if (do_truncate_remapping_names) { 918 FileSpec build_path(DBGBuildSourcePath.c_str()); 919 FileSpec source_path(DBGSourcePath.c_str()); 920 build_path.RemoveLastPathComponent(); 921 build_path.RemoveLastPathComponent(); 922 source_path.RemoveLastPathComponent(); 923 source_path.RemoveLastPathComponent(); 924 module_spec.GetSourceMappingList().Append( 925 build_path.GetPath(), source_path.GetPath(), true); 926 } 927 } 928 } 929 if (keys) 930 free(keys); 931 if (values) 932 free(values); 933 } 934 } 935 936 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the 937 // source remappings list. 938 939 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 940 CFSTR("DBGBuildSourcePath")); 941 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 942 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); 943 } 944 945 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 946 CFSTR("DBGSourcePath")); 947 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 948 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); 949 } 950 951 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 952 if (DBGSourcePath[0] == '~') { 953 FileSpec resolved_source_path(DBGSourcePath.c_str()); 954 FileSystem::Instance().Resolve(resolved_source_path); 955 DBGSourcePath = resolved_source_path.GetPath(); 956 } 957 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, 958 DBGSourcePath, true); 959 } 960 } 961 return success; 962 } 963 964 /// It's expensive to check for the DBGShellCommands defaults setting. Only do 965 /// it once per lldb run and cache the result. 966 static llvm::StringRef GetDbgShellCommand() { 967 static std::once_flag g_once_flag; 968 static std::string g_dbgshell_command; 969 std::call_once(g_once_flag, [&]() { 970 CFTypeRef defaults_setting = CFPreferencesCopyAppValue( 971 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols")); 972 if (defaults_setting && 973 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) { 974 char buffer[PATH_MAX]; 975 if (CFStringGetCString((CFStringRef)defaults_setting, buffer, 976 sizeof(buffer), kCFStringEncodingUTF8)) { 977 g_dbgshell_command = buffer; 978 } 979 } 980 if (defaults_setting) { 981 CFRelease(defaults_setting); 982 } 983 }); 984 return g_dbgshell_command; 985 } 986 987 /// Get the dsymForUUID executable and cache the result so we don't end up 988 /// stat'ing the binary over and over. 989 static FileSpec GetDsymForUUIDExecutable() { 990 // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the 991 // test suite to override the dsymForUUID location. Because we must be able 992 // to change the value within a single test, don't bother caching it. 993 if (const char *dsymForUUID_env = 994 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) { 995 FileSpec dsymForUUID_executable(dsymForUUID_env); 996 FileSystem::Instance().Resolve(dsymForUUID_executable); 997 if (FileSystem::Instance().Exists(dsymForUUID_executable)) 998 return dsymForUUID_executable; 999 } 1000 1001 static std::once_flag g_once_flag; 1002 static FileSpec g_dsymForUUID_executable; 1003 std::call_once(g_once_flag, [&]() { 1004 // Try the DBGShellCommand. 1005 llvm::StringRef dbgshell_command = GetDbgShellCommand(); 1006 if (!dbgshell_command.empty()) { 1007 g_dsymForUUID_executable = FileSpec(dbgshell_command); 1008 FileSystem::Instance().Resolve(g_dsymForUUID_executable); 1009 if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) 1010 return; 1011 } 1012 1013 // Try dsymForUUID in /usr/local/bin 1014 { 1015 g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID"); 1016 if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) 1017 return; 1018 } 1019 1020 // We couldn't find the dsymForUUID binary. 1021 g_dsymForUUID_executable = {}; 1022 }); 1023 return g_dsymForUUID_executable; 1024 } 1025 1026 bool SymbolLocatorDebugSymbols::DownloadObjectAndSymbolFile( 1027 ModuleSpec &module_spec, Status &error, bool force_lookup, 1028 bool copy_executable) { 1029 const UUID *uuid_ptr = module_spec.GetUUIDPtr(); 1030 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); 1031 1032 // If \a dbgshell_command is set, the user has specified 1033 // forced symbol lookup via that command. We'll get the 1034 // path back from GetDsymForUUIDExecutable() later. 1035 llvm::StringRef dbgshell_command = GetDbgShellCommand(); 1036 1037 // If forced lookup isn't set, by the user's \a dbgshell_command or 1038 // by the \a force_lookup argument, exit this method. 1039 if (!force_lookup && dbgshell_command.empty()) 1040 return false; 1041 1042 // We need a UUID or valid existing FileSpec. 1043 if (!uuid_ptr && 1044 (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr))) 1045 return false; 1046 1047 // We need a dsymForUUID binary or an equivalent executable/script. 1048 FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable(); 1049 if (!dsymForUUID_exe_spec) 1050 return false; 1051 1052 // Create the dsymForUUID command. 1053 const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath(); 1054 const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : ""; 1055 1056 std::string lookup_arg = uuid_str; 1057 if (lookup_arg.empty()) 1058 lookup_arg = file_spec_ptr ? file_spec_ptr->GetPath() : ""; 1059 if (lookup_arg.empty()) 1060 return false; 1061 1062 StreamString command; 1063 command << dsymForUUID_exe_path << " --ignoreNegativeCache "; 1064 if (copy_executable) 1065 command << "--copyExecutable "; 1066 command << lookup_arg; 1067 1068 // Log and report progress. 1069 std::string lookup_desc; 1070 if (uuid_ptr && file_spec_ptr) 1071 lookup_desc = 1072 llvm::formatv("{0} ({1})", file_spec_ptr->GetFilename().GetString(), 1073 uuid_ptr->GetAsString()); 1074 else if (uuid_ptr) 1075 lookup_desc = uuid_ptr->GetAsString(); 1076 else if (file_spec_ptr) 1077 lookup_desc = file_spec_ptr->GetFilename().GetString(); 1078 1079 Log *log = GetLog(LLDBLog::Host); 1080 LLDB_LOG(log, "Calling {0} for {1} to find dSYM: {2}", dsymForUUID_exe_path, 1081 lookup_desc, command.GetString()); 1082 1083 Progress progress("Downloading symbol file for", lookup_desc); 1084 1085 // Invoke dsymForUUID. 1086 int exit_status = -1; 1087 int signo = -1; 1088 std::string command_output; 1089 error = Host::RunShellCommand( 1090 command.GetData(), 1091 FileSpec(), // current working directory 1092 &exit_status, // Exit status 1093 &signo, // Signal int * 1094 &command_output, // Command output 1095 std::chrono::seconds( 1096 640), // Large timeout to allow for long dsym download times 1097 false); // Don't run in a shell (we don't need shell expansion) 1098 1099 if (error.Fail() || exit_status != 0 || command_output.empty()) { 1100 LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')", 1101 command.GetData(), exit_status, error.AsCString(), 1102 command_output.c_str()); 1103 return false; 1104 } 1105 1106 CFCData data( 1107 CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(), 1108 command_output.size(), kCFAllocatorNull)); 1109 1110 CFCReleaser<CFDictionaryRef> plist( 1111 (CFDictionaryRef)::CFPropertyListCreateWithData( 1112 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL)); 1113 1114 if (!plist.get()) { 1115 LLDB_LOGF(log, "'%s' failed: output is not a valid plist", 1116 command.GetData()); 1117 return false; 1118 } 1119 1120 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) { 1121 LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary", 1122 command.GetData()); 1123 return false; 1124 } 1125 1126 if (!uuid_str.empty()) { 1127 CFCString uuid_cfstr(uuid_str.c_str()); 1128 CFDictionaryRef uuid_dict = 1129 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get()); 1130 return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error, 1131 command.GetData()); 1132 } 1133 1134 if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) { 1135 std::vector<CFStringRef> keys(num_values, NULL); 1136 std::vector<CFDictionaryRef> values(num_values, NULL); 1137 ::CFDictionaryGetKeysAndValues(plist.get(), NULL, 1138 (const void **)&values[0]); 1139 if (num_values == 1) { 1140 return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error, 1141 command.GetData()); 1142 } 1143 1144 for (CFIndex i = 0; i < num_values; ++i) { 1145 ModuleSpec curr_module_spec; 1146 if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec, 1147 error, command.GetData())) { 1148 if (module_spec.GetArchitecture().IsCompatibleMatch( 1149 curr_module_spec.GetArchitecture())) { 1150 module_spec = curr_module_spec; 1151 return true; 1152 } 1153 } 1154 } 1155 } 1156 1157 return false; 1158 } 1159