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