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/HostInfo.h" 12#include "lldb/Utility/Args.h" 13#include "lldb/Utility/Log.h" 14 15#include "llvm/ADT/SmallString.h" 16#include "llvm/Support/FileSystem.h" 17#include "llvm/Support/Path.h" 18#include "llvm/Support/raw_ostream.h" 19 20// C++ Includes 21#include <string> 22 23// C inclues 24#include <stdlib.h> 25#include <sys/sysctl.h> 26#include <sys/syslimits.h> 27#include <sys/types.h> 28 29// Objective-C/C++ includes 30#include <CoreFoundation/CoreFoundation.h> 31#include <Foundation/Foundation.h> 32#include <mach-o/dyld.h> 33#include <objc/objc-auto.h> 34 35// These are needed when compiling on systems 36// that do not yet have these definitions 37#include <AvailabilityMacros.h> 38#ifndef CPU_SUBTYPE_X86_64_H 39#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) 40#endif 41#ifndef CPU_TYPE_ARM64 42#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64) 43#endif 44 45#ifndef CPU_TYPE_ARM64_32 46#define CPU_ARCH_ABI64_32 0x02000000 47#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32) 48#endif 49 50#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH 51 52using namespace lldb_private; 53 54bool HostInfoMacOSX::GetOSBuildString(std::string &s) { 55 int mib[2] = {CTL_KERN, KERN_OSVERSION}; 56 char cstr[PATH_MAX]; 57 size_t cstr_len = sizeof(cstr); 58 if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { 59 s.assign(cstr, cstr_len); 60 return true; 61 } 62 63 s.clear(); 64 return false; 65} 66 67bool HostInfoMacOSX::GetOSKernelDescription(std::string &s) { 68 int mib[2] = {CTL_KERN, KERN_VERSION}; 69 char cstr[PATH_MAX]; 70 size_t cstr_len = sizeof(cstr); 71 if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) { 72 s.assign(cstr, cstr_len); 73 return true; 74 } 75 s.clear(); 76 return false; 77} 78 79static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { 80 @autoreleasepool { 81 NSDictionary *version_info = 82 [NSDictionary dictionaryWithContentsOfFile: 83 @"/System/Library/CoreServices/SystemVersion.plist"]; 84 NSString *version_value = [version_info objectForKey: Key]; 85 const char *version_str = [version_value UTF8String]; 86 version.tryParse(version_str); 87 } 88} 89 90llvm::VersionTuple HostInfoMacOSX::GetOSVersion() { 91 static llvm::VersionTuple g_version; 92 if (g_version.empty()) 93 ParseOSVersion(g_version, @"ProductVersion"); 94 return g_version; 95} 96 97llvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() { 98 static llvm::VersionTuple g_version; 99 if (g_version.empty()) 100 ParseOSVersion(g_version, @"iOSSupportVersion"); 101 return g_version; 102} 103 104 105FileSpec HostInfoMacOSX::GetProgramFileSpec() { 106 static FileSpec g_program_filespec; 107 if (!g_program_filespec) { 108 char program_fullpath[PATH_MAX]; 109 // If DST is NULL, then return the number of bytes needed. 110 uint32_t len = sizeof(program_fullpath); 111 int err = _NSGetExecutablePath(program_fullpath, &len); 112 if (err == 0) 113 g_program_filespec.SetFile(program_fullpath, FileSpec::Style::native); 114 else if (err == -1) { 115 char *large_program_fullpath = (char *)::malloc(len + 1); 116 117 err = _NSGetExecutablePath(large_program_fullpath, &len); 118 if (err == 0) 119 g_program_filespec.SetFile(large_program_fullpath, 120 FileSpec::Style::native); 121 122 ::free(large_program_fullpath); 123 } 124 } 125 return g_program_filespec; 126} 127 128bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { 129 FileSpec lldb_file_spec = GetShlibDir(); 130 if (!lldb_file_spec) 131 return false; 132 133 std::string raw_path = lldb_file_spec.GetPath(); 134 135 size_t framework_pos = raw_path.find("LLDB.framework"); 136 if (framework_pos != std::string::npos) { 137 framework_pos += strlen("LLDB.framework"); 138#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 139 // Shallow bundle 140 raw_path.resize(framework_pos); 141#else 142 // Normal bundle 143 raw_path.resize(framework_pos); 144 raw_path.append("/Resources"); 145#endif 146 } else { 147 // Find the bin path relative to the lib path where the cmake-based 148 // OS X .dylib lives. This is not going to work if the bin and lib 149 // dir are not both in the same dir. 150 // 151 // It is not going to work to do it by the executable path either, 152 // as in the case of a python script, the executable is python, not 153 // the lldb driver. 154 raw_path.append("/../bin"); 155 FileSpec support_dir_spec(raw_path); 156 FileSystem::Instance().Resolve(support_dir_spec); 157 if (!FileSystem::Instance().IsDirectory(support_dir_spec)) { 158 Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); 159 LLDB_LOGF(log, "HostInfoMacOSX::%s(): failed to find support directory", 160 __FUNCTION__); 161 return false; 162 } 163 164 // Get normalization from support_dir_spec. Note the FileSpec resolve 165 // does not remove '..' in the path. 166 char *const dir_realpath = 167 realpath(support_dir_spec.GetPath().c_str(), NULL); 168 if (dir_realpath) { 169 raw_path = dir_realpath; 170 free(dir_realpath); 171 } else { 172 raw_path = support_dir_spec.GetPath(); 173 } 174 } 175 176 file_spec.GetDirectory().SetString( 177 llvm::StringRef(raw_path.c_str(), raw_path.size())); 178 return (bool)file_spec.GetDirectory(); 179} 180 181bool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) { 182 FileSpec lldb_file_spec = GetShlibDir(); 183 if (!lldb_file_spec) 184 return false; 185 186 std::string raw_path = lldb_file_spec.GetPath(); 187 188 size_t framework_pos = raw_path.find("LLDB.framework"); 189 if (framework_pos != std::string::npos) { 190 framework_pos += strlen("LLDB.framework"); 191 raw_path.resize(framework_pos); 192 raw_path.append("/Headers"); 193 } 194 file_spec.GetDirectory().SetString( 195 llvm::StringRef(raw_path.c_str(), raw_path.size())); 196 return true; 197} 198 199bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { 200 FileSpec lldb_file_spec = GetShlibDir(); 201 if (!lldb_file_spec) 202 return false; 203 204 std::string raw_path = lldb_file_spec.GetPath(); 205 206 size_t framework_pos = raw_path.find("LLDB.framework"); 207 if (framework_pos == std::string::npos) 208 return false; 209 210 framework_pos += strlen("LLDB.framework"); 211 raw_path.resize(framework_pos); 212 raw_path.append("/Resources/PlugIns"); 213 file_spec.GetDirectory().SetString( 214 llvm::StringRef(raw_path.c_str(), raw_path.size())); 215 return true; 216} 217 218bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { 219 FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); 220 FileSystem::Instance().Resolve(temp_file); 221 file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); 222 return true; 223} 224 225void HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, 226 ArchSpec &arch_64) { 227 // All apple systems support 32 bit execution. 228 uint32_t cputype, cpusubtype; 229 uint32_t is_64_bit_capable = false; 230 size_t len = sizeof(cputype); 231 ArchSpec host_arch; 232 // These will tell us about the kernel architecture, which even on a 64 233 // bit machine can be 32 bit... 234 if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) { 235 len = sizeof(cpusubtype); 236 if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) 237 cpusubtype = CPU_TYPE_ANY; 238 239 len = sizeof(is_64_bit_capable); 240 ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); 241 242 if (is_64_bit_capable) { 243 if (cputype & CPU_ARCH_ABI64) { 244 // We have a 64 bit kernel on a 64 bit system 245 arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 246 } else { 247 // We have a 64 bit kernel that is returning a 32 bit cputype, the 248 // cpusubtype will be correct as if it were for a 64 bit architecture 249 arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, 250 cpusubtype); 251 } 252 253 // Now we need modify the cpusubtype for the 32 bit slices. 254 uint32_t cpusubtype32 = cpusubtype; 255#if defined(__i386__) || defined(__x86_64__) 256 if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) 257 cpusubtype32 = CPU_SUBTYPE_I386_ALL; 258#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) 259 if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) 260 cpusubtype32 = CPU_SUBTYPE_ARM_V7S; 261#endif 262 arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), 263 cpusubtype32); 264 265 if (cputype == CPU_TYPE_ARM || 266 cputype == CPU_TYPE_ARM64 || 267 cputype == CPU_TYPE_ARM64_32) { 268// When running on a watch or tv, report the host os correctly 269#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 270 arch_32.GetTriple().setOS(llvm::Triple::TvOS); 271 arch_64.GetTriple().setOS(llvm::Triple::TvOS); 272#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1 273 arch_32.GetTriple().setOS(llvm::Triple::BridgeOS); 274 arch_64.GetTriple().setOS(llvm::Triple::BridgeOS); 275#elif defined(TARGET_OS_WATCHOS) && TARGET_OS_WATCHOS == 1 276 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 277 arch_64.GetTriple().setOS(llvm::Triple::WatchOS); 278#else 279 arch_32.GetTriple().setOS(llvm::Triple::IOS); 280 arch_64.GetTriple().setOS(llvm::Triple::IOS); 281#endif 282 } else { 283 arch_32.GetTriple().setOS(llvm::Triple::MacOSX); 284 arch_64.GetTriple().setOS(llvm::Triple::MacOSX); 285 } 286 } else { 287 // We have a 32 bit kernel on a 32 bit system 288 arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); 289#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 290 arch_32.GetTriple().setOS(llvm::Triple::WatchOS); 291#else 292 arch_32.GetTriple().setOS(llvm::Triple::IOS); 293#endif 294 arch_64.Clear(); 295 } 296 } 297} 298