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