xref: /openbsd-src/gnu/llvm/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
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