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