1//===-- LocateSymbolFileMacOSX.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 "lldb/Symbol/LocateSymbolFile.h" 10 11#include <dirent.h> 12#include <dlfcn.h> 13#include <pwd.h> 14 15#include <CoreFoundation/CoreFoundation.h> 16 17#include "Host/macosx/cfcpp/CFCBundle.h" 18#include "Host/macosx/cfcpp/CFCData.h" 19#include "Host/macosx/cfcpp/CFCReleaser.h" 20#include "Host/macosx/cfcpp/CFCString.h" 21#include "lldb/Core/Module.h" 22#include "lldb/Core/ModuleList.h" 23#include "lldb/Core/ModuleSpec.h" 24#include "lldb/Host/Host.h" 25#include "lldb/Host/HostInfo.h" 26#include "lldb/Symbol/ObjectFile.h" 27#include "lldb/Utility/ArchSpec.h" 28#include "lldb/Utility/DataBuffer.h" 29#include "lldb/Utility/DataExtractor.h" 30#include "lldb/Utility/Endian.h" 31#include "lldb/Utility/LLDBLog.h" 32#include "lldb/Utility/Log.h" 33#include "lldb/Utility/StreamString.h" 34#include "lldb/Utility/Timer.h" 35#include "lldb/Utility/UUID.h" 36#include "mach/machine.h" 37 38#include "llvm/ADT/ScopeExit.h" 39#include "llvm/Support/FileSystem.h" 40 41using namespace lldb; 42using namespace lldb_private; 43 44static CFURLRef (*g_dlsym_DBGCopyFullDSYMURLForUUID)( 45 CFUUIDRef uuid, CFURLRef exec_url) = nullptr; 46static CFDictionaryRef (*g_dlsym_DBGCopyDSYMPropertyLists)(CFURLRef dsym_url) = 47 nullptr; 48 49int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, 50 ModuleSpec &return_module_spec) { 51 Log *log = GetLog(LLDBLog::Host); 52 if (!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup()) { 53 LLDB_LOGF(log, "Spotlight lookup for .dSYM bundles is disabled."); 54 return 0; 55 } 56 57 return_module_spec = module_spec; 58 return_module_spec.GetFileSpec().Clear(); 59 return_module_spec.GetSymbolFileSpec().Clear(); 60 61 const UUID *uuid = module_spec.GetUUIDPtr(); 62 const ArchSpec *arch = module_spec.GetArchitecturePtr(); 63 64 int items_found = 0; 65 66 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || 67 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { 68 void *handle = dlopen( 69 "/System/Library/PrivateFrameworks/DebugSymbols.framework/DebugSymbols", 70 RTLD_LAZY | RTLD_LOCAL); 71 if (handle) { 72 g_dlsym_DBGCopyFullDSYMURLForUUID = 73 (CFURLRef(*)(CFUUIDRef, CFURLRef))dlsym(handle, 74 "DBGCopyFullDSYMURLForUUID"); 75 g_dlsym_DBGCopyDSYMPropertyLists = (CFDictionaryRef(*)(CFURLRef))dlsym( 76 handle, "DBGCopyDSYMPropertyLists"); 77 } 78 } 79 80 if (g_dlsym_DBGCopyFullDSYMURLForUUID == nullptr || 81 g_dlsym_DBGCopyDSYMPropertyLists == nullptr) { 82 return items_found; 83 } 84 85 if (uuid && uuid->IsValid()) { 86 // Try and locate the dSYM file using DebugSymbols first 87 llvm::ArrayRef<uint8_t> module_uuid = uuid->GetBytes(); 88 if (module_uuid.size() == 16) { 89 CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes( 90 NULL, module_uuid[0], module_uuid[1], module_uuid[2], module_uuid[3], 91 module_uuid[4], module_uuid[5], module_uuid[6], module_uuid[7], 92 module_uuid[8], module_uuid[9], module_uuid[10], module_uuid[11], 93 module_uuid[12], module_uuid[13], module_uuid[14], module_uuid[15])); 94 95 if (module_uuid_ref.get()) { 96 CFCReleaser<CFURLRef> exec_url; 97 const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); 98 if (exec_fspec) { 99 char exec_cf_path[PATH_MAX]; 100 if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) 101 exec_url.reset(::CFURLCreateFromFileSystemRepresentation( 102 NULL, (const UInt8 *)exec_cf_path, strlen(exec_cf_path), 103 FALSE)); 104 } 105 106 CFCReleaser<CFURLRef> dsym_url(g_dlsym_DBGCopyFullDSYMURLForUUID( 107 module_uuid_ref.get(), exec_url.get())); 108 char path[PATH_MAX]; 109 110 if (dsym_url.get()) { 111 if (::CFURLGetFileSystemRepresentation( 112 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 113 LLDB_LOGF(log, 114 "DebugSymbols framework returned dSYM path of %s for " 115 "UUID %s -- looking for the dSYM", 116 path, uuid->GetAsString().c_str()); 117 FileSpec dsym_filespec(path); 118 if (path[0] == '~') 119 FileSystem::Instance().Resolve(dsym_filespec); 120 121 if (FileSystem::Instance().IsDirectory(dsym_filespec)) { 122 dsym_filespec = 123 Symbols::FindSymbolFileInBundle(dsym_filespec, uuid, arch); 124 ++items_found; 125 } else { 126 ++items_found; 127 } 128 return_module_spec.GetSymbolFileSpec() = dsym_filespec; 129 } 130 131 bool success = false; 132 if (log) { 133 if (::CFURLGetFileSystemRepresentation( 134 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 135 LLDB_LOGF(log, 136 "DebugSymbols framework returned dSYM path of %s for " 137 "UUID %s -- looking for an exec file", 138 path, uuid->GetAsString().c_str()); 139 } 140 } 141 142 CFCReleaser<CFDictionaryRef> dict( 143 g_dlsym_DBGCopyDSYMPropertyLists(dsym_url.get())); 144 CFDictionaryRef uuid_dict = NULL; 145 if (dict.get()) { 146 CFCString uuid_cfstr(uuid->GetAsString().c_str()); 147 uuid_dict = static_cast<CFDictionaryRef>( 148 ::CFDictionaryGetValue(dict.get(), uuid_cfstr.get())); 149 } 150 151 // Check to see if we have the file on the local filesystem. 152 if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { 153 ModuleSpec exe_spec; 154 exe_spec.GetFileSpec() = module_spec.GetFileSpec(); 155 exe_spec.GetUUID() = module_spec.GetUUID(); 156 ModuleSP module_sp; 157 module_sp.reset(new Module(exe_spec)); 158 if (module_sp && module_sp->GetObjectFile() && 159 module_sp->MatchesModuleSpec(exe_spec)) { 160 success = true; 161 return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); 162 LLDB_LOGF(log, "using original binary filepath %s for UUID %s", 163 module_spec.GetFileSpec().GetPath().c_str(), 164 uuid->GetAsString().c_str()); 165 ++items_found; 166 } 167 } 168 169 // Check if the requested image is in our shared cache. 170 if (!success) { 171 SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo( 172 module_spec.GetFileSpec().GetPath()); 173 174 // If we found it and it has the correct UUID, let's proceed with 175 // creating a module from the memory contents. 176 if (image_info.uuid && (!module_spec.GetUUID() || 177 module_spec.GetUUID() == image_info.uuid)) { 178 success = true; 179 return_module_spec.GetFileSpec() = module_spec.GetFileSpec(); 180 LLDB_LOGF(log, 181 "using binary from shared cache for filepath %s for " 182 "UUID %s", 183 module_spec.GetFileSpec().GetPath().c_str(), 184 uuid->GetAsString().c_str()); 185 ++items_found; 186 } 187 } 188 189 // Use the DBGSymbolRichExecutable filepath if present 190 if (!success && uuid_dict) { 191 CFStringRef exec_cf_path = 192 static_cast<CFStringRef>(::CFDictionaryGetValue( 193 uuid_dict, CFSTR("DBGSymbolRichExecutable"))); 194 if (exec_cf_path && ::CFStringGetFileSystemRepresentation( 195 exec_cf_path, path, sizeof(path))) { 196 LLDB_LOGF(log, "plist bundle has exec path of %s for UUID %s", 197 path, uuid->GetAsString().c_str()); 198 ++items_found; 199 FileSpec exec_filespec(path); 200 if (path[0] == '~') 201 FileSystem::Instance().Resolve(exec_filespec); 202 if (FileSystem::Instance().Exists(exec_filespec)) { 203 success = true; 204 return_module_spec.GetFileSpec() = exec_filespec; 205 } 206 } 207 } 208 209 // Look next to the dSYM for the binary file. 210 if (!success) { 211 if (::CFURLGetFileSystemRepresentation( 212 dsym_url.get(), true, (UInt8 *)path, sizeof(path) - 1)) { 213 char *dsym_extension_pos = ::strstr(path, ".dSYM"); 214 if (dsym_extension_pos) { 215 *dsym_extension_pos = '\0'; 216 LLDB_LOGF(log, 217 "Looking for executable binary next to dSYM " 218 "bundle with name with name %s", 219 path); 220 FileSpec file_spec(path); 221 FileSystem::Instance().Resolve(file_spec); 222 ModuleSpecList module_specs; 223 ModuleSpec matched_module_spec; 224 using namespace llvm::sys::fs; 225 switch (get_file_type(file_spec.GetPath())) { 226 227 case file_type::directory_file: // Bundle directory? 228 { 229 CFCBundle bundle(path); 230 CFCReleaser<CFURLRef> bundle_exe_url( 231 bundle.CopyExecutableURL()); 232 if (bundle_exe_url.get()) { 233 if (::CFURLGetFileSystemRepresentation(bundle_exe_url.get(), 234 true, (UInt8 *)path, 235 sizeof(path) - 1)) { 236 FileSpec bundle_exe_file_spec(path); 237 FileSystem::Instance().Resolve(bundle_exe_file_spec); 238 if (ObjectFile::GetModuleSpecifications( 239 bundle_exe_file_spec, 0, 0, module_specs) && 240 module_specs.FindMatchingModuleSpec( 241 module_spec, matched_module_spec)) 242 243 { 244 ++items_found; 245 return_module_spec.GetFileSpec() = bundle_exe_file_spec; 246 LLDB_LOGF(log, 247 "Executable binary %s next to dSYM is " 248 "compatible; using", 249 path); 250 } 251 } 252 } 253 } break; 254 255 case file_type::fifo_file: // Forget pipes 256 case file_type::socket_file: // We can't process socket files 257 case file_type::file_not_found: // File doesn't exist... 258 case file_type::status_error: 259 break; 260 261 case file_type::type_unknown: 262 case file_type::regular_file: 263 case file_type::symlink_file: 264 case file_type::block_file: 265 case file_type::character_file: 266 if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, 267 module_specs) && 268 module_specs.FindMatchingModuleSpec(module_spec, 269 matched_module_spec)) 270 271 { 272 ++items_found; 273 return_module_spec.GetFileSpec() = file_spec; 274 LLDB_LOGF(log, 275 "Executable binary %s next to dSYM is " 276 "compatible; using", 277 path); 278 } 279 break; 280 } 281 } 282 } 283 } 284 } 285 } 286 } 287 } 288 289 return items_found; 290} 291 292FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &dsym_bundle_fspec, 293 const lldb_private::UUID *uuid, 294 const ArchSpec *arch) { 295 std::string dsym_bundle_path = dsym_bundle_fspec.GetPath(); 296 llvm::SmallString<128> buffer(dsym_bundle_path); 297 llvm::sys::path::append(buffer, "Contents", "Resources", "DWARF"); 298 299 std::error_code EC; 300 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = 301 FileSystem::Instance().GetVirtualFileSystem(); 302 llvm::vfs::recursive_directory_iterator Iter(*vfs, buffer.str(), EC); 303 llvm::vfs::recursive_directory_iterator End; 304 for (; Iter != End && !EC; Iter.increment(EC)) { 305 llvm::ErrorOr<llvm::vfs::Status> Status = vfs->status(Iter->path()); 306 if (Status->isDirectory()) 307 continue; 308 309 FileSpec dsym_fspec(Iter->path()); 310 ModuleSpecList module_specs; 311 if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) { 312 ModuleSpec spec; 313 for (size_t i = 0; i < module_specs.GetSize(); ++i) { 314 bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec); 315 assert(got_spec); // The call has side-effects so can't be inlined. 316 UNUSED_IF_ASSERT_DISABLED(got_spec); 317 if ((uuid == nullptr || 318 (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && 319 (arch == nullptr || 320 (spec.GetArchitecturePtr() && 321 spec.GetArchitecture().IsCompatibleMatch(*arch)))) { 322 return dsym_fspec; 323 } 324 } 325 } 326 } 327 328 return {}; 329} 330 331static bool GetModuleSpecInfoFromUUIDDictionary(CFDictionaryRef uuid_dict, 332 ModuleSpec &module_spec, 333 Status &error) { 334 Log *log = GetLog(LLDBLog::Host); 335 bool success = false; 336 if (uuid_dict != NULL && CFGetTypeID(uuid_dict) == CFDictionaryGetTypeID()) { 337 std::string str; 338 CFStringRef cf_str; 339 CFDictionaryRef cf_dict; 340 341 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 342 CFSTR("DBGError")); 343 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 344 if (CFCString::FileSystemRepresentation(cf_str, str)) { 345 error.SetErrorString(str); 346 } 347 } 348 349 cf_str = (CFStringRef)CFDictionaryGetValue( 350 (CFDictionaryRef)uuid_dict, CFSTR("DBGSymbolRichExecutable")); 351 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 352 if (CFCString::FileSystemRepresentation(cf_str, str)) { 353 module_spec.GetFileSpec().SetFile(str.c_str(), FileSpec::Style::native); 354 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 355 LLDB_LOGF(log, 356 "From dsymForUUID plist: Symbol rich executable is at '%s'", 357 str.c_str()); 358 } 359 } 360 361 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 362 CFSTR("DBGDSYMPath")); 363 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 364 if (CFCString::FileSystemRepresentation(cf_str, str)) { 365 module_spec.GetSymbolFileSpec().SetFile(str.c_str(), 366 FileSpec::Style::native); 367 FileSystem::Instance().Resolve(module_spec.GetFileSpec()); 368 success = true; 369 LLDB_LOGF(log, "From dsymForUUID plist: dSYM is at '%s'", str.c_str()); 370 } 371 } 372 373 std::string DBGBuildSourcePath; 374 std::string DBGSourcePath; 375 376 // If DBGVersion 1 or DBGVersion missing, ignore DBGSourcePathRemapping. 377 // If DBGVersion 2, strip last two components of path remappings from 378 // entries to fix an issue with a specific set of 379 // DBGSourcePathRemapping entries that lldb worked 380 // with. 381 // If DBGVersion 3, trust & use the source path remappings as-is. 382 // 383 cf_dict = (CFDictionaryRef)CFDictionaryGetValue( 384 (CFDictionaryRef)uuid_dict, CFSTR("DBGSourcePathRemapping")); 385 if (cf_dict && CFGetTypeID(cf_dict) == CFDictionaryGetTypeID()) { 386 // If we see DBGVersion with a value of 2 or higher, this is a new style 387 // DBGSourcePathRemapping dictionary 388 bool new_style_source_remapping_dictionary = false; 389 bool do_truncate_remapping_names = false; 390 std::string original_DBGSourcePath_value = DBGSourcePath; 391 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 392 CFSTR("DBGVersion")); 393 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 394 std::string version; 395 CFCString::FileSystemRepresentation(cf_str, version); 396 if (!version.empty() && isdigit(version[0])) { 397 int version_number = atoi(version.c_str()); 398 if (version_number > 1) { 399 new_style_source_remapping_dictionary = true; 400 } 401 if (version_number == 2) { 402 do_truncate_remapping_names = true; 403 } 404 } 405 } 406 407 CFIndex kv_pair_count = CFDictionaryGetCount((CFDictionaryRef)uuid_dict); 408 if (kv_pair_count > 0) { 409 CFStringRef *keys = 410 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 411 CFStringRef *values = 412 (CFStringRef *)malloc(kv_pair_count * sizeof(CFStringRef)); 413 if (keys != nullptr && values != nullptr) { 414 CFDictionaryGetKeysAndValues((CFDictionaryRef)uuid_dict, 415 (const void **)keys, 416 (const void **)values); 417 } 418 for (CFIndex i = 0; i < kv_pair_count; i++) { 419 DBGBuildSourcePath.clear(); 420 DBGSourcePath.clear(); 421 if (keys[i] && CFGetTypeID(keys[i]) == CFStringGetTypeID()) { 422 CFCString::FileSystemRepresentation(keys[i], DBGBuildSourcePath); 423 } 424 if (values[i] && CFGetTypeID(values[i]) == CFStringGetTypeID()) { 425 CFCString::FileSystemRepresentation(values[i], DBGSourcePath); 426 } 427 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 428 // In the "old style" DBGSourcePathRemapping dictionary, the 429 // DBGSourcePath values (the "values" half of key-value path pairs) 430 // were wrong. Ignore them and use the universal DBGSourcePath 431 // string from earlier. 432 if (new_style_source_remapping_dictionary && 433 !original_DBGSourcePath_value.empty()) { 434 DBGSourcePath = original_DBGSourcePath_value; 435 } 436 if (DBGSourcePath[0] == '~') { 437 FileSpec resolved_source_path(DBGSourcePath.c_str()); 438 FileSystem::Instance().Resolve(resolved_source_path); 439 DBGSourcePath = resolved_source_path.GetPath(); 440 } 441 // With version 2 of DBGSourcePathRemapping, we can chop off the 442 // last two filename parts from the source remapping and get a more 443 // general source remapping that still works. Add this as another 444 // option in addition to the full source path remap. 445 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, 446 DBGSourcePath, true); 447 if (do_truncate_remapping_names) { 448 FileSpec build_path(DBGBuildSourcePath.c_str()); 449 FileSpec source_path(DBGSourcePath.c_str()); 450 build_path.RemoveLastPathComponent(); 451 build_path.RemoveLastPathComponent(); 452 source_path.RemoveLastPathComponent(); 453 source_path.RemoveLastPathComponent(); 454 module_spec.GetSourceMappingList().Append( 455 build_path.GetPath(), source_path.GetPath(), true); 456 } 457 } 458 } 459 if (keys) 460 free(keys); 461 if (values) 462 free(values); 463 } 464 } 465 466 // If we have a DBGBuildSourcePath + DBGSourcePath pair, append them to the 467 // source remappings list. 468 469 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 470 CFSTR("DBGBuildSourcePath")); 471 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 472 CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); 473 } 474 475 cf_str = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)uuid_dict, 476 CFSTR("DBGSourcePath")); 477 if (cf_str && CFGetTypeID(cf_str) == CFStringGetTypeID()) { 478 CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); 479 } 480 481 if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) { 482 if (DBGSourcePath[0] == '~') { 483 FileSpec resolved_source_path(DBGSourcePath.c_str()); 484 FileSystem::Instance().Resolve(resolved_source_path); 485 DBGSourcePath = resolved_source_path.GetPath(); 486 } 487 module_spec.GetSourceMappingList().Append(DBGBuildSourcePath, 488 DBGSourcePath, true); 489 } 490 } 491 return success; 492} 493 494/// It's expensive to check for the DBGShellCommands defaults setting. Only do 495/// it once per lldb run and cache the result. 496static llvm::StringRef GetDbgShellCommand() { 497 static std::once_flag g_once_flag; 498 static std::string g_dbgshell_command; 499 std::call_once(g_once_flag, [&]() { 500 CFTypeRef defaults_setting = CFPreferencesCopyAppValue( 501 CFSTR("DBGShellCommands"), CFSTR("com.apple.DebugSymbols")); 502 if (defaults_setting && 503 CFGetTypeID(defaults_setting) == CFStringGetTypeID()) { 504 char buffer[PATH_MAX]; 505 if (CFStringGetCString((CFStringRef)defaults_setting, buffer, 506 sizeof(buffer), kCFStringEncodingUTF8)) { 507 g_dbgshell_command = buffer; 508 } 509 } 510 if (defaults_setting) { 511 CFRelease(defaults_setting); 512 } 513 }); 514 return g_dbgshell_command; 515} 516 517/// Get the dsymForUUID executable and cache the result so we don't end up 518/// stat'ing the binary over and over. 519static FileSpec GetDsymForUUIDExecutable() { 520 // The LLDB_APPLE_DSYMFORUUID_EXECUTABLE environment variable is used by the 521 // test suite to override the dsymForUUID location. Because we must be able 522 // to change the value within a single test, don't bother caching it. 523 if (const char *dsymForUUID_env = 524 getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE")) { 525 FileSpec dsymForUUID_executable(dsymForUUID_env); 526 FileSystem::Instance().Resolve(dsymForUUID_executable); 527 if (FileSystem::Instance().Exists(dsymForUUID_executable)) 528 return dsymForUUID_executable; 529 } 530 531 static std::once_flag g_once_flag; 532 static FileSpec g_dsymForUUID_executable; 533 std::call_once(g_once_flag, [&]() { 534 // Try the DBGShellCommand. 535 llvm::StringRef dbgshell_command = GetDbgShellCommand(); 536 if (!dbgshell_command.empty()) { 537 g_dsymForUUID_executable = FileSpec(dbgshell_command); 538 FileSystem::Instance().Resolve(g_dsymForUUID_executable); 539 if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) 540 return; 541 } 542 543 // Try dsymForUUID in /usr/local/bin 544 { 545 g_dsymForUUID_executable = FileSpec("/usr/local/bin/dsymForUUID"); 546 if (FileSystem::Instance().Exists(g_dsymForUUID_executable)) 547 return; 548 } 549 550 // We couldn't find the dsymForUUID binary. 551 g_dsymForUUID_executable = {}; 552 }); 553 return g_dsymForUUID_executable; 554} 555 556bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec, 557 Status &error, bool force_lookup, 558 bool copy_executable) { 559 const UUID *uuid_ptr = module_spec.GetUUIDPtr(); 560 const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); 561 562 llvm::StringRef dbgshell_command = GetDbgShellCommand(); 563 564 // When dbgshell_command is empty, the user has not enabled the use of an 565 // external program to find the symbols, don't run it for them. 566 if (!force_lookup && dbgshell_command.empty()) 567 return false; 568 569 // We need a UUID or valid (existing FileSpec. 570 if (!uuid_ptr && 571 (!file_spec_ptr || !FileSystem::Instance().Exists(*file_spec_ptr))) 572 return false; 573 574 // We need a dsymForUUID binary or an equivalent executable/script. 575 FileSpec dsymForUUID_exe_spec = GetDsymForUUIDExecutable(); 576 if (!dsymForUUID_exe_spec) 577 return false; 578 579 const std::string dsymForUUID_exe_path = dsymForUUID_exe_spec.GetPath(); 580 const std::string uuid_str = uuid_ptr ? uuid_ptr->GetAsString() : ""; 581 const std::string file_path_str = 582 file_spec_ptr ? file_spec_ptr->GetPath() : ""; 583 584 Log *log = GetLog(LLDBLog::Host); 585 586 // Create the dsymForUUID command. 587 StreamString command; 588 const char *copy_executable_arg = copy_executable ? "--copyExecutable " : ""; 589 if (!uuid_str.empty()) { 590 command.Printf("%s --ignoreNegativeCache %s%s", 591 dsymForUUID_exe_path.c_str(), copy_executable_arg, 592 uuid_str.c_str()); 593 LLDB_LOGF(log, "Calling %s with UUID %s to find dSYM: %s", 594 dsymForUUID_exe_path.c_str(), uuid_str.c_str(), 595 command.GetString().data()); 596 } else if (!file_path_str.empty()) { 597 command.Printf("%s --ignoreNegativeCache %s%s", 598 dsymForUUID_exe_path.c_str(), copy_executable_arg, 599 file_path_str.c_str()); 600 LLDB_LOGF(log, "Calling %s with file %s to find dSYM: %s", 601 dsymForUUID_exe_path.c_str(), file_path_str.c_str(), 602 command.GetString().data()); 603 } else { 604 return false; 605 } 606 607 // Invoke dsymForUUID. 608 int exit_status = -1; 609 int signo = -1; 610 std::string command_output; 611 error = Host::RunShellCommand( 612 command.GetData(), 613 FileSpec(), // current working directory 614 &exit_status, // Exit status 615 &signo, // Signal int * 616 &command_output, // Command output 617 std::chrono::seconds( 618 640), // Large timeout to allow for long dsym download times 619 false); // Don't run in a shell (we don't need shell expansion) 620 621 if (error.Fail() || exit_status != 0 || command_output.empty()) { 622 LLDB_LOGF(log, "'%s' failed (exit status: %d, error: '%s', output: '%s')", 623 command.GetData(), exit_status, error.AsCString(), 624 command_output.c_str()); 625 return false; 626 } 627 628 CFCData data( 629 CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)command_output.data(), 630 command_output.size(), kCFAllocatorNull)); 631 632 CFCReleaser<CFDictionaryRef> plist( 633 (CFDictionaryRef)::CFPropertyListCreateWithData( 634 NULL, data.get(), kCFPropertyListImmutable, NULL, NULL)); 635 636 if (!plist.get()) { 637 LLDB_LOGF(log, "'%s' failed: output is not a valid plist", 638 command.GetData()); 639 return false; 640 } 641 642 if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) { 643 LLDB_LOGF(log, "'%s' failed: output plist is not a valid CFDictionary", 644 command.GetData()); 645 return false; 646 } 647 648 if (!uuid_str.empty()) { 649 CFCString uuid_cfstr(uuid_str.c_str()); 650 CFDictionaryRef uuid_dict = 651 (CFDictionaryRef)CFDictionaryGetValue(plist.get(), uuid_cfstr.get()); 652 return GetModuleSpecInfoFromUUIDDictionary(uuid_dict, module_spec, error); 653 } 654 655 if (const CFIndex num_values = ::CFDictionaryGetCount(plist.get())) { 656 std::vector<CFStringRef> keys(num_values, NULL); 657 std::vector<CFDictionaryRef> values(num_values, NULL); 658 ::CFDictionaryGetKeysAndValues(plist.get(), NULL, 659 (const void **)&values[0]); 660 if (num_values == 1) { 661 return GetModuleSpecInfoFromUUIDDictionary(values[0], module_spec, error); 662 } 663 664 for (CFIndex i = 0; i < num_values; ++i) { 665 ModuleSpec curr_module_spec; 666 if (GetModuleSpecInfoFromUUIDDictionary(values[i], curr_module_spec, 667 error)) { 668 if (module_spec.GetArchitecture().IsCompatibleMatch( 669 curr_module_spec.GetArchitecture())) { 670 module_spec = curr_module_spec; 671 return true; 672 } 673 } 674 } 675 } 676 677 return false; 678} 679