xref: /openbsd-src/gnu/llvm/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===//
2061da546Spatrick//
3061da546Spatrick// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick// See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick//
7061da546Spatrick//===----------------------------------------------------------------------===//
8061da546Spatrick
9*f6aab3d8Srobert#include "lldb/Host/macosx/HostInfoMacOSX.h"
10*f6aab3d8Srobert#include "Utility/UuidCompatibility.h"
11061da546Spatrick#include "lldb/Host/FileSystem.h"
12dda28197Spatrick#include "lldb/Host/Host.h"
13061da546Spatrick#include "lldb/Host/HostInfo.h"
14061da546Spatrick#include "lldb/Utility/Args.h"
15*f6aab3d8Srobert#include "lldb/Utility/LLDBLog.h"
16061da546Spatrick#include "lldb/Utility/Log.h"
17be691f3bSpatrick#include "lldb/Utility/Timer.h"
18061da546Spatrick
19*f6aab3d8Srobert#include "llvm/ADT/ScopeExit.h"
20061da546Spatrick#include "llvm/ADT/SmallString.h"
21be691f3bSpatrick#include "llvm/ADT/StringMap.h"
22061da546Spatrick#include "llvm/Support/FileSystem.h"
23061da546Spatrick#include "llvm/Support/Path.h"
24061da546Spatrick#include "llvm/Support/raw_ostream.h"
25061da546Spatrick
26061da546Spatrick// C++ Includes
27*f6aab3d8Srobert#include <optional>
28061da546Spatrick#include <string>
29061da546Spatrick
30061da546Spatrick// C inclues
31be691f3bSpatrick#include <cstdlib>
32061da546Spatrick#include <sys/sysctl.h>
33061da546Spatrick#include <sys/syslimits.h>
34061da546Spatrick#include <sys/types.h>
35061da546Spatrick
36061da546Spatrick// Objective-C/C++ includes
37061da546Spatrick#include <CoreFoundation/CoreFoundation.h>
38061da546Spatrick#include <Foundation/Foundation.h>
39061da546Spatrick#include <mach-o/dyld.h>
40*f6aab3d8Srobert#if __has_include(<mach-o/dyld_introspection.h>)
41*f6aab3d8Srobert#include <mach-o/dyld_introspection.h>
42*f6aab3d8Srobert#define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS
43*f6aab3d8Srobert#endif
44061da546Spatrick#include <objc/objc-auto.h>
45061da546Spatrick
46061da546Spatrick// These are needed when compiling on systems
47061da546Spatrick// that do not yet have these definitions
48061da546Spatrick#include <AvailabilityMacros.h>
49061da546Spatrick#ifndef CPU_SUBTYPE_X86_64_H
50061da546Spatrick#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8)
51061da546Spatrick#endif
52061da546Spatrick#ifndef CPU_TYPE_ARM64
53061da546Spatrick#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
54061da546Spatrick#endif
55061da546Spatrick
56061da546Spatrick#ifndef CPU_TYPE_ARM64_32
57061da546Spatrick#define CPU_ARCH_ABI64_32 0x02000000
58061da546Spatrick#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
59061da546Spatrick#endif
60061da546Spatrick
61061da546Spatrick#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH
62061da546Spatrick
63061da546Spatrickusing namespace lldb_private;
64061da546Spatrick
65*f6aab3d8Srobertstd::optional<std::string> HostInfoMacOSX::GetOSBuildString() {
66061da546Spatrick  int mib[2] = {CTL_KERN, KERN_OSVERSION};
67061da546Spatrick  char cstr[PATH_MAX];
68061da546Spatrick  size_t cstr_len = sizeof(cstr);
69*f6aab3d8Srobert  if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0)
70*f6aab3d8Srobert    return std::string(cstr, cstr_len - 1);
71061da546Spatrick
72*f6aab3d8Srobert  return std::nullopt;
73061da546Spatrick}
74061da546Spatrick
75061da546Spatrickstatic void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) {
76061da546Spatrick  @autoreleasepool {
77061da546Spatrick    NSDictionary *version_info =
78061da546Spatrick      [NSDictionary dictionaryWithContentsOfFile:
79061da546Spatrick       @"/System/Library/CoreServices/SystemVersion.plist"];
80061da546Spatrick    NSString *version_value = [version_info objectForKey: Key];
81061da546Spatrick    const char *version_str = [version_value UTF8String];
82061da546Spatrick    version.tryParse(version_str);
83061da546Spatrick  }
84061da546Spatrick}
85061da546Spatrick
86061da546Spatrickllvm::VersionTuple HostInfoMacOSX::GetOSVersion() {
87061da546Spatrick  static llvm::VersionTuple g_version;
88061da546Spatrick  if (g_version.empty())
89061da546Spatrick    ParseOSVersion(g_version, @"ProductVersion");
90061da546Spatrick  return g_version;
91061da546Spatrick}
92061da546Spatrick
93061da546Spatrickllvm::VersionTuple HostInfoMacOSX::GetMacCatalystVersion() {
94061da546Spatrick  static llvm::VersionTuple g_version;
95061da546Spatrick  if (g_version.empty())
96061da546Spatrick    ParseOSVersion(g_version, @"iOSSupportVersion");
97061da546Spatrick  return g_version;
98061da546Spatrick}
99061da546Spatrick
100061da546Spatrick
101061da546SpatrickFileSpec HostInfoMacOSX::GetProgramFileSpec() {
102061da546Spatrick  static FileSpec g_program_filespec;
103061da546Spatrick  if (!g_program_filespec) {
104061da546Spatrick    char program_fullpath[PATH_MAX];
105061da546Spatrick    // If DST is NULL, then return the number of bytes needed.
106061da546Spatrick    uint32_t len = sizeof(program_fullpath);
107061da546Spatrick    int err = _NSGetExecutablePath(program_fullpath, &len);
108061da546Spatrick    if (err == 0)
109061da546Spatrick      g_program_filespec.SetFile(program_fullpath, FileSpec::Style::native);
110061da546Spatrick    else if (err == -1) {
111061da546Spatrick      char *large_program_fullpath = (char *)::malloc(len + 1);
112061da546Spatrick
113061da546Spatrick      err = _NSGetExecutablePath(large_program_fullpath, &len);
114061da546Spatrick      if (err == 0)
115061da546Spatrick        g_program_filespec.SetFile(large_program_fullpath,
116061da546Spatrick                                   FileSpec::Style::native);
117061da546Spatrick
118061da546Spatrick      ::free(large_program_fullpath);
119061da546Spatrick    }
120061da546Spatrick  }
121061da546Spatrick  return g_program_filespec;
122061da546Spatrick}
123061da546Spatrick
124061da546Spatrickbool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) {
125061da546Spatrick  FileSpec lldb_file_spec = GetShlibDir();
126061da546Spatrick  if (!lldb_file_spec)
127061da546Spatrick    return false;
128061da546Spatrick
129061da546Spatrick  std::string raw_path = lldb_file_spec.GetPath();
130061da546Spatrick
131061da546Spatrick  size_t framework_pos = raw_path.find("LLDB.framework");
132061da546Spatrick  if (framework_pos != std::string::npos) {
133061da546Spatrick    framework_pos += strlen("LLDB.framework");
134be691f3bSpatrick#if TARGET_OS_IPHONE
135061da546Spatrick    // Shallow bundle
136061da546Spatrick    raw_path.resize(framework_pos);
137061da546Spatrick#else
138061da546Spatrick    // Normal bundle
139061da546Spatrick    raw_path.resize(framework_pos);
140061da546Spatrick    raw_path.append("/Resources");
141061da546Spatrick#endif
142061da546Spatrick  } else {
143061da546Spatrick    // Find the bin path relative to the lib path where the cmake-based
144061da546Spatrick    // OS X .dylib lives.  This is not going to work if the bin and lib
145061da546Spatrick    // dir are not both in the same dir.
146061da546Spatrick    //
147061da546Spatrick    // It is not going to work to do it by the executable path either,
148061da546Spatrick    // as in the case of a python script, the executable is python, not
149061da546Spatrick    // the lldb driver.
150061da546Spatrick    raw_path.append("/../bin");
151061da546Spatrick    FileSpec support_dir_spec(raw_path);
152061da546Spatrick    FileSystem::Instance().Resolve(support_dir_spec);
153061da546Spatrick    if (!FileSystem::Instance().IsDirectory(support_dir_spec)) {
154*f6aab3d8Srobert      Log *log = GetLog(LLDBLog::Host);
155061da546Spatrick      LLDB_LOGF(log, "HostInfoMacOSX::%s(): failed to find support directory",
156061da546Spatrick                __FUNCTION__);
157061da546Spatrick      return false;
158061da546Spatrick    }
159061da546Spatrick
160061da546Spatrick    // Get normalization from support_dir_spec.  Note the FileSpec resolve
161061da546Spatrick    // does not remove '..' in the path.
162061da546Spatrick    char *const dir_realpath =
163061da546Spatrick        realpath(support_dir_spec.GetPath().c_str(), NULL);
164061da546Spatrick    if (dir_realpath) {
165061da546Spatrick      raw_path = dir_realpath;
166061da546Spatrick      free(dir_realpath);
167061da546Spatrick    } else {
168061da546Spatrick      raw_path = support_dir_spec.GetPath();
169061da546Spatrick    }
170061da546Spatrick  }
171061da546Spatrick
172*f6aab3d8Srobert  file_spec.SetDirectory(raw_path);
173061da546Spatrick  return (bool)file_spec.GetDirectory();
174061da546Spatrick}
175061da546Spatrick
176061da546Spatrickbool HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) {
177061da546Spatrick  FileSpec lldb_file_spec = GetShlibDir();
178061da546Spatrick  if (!lldb_file_spec)
179061da546Spatrick    return false;
180061da546Spatrick
181061da546Spatrick  std::string raw_path = lldb_file_spec.GetPath();
182061da546Spatrick
183061da546Spatrick  size_t framework_pos = raw_path.find("LLDB.framework");
184061da546Spatrick  if (framework_pos != std::string::npos) {
185061da546Spatrick    framework_pos += strlen("LLDB.framework");
186061da546Spatrick    raw_path.resize(framework_pos);
187061da546Spatrick    raw_path.append("/Headers");
188061da546Spatrick  }
189*f6aab3d8Srobert  file_spec.SetDirectory(raw_path);
190061da546Spatrick  return true;
191061da546Spatrick}
192061da546Spatrick
193061da546Spatrickbool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
194061da546Spatrick  FileSpec lldb_file_spec = GetShlibDir();
195061da546Spatrick  if (!lldb_file_spec)
196061da546Spatrick    return false;
197061da546Spatrick
198061da546Spatrick  std::string raw_path = lldb_file_spec.GetPath();
199061da546Spatrick
200061da546Spatrick  size_t framework_pos = raw_path.find("LLDB.framework");
201061da546Spatrick  if (framework_pos == std::string::npos)
202061da546Spatrick    return false;
203061da546Spatrick
204061da546Spatrick  framework_pos += strlen("LLDB.framework");
205061da546Spatrick  raw_path.resize(framework_pos);
206061da546Spatrick  raw_path.append("/Resources/PlugIns");
207*f6aab3d8Srobert  file_spec.SetDirectory(raw_path);
208061da546Spatrick  return true;
209061da546Spatrick}
210061da546Spatrick
211061da546Spatrickbool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) {
212061da546Spatrick  FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns");
213061da546Spatrick  FileSystem::Instance().Resolve(temp_file);
214*f6aab3d8Srobert  file_spec.SetDirectory(temp_file.GetPathAsConstString());
215061da546Spatrick  return true;
216061da546Spatrick}
217061da546Spatrick
218061da546Spatrickvoid HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32,
219061da546Spatrick                                                    ArchSpec &arch_64) {
220061da546Spatrick  // All apple systems support 32 bit execution.
221061da546Spatrick  uint32_t cputype, cpusubtype;
222061da546Spatrick  uint32_t is_64_bit_capable = false;
223061da546Spatrick  size_t len = sizeof(cputype);
224061da546Spatrick  ArchSpec host_arch;
225061da546Spatrick  // These will tell us about the kernel architecture, which even on a 64
226061da546Spatrick  // bit machine can be 32 bit...
227061da546Spatrick  if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) {
228061da546Spatrick    len = sizeof(cpusubtype);
229061da546Spatrick    if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0)
230061da546Spatrick      cpusubtype = CPU_TYPE_ANY;
231061da546Spatrick
232061da546Spatrick    len = sizeof(is_64_bit_capable);
233061da546Spatrick    ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0);
234061da546Spatrick
235be691f3bSpatrick    if (cputype == CPU_TYPE_ARM64 && cpusubtype == CPU_SUBTYPE_ARM64E) {
236be691f3bSpatrick      // The arm64e architecture is a preview. Pretend the host architecture
237be691f3bSpatrick      // is arm64.
238be691f3bSpatrick      cpusubtype = CPU_SUBTYPE_ARM64_ALL;
239be691f3bSpatrick    }
240be691f3bSpatrick
241061da546Spatrick    if (is_64_bit_capable) {
242061da546Spatrick      if (cputype & CPU_ARCH_ABI64) {
243061da546Spatrick        // We have a 64 bit kernel on a 64 bit system
244061da546Spatrick        arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype);
245061da546Spatrick      } else {
246061da546Spatrick        // We have a 64 bit kernel that is returning a 32 bit cputype, the
247061da546Spatrick        // cpusubtype will be correct as if it were for a 64 bit architecture
248061da546Spatrick        arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64,
249061da546Spatrick                                cpusubtype);
250061da546Spatrick      }
251061da546Spatrick
252061da546Spatrick      // Now we need modify the cpusubtype for the 32 bit slices.
253061da546Spatrick      uint32_t cpusubtype32 = cpusubtype;
254061da546Spatrick#if defined(__i386__) || defined(__x86_64__)
255061da546Spatrick      if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H)
256061da546Spatrick        cpusubtype32 = CPU_SUBTYPE_I386_ALL;
257061da546Spatrick#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__)
258061da546Spatrick      if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64)
259061da546Spatrick        cpusubtype32 = CPU_SUBTYPE_ARM_V7S;
260061da546Spatrick#endif
261061da546Spatrick      arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK),
262061da546Spatrick                              cpusubtype32);
263061da546Spatrick
264061da546Spatrick      if (cputype == CPU_TYPE_ARM ||
265061da546Spatrick          cputype == CPU_TYPE_ARM64 ||
266061da546Spatrick          cputype == CPU_TYPE_ARM64_32) {
267061da546Spatrick// When running on a watch or tv, report the host os correctly
268061da546Spatrick#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
269061da546Spatrick        arch_32.GetTriple().setOS(llvm::Triple::TvOS);
270061da546Spatrick        arch_64.GetTriple().setOS(llvm::Triple::TvOS);
271061da546Spatrick#elif defined(TARGET_OS_BRIDGE) && TARGET_OS_BRIDGE == 1
272061da546Spatrick        arch_32.GetTriple().setOS(llvm::Triple::BridgeOS);
273061da546Spatrick        arch_64.GetTriple().setOS(llvm::Triple::BridgeOS);
274061da546Spatrick#elif defined(TARGET_OS_WATCHOS) && TARGET_OS_WATCHOS == 1
275061da546Spatrick        arch_32.GetTriple().setOS(llvm::Triple::WatchOS);
276061da546Spatrick        arch_64.GetTriple().setOS(llvm::Triple::WatchOS);
277dda28197Spatrick#elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1
278dda28197Spatrick        arch_32.GetTriple().setOS(llvm::Triple::MacOSX);
279dda28197Spatrick        arch_64.GetTriple().setOS(llvm::Triple::MacOSX);
280061da546Spatrick#else
281061da546Spatrick        arch_32.GetTriple().setOS(llvm::Triple::IOS);
282061da546Spatrick        arch_64.GetTriple().setOS(llvm::Triple::IOS);
283061da546Spatrick#endif
284061da546Spatrick      } else {
285061da546Spatrick        arch_32.GetTriple().setOS(llvm::Triple::MacOSX);
286061da546Spatrick        arch_64.GetTriple().setOS(llvm::Triple::MacOSX);
287061da546Spatrick      }
288061da546Spatrick    } else {
289061da546Spatrick      // We have a 32 bit kernel on a 32 bit system
290061da546Spatrick      arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype);
291061da546Spatrick#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
292061da546Spatrick      arch_32.GetTriple().setOS(llvm::Triple::WatchOS);
293061da546Spatrick#else
294061da546Spatrick      arch_32.GetTriple().setOS(llvm::Triple::IOS);
295061da546Spatrick#endif
296061da546Spatrick      arch_64.Clear();
297061da546Spatrick    }
298061da546Spatrick  }
299061da546Spatrick}
300dda28197Spatrick
301dda28197Spatrick/// Return and cache $DEVELOPER_DIR if it is set and exists.
302dda28197Spatrickstatic std::string GetEnvDeveloperDir() {
303dda28197Spatrick  static std::string g_env_developer_dir;
304dda28197Spatrick  static std::once_flag g_once_flag;
305dda28197Spatrick  std::call_once(g_once_flag, [&]() {
306dda28197Spatrick    if (const char *developer_dir_env_var = getenv("DEVELOPER_DIR")) {
307dda28197Spatrick      FileSpec fspec(developer_dir_env_var);
308dda28197Spatrick      if (FileSystem::Instance().Exists(fspec))
309dda28197Spatrick        g_env_developer_dir = fspec.GetPath();
310dda28197Spatrick    }});
311dda28197Spatrick  return g_env_developer_dir;
312dda28197Spatrick}
313dda28197Spatrick
314dda28197SpatrickFileSpec HostInfoMacOSX::GetXcodeContentsDirectory() {
315dda28197Spatrick  static FileSpec g_xcode_contents_path;
316dda28197Spatrick  static std::once_flag g_once_flag;
317dda28197Spatrick  std::call_once(g_once_flag, [&]() {
318dda28197Spatrick    // Try the shlib dir first.
319dda28197Spatrick    if (FileSpec fspec = HostInfo::GetShlibDir()) {
320dda28197Spatrick      if (FileSystem::Instance().Exists(fspec)) {
321dda28197Spatrick        std::string xcode_contents_dir =
322dda28197Spatrick            XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath());
323dda28197Spatrick        if (!xcode_contents_dir.empty()) {
324dda28197Spatrick          g_xcode_contents_path = FileSpec(xcode_contents_dir);
325dda28197Spatrick          return;
326dda28197Spatrick        }
327dda28197Spatrick      }
328dda28197Spatrick    }
329dda28197Spatrick
330dda28197Spatrick    llvm::SmallString<128> env_developer_dir(GetEnvDeveloperDir());
331dda28197Spatrick    if (!env_developer_dir.empty()) {
332dda28197Spatrick      llvm::sys::path::append(env_developer_dir, "Contents");
333dda28197Spatrick      std::string xcode_contents_dir =
334dda28197Spatrick          XcodeSDK::FindXcodeContentsDirectoryInPath(env_developer_dir);
335dda28197Spatrick      if (!xcode_contents_dir.empty()) {
336dda28197Spatrick        g_xcode_contents_path = FileSpec(xcode_contents_dir);
337dda28197Spatrick        return;
338dda28197Spatrick      }
339dda28197Spatrick    }
340dda28197Spatrick
341*f6aab3d8Srobert    auto sdk_path_or_err = HostInfo::GetXcodeSDKPath(XcodeSDK::GetAnyMacOS());
342*f6aab3d8Srobert    if (!sdk_path_or_err) {
343*f6aab3d8Srobert      Log *log = GetLog(LLDBLog::Host);
344*f6aab3d8Srobert      LLDB_LOGF(log, "Error while searching for Xcode SDK: %s",
345*f6aab3d8Srobert                toString(sdk_path_or_err.takeError()).c_str());
346*f6aab3d8Srobert      return;
347*f6aab3d8Srobert    }
348*f6aab3d8Srobert    FileSpec fspec(*sdk_path_or_err);
349dda28197Spatrick    if (fspec) {
350dda28197Spatrick      if (FileSystem::Instance().Exists(fspec)) {
351dda28197Spatrick        std::string xcode_contents_dir =
352dda28197Spatrick            XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath());
353dda28197Spatrick        if (!xcode_contents_dir.empty()) {
354dda28197Spatrick          g_xcode_contents_path = FileSpec(xcode_contents_dir);
355dda28197Spatrick          return;
356dda28197Spatrick        }
357dda28197Spatrick      }
358dda28197Spatrick    }
359dda28197Spatrick  });
360dda28197Spatrick  return g_xcode_contents_path;
361dda28197Spatrick}
362dda28197Spatrick
363dda28197Spatricklldb_private::FileSpec HostInfoMacOSX::GetXcodeDeveloperDirectory() {
364dda28197Spatrick  static lldb_private::FileSpec g_developer_directory;
365dda28197Spatrick  static llvm::once_flag g_once_flag;
366dda28197Spatrick  llvm::call_once(g_once_flag, []() {
367dda28197Spatrick    if (FileSpec fspec = GetXcodeContentsDirectory()) {
368dda28197Spatrick      fspec.AppendPathComponent("Developer");
369dda28197Spatrick      if (FileSystem::Instance().Exists(fspec))
370dda28197Spatrick        g_developer_directory = fspec;
371dda28197Spatrick    }
372dda28197Spatrick  });
373dda28197Spatrick  return g_developer_directory;
374dda28197Spatrick}
375dda28197Spatrick
376*f6aab3d8Srobertllvm::Expected<std::string> GetXcodeSDK(XcodeSDK sdk) {
377dda28197Spatrick  XcodeSDK::Info info = sdk.Parse();
378dda28197Spatrick  std::string sdk_name = XcodeSDK::GetCanonicalName(info);
379*f6aab3d8Srobert  if (sdk_name.empty())
380*f6aab3d8Srobert    return llvm::createStringError(llvm::inconvertibleErrorCode(),
381*f6aab3d8Srobert                                   "Unrecognized SDK type: " + sdk.GetString());
382*f6aab3d8Srobert
383*f6aab3d8Srobert  Log *log = GetLog(LLDBLog::Host);
384be691f3bSpatrick
385be691f3bSpatrick  auto xcrun = [](const std::string &sdk,
386*f6aab3d8Srobert                  llvm::StringRef developer_dir =
387*f6aab3d8Srobert                      "") -> llvm::Expected<std::string> {
388be691f3bSpatrick    Args args;
389be691f3bSpatrick    if (!developer_dir.empty()) {
390be691f3bSpatrick      args.AppendArgument("/usr/bin/env");
391be691f3bSpatrick      args.AppendArgument("DEVELOPER_DIR=" + developer_dir.str());
392dda28197Spatrick    }
393be691f3bSpatrick    args.AppendArgument("/usr/bin/xcrun");
394be691f3bSpatrick    args.AppendArgument("--show-sdk-path");
395be691f3bSpatrick    args.AppendArgument("--sdk");
396be691f3bSpatrick    args.AppendArgument(sdk);
397dda28197Spatrick
398*f6aab3d8Srobert    Log *log = GetLog(LLDBLog::Host);
399*f6aab3d8Srobert    if (log) {
400*f6aab3d8Srobert      std::string cmdstr;
401*f6aab3d8Srobert      args.GetCommandString(cmdstr);
402*f6aab3d8Srobert      log->Printf("GetXcodeSDK() running shell cmd '%s'", cmdstr.c_str());
403*f6aab3d8Srobert    }
404*f6aab3d8Srobert
405dda28197Spatrick    int status = 0;
406dda28197Spatrick    int signo = 0;
407dda28197Spatrick    std::string output_str;
408*f6aab3d8Srobert    // The first time after Xcode was updated or freshly installed,
409*f6aab3d8Srobert    // xcrun can take surprisingly long to build up its database.
410*f6aab3d8Srobert    auto timeout = std::chrono::seconds(60);
411*f6aab3d8Srobert    bool run_in_shell = false;
412*f6aab3d8Srobert    lldb_private::Status error = Host::RunShellCommand(
413*f6aab3d8Srobert        args, FileSpec(), &status, &signo, &output_str, timeout, run_in_shell);
414dda28197Spatrick
415*f6aab3d8Srobert    // Check that xcrun returned something useful.
416*f6aab3d8Srobert    if (error.Fail()) {
417*f6aab3d8Srobert      // Catastrophic error.
418*f6aab3d8Srobert      LLDB_LOG(log, "xcrun failed to execute: %s", error.AsCString());
419*f6aab3d8Srobert      return error.ToError();
420*f6aab3d8Srobert    }
421*f6aab3d8Srobert    if (status != 0) {
422*f6aab3d8Srobert      // xcrun didn't find a matching SDK. Not an error, we'll try
423*f6aab3d8Srobert      // different spellings.
424*f6aab3d8Srobert      LLDB_LOG(log, "xcrun returned exit code %d", status);
425*f6aab3d8Srobert      return "";
426*f6aab3d8Srobert    }
427*f6aab3d8Srobert    if (output_str.empty()) {
428*f6aab3d8Srobert      LLDB_LOG(log, "xcrun returned no results");
429*f6aab3d8Srobert      return "";
430*f6aab3d8Srobert    }
431dda28197Spatrick
432dda28197Spatrick    // Convert to a StringRef so we can manipulate the string without modifying
433dda28197Spatrick    // the underlying data.
434dda28197Spatrick    llvm::StringRef output(output_str);
435dda28197Spatrick
436dda28197Spatrick    // Remove any trailing newline characters.
437dda28197Spatrick    output = output.rtrim();
438dda28197Spatrick
439dda28197Spatrick    // Strip any leading newline characters and everything before them.
440dda28197Spatrick    const size_t last_newline = output.rfind('\n');
441dda28197Spatrick    if (last_newline != llvm::StringRef::npos)
442dda28197Spatrick      output = output.substr(last_newline + 1);
443dda28197Spatrick
444dda28197Spatrick    return output.str();
445dda28197Spatrick  };
446dda28197Spatrick
447*f6aab3d8Srobert  auto find_sdk =
448*f6aab3d8Srobert      [&xcrun](const std::string &sdk_name) -> llvm::Expected<std::string> {
449be691f3bSpatrick    // Invoke xcrun with the developer dir specified in the environment.
450be691f3bSpatrick    std::string developer_dir = GetEnvDeveloperDir();
451be691f3bSpatrick    if (!developer_dir.empty()) {
452be691f3bSpatrick      // Don't fallback if DEVELOPER_DIR was set.
453be691f3bSpatrick      return xcrun(sdk_name, developer_dir);
454be691f3bSpatrick    }
455be691f3bSpatrick
456be691f3bSpatrick    // Invoke xcrun with the shlib dir.
457be691f3bSpatrick    if (FileSpec fspec = HostInfo::GetShlibDir()) {
458be691f3bSpatrick      if (FileSystem::Instance().Exists(fspec)) {
459be691f3bSpatrick        std::string contents_dir =
460be691f3bSpatrick            XcodeSDK::FindXcodeContentsDirectoryInPath(fspec.GetPath());
461be691f3bSpatrick        llvm::StringRef shlib_developer_dir =
462be691f3bSpatrick            llvm::sys::path::parent_path(contents_dir);
463be691f3bSpatrick        if (!shlib_developer_dir.empty()) {
464*f6aab3d8Srobert          auto sdk = xcrun(sdk_name, std::move(shlib_developer_dir));
465*f6aab3d8Srobert          if (!sdk)
466*f6aab3d8Srobert            return sdk.takeError();
467*f6aab3d8Srobert          if (!sdk->empty())
468be691f3bSpatrick            return sdk;
469be691f3bSpatrick        }
470be691f3bSpatrick      }
471be691f3bSpatrick    }
472be691f3bSpatrick
473be691f3bSpatrick    // Invoke xcrun without a developer dir as a last resort.
474be691f3bSpatrick    return xcrun(sdk_name);
475be691f3bSpatrick  };
476be691f3bSpatrick
477*f6aab3d8Srobert  auto path_or_err = find_sdk(sdk_name);
478*f6aab3d8Srobert  if (!path_or_err)
479*f6aab3d8Srobert    return path_or_err.takeError();
480*f6aab3d8Srobert  std::string path = *path_or_err;
481dda28197Spatrick  while (path.empty()) {
482dda28197Spatrick    // Try an alternate spelling of the name ("macosx10.9internal").
483dda28197Spatrick    if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() &&
484dda28197Spatrick        info.internal) {
485dda28197Spatrick      llvm::StringRef fixed(sdk_name);
486dda28197Spatrick      if (fixed.consume_back(".internal"))
487dda28197Spatrick        sdk_name = fixed.str() + "internal";
488*f6aab3d8Srobert      path_or_err = find_sdk(sdk_name);
489*f6aab3d8Srobert      if (!path_or_err)
490*f6aab3d8Srobert        return path_or_err.takeError();
491*f6aab3d8Srobert      path = *path_or_err;
492dda28197Spatrick      if (!path.empty())
493dda28197Spatrick        break;
494dda28197Spatrick    }
495dda28197Spatrick    LLDB_LOGF(log, "Couldn't find SDK %s on host", sdk_name.c_str());
496dda28197Spatrick
497dda28197Spatrick    // Try without the version.
498dda28197Spatrick    if (!info.version.empty()) {
499dda28197Spatrick      info.version = {};
500dda28197Spatrick      sdk_name = XcodeSDK::GetCanonicalName(info);
501*f6aab3d8Srobert      path_or_err = find_sdk(sdk_name);
502*f6aab3d8Srobert      if (!path_or_err)
503*f6aab3d8Srobert        return path_or_err.takeError();
504*f6aab3d8Srobert      path = *path_or_err;
505dda28197Spatrick      if (!path.empty())
506dda28197Spatrick        break;
507dda28197Spatrick    }
508dda28197Spatrick
509dda28197Spatrick    LLDB_LOGF(log, "Couldn't find any matching SDK on host");
510*f6aab3d8Srobert    return "";
511dda28197Spatrick  }
512dda28197Spatrick
513dda28197Spatrick  // Whatever is left in output should be a valid path.
514*f6aab3d8Srobert  if (!FileSystem::Instance().Exists(path)) {
515*f6aab3d8Srobert    LLDB_LOGF(log, "SDK returned by xcrun doesn't exist");
516*f6aab3d8Srobert    return llvm::createStringError(llvm::inconvertibleErrorCode(),
517*f6aab3d8Srobert                                   "SDK returned by xcrun doesn't exist");
518*f6aab3d8Srobert  }
519dda28197Spatrick  return path;
520dda28197Spatrick}
521dda28197Spatrick
522*f6aab3d8Srobertllvm::Expected<llvm::StringRef> HostInfoMacOSX::GetXcodeSDKPath(XcodeSDK sdk) {
523*f6aab3d8Srobert  struct ErrorOrPath {
524*f6aab3d8Srobert    std::string str;
525*f6aab3d8Srobert    bool is_error;
526*f6aab3d8Srobert  };
527*f6aab3d8Srobert  static llvm::StringMap<ErrorOrPath> g_sdk_path;
528dda28197Spatrick  static std::mutex g_sdk_path_mutex;
529dda28197Spatrick
530dda28197Spatrick  std::lock_guard<std::mutex> guard(g_sdk_path_mutex);
531be691f3bSpatrick  LLDB_SCOPED_TIMER();
532be691f3bSpatrick
533*f6aab3d8Srobert  auto key = sdk.GetString();
534*f6aab3d8Srobert  auto it = g_sdk_path.find(key);
535*f6aab3d8Srobert  if (it != g_sdk_path.end()) {
536*f6aab3d8Srobert    if (it->second.is_error)
537*f6aab3d8Srobert      return llvm::createStringError(llvm::inconvertibleErrorCode(),
538*f6aab3d8Srobert                                     it->second.str);
539*f6aab3d8Srobert    else
540*f6aab3d8Srobert      return it->second.str;
541*f6aab3d8Srobert  }
542*f6aab3d8Srobert  auto path_or_err = GetXcodeSDK(sdk);
543*f6aab3d8Srobert  if (!path_or_err) {
544*f6aab3d8Srobert    std::string error = toString(path_or_err.takeError());
545*f6aab3d8Srobert    g_sdk_path.insert({key, {error, true}});
546*f6aab3d8Srobert    return llvm::createStringError(llvm::inconvertibleErrorCode(), error);
547*f6aab3d8Srobert  }
548*f6aab3d8Srobert  auto it_new = g_sdk_path.insert({key, {*path_or_err, false}});
549*f6aab3d8Srobert  return it_new.first->second.str;
550dda28197Spatrick}
551be691f3bSpatrick
552be691f3bSpatricknamespace {
553be691f3bSpatrickstruct dyld_shared_cache_dylib_text_info {
554be691f3bSpatrick  uint64_t version; // current version 1
555be691f3bSpatrick  // following fields all exist in version 1
556be691f3bSpatrick  uint64_t loadAddressUnslid;
557be691f3bSpatrick  uint64_t textSegmentSize;
558be691f3bSpatrick  uuid_t dylibUuid;
559be691f3bSpatrick  const char *path; // pointer invalid at end of iterations
560be691f3bSpatrick  // following fields all exist in version 2
561be691f3bSpatrick  uint64_t textSegmentOffset; // offset from start of cache
562be691f3bSpatrick};
563be691f3bSpatricktypedef struct dyld_shared_cache_dylib_text_info
564be691f3bSpatrick    dyld_shared_cache_dylib_text_info;
565be691f3bSpatrick}
566be691f3bSpatrick
567be691f3bSpatrickextern "C" int dyld_shared_cache_iterate_text(
568be691f3bSpatrick    const uuid_t cacheUuid,
569be691f3bSpatrick    void (^callback)(const dyld_shared_cache_dylib_text_info *info));
570be691f3bSpatrickextern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length);
571be691f3bSpatrickextern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid);
572be691f3bSpatrick
573be691f3bSpatricknamespace {
574be691f3bSpatrickclass SharedCacheInfo {
575be691f3bSpatrickpublic:
576be691f3bSpatrick  const UUID &GetUUID() const { return m_uuid; }
577be691f3bSpatrick  const llvm::StringMap<SharedCacheImageInfo> &GetImages() const {
578be691f3bSpatrick    return m_images;
579be691f3bSpatrick  }
580be691f3bSpatrick
581be691f3bSpatrick  SharedCacheInfo();
582be691f3bSpatrick
583be691f3bSpatrickprivate:
584*f6aab3d8Srobert  bool CreateSharedCacheInfoWithInstrospectionSPIs();
585*f6aab3d8Srobert
586be691f3bSpatrick  llvm::StringMap<SharedCacheImageInfo> m_images;
587be691f3bSpatrick  UUID m_uuid;
588be691f3bSpatrick};
589be691f3bSpatrick}
590be691f3bSpatrick
591*f6aab3d8Srobertbool SharedCacheInfo::CreateSharedCacheInfoWithInstrospectionSPIs() {
592*f6aab3d8Srobert#if defined(SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS)
593*f6aab3d8Srobert  dyld_process_t dyld_process = dyld_process_create_for_current_task();
594*f6aab3d8Srobert  if (!dyld_process)
595*f6aab3d8Srobert    return false;
596*f6aab3d8Srobert
597*f6aab3d8Srobert  dyld_process_snapshot_t snapshot =
598*f6aab3d8Srobert      dyld_process_snapshot_create_for_process(dyld_process, nullptr);
599*f6aab3d8Srobert  if (!snapshot)
600*f6aab3d8Srobert    return false;
601*f6aab3d8Srobert
602*f6aab3d8Srobert  auto on_exit =
603*f6aab3d8Srobert      llvm::make_scope_exit([&]() { dyld_process_snapshot_dispose(snapshot); });
604*f6aab3d8Srobert
605*f6aab3d8Srobert  dyld_shared_cache_t shared_cache =
606*f6aab3d8Srobert      dyld_process_snapshot_get_shared_cache(snapshot);
607*f6aab3d8Srobert  if (!shared_cache)
608*f6aab3d8Srobert    return false;
609*f6aab3d8Srobert
610*f6aab3d8Srobert  dyld_shared_cache_for_each_image(shared_cache, ^(dyld_image_t image) {
611*f6aab3d8Srobert    __block uint64_t minVmAddr = UINT64_MAX;
612*f6aab3d8Srobert    __block uint64_t maxVmAddr = 0;
613*f6aab3d8Srobert    uuid_t uuidStore;
614*f6aab3d8Srobert    __block uuid_t *uuid = &uuidStore;
615*f6aab3d8Srobert
616*f6aab3d8Srobert    dyld_image_for_each_segment_info(
617*f6aab3d8Srobert        image,
618*f6aab3d8Srobert        ^(const char *segmentName, uint64_t vmAddr, uint64_t vmSize, int perm) {
619*f6aab3d8Srobert          minVmAddr = std::min(minVmAddr, vmAddr);
620*f6aab3d8Srobert          maxVmAddr = std::max(maxVmAddr, vmAddr + vmSize);
621*f6aab3d8Srobert          dyld_image_copy_uuid(image, uuid);
622*f6aab3d8Srobert        });
623*f6aab3d8Srobert    assert(minVmAddr != UINT_MAX);
624*f6aab3d8Srobert    assert(maxVmAddr != 0);
625*f6aab3d8Srobert    m_images[dyld_image_get_installname(image)] = SharedCacheImageInfo{
626*f6aab3d8Srobert        UUID(uuid, 16), std::make_shared<DataBufferUnowned>(
627*f6aab3d8Srobert                            (uint8_t *)minVmAddr, maxVmAddr - minVmAddr)};
628*f6aab3d8Srobert  });
629*f6aab3d8Srobert  return true;
630*f6aab3d8Srobert#endif
631*f6aab3d8Srobert  return false;
632*f6aab3d8Srobert}
633*f6aab3d8Srobert
634be691f3bSpatrickSharedCacheInfo::SharedCacheInfo() {
635*f6aab3d8Srobert  if (CreateSharedCacheInfoWithInstrospectionSPIs())
636*f6aab3d8Srobert    return;
637*f6aab3d8Srobert
638be691f3bSpatrick  size_t shared_cache_size;
639be691f3bSpatrick  uint8_t *shared_cache_start =
640be691f3bSpatrick      _dyld_get_shared_cache_range(&shared_cache_size);
641be691f3bSpatrick  uuid_t dsc_uuid;
642be691f3bSpatrick  _dyld_get_shared_cache_uuid(dsc_uuid);
643*f6aab3d8Srobert  m_uuid = UUID(dsc_uuid);
644be691f3bSpatrick
645be691f3bSpatrick  dyld_shared_cache_iterate_text(
646be691f3bSpatrick      dsc_uuid, ^(const dyld_shared_cache_dylib_text_info *info) {
647be691f3bSpatrick        m_images[info->path] = SharedCacheImageInfo{
648*f6aab3d8Srobert            UUID(info->dylibUuid, 16),
649be691f3bSpatrick            std::make_shared<DataBufferUnowned>(
650be691f3bSpatrick                shared_cache_start + info->textSegmentOffset,
651be691f3bSpatrick                shared_cache_size - info->textSegmentOffset)};
652be691f3bSpatrick      });
653be691f3bSpatrick}
654be691f3bSpatrick
655be691f3bSpatrickSharedCacheImageInfo
656be691f3bSpatrickHostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) {
657be691f3bSpatrick  static SharedCacheInfo g_shared_cache_info;
658be691f3bSpatrick  return g_shared_cache_info.GetImages().lookup(image_name);
659be691f3bSpatrick}
660