1061da546Spatrick//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// 2061da546Spatrick// 3061da546Spatrick// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick// See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick// 7061da546Spatrick//===----------------------------------------------------------------------===// 8061da546Spatrick 9*f6aab3d8Srobert#include "lldb/Host/macosx/HostInfoMacOSX.h" 10*f6aab3d8Srobert#include "Utility/UuidCompatibility.h" 11061da546Spatrick#include "lldb/Host/FileSystem.h" 12dda28197Spatrick#include "lldb/Host/Host.h" 13061da546Spatrick#include "lldb/Host/HostInfo.h" 14061da546Spatrick#include "lldb/Utility/Args.h" 15*f6aab3d8Srobert#include "lldb/Utility/LLDBLog.h" 16061da546Spatrick#include "lldb/Utility/Log.h" 17be691f3bSpatrick#include "lldb/Utility/Timer.h" 18061da546Spatrick 19*f6aab3d8Srobert#include "llvm/ADT/ScopeExit.h" 20061da546Spatrick#include "llvm/ADT/SmallString.h" 21be691f3bSpatrick#include "llvm/ADT/StringMap.h" 22061da546Spatrick#include "llvm/Support/FileSystem.h" 23061da546Spatrick#include "llvm/Support/Path.h" 24061da546Spatrick#include "llvm/Support/raw_ostream.h" 25061da546Spatrick 26061da546Spatrick// C++ Includes 27*f6aab3d8Srobert#include <optional> 28061da546Spatrick#include <string> 29061da546Spatrick 30061da546Spatrick// C inclues 31be691f3bSpatrick#include <cstdlib> 32061da546Spatrick#include <sys/sysctl.h> 33061da546Spatrick#include <sys/syslimits.h> 34061da546Spatrick#include <sys/types.h> 35061da546Spatrick 36061da546Spatrick// Objective-C/C++ includes 37061da546Spatrick#include <CoreFoundation/CoreFoundation.h> 38061da546Spatrick#include <Foundation/Foundation.h> 39061da546Spatrick#include <mach-o/dyld.h> 40*f6aab3d8Srobert#if __has_include(<mach-o/dyld_introspection.h>) 41*f6aab3d8Srobert#include <mach-o/dyld_introspection.h> 42*f6aab3d8Srobert#define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS 43*f6aab3d8Srobert#endif 44061da546Spatrick#include <objc/objc-auto.h> 45061da546Spatrick 46061da546Spatrick// These are needed when compiling on systems 47061da546Spatrick// that do not yet have these definitions 48061da546Spatrick#include <AvailabilityMacros.h> 49061da546Spatrick#ifndef CPU_SUBTYPE_X86_64_H 50061da546Spatrick#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) 51061da546Spatrick#endif 52061da546Spatrick#ifndef CPU_TYPE_ARM64 53061da546Spatrick#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 54061da546Spatrick#endif 55061da546Spatrick 56061da546Spatrick#ifndef CPU_TYPE_ARM64_32 57061da546Spatrick#define CPU_ARCH_ABI64_32 0x02000000 58061da546Spatrick#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32) 59061da546Spatrick#endif 60061da546Spatrick 61061da546Spatrick#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH 62061da546Spatrick 63061da546Spatrickusing namespace lldb_private; 64061da546Spatrick 65*f6aab3d8Srobertstd::optional<std::string> HostInfoMacOSX::GetOSBuildString() { 66061da546Spatrick int mib[2] = {CTL_KERN, KERN_OSVERSION}; 67061da546Spatrick char cstr[PATH_MAX]; 68061da546Spatrick size_t cstr_len = sizeof(cstr); 69*f6aab3d8Srobert if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) 70*f6aab3d8Srobert return std::string(cstr, cstr_len - 1); 71061da546Spatrick 72*f6aab3d8Srobert return std::nullopt; 73061da546Spatrick} 74061da546Spatrick 75061da546Spatrickstatic void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { 76061da546Spatrick @autoreleasepool { 77061da546Spatrick NSDictionary *version_info = 78061da546Spatrick [NSDictionary dictionaryWithContentsOfFile: 79061da546Spatrick @"/System/Library/CoreServices/SystemVersion.plist"]; 80061da546Spatrick NSString *version_value = [version_info objectForKey: Key]; 81061da546Spatrick const char *version_str = [version_value UTF8String]; 82061da546Spatrick version.tryParse(version_str); 83061da546Spatrick } 84061da546Spatrick} 85061da546Spatrick 86061da546Spatrickllvm::VersionTuple HostInfoMacOSX::GetOSVersion() { 87061da546Spatrick static llvm::VersionTuple g_version; 88061da546Spatrick if (g_version.empty()) 89061da546Spatrick ParseOSVersion(g_version, @"ProductVersion"); 90061da546Spatrick return g_version; 91061da546Spatrick} 92061da546Spatrick 93061da546Spatrickllvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() { 94061da546Spatrick static llvm::VersionTuple g_version; 95061da546Spatrick if (g_version.empty()) 96061da546Spatrick ParseOSVersion(g_version, @"iOSSupportVersion"); 97061da546Spatrick return g_version; 98061da546Spatrick} 99061da546Spatrick 100061da546Spatrick 101061da546SpatrickFileSpec HostInfoMacOSX::GetProgramFileSpec() { 102061da546Spatrick static FileSpec g_program_filespec; 103061da546Spatrick if (!g_program_filespec) { 104061da546Spatrick char program_fullpath[PATH_MAX]; 105061da546Spatrick // If DST is NULL, then return the number of bytes needed. 106061da546Spatrick uint32_t len = sizeof(program_fullpath); 107061da546Spatrick int err = _NSGetExecutablePath(program_fullpath, &len); 108061da546Spatrick if (err == 0) 109061da546Spatrick g_program_filespec.SetFile(program_fullpath, FileSpec::Style::native); 110061da546Spatrick else if (err == -1) { 111061da546Spatrick char *large_program_fullpath = (char *)::malloc(len + 1); 112061da546Spatrick 113061da546Spatrick err = _NSGetExecutablePath(large_program_fullpath, &len); 114061da546Spatrick if (err == 0) 115061da546Spatrick g_program_filespec.SetFile(large_program_fullpath, 116061da546Spatrick FileSpec::Style::native); 117061da546Spatrick 118061da546Spatrick ::free(large_program_fullpath); 119061da546Spatrick } 120061da546Spatrick } 121061da546Spatrick return g_program_filespec; 122061da546Spatrick} 123061da546Spatrick 124061da546Spatrickbool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { 125061da546Spatrick FileSpec lldb_file_spec = GetShlibDir(); 126061da546Spatrick if (!lldb_file_spec) 127061da546Spatrick return false; 128061da546Spatrick 129061da546Spatrick std::string raw_path = lldb_file_spec.GetPath(); 130061da546Spatrick 131061da546Spatrick size_t framework_pos = raw_path.find("LLDB.framework"); 132061da546Spatrick if (framework_pos != std::string::npos) { 133061da546Spatrick framework_pos += strlen("LLDB.framework"); 134be691f3bSpatrick#if TARGET_OS_IPHONE 135061da546Spatrick // Shallow bundle 136061da546Spatrick raw_path.resize(framework_pos); 137061da546Spatrick#else 138061da546Spatrick // Normal bundle 139061da546Spatrick raw_path.resize(framework_pos); 140061da546Spatrick raw_path.append("/Resources"); 141061da546Spatrick#endif 142061da546Spatrick } else { 143061da546Spatrick // Find the bin path relative to the lib path where the cmake-based 144061da546Spatrick // OS X .dylib lives. This is not going to work if the bin and lib 145061da546Spatrick // dir are not both in the same dir. 146061da546Spatrick // 147061da546Spatrick // It is not going to work to do it by the executable path either, 148061da546Spatrick // as in the case of a python script, the executable is python, not 149061da546Spatrick // the lldb driver. 150061da546Spatrick raw_path.append("/../bin"); 151061da546Spatrick FileSpec support_dir_spec(raw_path); 152061da546Spatrick FileSystem::Instance().Resolve(support_dir_spec); 153061da546Spatrick if (!FileSystem::Instance().IsDirectory(support_dir_spec)) { 154*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host); 155061da546Spatrick LLDB_LOGF(log, "HostInfoMacOSX::%s(): failed to find support directory", 156061da546Spatrick __FUNCTION__); 157061da546Spatrick return false; 158061da546Spatrick } 159061da546Spatrick 160061da546Spatrick // Get normalization from support_dir_spec. Note the FileSpec resolve 161061da546Spatrick // does not remove '..' in the path. 162061da546Spatrick char *const dir_realpath = 163061da546Spatrick realpath(support_dir_spec.GetPath().c_str(), NULL); 164061da546Spatrick if (dir_realpath) { 165061da546Spatrick raw_path = dir_realpath; 166061da546Spatrick free(dir_realpath); 167061da546Spatrick } else { 168061da546Spatrick raw_path = support_dir_spec.GetPath(); 169061da546Spatrick } 170061da546Spatrick } 171061da546Spatrick 172*f6aab3d8Srobert file_spec.SetDirectory(raw_path); 173061da546Spatrick return (bool)file_spec.GetDirectory(); 174061da546Spatrick} 175061da546Spatrick 176061da546Spatrickbool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { 177061da546Spatrick FileSpec lldb_file_spec = GetShlibDir(); 178061da546Spatrick if (!lldb_file_spec) 179061da546Spatrick return false; 180061da546Spatrick 181061da546Spatrick std::string raw_path = lldb_file_spec.GetPath(); 182061da546Spatrick 183061da546Spatrick size_t framework_pos = raw_path.find("LLDB.framework"); 184061da546Spatrick if (framework_pos != std::string::npos) { 185061da546Spatrick framework_pos += strlen("LLDB.framework"); 186061da546Spatrick raw_path.resize(framework_pos); 187061da546Spatrick raw_path.append("/Headers"); 188061da546Spatrick } 189*f6aab3d8Srobert file_spec.SetDirectory(raw_path); 190061da546Spatrick return true; 191061da546Spatrick} 192061da546Spatrick 193061da546Spatrickbool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { 194061da546Spatrick FileSpec lldb_file_spec = GetShlibDir(); 195061da546Spatrick if (!lldb_file_spec) 196061da546Spatrick return false; 197061da546Spatrick 198061da546Spatrick std::string raw_path = lldb_file_spec.GetPath(); 199061da546Spatrick 200061da546Spatrick size_t framework_pos = raw_path.find("LLDB.framework"); 201061da546Spatrick if (framework_pos == std::string::npos) 202061da546Spatrick return false; 203061da546Spatrick 204061da546Spatrick framework_pos += strlen("LLDB.framework"); 205061da546Spatrick raw_path.resize(framework_pos); 206061da546Spatrick raw_path.append("/Resources/PlugIns"); 207*f6aab3d8Srobert file_spec.SetDirectory(raw_path); 208061da546Spatrick return true; 209061da546Spatrick} 210061da546Spatrick 211061da546Spatrickbool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { 212061da546Spatrick FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); 213061da546Spatrick FileSystem::Instance().Resolve(temp_file); 214*f6aab3d8Srobert file_spec.SetDirectory(temp_file.GetPathAsConstString()); 215061da546Spatrick return true; 216061da546Spatrick} 217061da546Spatrick 218061da546Spatrickvoid HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, 219061da546Spatrick ArchSpec &arch_64) { 220061da546Spatrick // All apple systems support 32 bit execution. 221061da546Spatrick uint32_t cputype, cpusubtype; 222061da546Spatrick uint32_t is_64_bit_capable = false; 223061da546Spatrick size_t len = sizeof(cputype); 224061da546Spatrick ArchSpec host_arch; 225061da546Spatrick // These will tell us about the kernel architecture, which even on a 64 226061da546Spatrick // bit machine can be 32 bit... 227061da546Spatrick if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { 228061da546Spatrick len = sizeof(cpusubtype); 229061da546Spatrick if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) 230061da546Spatrick cpusubtype = CPU_TYPE_ANY; 231061da546Spatrick 232061da546Spatrick len = sizeof(is_64_bit_capable); 233061da546Spatrick ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); 234061da546Spatrick 235be691f3bSpatrick if (cputype == CPU_TYPE_ARM64 && cpusubtype == CPU_SUBTYPE_ARM64E) { 236be691f3bSpatrick // The arm64e architecture is a preview. Pretend the host architecture 237be691f3bSpatrick // is arm64. 238be691f3bSpatrick cpusubtype = CPU_SUBTYPE_ARM64_ALL; 239be691f3bSpatrick } 240be691f3bSpatrick 241061da546Spatrick if (is_64_bit_capable) { 242061da546Spatrick if (cputype & CPU_ARCH_ABI64) { 243061da546Spatrick // We have a 64 bit kernel on a 64 bit system 244061da546Spatrick arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 245061da546Spatrick } else { 246061da546Spatrick // We have a 64 bit kernel that is returning a 32 bit cputype, the 247061da546Spatrick // cpusubtype will be correct as if it were for a 64 bit architecture 248061da546Spatrick arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, 249061da546Spatrick cpusubtype); 250061da546Spatrick } 251061da546Spatrick 252061da546Spatrick // Now we need modify the cpusubtype for the 32 bit slices. 253061da546Spatrick uint32_t cpusubtype32 = cpusubtype; 254061da546Spatrick#if defined(__i386__) || defined(__x86_64__) 255061da546Spatrick if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) 256061da546Spatrick cpusubtype32 = CPU_SUBTYPE_I386_ALL; 257061da546Spatrick#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 258061da546Spatrick if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) 259061da546Spatrick cpusubtype32 = CPU_SUBTYPE_ARM_V7S; 260061da546Spatrick#endif 261061da546Spatrick arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), 262061da546Spatrick cpusubtype32); 263061da546Spatrick 264061da546Spatrick if (cputype == CPU_TYPE_ARM || 265061da546Spatrick cputype == CPU_TYPE_ARM64 || 266061da546Spatrick cputype == CPU_TYPE_ARM64_32) { 267061da546Spatrick// When running on a watch or tv, report the host os correctly 268061da546Spatrick#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 269061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::TvOS); 270061da546Spatrick arch_64.GetTriple().setOS(llvm::Triple::TvOS); 271061da546Spatrick#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 272061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::BridgeOS); 273061da546Spatrick arch_64.GetTriple().setOS(llvm::Triple::BridgeOS); 274061da546Spatrick#elif defined(TARGET_OS_WATCHOS) && TARGET_OS_WATCHOS == 1 275061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 276061da546Spatrick arch_64.GetTriple().setOS(llvm::Triple::WatchOS); 277dda28197Spatrick#elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 278dda28197Spatrick arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 279dda28197Spatrick arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 280061da546Spatrick#else 281061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::IOS); 282061da546Spatrick arch_64.GetTriple().setOS(llvm::Triple::IOS); 283061da546Spatrick#endif 284061da546Spatrick } else { 285061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 286061da546Spatrick arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 287061da546Spatrick } 288061da546Spatrick } else { 289061da546Spatrick // We have a 32 bit kernel on a 32 bit system 290061da546Spatrick arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 291061da546Spatrick#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 292061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 293061da546Spatrick#else 294061da546Spatrick arch_32.GetTriple().setOS(llvm::Triple::IOS); 295061da546Spatrick#endif 296061da546Spatrick arch_64.Clear(); 297061da546Spatrick } 298061da546Spatrick } 299061da546Spatrick} 300dda28197Spatrick 301dda28197Spatrick/// Return and cache $DEVELOPER_DIR if it is set and exists. 302dda28197Spatrickstatic std::string GetEnvDeveloperDir() { 303dda28197Spatrick static std::string g_env_developer_dir; 304dda28197Spatrick static std::once_flag g_once_flag; 305dda28197Spatrick std::call_once(g_once_flag, [&]() { 306dda28197Spatrick if (const char *developer_dir_env_var = getenv("DEVELOPER_DIR")) { 307dda28197Spatrick FileSpec fspec(developer_dir_env_var); 308dda28197Spatrick if (FileSystem::Instance().Exists(fspec)) 309dda28197Spatrick g_env_developer_dir = fspec.GetPath(); 310dda28197Spatrick }}); 311dda28197Spatrick return g_env_developer_dir; 312dda28197Spatrick} 313dda28197Spatrick 314dda28197SpatrickFileSpec HostInfoMacOSX::GetXcodeContentsDirectory() { 315dda28197Spatrick static FileSpec g_xcode_contents_path; 316dda28197Spatrick static std::once_flag g_once_flag; 317dda28197Spatrick std::call_once(g_once_flag, [&]() { 318dda28197Spatrick // Try the shlib dir first. 319dda28197Spatrick if (FileSpec fspec = HostInfo::GetShlibDir()) { 320dda28197Spatrick if (FileSystem::Instance().Exists(fspec)) { 321dda28197Spatrick std::string xcode_contents_dir = 322dda28197Spatrick XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 323dda28197Spatrick if (!xcode_contents_dir.empty()) { 324dda28197Spatrick g_xcode_contents_path = FileSpec(xcode_contents_dir); 325dda28197Spatrick return; 326dda28197Spatrick } 327dda28197Spatrick } 328dda28197Spatrick } 329dda28197Spatrick 330dda28197Spatrick llvm::SmallString<128> env_developer_dir(GetEnvDeveloperDir()); 331dda28197Spatrick if (!env_developer_dir.empty()) { 332dda28197Spatrick llvm::sys::path::append(env_developer_dir, "Contents"); 333dda28197Spatrick std::string xcode_contents_dir = 334dda28197Spatrick XcodeSDK::FindXcodeContentsDirectoryInPath(env_developer_dir); 335dda28197Spatrick if (!xcode_contents_dir.empty()) { 336dda28197Spatrick g_xcode_contents_path = FileSpec(xcode_contents_dir); 337dda28197Spatrick return; 338dda28197Spatrick } 339dda28197Spatrick } 340dda28197Spatrick 341*f6aab3d8Srobert auto sdk_path_or_err = HostInfo::GetXcodeSDKPath(XcodeSDK::GetAnyMacOS()); 342*f6aab3d8Srobert if (!sdk_path_or_err) { 343*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host); 344*f6aab3d8Srobert LLDB_LOGF(log, "Error while searching for Xcode SDK: %s", 345*f6aab3d8Srobert toString(sdk_path_or_err.takeError()).c_str()); 346*f6aab3d8Srobert return; 347*f6aab3d8Srobert } 348*f6aab3d8Srobert FileSpec fspec(*sdk_path_or_err); 349dda28197Spatrick if (fspec) { 350dda28197Spatrick if (FileSystem::Instance().Exists(fspec)) { 351dda28197Spatrick std::string xcode_contents_dir = 352dda28197Spatrick XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 353dda28197Spatrick if (!xcode_contents_dir.empty()) { 354dda28197Spatrick g_xcode_contents_path = FileSpec(xcode_contents_dir); 355dda28197Spatrick return; 356dda28197Spatrick } 357dda28197Spatrick } 358dda28197Spatrick } 359dda28197Spatrick }); 360dda28197Spatrick return g_xcode_contents_path; 361dda28197Spatrick} 362dda28197Spatrick 363dda28197Spatricklldb_private::FileSpec HostInfoMacOSX::GetXcodeDeveloperDirectory() { 364dda28197Spatrick static lldb_private::FileSpec g_developer_directory; 365dda28197Spatrick static llvm::once_flag g_once_flag; 366dda28197Spatrick llvm::call_once(g_once_flag, []() { 367dda28197Spatrick if (FileSpec fspec = GetXcodeContentsDirectory()) { 368dda28197Spatrick fspec.AppendPathComponent("Developer"); 369dda28197Spatrick if (FileSystem::Instance().Exists(fspec)) 370dda28197Spatrick g_developer_directory = fspec; 371dda28197Spatrick } 372dda28197Spatrick }); 373dda28197Spatrick return g_developer_directory; 374dda28197Spatrick} 375dda28197Spatrick 376*f6aab3d8Srobertllvm::Expected<std::string> GetXcodeSDK(XcodeSDK sdk) { 377dda28197Spatrick XcodeSDK::Info info = sdk.Parse(); 378dda28197Spatrick std::string sdk_name = XcodeSDK::GetCanonicalName(info); 379*f6aab3d8Srobert if (sdk_name.empty()) 380*f6aab3d8Srobert return llvm::createStringError(llvm::inconvertibleErrorCode(), 381*f6aab3d8Srobert "Unrecognized SDK type: " + sdk.GetString()); 382*f6aab3d8Srobert 383*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host); 384be691f3bSpatrick 385be691f3bSpatrick auto xcrun = [](const std::string &sdk, 386*f6aab3d8Srobert llvm::StringRef developer_dir = 387*f6aab3d8Srobert "") -> llvm::Expected<std::string> { 388be691f3bSpatrick Args args; 389be691f3bSpatrick if (!developer_dir.empty()) { 390be691f3bSpatrick args.AppendArgument("/usr/bin/env"); 391be691f3bSpatrick args.AppendArgument("DEVELOPER_DIR=" + developer_dir.str()); 392dda28197Spatrick } 393be691f3bSpatrick args.AppendArgument("/usr/bin/xcrun"); 394be691f3bSpatrick args.AppendArgument("--show-sdk-path"); 395be691f3bSpatrick args.AppendArgument("--sdk"); 396be691f3bSpatrick args.AppendArgument(sdk); 397dda28197Spatrick 398*f6aab3d8Srobert Log *log = GetLog(LLDBLog::Host); 399*f6aab3d8Srobert if (log) { 400*f6aab3d8Srobert std::string cmdstr; 401*f6aab3d8Srobert args.GetCommandString(cmdstr); 402*f6aab3d8Srobert log->Printf("GetXcodeSDK() running shell cmd '%s'", cmdstr.c_str()); 403*f6aab3d8Srobert } 404*f6aab3d8Srobert 405dda28197Spatrick int status = 0; 406dda28197Spatrick int signo = 0; 407dda28197Spatrick std::string output_str; 408*f6aab3d8Srobert // The first time after Xcode was updated or freshly installed, 409*f6aab3d8Srobert // xcrun can take surprisingly long to build up its database. 410*f6aab3d8Srobert auto timeout = std::chrono::seconds(60); 411*f6aab3d8Srobert bool run_in_shell = false; 412*f6aab3d8Srobert lldb_private::Status error = Host::RunShellCommand( 413*f6aab3d8Srobert args, FileSpec(), &status, &signo, &output_str, timeout, run_in_shell); 414dda28197Spatrick 415*f6aab3d8Srobert // Check that xcrun returned something useful. 416*f6aab3d8Srobert if (error.Fail()) { 417*f6aab3d8Srobert // Catastrophic error. 418*f6aab3d8Srobert LLDB_LOG(log, "xcrun failed to execute: %s", error.AsCString()); 419*f6aab3d8Srobert return error.ToError(); 420*f6aab3d8Srobert } 421*f6aab3d8Srobert if (status != 0) { 422*f6aab3d8Srobert // xcrun didn't find a matching SDK. Not an error, we'll try 423*f6aab3d8Srobert // different spellings. 424*f6aab3d8Srobert LLDB_LOG(log, "xcrun returned exit code %d", status); 425*f6aab3d8Srobert return ""; 426*f6aab3d8Srobert } 427*f6aab3d8Srobert if (output_str.empty()) { 428*f6aab3d8Srobert LLDB_LOG(log, "xcrun returned no results"); 429*f6aab3d8Srobert return ""; 430*f6aab3d8Srobert } 431dda28197Spatrick 432dda28197Spatrick // Convert to a StringRef so we can manipulate the string without modifying 433dda28197Spatrick // the underlying data. 434dda28197Spatrick llvm::StringRef output(output_str); 435dda28197Spatrick 436dda28197Spatrick // Remove any trailing newline characters. 437dda28197Spatrick output = output.rtrim(); 438dda28197Spatrick 439dda28197Spatrick // Strip any leading newline characters and everything before them. 440dda28197Spatrick const size_t last_newline = output.rfind('\n'); 441dda28197Spatrick if (last_newline != llvm::StringRef::npos) 442dda28197Spatrick output = output.substr(last_newline + 1); 443dda28197Spatrick 444dda28197Spatrick return output.str(); 445dda28197Spatrick }; 446dda28197Spatrick 447*f6aab3d8Srobert auto find_sdk = 448*f6aab3d8Srobert [&xcrun](const std::string &sdk_name) -> llvm::Expected<std::string> { 449be691f3bSpatrick // Invoke xcrun with the developer dir specified in the environment. 450be691f3bSpatrick std::string developer_dir = GetEnvDeveloperDir(); 451be691f3bSpatrick if (!developer_dir.empty()) { 452be691f3bSpatrick // Don't fallback if DEVELOPER_DIR was set. 453be691f3bSpatrick return xcrun(sdk_name, developer_dir); 454be691f3bSpatrick } 455be691f3bSpatrick 456be691f3bSpatrick // Invoke xcrun with the shlib dir. 457be691f3bSpatrick if (FileSpec fspec = HostInfo::GetShlibDir()) { 458be691f3bSpatrick if (FileSystem::Instance().Exists(fspec)) { 459be691f3bSpatrick std::string contents_dir = 460be691f3bSpatrick XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 461be691f3bSpatrick llvm::StringRef shlib_developer_dir = 462be691f3bSpatrick llvm::sys::path::parent_path(contents_dir); 463be691f3bSpatrick if (!shlib_developer_dir.empty()) { 464*f6aab3d8Srobert auto sdk = xcrun(sdk_name, std::move(shlib_developer_dir)); 465*f6aab3d8Srobert if (!sdk) 466*f6aab3d8Srobert return sdk.takeError(); 467*f6aab3d8Srobert if (!sdk->empty()) 468be691f3bSpatrick return sdk; 469be691f3bSpatrick } 470be691f3bSpatrick } 471be691f3bSpatrick } 472be691f3bSpatrick 473be691f3bSpatrick // Invoke xcrun without a developer dir as a last resort. 474be691f3bSpatrick return xcrun(sdk_name); 475be691f3bSpatrick }; 476be691f3bSpatrick 477*f6aab3d8Srobert auto path_or_err = find_sdk(sdk_name); 478*f6aab3d8Srobert if (!path_or_err) 479*f6aab3d8Srobert return path_or_err.takeError(); 480*f6aab3d8Srobert std::string path = *path_or_err; 481dda28197Spatrick while (path.empty()) { 482dda28197Spatrick // Try an alternate spelling of the name ("macosx10.9internal"). 483dda28197Spatrick if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() && 484dda28197Spatrick info.internal) { 485dda28197Spatrick llvm::StringRef fixed(sdk_name); 486dda28197Spatrick if (fixed.consume_back(".internal")) 487dda28197Spatrick sdk_name = fixed.str() + "internal"; 488*f6aab3d8Srobert path_or_err = find_sdk(sdk_name); 489*f6aab3d8Srobert if (!path_or_err) 490*f6aab3d8Srobert return path_or_err.takeError(); 491*f6aab3d8Srobert path = *path_or_err; 492dda28197Spatrick if (!path.empty()) 493dda28197Spatrick break; 494dda28197Spatrick } 495dda28197Spatrick LLDB_LOGF(log, "Couldn't find SDK %s on host", sdk_name.c_str()); 496dda28197Spatrick 497dda28197Spatrick // Try without the version. 498dda28197Spatrick if (!info.version.empty()) { 499dda28197Spatrick info.version = {}; 500dda28197Spatrick sdk_name = XcodeSDK::GetCanonicalName(info); 501*f6aab3d8Srobert path_or_err = find_sdk(sdk_name); 502*f6aab3d8Srobert if (!path_or_err) 503*f6aab3d8Srobert return path_or_err.takeError(); 504*f6aab3d8Srobert path = *path_or_err; 505dda28197Spatrick if (!path.empty()) 506dda28197Spatrick break; 507dda28197Spatrick } 508dda28197Spatrick 509dda28197Spatrick LLDB_LOGF(log, "Couldn't find any matching SDK on host"); 510*f6aab3d8Srobert return ""; 511dda28197Spatrick } 512dda28197Spatrick 513dda28197Spatrick // Whatever is left in output should be a valid path. 514*f6aab3d8Srobert if (!FileSystem::Instance().Exists(path)) { 515*f6aab3d8Srobert LLDB_LOGF(log, "SDK returned by xcrun doesn't exist"); 516*f6aab3d8Srobert return llvm::createStringError(llvm::inconvertibleErrorCode(), 517*f6aab3d8Srobert "SDK returned by xcrun doesn't exist"); 518*f6aab3d8Srobert } 519dda28197Spatrick return path; 520dda28197Spatrick} 521dda28197Spatrick 522*f6aab3d8Srobertllvm::Expected<llvm::StringRef> HostInfoMacOSX::GetXcodeSDKPath(XcodeSDK sdk) { 523*f6aab3d8Srobert struct ErrorOrPath { 524*f6aab3d8Srobert std::string str; 525*f6aab3d8Srobert bool is_error; 526*f6aab3d8Srobert }; 527*f6aab3d8Srobert static llvm::StringMap<ErrorOrPath> g_sdk_path; 528dda28197Spatrick static std::mutex g_sdk_path_mutex; 529dda28197Spatrick 530dda28197Spatrick std::lock_guard<std::mutex> guard(g_sdk_path_mutex); 531be691f3bSpatrick LLDB_SCOPED_TIMER(); 532be691f3bSpatrick 533*f6aab3d8Srobert auto key = sdk.GetString(); 534*f6aab3d8Srobert auto it = g_sdk_path.find(key); 535*f6aab3d8Srobert if (it != g_sdk_path.end()) { 536*f6aab3d8Srobert if (it->second.is_error) 537*f6aab3d8Srobert return llvm::createStringError(llvm::inconvertibleErrorCode(), 538*f6aab3d8Srobert it->second.str); 539*f6aab3d8Srobert else 540*f6aab3d8Srobert return it->second.str; 541*f6aab3d8Srobert } 542*f6aab3d8Srobert auto path_or_err = GetXcodeSDK(sdk); 543*f6aab3d8Srobert if (!path_or_err) { 544*f6aab3d8Srobert std::string error = toString(path_or_err.takeError()); 545*f6aab3d8Srobert g_sdk_path.insert({key, {error, true}}); 546*f6aab3d8Srobert return llvm::createStringError(llvm::inconvertibleErrorCode(), error); 547*f6aab3d8Srobert } 548*f6aab3d8Srobert auto it_new = g_sdk_path.insert({key, {*path_or_err, false}}); 549*f6aab3d8Srobert return it_new.first->second.str; 550dda28197Spatrick} 551be691f3bSpatrick 552be691f3bSpatricknamespace { 553be691f3bSpatrickstruct dyld_shared_cache_dylib_text_info { 554be691f3bSpatrick uint64_t version; // current version 1 555be691f3bSpatrick // following fields all exist in version 1 556be691f3bSpatrick uint64_t loadAddressUnslid; 557be691f3bSpatrick uint64_t textSegmentSize; 558be691f3bSpatrick uuid_t dylibUuid; 559be691f3bSpatrick const char *path; // pointer invalid at end of iterations 560be691f3bSpatrick // following fields all exist in version 2 561be691f3bSpatrick uint64_t textSegmentOffset; // offset from start of cache 562be691f3bSpatrick}; 563be691f3bSpatricktypedef struct dyld_shared_cache_dylib_text_info 564be691f3bSpatrick dyld_shared_cache_dylib_text_info; 565be691f3bSpatrick} 566be691f3bSpatrick 567be691f3bSpatrickextern "C" int dyld_shared_cache_iterate_text( 568be691f3bSpatrick const uuid_t cacheUuid, 569be691f3bSpatrick void (^callback)(const dyld_shared_cache_dylib_text_info *info)); 570be691f3bSpatrickextern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length); 571be691f3bSpatrickextern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid); 572be691f3bSpatrick 573be691f3bSpatricknamespace { 574be691f3bSpatrickclass SharedCacheInfo { 575be691f3bSpatrickpublic: 576be691f3bSpatrick const UUID &GetUUID() const { return m_uuid; } 577be691f3bSpatrick const llvm::StringMap<SharedCacheImageInfo> &GetImages() const { 578be691f3bSpatrick return m_images; 579be691f3bSpatrick } 580be691f3bSpatrick 581be691f3bSpatrick SharedCacheInfo(); 582be691f3bSpatrick 583be691f3bSpatrickprivate: 584*f6aab3d8Srobert bool CreateSharedCacheInfoWithInstrospectionSPIs(); 585*f6aab3d8Srobert 586be691f3bSpatrick llvm::StringMap<SharedCacheImageInfo> m_images; 587be691f3bSpatrick UUID m_uuid; 588be691f3bSpatrick}; 589be691f3bSpatrick} 590be691f3bSpatrick 591*f6aab3d8Srobertbool SharedCacheInfo::CreateSharedCacheInfoWithInstrospectionSPIs() { 592*f6aab3d8Srobert#if defined(SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS) 593*f6aab3d8Srobert dyld_process_t dyld_process = dyld_process_create_for_current_task(); 594*f6aab3d8Srobert if (!dyld_process) 595*f6aab3d8Srobert return false; 596*f6aab3d8Srobert 597*f6aab3d8Srobert dyld_process_snapshot_t snapshot = 598*f6aab3d8Srobert dyld_process_snapshot_create_for_process(dyld_process, nullptr); 599*f6aab3d8Srobert if (!snapshot) 600*f6aab3d8Srobert return false; 601*f6aab3d8Srobert 602*f6aab3d8Srobert auto on_exit = 603*f6aab3d8Srobert llvm::make_scope_exit([&]() { dyld_process_snapshot_dispose(snapshot); }); 604*f6aab3d8Srobert 605*f6aab3d8Srobert dyld_shared_cache_t shared_cache = 606*f6aab3d8Srobert dyld_process_snapshot_get_shared_cache(snapshot); 607*f6aab3d8Srobert if (!shared_cache) 608*f6aab3d8Srobert return false; 609*f6aab3d8Srobert 610*f6aab3d8Srobert dyld_shared_cache_for_each_image(shared_cache, ^(dyld_image_t image) { 611*f6aab3d8Srobert __block uint64_t minVmAddr = UINT64_MAX; 612*f6aab3d8Srobert __block uint64_t maxVmAddr = 0; 613*f6aab3d8Srobert uuid_t uuidStore; 614*f6aab3d8Srobert __block uuid_t *uuid = &uuidStore; 615*f6aab3d8Srobert 616*f6aab3d8Srobert dyld_image_for_each_segment_info( 617*f6aab3d8Srobert image, 618*f6aab3d8Srobert ^(const char *segmentName, uint64_t vmAddr, uint64_t vmSize, int perm) { 619*f6aab3d8Srobert minVmAddr = std::min(minVmAddr, vmAddr); 620*f6aab3d8Srobert maxVmAddr = std::max(maxVmAddr, vmAddr + vmSize); 621*f6aab3d8Srobert dyld_image_copy_uuid(image, uuid); 622*f6aab3d8Srobert }); 623*f6aab3d8Srobert assert(minVmAddr != UINT_MAX); 624*f6aab3d8Srobert assert(maxVmAddr != 0); 625*f6aab3d8Srobert m_images[dyld_image_get_installname(image)] = SharedCacheImageInfo{ 626*f6aab3d8Srobert UUID(uuid, 16), std::make_shared<DataBufferUnowned>( 627*f6aab3d8Srobert (uint8_t *)minVmAddr, maxVmAddr - minVmAddr)}; 628*f6aab3d8Srobert }); 629*f6aab3d8Srobert return true; 630*f6aab3d8Srobert#endif 631*f6aab3d8Srobert return false; 632*f6aab3d8Srobert} 633*f6aab3d8Srobert 634be691f3bSpatrickSharedCacheInfo::SharedCacheInfo() { 635*f6aab3d8Srobert if (CreateSharedCacheInfoWithInstrospectionSPIs()) 636*f6aab3d8Srobert return; 637*f6aab3d8Srobert 638be691f3bSpatrick size_t shared_cache_size; 639be691f3bSpatrick uint8_t *shared_cache_start = 640be691f3bSpatrick _dyld_get_shared_cache_range(&shared_cache_size); 641be691f3bSpatrick uuid_t dsc_uuid; 642be691f3bSpatrick _dyld_get_shared_cache_uuid(dsc_uuid); 643*f6aab3d8Srobert m_uuid = UUID(dsc_uuid); 644be691f3bSpatrick 645be691f3bSpatrick dyld_shared_cache_iterate_text( 646be691f3bSpatrick dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) { 647be691f3bSpatrick m_images[info->path] = SharedCacheImageInfo{ 648*f6aab3d8Srobert UUID(info->dylibUuid, 16), 649be691f3bSpatrick std::make_shared<DataBufferUnowned>( 650be691f3bSpatrick shared_cache_start + info->textSegmentOffset, 651be691f3bSpatrick shared_cache_size - info->textSegmentOffset)}; 652be691f3bSpatrick }); 653be691f3bSpatrick} 654be691f3bSpatrick 655be691f3bSpatrickSharedCacheImageInfo 656be691f3bSpatrickHostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) { 657be691f3bSpatrick static SharedCacheInfo g_shared_cache_info; 658be691f3bSpatrick return g_shared_cache_info.GetImages().lookup(image_name); 659be691f3bSpatrick} 660