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