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