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