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