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