1//===-- HostInfoMacOSX.mm ---------------------------------------*- 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/Host/macosx/HostInfoMacOSX.h" 10#include "lldb/Host/FileSystem.h" 11#include "lldb/Host/Host.h" 12#include "lldb/Host/HostInfo.h" 13#include "lldb/Utility/Args.h" 14#include "lldb/Utility/LLDBLog.h" 15#include "lldb/Utility/Log.h" 16#include "lldb/Utility/Timer.h" 17 18#include "llvm/ADT/ScopeExit.h" 19#include "llvm/ADT/SmallString.h" 20#include "llvm/ADT/StringMap.h" 21#include "llvm/Support/FileSystem.h" 22#include "llvm/Support/Path.h" 23#include "llvm/Support/raw_ostream.h" 24 25// C++ Includes 26#include <optional> 27#include <string> 28 29// C inclues 30#include <cstdlib> 31#include <sys/sysctl.h> 32#include <sys/syslimits.h> 33#include <sys/types.h> 34#include <uuid/uuid.h> 35 36// Objective-C/C++ includes 37#include <AvailabilityMacros.h> 38#include <CoreFoundation/CoreFoundation.h> 39#include <Foundation/Foundation.h> 40#include <mach-o/dyld.h> 41#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ 42 MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 43#if __has_include(<mach-o/dyld_introspection.h>) 44#include <mach-o/dyld_introspection.h> 45#define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS 46#endif 47#endif 48#include <objc/objc-auto.h> 49 50// These are needed when compiling on systems 51// that do not yet have these definitions 52#ifndef CPU_SUBTYPE_X86_64_H 53#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) 54#endif 55#ifndef CPU_TYPE_ARM64 56#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 57#endif 58 59#ifndef CPU_TYPE_ARM64_32 60#define CPU_ARCH_ABI64_32 0x02000000 61#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32) 62#endif 63 64#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH 65 66using namespace lldb_private; 67 68std::optional<std::string> HostInfoMacOSX::GetOSBuildString() { 69 int mib[2] = {CTL_KERN, KERN_OSVERSION}; 70 char cstr[PATH_MAX]; 71 size_t cstr_len = sizeof(cstr); 72 if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) 73 return std::string(cstr, cstr_len - 1); 74 75 return std::nullopt; 76} 77 78static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { 79 @autoreleasepool { 80 NSDictionary *version_info = 81 [NSDictionary dictionaryWithContentsOfFile: 82 @"/System/Library/CoreServices/SystemVersion.plist"]; 83 NSString *version_value = [version_info objectForKey: Key]; 84 const char *version_str = [version_value UTF8String]; 85 version.tryParse(version_str); 86 } 87} 88 89llvm::VersionTuple HostInfoMacOSX::GetOSVersion() { 90 static llvm::VersionTuple g_version; 91 if (g_version.empty()) 92 ParseOSVersion(g_version, @"ProductVersion"); 93 return g_version; 94} 95 96llvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() { 97 static llvm::VersionTuple g_version; 98 if (g_version.empty()) 99 ParseOSVersion(g_version, @"iOSSupportVersion"); 100 return g_version; 101} 102 103 104FileSpec HostInfoMacOSX::GetProgramFileSpec() { 105 static FileSpec g_program_filespec; 106 if (!g_program_filespec) { 107 char program_fullpath[PATH_MAX]; 108 // If DST is NULL, then return the number of bytes needed. 109 uint32_t len = sizeof(program_fullpath); 110 int err = _NSGetExecutablePath(program_fullpath, &len); 111 if (err == 0) 112 g_program_filespec.SetFile(program_fullpath, FileSpec::Style::native); 113 else if (err == -1) { 114 char *large_program_fullpath = (char *)::malloc(len + 1); 115 116 err = _NSGetExecutablePath(large_program_fullpath, &len); 117 if (err == 0) 118 g_program_filespec.SetFile(large_program_fullpath, 119 FileSpec::Style::native); 120 121 ::free(large_program_fullpath); 122 } 123 } 124 return g_program_filespec; 125} 126 127/// Resolve the given candidate support dir and return true if it's valid. 128static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) { 129 FileSystem::Instance().Resolve(path); 130 return FileSystem::Instance().IsDirectory(path); 131} 132 133bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { 134 FileSpec lldb_file_spec = GetShlibDir(); 135 if (!lldb_file_spec) 136 return false; 137 138 std::string raw_path = lldb_file_spec.GetPath(); 139 140 size_t framework_pos = raw_path.find("LLDB.framework"); 141 if (framework_pos != std::string::npos) { 142 framework_pos += strlen("LLDB.framework"); 143#if TARGET_OS_IPHONE 144 // Shallow bundle 145 raw_path.resize(framework_pos); 146#else 147 // Normal bundle 148 raw_path.resize(framework_pos); 149 raw_path.append("/Resources"); 150#endif 151 } else { 152 // Find the bin path relative to the lib path where the cmake-based 153 // OS X .dylib lives. We try looking first at a possible sibling `bin` 154 // directory, and then at the `lib` directory itself. This last case is 155 // useful for supporting build systems like Bazel which in many cases prefer 156 // to place support binaries right next to dylibs. 157 // 158 // It is not going to work to do it by the executable path, 159 // as in the case of a python script, the executable is python, not 160 // the lldb driver. 161 FileSpec support_dir_spec_lib(raw_path); 162 FileSpec support_dir_spec_bin = 163 support_dir_spec_lib.CopyByAppendingPathComponent("/../bin"); 164 FileSpec support_dir_spec; 165 166 if (ResolveAndVerifyCandidateSupportDir(support_dir_spec_bin)) { 167 support_dir_spec = support_dir_spec_bin; 168 } else if (ResolveAndVerifyCandidateSupportDir(support_dir_spec_lib)) { 169 support_dir_spec = support_dir_spec_lib; 170 } else { 171 Log *log = GetLog(LLDBLog::Host); 172 LLDB_LOG(log, "failed to find support directory"); 173 return false; 174 } 175 176 // Get normalization from support_dir_spec. Note the FileSpec resolve 177 // does not remove '..' in the path. 178 char *const dir_realpath = 179 realpath(support_dir_spec.GetPath().c_str(), NULL); 180 if (dir_realpath) { 181 raw_path = dir_realpath; 182 free(dir_realpath); 183 } else { 184 raw_path = support_dir_spec.GetPath(); 185 } 186 } 187 188 file_spec.SetDirectory(raw_path); 189 return (bool)file_spec.GetDirectory(); 190} 191 192bool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { 193 FileSpec lldb_file_spec = GetShlibDir(); 194 if (!lldb_file_spec) 195 return false; 196 197 std::string raw_path = lldb_file_spec.GetPath(); 198 199 size_t framework_pos = raw_path.find("LLDB.framework"); 200 if (framework_pos != std::string::npos) { 201 framework_pos += strlen("LLDB.framework"); 202 raw_path.resize(framework_pos); 203 raw_path.append("/Headers"); 204 } 205 file_spec.SetDirectory(raw_path); 206 return true; 207} 208 209bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { 210 FileSpec lldb_file_spec = GetShlibDir(); 211 if (!lldb_file_spec) 212 return false; 213 214 std::string raw_path = lldb_file_spec.GetPath(); 215 216 size_t framework_pos = raw_path.find("LLDB.framework"); 217 if (framework_pos == std::string::npos) 218 return false; 219 220 framework_pos += strlen("LLDB.framework"); 221 raw_path.resize(framework_pos); 222 raw_path.append("/Resources/PlugIns"); 223 file_spec.SetDirectory(raw_path); 224 return true; 225} 226 227bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { 228 FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); 229 FileSystem::Instance().Resolve(temp_file); 230 file_spec.SetDirectory(temp_file.GetPathAsConstString()); 231 return true; 232} 233 234void HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, 235 ArchSpec &arch_64) { 236 // All apple systems support 32 bit execution. 237 uint32_t cputype, cpusubtype; 238 uint32_t is_64_bit_capable = false; 239 size_t len = sizeof(cputype); 240 ArchSpec host_arch; 241 // These will tell us about the kernel architecture, which even on a 64 242 // bit machine can be 32 bit... 243 if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { 244 len = sizeof(cpusubtype); 245 if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) 246 cpusubtype = CPU_TYPE_ANY; 247 248 len = sizeof(is_64_bit_capable); 249 ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); 250 251 if (cputype == CPU_TYPE_ARM64 && cpusubtype == CPU_SUBTYPE_ARM64E) { 252 // The arm64e architecture is a preview. Pretend the host architecture 253 // is arm64. 254 cpusubtype = CPU_SUBTYPE_ARM64_ALL; 255 } 256 257 if (is_64_bit_capable) { 258 if (cputype & CPU_ARCH_ABI64) { 259 // We have a 64 bit kernel on a 64 bit system 260 arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 261 } else { 262 // We have a 64 bit kernel that is returning a 32 bit cputype, the 263 // cpusubtype will be correct as if it were for a 64 bit architecture 264 arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, 265 cpusubtype); 266 } 267 268 // Now we need modify the cpusubtype for the 32 bit slices. 269 uint32_t cpusubtype32 = cpusubtype; 270#if defined(__i386__) || defined(__x86_64__) 271 if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) 272 cpusubtype32 = CPU_SUBTYPE_I386_ALL; 273#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 274 if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) 275 cpusubtype32 = CPU_SUBTYPE_ARM_V7S; 276#endif 277 arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), 278 cpusubtype32); 279 280 if (cputype == CPU_TYPE_ARM || 281 cputype == CPU_TYPE_ARM64 || 282 cputype == CPU_TYPE_ARM64_32) { 283// When running on a watch or tv, report the host os correctly 284#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 285 arch_32.GetTriple().setOS(llvm::Triple::TvOS); 286 arch_64.GetTriple().setOS(llvm::Triple::TvOS); 287#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 288 arch_32.GetTriple().setOS(llvm::Triple::BridgeOS); 289 arch_64.GetTriple().setOS(llvm::Triple::BridgeOS); 290#elif defined(TARGET_OS_WATCHOS) && TARGET_OS_WATCHOS == 1 291 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 292 arch_64.GetTriple().setOS(llvm::Triple::WatchOS); 293#elif defined(TARGET_OS_XR) && TARGET_OS_XR == 1 294 arch_32.GetTriple().setOS(llvm::Triple::XROS); 295 arch_64.GetTriple().setOS(llvm::Triple::XROS); 296#elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 297 arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 298 arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 299#else 300 arch_32.GetTriple().setOS(llvm::Triple::IOS); 301 arch_64.GetTriple().setOS(llvm::Triple::IOS); 302#endif 303 } else { 304 arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 305 arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 306 } 307 } else { 308 // We have a 32 bit kernel on a 32 bit system 309 arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 310#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 311 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 312#else 313 arch_32.GetTriple().setOS(llvm::Triple::IOS); 314#endif 315 arch_64.Clear(); 316 } 317 } 318} 319 320/// Return and cache $DEVELOPER_DIR if it is set and exists. 321static std::string GetEnvDeveloperDir() { 322 static std::string g_env_developer_dir; 323 static std::once_flag g_once_flag; 324 std::call_once(g_once_flag, [&]() { 325 if (const char *developer_dir_env_var = getenv("DEVELOPER_DIR")) { 326 FileSpec fspec(developer_dir_env_var); 327 if (FileSystem::Instance().Exists(fspec)) 328 g_env_developer_dir = fspec.GetPath(); 329 }}); 330 return g_env_developer_dir; 331} 332 333FileSpec HostInfoMacOSX::GetXcodeContentsDirectory() { 334 static FileSpec g_xcode_contents_path; 335 static std::once_flag g_once_flag; 336 std::call_once(g_once_flag, [&]() { 337 // Try the shlib dir first. 338 if (FileSpec fspec = HostInfo::GetShlibDir()) { 339 if (FileSystem::Instance().Exists(fspec)) { 340 std::string xcode_contents_dir = 341 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 342 if (!xcode_contents_dir.empty()) { 343 g_xcode_contents_path = FileSpec(xcode_contents_dir); 344 return; 345 } 346 } 347 } 348 349 llvm::SmallString<128> env_developer_dir(GetEnvDeveloperDir()); 350 if (!env_developer_dir.empty()) { 351 llvm::sys::path::append(env_developer_dir, "Contents"); 352 std::string xcode_contents_dir = 353 XcodeSDK::FindXcodeContentsDirectoryInPath(env_developer_dir); 354 if (!xcode_contents_dir.empty()) { 355 g_xcode_contents_path = FileSpec(xcode_contents_dir); 356 return; 357 } 358 } 359 360 auto sdk_path_or_err = 361 HostInfo::GetSDKRoot(SDKOptions{XcodeSDK::GetAnyMacOS()}); 362 if (!sdk_path_or_err) { 363 Log *log = GetLog(LLDBLog::Host); 364 LLDB_LOG_ERROR(log, sdk_path_or_err.takeError(), 365 "Error while searching for Xcode SDK: {0}"); 366 return; 367 } 368 FileSpec fspec(*sdk_path_or_err); 369 if (fspec) { 370 if (FileSystem::Instance().Exists(fspec)) { 371 std::string xcode_contents_dir = 372 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 373 if (!xcode_contents_dir.empty()) { 374 g_xcode_contents_path = FileSpec(xcode_contents_dir); 375 return; 376 } 377 } 378 } 379 }); 380 return g_xcode_contents_path; 381} 382 383lldb_private::FileSpec HostInfoMacOSX::GetXcodeDeveloperDirectory() { 384 static lldb_private::FileSpec g_developer_directory; 385 static llvm::once_flag g_once_flag; 386 llvm::call_once(g_once_flag, []() { 387 if (FileSpec fspec = GetXcodeContentsDirectory()) { 388 fspec.AppendPathComponent("Developer"); 389 if (FileSystem::Instance().Exists(fspec)) 390 g_developer_directory = fspec; 391 } 392 }); 393 return g_developer_directory; 394} 395 396static llvm::Expected<std::string> 397xcrun(const std::string &sdk, llvm::ArrayRef<llvm::StringRef> arguments, 398 llvm::StringRef developer_dir = "") { 399 Args args; 400 if (!developer_dir.empty()) { 401 args.AppendArgument("/usr/bin/env"); 402 args.AppendArgument("DEVELOPER_DIR=" + developer_dir.str()); 403 } 404 args.AppendArgument("/usr/bin/xcrun"); 405 args.AppendArgument("--sdk"); 406 args.AppendArgument(sdk); 407 for (auto arg: arguments) 408 args.AppendArgument(arg); 409 410 Log *log = GetLog(LLDBLog::Host); 411 if (log) { 412 std::string cmdstr; 413 args.GetCommandString(cmdstr); 414 LLDB_LOG(log, "GetXcodeSDK() running shell cmd '{0}'", cmdstr); 415 } 416 417 int status = 0; 418 int signo = 0; 419 std::string output_str; 420 // The first time after Xcode was updated or freshly installed, 421 // xcrun can take surprisingly long to build up its database. 422 auto timeout = std::chrono::seconds(60); 423 bool run_in_shell = false; 424 lldb_private::Status error = Host::RunShellCommand( 425 args, FileSpec(), &status, &signo, &output_str, timeout, run_in_shell); 426 427 // Check that xcrun returned something useful. 428 if (error.Fail()) { 429 // Catastrophic error. 430 LLDB_LOG(log, "xcrun failed to execute: {0}", error); 431 return error.ToError(); 432 } 433 if (status != 0) { 434 // xcrun didn't find a matching SDK. Not an error, we'll try 435 // different spellings. 436 LLDB_LOG(log, "xcrun returned exit code {0}", status); 437 if (!output_str.empty()) 438 LLDB_LOG(log, "xcrun output was:\n{0}", output_str); 439 return ""; 440 } 441 if (output_str.empty()) { 442 LLDB_LOG(log, "xcrun returned no results"); 443 return ""; 444 } 445 446 // Convert to a StringRef so we can manipulate the string without modifying 447 // the underlying data. 448 llvm::StringRef output(output_str); 449 450 // Remove any trailing newline characters. 451 output = output.rtrim(); 452 453 // Strip any leading newline characters and everything before them. 454 const size_t last_newline = output.rfind('\n'); 455 if (last_newline != llvm::StringRef::npos) 456 output = output.substr(last_newline + 1); 457 458 return output.str(); 459} 460 461static llvm::Expected<std::string> GetXcodeSDK(XcodeSDK sdk) { 462 XcodeSDK::Info info = sdk.Parse(); 463 std::string sdk_name = XcodeSDK::GetCanonicalName(info); 464 if (sdk_name.empty()) 465 return llvm::createStringError(llvm::inconvertibleErrorCode(), 466 "Unrecognized SDK type: " + sdk.GetString()); 467 468 Log *log = GetLog(LLDBLog::Host); 469 470 auto find_sdk = 471 [](const std::string &sdk_name) -> llvm::Expected<std::string> { 472 llvm::SmallVector<llvm::StringRef, 1> show_sdk_path = {"--show-sdk-path"}; 473 // Invoke xcrun with the developer dir specified in the environment. 474 std::string developer_dir = GetEnvDeveloperDir(); 475 if (!developer_dir.empty()) { 476 // Don't fallback if DEVELOPER_DIR was set. 477 return xcrun(sdk_name, show_sdk_path, developer_dir); 478 } 479 480 // Invoke xcrun with the shlib dir. 481 if (FileSpec fspec = HostInfo::GetShlibDir()) { 482 if (FileSystem::Instance().Exists(fspec)) { 483 llvm::SmallString<0> shlib_developer_dir( 484 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath())); 485 llvm::sys::path::append(shlib_developer_dir, "Developer"); 486 if (FileSystem::Instance().Exists(shlib_developer_dir)) { 487 auto sdk = xcrun(sdk_name, show_sdk_path, shlib_developer_dir); 488 if (!sdk) 489 return sdk.takeError(); 490 if (!sdk->empty()) 491 return sdk; 492 } 493 } 494 } 495 496 // Invoke xcrun without a developer dir as a last resort. 497 return xcrun(sdk_name, show_sdk_path); 498 }; 499 500 auto path_or_err = find_sdk(sdk_name); 501 if (!path_or_err) 502 return path_or_err.takeError(); 503 std::string path = *path_or_err; 504 while (path.empty()) { 505 // Try an alternate spelling of the name ("macosx10.9internal"). 506 if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() && 507 info.internal) { 508 llvm::StringRef fixed(sdk_name); 509 if (fixed.consume_back(".internal")) 510 sdk_name = fixed.str() + "internal"; 511 path_or_err = find_sdk(sdk_name); 512 if (!path_or_err) 513 return path_or_err.takeError(); 514 path = *path_or_err; 515 if (!path.empty()) 516 break; 517 } 518 LLDB_LOG(log, "Couldn't find SDK {0} on host", sdk_name); 519 520 // Try without the version. 521 if (!info.version.empty()) { 522 info.version = {}; 523 sdk_name = XcodeSDK::GetCanonicalName(info); 524 path_or_err = find_sdk(sdk_name); 525 if (!path_or_err) 526 return path_or_err.takeError(); 527 path = *path_or_err; 528 if (!path.empty()) 529 break; 530 } 531 532 LLDB_LOG(log, "Couldn't find any matching SDK on host"); 533 return ""; 534 } 535 536 // Whatever is left in output should be a valid path. 537 if (!FileSystem::Instance().Exists(path)) { 538 LLDB_LOG(log, "SDK returned by xcrun doesn't exist"); 539 return llvm::createStringError(llvm::inconvertibleErrorCode(), 540 "SDK returned by xcrun doesn't exist"); 541 } 542 return path; 543} 544 545namespace { 546struct ErrorOrPath { 547 std::string str; 548 bool is_error; 549}; 550} // namespace 551 552static llvm::Expected<llvm::StringRef> 553find_cached_path(llvm::StringMap<ErrorOrPath> &cache, std::mutex &mutex, 554 llvm::StringRef key, 555 std::function<llvm::Expected<std::string>(void)> compute) { 556 std::lock_guard<std::mutex> guard(mutex); 557 LLDB_SCOPED_TIMER(); 558 559 auto it = cache.find(key); 560 if (it != cache.end()) { 561 if (it->second.is_error) 562 return llvm::createStringError(llvm::inconvertibleErrorCode(), 563 it->second.str); 564 return it->second.str; 565 } 566 auto path_or_err = compute(); 567 if (!path_or_err) { 568 std::string error = toString(path_or_err.takeError()); 569 cache.insert({key, {error, true}}); 570 return llvm::createStringError(llvm::inconvertibleErrorCode(), error); 571 } 572 auto it_new = cache.insert({key, {*path_or_err, false}}); 573 return it_new.first->second.str; 574} 575 576llvm::Expected<llvm::StringRef> HostInfoMacOSX::GetSDKRoot(SDKOptions options) { 577 static llvm::StringMap<ErrorOrPath> g_sdk_path; 578 static std::mutex g_sdk_path_mutex; 579 if (!options.XcodeSDKSelection) 580 return llvm::createStringError(llvm::inconvertibleErrorCode(), 581 "XcodeSDK not specified"); 582 XcodeSDK sdk = *options.XcodeSDKSelection; 583 auto key = sdk.GetString(); 584 return find_cached_path(g_sdk_path, g_sdk_path_mutex, key, [&](){ 585 return GetXcodeSDK(sdk); 586 }); 587} 588 589llvm::Expected<llvm::StringRef> 590HostInfoMacOSX::FindSDKTool(XcodeSDK sdk, llvm::StringRef tool) { 591 static llvm::StringMap<ErrorOrPath> g_tool_path; 592 static std::mutex g_tool_path_mutex; 593 std::string key; 594 llvm::raw_string_ostream(key) << sdk.GetString() << ":" << tool; 595 return find_cached_path( 596 g_tool_path, g_tool_path_mutex, key, 597 [&]() -> llvm::Expected<std::string> { 598 std::string sdk_name = XcodeSDK::GetCanonicalName(sdk.Parse()); 599 if (sdk_name.empty()) 600 return llvm::createStringError(llvm::inconvertibleErrorCode(), 601 "Unrecognized SDK type: " + 602 sdk.GetString()); 603 llvm::SmallVector<llvm::StringRef, 2> find = {"-find", tool}; 604 return xcrun(sdk_name, find); 605 }); 606} 607 608namespace { 609struct dyld_shared_cache_dylib_text_info { 610 uint64_t version; // current version 1 611 // following fields all exist in version 1 612 uint64_t loadAddressUnslid; 613 uint64_t textSegmentSize; 614 uuid_t dylibUuid; 615 const char *path; // pointer invalid at end of iterations 616 // following fields all exist in version 2 617 uint64_t textSegmentOffset; // offset from start of cache 618}; 619typedef struct dyld_shared_cache_dylib_text_info 620 dyld_shared_cache_dylib_text_info; 621} 622 623extern "C" int dyld_shared_cache_iterate_text( 624 const uuid_t cacheUuid, 625 void (^callback)(const dyld_shared_cache_dylib_text_info *info)); 626extern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length); 627extern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid); 628 629namespace { 630class SharedCacheInfo { 631public: 632 const UUID &GetUUID() const { return m_uuid; } 633 const llvm::StringMap<SharedCacheImageInfo> &GetImages() const { 634 return m_images; 635 } 636 637 SharedCacheInfo(); 638 639private: 640 bool CreateSharedCacheInfoWithInstrospectionSPIs(); 641 642 llvm::StringMap<SharedCacheImageInfo> m_images; 643 UUID m_uuid; 644}; 645} 646 647bool SharedCacheInfo::CreateSharedCacheInfoWithInstrospectionSPIs() { 648#if defined(SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS) 649 dyld_process_t dyld_process = dyld_process_create_for_current_task(); 650 if (!dyld_process) 651 return false; 652 653 auto cleanup_process_on_exit = 654 llvm::make_scope_exit([&]() { dyld_process_dispose(dyld_process); }); 655 656 dyld_process_snapshot_t snapshot = 657 dyld_process_snapshot_create_for_process(dyld_process, nullptr); 658 if (!snapshot) 659 return false; 660 661 auto cleanup_snapshot_on_exit = 662 llvm::make_scope_exit([&]() { dyld_process_snapshot_dispose(snapshot); }); 663 664 dyld_shared_cache_t shared_cache = 665 dyld_process_snapshot_get_shared_cache(snapshot); 666 if (!shared_cache) 667 return false; 668 669 dyld_shared_cache_for_each_image(shared_cache, ^(dyld_image_t image) { 670 __block uint64_t minVmAddr = UINT64_MAX; 671 __block uint64_t maxVmAddr = 0; 672 uuid_t uuidStore; 673 __block uuid_t *uuid = &uuidStore; 674 675 dyld_image_for_each_segment_info( 676 image, 677 ^(const char *segmentName, uint64_t vmAddr, uint64_t vmSize, int perm) { 678 minVmAddr = std::min(minVmAddr, vmAddr); 679 maxVmAddr = std::max(maxVmAddr, vmAddr + vmSize); 680 dyld_image_copy_uuid(image, uuid); 681 }); 682 assert(minVmAddr != UINT_MAX); 683 assert(maxVmAddr != 0); 684 m_images[dyld_image_get_installname(image)] = SharedCacheImageInfo{ 685 UUID(uuid, 16), std::make_shared<DataBufferUnowned>( 686 (uint8_t *)minVmAddr, maxVmAddr - minVmAddr)}; 687 }); 688 return true; 689#endif 690 return false; 691} 692 693SharedCacheInfo::SharedCacheInfo() { 694 if (CreateSharedCacheInfoWithInstrospectionSPIs()) 695 return; 696 697 size_t shared_cache_size; 698 uint8_t *shared_cache_start = 699 _dyld_get_shared_cache_range(&shared_cache_size); 700 uuid_t dsc_uuid; 701 _dyld_get_shared_cache_uuid(dsc_uuid); 702 m_uuid = UUID(dsc_uuid); 703 704 dyld_shared_cache_iterate_text( 705 dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) { 706 m_images[info->path] = SharedCacheImageInfo{ 707 UUID(info->dylibUuid, 16), 708 std::make_shared<DataBufferUnowned>( 709 shared_cache_start + info->textSegmentOffset, 710 shared_cache_size - info->textSegmentOffset)}; 711 }); 712} 713 714SharedCacheImageInfo 715HostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) { 716 static SharedCacheInfo g_shared_cache_info; 717 return g_shared_cache_info.GetImages().lookup(image_name); 718} 719