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/Log.h" 15#include "Utility/UuidCompatibility.h" 16 17#include "llvm/ADT/SmallString.h" 18#include "llvm/ADT/StringMap.h" 19#include "llvm/Support/FileSystem.h" 20#include "llvm/Support/Path.h" 21#include "llvm/Support/raw_ostream.h" 22 23// C++ Includes 24#include <string> 25 26// C inclues 27#include <stdlib.h> 28#include <sys/sysctl.h> 29#include <sys/syslimits.h> 30#include <sys/types.h> 31 32// Objective-C/C++ includes 33#include <CoreFoundation/CoreFoundation.h> 34#include <Foundation/Foundation.h> 35#include <mach-o/dyld.h> 36#include <objc/objc-auto.h> 37 38// These are needed when compiling on systems 39// that do not yet have these definitions 40#include <AvailabilityMacros.h> 41#ifndef CPU_SUBTYPE_X86_64_H 42#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) 43#endif 44#ifndef CPU_TYPE_ARM64 45#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 46#endif 47 48#ifndef CPU_TYPE_ARM64_32 49#define CPU_ARCH_ABI64_32 0x02000000 50#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32) 51#endif 52 53#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH 54 55using namespace lldb_private; 56 57bool HostInfoMacOSX::GetOSBuildString(std::string &s) { 58 int mib[2] = {CTL_KERN, KERN_OSVERSION}; 59 char cstr[PATH_MAX]; 60 size_t cstr_len = sizeof(cstr); 61 if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { 62 s.assign(cstr, cstr_len); 63 return true; 64 } 65 66 s.clear(); 67 return false; 68} 69 70bool HostInfoMacOSX::GetOSKernelDescription(std::string &s) { 71 int mib[2] = {CTL_KERN, KERN_VERSION}; 72 char cstr[PATH_MAX]; 73 size_t cstr_len = sizeof(cstr); 74 if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { 75 s.assign(cstr, cstr_len); 76 return true; 77 } 78 s.clear(); 79 return false; 80} 81 82static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { 83 @autoreleasepool { 84 NSDictionary *version_info = 85 [NSDictionary dictionaryWithContentsOfFile: 86 @"/System/Library/CoreServices/SystemVersion.plist"]; 87 NSString *version_value = [version_info objectForKey: Key]; 88 const char *version_str = [version_value UTF8String]; 89 version.tryParse(version_str); 90 } 91} 92 93llvm::VersionTuple HostInfoMacOSX::GetOSVersion() { 94 static llvm::VersionTuple g_version; 95 if (g_version.empty()) 96 ParseOSVersion(g_version, @"ProductVersion"); 97 return g_version; 98} 99 100llvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() { 101 static llvm::VersionTuple g_version; 102 if (g_version.empty()) 103 ParseOSVersion(g_version, @"iOSSupportVersion"); 104 return g_version; 105} 106 107 108FileSpec HostInfoMacOSX::GetProgramFileSpec() { 109 static FileSpec g_program_filespec; 110 if (!g_program_filespec) { 111 char program_fullpath[PATH_MAX]; 112 // If DST is NULL, then return the number of bytes needed. 113 uint32_t len = sizeof(program_fullpath); 114 int err = _NSGetExecutablePath(program_fullpath, &len); 115 if (err == 0) 116 g_program_filespec.SetFile(program_fullpath, FileSpec::Style::native); 117 else if (err == -1) { 118 char *large_program_fullpath = (char *)::malloc(len + 1); 119 120 err = _NSGetExecutablePath(large_program_fullpath, &len); 121 if (err == 0) 122 g_program_filespec.SetFile(large_program_fullpath, 123 FileSpec::Style::native); 124 125 ::free(large_program_fullpath); 126 } 127 } 128 return g_program_filespec; 129} 130 131bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { 132 FileSpec lldb_file_spec = GetShlibDir(); 133 if (!lldb_file_spec) 134 return false; 135 136 std::string raw_path = lldb_file_spec.GetPath(); 137 138 size_t framework_pos = raw_path.find("LLDB.framework"); 139 if (framework_pos != std::string::npos) { 140 framework_pos += strlen("LLDB.framework"); 141#if TARGET_OS_EMBEDDED 142 // Shallow bundle 143 raw_path.resize(framework_pos); 144#else 145 // Normal bundle 146 raw_path.resize(framework_pos); 147 raw_path.append("/Resources"); 148#endif 149 } else { 150 // Find the bin path relative to the lib path where the cmake-based 151 // OS X .dylib lives. This is not going to work if the bin and lib 152 // dir are not both in the same dir. 153 // 154 // It is not going to work to do it by the executable path either, 155 // as in the case of a python script, the executable is python, not 156 // the lldb driver. 157 raw_path.append("/../bin"); 158 FileSpec support_dir_spec(raw_path); 159 FileSystem::Instance().Resolve(support_dir_spec); 160 if (!FileSystem::Instance().IsDirectory(support_dir_spec)) { 161 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 162 LLDB_LOGF(log, "HostInfoMacOSX::%s(): failed to find support directory", 163 __FUNCTION__); 164 return false; 165 } 166 167 // Get normalization from support_dir_spec. Note the FileSpec resolve 168 // does not remove '..' in the path. 169 char *const dir_realpath = 170 realpath(support_dir_spec.GetPath().c_str(), NULL); 171 if (dir_realpath) { 172 raw_path = dir_realpath; 173 free(dir_realpath); 174 } else { 175 raw_path = support_dir_spec.GetPath(); 176 } 177 } 178 179 file_spec.GetDirectory().SetString( 180 llvm::StringRef(raw_path.c_str(), raw_path.size())); 181 return (bool)file_spec.GetDirectory(); 182} 183 184bool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { 185 FileSpec lldb_file_spec = GetShlibDir(); 186 if (!lldb_file_spec) 187 return false; 188 189 std::string raw_path = lldb_file_spec.GetPath(); 190 191 size_t framework_pos = raw_path.find("LLDB.framework"); 192 if (framework_pos != std::string::npos) { 193 framework_pos += strlen("LLDB.framework"); 194 raw_path.resize(framework_pos); 195 raw_path.append("/Headers"); 196 } 197 file_spec.GetDirectory().SetString( 198 llvm::StringRef(raw_path.c_str(), raw_path.size())); 199 return true; 200} 201 202bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { 203 FileSpec lldb_file_spec = GetShlibDir(); 204 if (!lldb_file_spec) 205 return false; 206 207 std::string raw_path = lldb_file_spec.GetPath(); 208 209 size_t framework_pos = raw_path.find("LLDB.framework"); 210 if (framework_pos == std::string::npos) 211 return false; 212 213 framework_pos += strlen("LLDB.framework"); 214 raw_path.resize(framework_pos); 215 raw_path.append("/Resources/PlugIns"); 216 file_spec.GetDirectory().SetString( 217 llvm::StringRef(raw_path.c_str(), raw_path.size())); 218 return true; 219} 220 221bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { 222 FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); 223 FileSystem::Instance().Resolve(temp_file); 224 file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); 225 return true; 226} 227 228void HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, 229 ArchSpec &arch_64) { 230 // All apple systems support 32 bit execution. 231 uint32_t cputype, cpusubtype; 232 uint32_t is_64_bit_capable = false; 233 size_t len = sizeof(cputype); 234 ArchSpec host_arch; 235 // These will tell us about the kernel architecture, which even on a 64 236 // bit machine can be 32 bit... 237 if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { 238 len = sizeof(cpusubtype); 239 if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) 240 cpusubtype = CPU_TYPE_ANY; 241 242 len = sizeof(is_64_bit_capable); 243 ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); 244 245 if (is_64_bit_capable) { 246 if (cputype & CPU_ARCH_ABI64) { 247 // We have a 64 bit kernel on a 64 bit system 248 arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 249 } else { 250 // We have a 64 bit kernel that is returning a 32 bit cputype, the 251 // cpusubtype will be correct as if it were for a 64 bit architecture 252 arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, 253 cpusubtype); 254 } 255 256 // Now we need modify the cpusubtype for the 32 bit slices. 257 uint32_t cpusubtype32 = cpusubtype; 258#if defined(__i386__) || defined(__x86_64__) 259 if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) 260 cpusubtype32 = CPU_SUBTYPE_I386_ALL; 261#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 262 if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) 263 cpusubtype32 = CPU_SUBTYPE_ARM_V7S; 264#endif 265 arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), 266 cpusubtype32); 267 268 if (cputype == CPU_TYPE_ARM || 269 cputype == CPU_TYPE_ARM64 || 270 cputype == CPU_TYPE_ARM64_32) { 271// When running on a watch or tv, report the host os correctly 272#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 273 arch_32.GetTriple().setOS(llvm::Triple::TvOS); 274 arch_64.GetTriple().setOS(llvm::Triple::TvOS); 275#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 276 arch_32.GetTriple().setOS(llvm::Triple::BridgeOS); 277 arch_64.GetTriple().setOS(llvm::Triple::BridgeOS); 278#elif defined(TARGET_OS_WATCHOS) && TARGET_OS_WATCHOS == 1 279 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 280 arch_64.GetTriple().setOS(llvm::Triple::WatchOS); 281#elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 282 arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 283 arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 284#else 285 arch_32.GetTriple().setOS(llvm::Triple::IOS); 286 arch_64.GetTriple().setOS(llvm::Triple::IOS); 287#endif 288 } else { 289 arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 290 arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 291 } 292 } else { 293 // We have a 32 bit kernel on a 32 bit system 294 arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 295#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 296 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 297#else 298 arch_32.GetTriple().setOS(llvm::Triple::IOS); 299#endif 300 arch_64.Clear(); 301 } 302 } 303} 304 305/// Return and cache $DEVELOPER_DIR if it is set and exists. 306static std::string GetEnvDeveloperDir() { 307 static std::string g_env_developer_dir; 308 static std::once_flag g_once_flag; 309 std::call_once(g_once_flag, [&]() { 310 if (const char *developer_dir_env_var = getenv("DEVELOPER_DIR")) { 311 FileSpec fspec(developer_dir_env_var); 312 if (FileSystem::Instance().Exists(fspec)) 313 g_env_developer_dir = fspec.GetPath(); 314 }}); 315 return g_env_developer_dir; 316} 317 318FileSpec HostInfoMacOSX::GetXcodeContentsDirectory() { 319 static FileSpec g_xcode_contents_path; 320 static std::once_flag g_once_flag; 321 std::call_once(g_once_flag, [&]() { 322 // Try the shlib dir first. 323 if (FileSpec fspec = HostInfo::GetShlibDir()) { 324 if (FileSystem::Instance().Exists(fspec)) { 325 std::string xcode_contents_dir = 326 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 327 if (!xcode_contents_dir.empty()) { 328 g_xcode_contents_path = FileSpec(xcode_contents_dir); 329 return; 330 } 331 } 332 } 333 334 llvm::SmallString<128> env_developer_dir(GetEnvDeveloperDir()); 335 if (!env_developer_dir.empty()) { 336 llvm::sys::path::append(env_developer_dir, "Contents"); 337 std::string xcode_contents_dir = 338 XcodeSDK::FindXcodeContentsDirectoryInPath(env_developer_dir); 339 if (!xcode_contents_dir.empty()) { 340 g_xcode_contents_path = FileSpec(xcode_contents_dir); 341 return; 342 } 343 } 344 345 FileSpec fspec(HostInfo::GetXcodeSDKPath(XcodeSDK::GetAnyMacOS())); 346 if (fspec) { 347 if (FileSystem::Instance().Exists(fspec)) { 348 std::string xcode_contents_dir = 349 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 350 if (!xcode_contents_dir.empty()) { 351 g_xcode_contents_path = FileSpec(xcode_contents_dir); 352 return; 353 } 354 } 355 } 356 }); 357 return g_xcode_contents_path; 358} 359 360lldb_private::FileSpec HostInfoMacOSX::GetXcodeDeveloperDirectory() { 361 static lldb_private::FileSpec g_developer_directory; 362 static llvm::once_flag g_once_flag; 363 llvm::call_once(g_once_flag, []() { 364 if (FileSpec fspec = GetXcodeContentsDirectory()) { 365 fspec.AppendPathComponent("Developer"); 366 if (FileSystem::Instance().Exists(fspec)) 367 g_developer_directory = fspec; 368 } 369 }); 370 return g_developer_directory; 371} 372 373static std::string GetXcodeSDK(XcodeSDK sdk) { 374 XcodeSDK::Info info = sdk.Parse(); 375 std::string sdk_name = XcodeSDK::GetCanonicalName(info); 376 377 auto xcrun = [](const std::string &sdk, 378 llvm::StringRef developer_dir = "") -> std::string { 379 std::string xcrun_cmd = "xcrun --show-sdk-path --sdk " + sdk; 380 if (!developer_dir.empty()) 381 xcrun_cmd = "/usr/bin/env DEVELOPER_DIR=\"" + developer_dir.str() + 382 "\" " + xcrun_cmd; 383 384 int status = 0; 385 int signo = 0; 386 std::string output_str; 387 lldb_private::Status error = 388 Host::RunShellCommand(xcrun_cmd, FileSpec(), &status, &signo, 389 &output_str, std::chrono::seconds(15)); 390 391 // Check that xcrun return something useful. 392 if (status != 0 || output_str.empty()) 393 return {}; 394 395 // Convert to a StringRef so we can manipulate the string without modifying 396 // the underlying data. 397 llvm::StringRef output(output_str); 398 399 // Remove any trailing newline characters. 400 output = output.rtrim(); 401 402 // Strip any leading newline characters and everything before them. 403 const size_t last_newline = output.rfind('\n'); 404 if (last_newline != llvm::StringRef::npos) 405 output = output.substr(last_newline + 1); 406 407 return output.str(); 408 }; 409 410 auto find_sdk = [&xcrun](const std::string &sdk_name) -> std::string { 411 // Invoke xcrun with the developer dir specified in the environment. 412 std::string developer_dir = GetEnvDeveloperDir(); 413 if (!developer_dir.empty()) { 414 // Don't fallback if DEVELOPER_DIR was set. 415 return xcrun(sdk_name, developer_dir); 416 } 417 418 // Invoke xcrun with the shlib dir. 419 if (FileSpec fspec = HostInfo::GetShlibDir()) { 420 if (FileSystem::Instance().Exists(fspec)) { 421 std::string contents_dir = 422 XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath()); 423 llvm::StringRef shlib_developer_dir = 424 llvm::sys::path::parent_path(contents_dir); 425 if (!shlib_developer_dir.empty()) { 426 std::string sdk = xcrun(sdk_name, std::move(shlib_developer_dir)); 427 if (!sdk.empty()) 428 return sdk; 429 } 430 } 431 } 432 433 // Invoke xcrun without a developer dir as a last resort. 434 return xcrun(sdk_name); 435 }; 436 437 std::string path = find_sdk(sdk_name); 438 while (path.empty()) { 439 // Try an alternate spelling of the name ("macosx10.9internal"). 440 if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() && 441 info.internal) { 442 llvm::StringRef fixed(sdk_name); 443 if (fixed.consume_back(".internal")) 444 sdk_name = fixed.str() + "internal"; 445 path = find_sdk(sdk_name); 446 if (!path.empty()) 447 break; 448 } 449 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 450 LLDB_LOGF(log, "Couldn't find SDK %s on host", sdk_name.c_str()); 451 452 // Try without the version. 453 if (!info.version.empty()) { 454 info.version = {}; 455 sdk_name = XcodeSDK::GetCanonicalName(info); 456 path = find_sdk(sdk_name); 457 if (!path.empty()) 458 break; 459 } 460 461 LLDB_LOGF(log, "Couldn't find any matching SDK on host"); 462 return {}; 463 } 464 465 // Whatever is left in output should be a valid path. 466 if (!FileSystem::Instance().Exists(path)) 467 return {}; 468 return path; 469} 470 471llvm::StringRef HostInfoMacOSX::GetXcodeSDKPath(XcodeSDK sdk) { 472 static llvm::StringMap<std::string> g_sdk_path; 473 static std::mutex g_sdk_path_mutex; 474 475 std::lock_guard<std::mutex> guard(g_sdk_path_mutex); 476 auto it = g_sdk_path.find(sdk.GetString()); 477 if (it != g_sdk_path.end()) 478 return it->second; 479 auto it_new = g_sdk_path.insert({sdk.GetString(), GetXcodeSDK(sdk)}); 480 return it_new.first->second; 481} 482 483namespace { 484struct dyld_shared_cache_dylib_text_info { 485 uint64_t version; // current version 1 486 // following fields all exist in version 1 487 uint64_t loadAddressUnslid; 488 uint64_t textSegmentSize; 489 uuid_t dylibUuid; 490 const char *path; // pointer invalid at end of iterations 491 // following fields all exist in version 2 492 uint64_t textSegmentOffset; // offset from start of cache 493}; 494typedef struct dyld_shared_cache_dylib_text_info 495 dyld_shared_cache_dylib_text_info; 496} 497 498extern "C" int dyld_shared_cache_iterate_text( 499 const uuid_t cacheUuid, 500 void (^callback)(const dyld_shared_cache_dylib_text_info *info)); 501extern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length); 502extern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid); 503 504namespace { 505class SharedCacheInfo { 506public: 507 const UUID &GetUUID() const { return m_uuid; }; 508 const llvm::StringMap<SharedCacheImageInfo> &GetImages() const { 509 return m_images; 510 }; 511 512 SharedCacheInfo(); 513 514private: 515 llvm::StringMap<SharedCacheImageInfo> m_images; 516 UUID m_uuid; 517}; 518} 519 520SharedCacheInfo::SharedCacheInfo() { 521 size_t shared_cache_size; 522 uint8_t *shared_cache_start = 523 _dyld_get_shared_cache_range(&shared_cache_size); 524 uuid_t dsc_uuid; 525 _dyld_get_shared_cache_uuid(dsc_uuid); 526 m_uuid = UUID::fromData(dsc_uuid); 527 528 dyld_shared_cache_iterate_text( 529 dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) { 530 m_images[info->path] = SharedCacheImageInfo{ 531 UUID::fromData(info->dylibUuid, 16), 532 std::make_shared<DataBufferUnowned>( 533 shared_cache_start + info->textSegmentOffset, 534 shared_cache_size - info->textSegmentOffset)}; 535 }); 536} 537 538SharedCacheImageInfo 539HostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) { 540 static SharedCacheInfo g_shared_cache_info; 541 return g_shared_cache_info.GetImages().lookup(image_name); 542} 543