1 | //===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Host/macosx/HostInfoMacOSX.h" |
10 | #include "lldb/Host/FileSystem.h" |
11 | #include "lldb/Host/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" |
17 | |
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" |
24 | |
25 | // C++ Includes |
26 | #include <optional> |
27 | #include <string> |
28 | |
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> |
35 | |
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> |
49 | |
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 |
58 | |
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 |
63 | |
64 | #include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH |
65 | |
66 | using namespace lldb_private; |
67 | |
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); |
74 | |
75 | return std::nullopt; |
76 | } |
77 | |
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(string: version_str); |
86 | } |
87 | } |
88 | |
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; |
94 | } |
95 | |
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; |
101 | } |
102 | |
103 | |
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(path: program_fullpath, style: FileSpec::Style::native); |
113 | else if (err == -1) { |
114 | char *large_program_fullpath = (char *)::malloc(size: len + 1); |
115 | |
116 | err = _NSGetExecutablePath(large_program_fullpath, &len); |
117 | if (err == 0) |
118 | g_program_filespec.SetFile(path: large_program_fullpath, |
119 | style: FileSpec::Style::native); |
120 | |
121 | ::free(ptr: large_program_fullpath); |
122 | } |
123 | } |
124 | return g_program_filespec; |
125 | } |
126 | |
127 | bool HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) { |
128 | FileSpec lldb_file_spec = GetShlibDir(); |
129 | if (!lldb_file_spec) |
130 | return false; |
131 | |
132 | std::string raw_path = lldb_file_spec.GetPath(); |
133 | |
134 | size_t framework_pos = raw_path.find(s: "LLDB.framework" ); |
135 | if (framework_pos != std::string::npos) { |
136 | framework_pos += strlen(s: "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(n: framework_pos); |
143 | raw_path.append(s: "/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(s: "/../bin" ); |
154 | FileSpec support_dir_spec(raw_path); |
155 | FileSystem::Instance().Resolve(file_spec&: support_dir_spec); |
156 | if (!FileSystem::Instance().IsDirectory(file_spec: support_dir_spec)) { |
157 | Log *log = GetLog(mask: LLDBLog::Host); |
158 | LLDB_LOG(log, "failed to find support directory" ); |
159 | return false; |
160 | } |
161 | |
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(name: support_dir_spec.GetPath().c_str(), NULL); |
166 | if (dir_realpath) { |
167 | raw_path = dir_realpath; |
168 | free(ptr: dir_realpath); |
169 | } else { |
170 | raw_path = support_dir_spec.GetPath(); |
171 | } |
172 | } |
173 | |
174 | file_spec.SetDirectory(raw_path); |
175 | return (bool)file_spec.GetDirectory(); |
176 | } |
177 | |
178 | bool HostInfoMacOSX::(FileSpec &file_spec) { |
179 | FileSpec lldb_file_spec = GetShlibDir(); |
180 | if (!lldb_file_spec) |
181 | return false; |
182 | |
183 | std::string raw_path = lldb_file_spec.GetPath(); |
184 | |
185 | size_t framework_pos = raw_path.find(s: "LLDB.framework" ); |
186 | if (framework_pos != std::string::npos) { |
187 | framework_pos += strlen(s: "LLDB.framework" ); |
188 | raw_path.resize(n: framework_pos); |
189 | raw_path.append(s: "/Headers" ); |
190 | } |
191 | file_spec.SetDirectory(raw_path); |
192 | return true; |
193 | } |
194 | |
195 | bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { |
196 | FileSpec lldb_file_spec = GetShlibDir(); |
197 | if (!lldb_file_spec) |
198 | return false; |
199 | |
200 | std::string raw_path = lldb_file_spec.GetPath(); |
201 | |
202 | size_t framework_pos = raw_path.find(s: "LLDB.framework" ); |
203 | if (framework_pos == std::string::npos) |
204 | return false; |
205 | |
206 | framework_pos += strlen(s: "LLDB.framework" ); |
207 | raw_path.resize(n: framework_pos); |
208 | raw_path.append(s: "/Resources/PlugIns" ); |
209 | file_spec.SetDirectory(raw_path); |
210 | return true; |
211 | } |
212 | |
213 | bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { |
214 | FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns" ); |
215 | FileSystem::Instance().Resolve(file_spec&: temp_file); |
216 | file_spec.SetDirectory(temp_file.GetPathAsConstString()); |
217 | return true; |
218 | } |
219 | |
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; |
233 | |
234 | len = sizeof(is_64_bit_capable); |
235 | ::sysctlbyname("hw.cpu64bit_capable" , &is_64_bit_capable, &len, NULL, 0); |
236 | |
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 | } |
242 | |
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(arch_type: eArchTypeMachO, cpu: cputype, sub: 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(arch_type: eArchTypeMachO, cpu: cputype | CPU_ARCH_ABI64, |
251 | sub: cpusubtype); |
252 | } |
253 | |
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); |
265 | |
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_XR) && TARGET_OS_XR == 1 |
280 | arch_32.GetTriple().setOS(llvm::Triple::XROS); |
281 | arch_64.GetTriple().setOS(llvm::Triple::XROS); |
282 | #elif defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1 |
283 | arch_32.GetTriple().setOS(llvm::Triple::MacOSX); |
284 | arch_64.GetTriple().setOS(llvm::Triple::MacOSX); |
285 | #else |
286 | arch_32.GetTriple().setOS(llvm::Triple::IOS); |
287 | arch_64.GetTriple().setOS(llvm::Triple::IOS); |
288 | #endif |
289 | } else { |
290 | arch_32.GetTriple().setOS(llvm::Triple::MacOSX); |
291 | arch_64.GetTriple().setOS(llvm::Triple::MacOSX); |
292 | } |
293 | } else { |
294 | // We have a 32 bit kernel on a 32 bit system |
295 | arch_32.SetArchitecture(arch_type: eArchTypeMachO, cpu: cputype, sub: cpusubtype); |
296 | #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 |
297 | arch_32.GetTriple().setOS(llvm::Triple::WatchOS); |
298 | #else |
299 | arch_32.GetTriple().setOS(llvm::Triple::IOS); |
300 | #endif |
301 | arch_64.Clear(); |
302 | } |
303 | } |
304 | } |
305 | |
306 | /// Return and cache $DEVELOPER_DIR if it is set and exists. |
307 | static std::string GetEnvDeveloperDir() { |
308 | static std::string g_env_developer_dir; |
309 | static std::once_flag g_once_flag; |
310 | std::call_once(once&: g_once_flag, f: [&]() { |
311 | if (const char *developer_dir_env_var = getenv(name: "DEVELOPER_DIR" )) { |
312 | FileSpec fspec(developer_dir_env_var); |
313 | if (FileSystem::Instance().Exists(file_spec: fspec)) |
314 | g_env_developer_dir = fspec.GetPath(); |
315 | }}); |
316 | return g_env_developer_dir; |
317 | } |
318 | |
319 | FileSpec HostInfoMacOSX::GetXcodeContentsDirectory() { |
320 | static FileSpec g_xcode_contents_path; |
321 | static std::once_flag g_once_flag; |
322 | std::call_once(once&: g_once_flag, f: [&]() { |
323 | // Try the shlib dir first. |
324 | if (FileSpec fspec = HostInfo::GetShlibDir()) { |
325 | if (FileSystem::Instance().Exists(file_spec: fspec)) { |
326 | std::string xcode_contents_dir = |
327 | XcodeSDK::FindXcodeContentsDirectoryInPath(path: fspec.GetPath()); |
328 | if (!xcode_contents_dir.empty()) { |
329 | g_xcode_contents_path = FileSpec(xcode_contents_dir); |
330 | return; |
331 | } |
332 | } |
333 | } |
334 | |
335 | llvm::SmallString<128> env_developer_dir(GetEnvDeveloperDir()); |
336 | if (!env_developer_dir.empty()) { |
337 | llvm::sys::path::append(path&: env_developer_dir, a: "Contents" ); |
338 | std::string xcode_contents_dir = |
339 | XcodeSDK::FindXcodeContentsDirectoryInPath(path: env_developer_dir); |
340 | if (!xcode_contents_dir.empty()) { |
341 | g_xcode_contents_path = FileSpec(xcode_contents_dir); |
342 | return; |
343 | } |
344 | } |
345 | |
346 | auto sdk_path_or_err = |
347 | HostInfo::GetSDKRoot(options: SDKOptions{.XcodeSDKSelection: XcodeSDK::GetAnyMacOS()}); |
348 | if (!sdk_path_or_err) { |
349 | Log *log = GetLog(mask: LLDBLog::Host); |
350 | LLDB_LOG_ERROR(log, sdk_path_or_err.takeError(), |
351 | "Error while searching for Xcode SDK: {0}" ); |
352 | return; |
353 | } |
354 | FileSpec fspec(*sdk_path_or_err); |
355 | if (fspec) { |
356 | if (FileSystem::Instance().Exists(file_spec: fspec)) { |
357 | std::string xcode_contents_dir = |
358 | XcodeSDK::FindXcodeContentsDirectoryInPath(path: fspec.GetPath()); |
359 | if (!xcode_contents_dir.empty()) { |
360 | g_xcode_contents_path = FileSpec(xcode_contents_dir); |
361 | return; |
362 | } |
363 | } |
364 | } |
365 | }); |
366 | return g_xcode_contents_path; |
367 | } |
368 | |
369 | lldb_private::FileSpec HostInfoMacOSX::GetXcodeDeveloperDirectory() { |
370 | static lldb_private::FileSpec g_developer_directory; |
371 | static llvm::once_flag g_once_flag; |
372 | llvm::call_once(flag&: g_once_flag, F: []() { |
373 | if (FileSpec fspec = GetXcodeContentsDirectory()) { |
374 | fspec.AppendPathComponent(component: "Developer" ); |
375 | if (FileSystem::Instance().Exists(file_spec: fspec)) |
376 | g_developer_directory = fspec; |
377 | } |
378 | }); |
379 | return g_developer_directory; |
380 | } |
381 | |
382 | static llvm::Expected<std::string> |
383 | xcrun(const std::string &sdk, llvm::ArrayRef<llvm::StringRef> arguments, |
384 | llvm::StringRef developer_dir = "" ) { |
385 | Args args; |
386 | if (!developer_dir.empty()) { |
387 | args.AppendArgument(arg_str: "/usr/bin/env" ); |
388 | args.AppendArgument(arg_str: "DEVELOPER_DIR=" + developer_dir.str()); |
389 | } |
390 | args.AppendArgument(arg_str: "/usr/bin/xcrun" ); |
391 | args.AppendArgument(arg_str: "--sdk" ); |
392 | args.AppendArgument(arg_str: sdk); |
393 | for (auto arg: arguments) |
394 | args.AppendArgument(arg_str: arg); |
395 | |
396 | Log *log = GetLog(mask: LLDBLog::Host); |
397 | if (log) { |
398 | std::string cmdstr; |
399 | args.GetCommandString(command&: cmdstr); |
400 | LLDB_LOG(log, "GetXcodeSDK() running shell cmd '{0}'" , cmdstr); |
401 | } |
402 | |
403 | int status = 0; |
404 | int signo = 0; |
405 | std::string output_str; |
406 | // The first time after Xcode was updated or freshly installed, |
407 | // xcrun can take surprisingly long to build up its database. |
408 | auto timeout = std::chrono::seconds(60); |
409 | bool run_in_shell = false; |
410 | lldb_private::Status error = Host::RunShellCommand( |
411 | args, working_dir: FileSpec(), status_ptr: &status, signo_ptr: &signo, command_output: &output_str, timeout, run_in_shell); |
412 | |
413 | // Check that xcrun returned something useful. |
414 | if (error.Fail()) { |
415 | // Catastrophic error. |
416 | LLDB_LOG(log, "xcrun failed to execute: {0}" , error); |
417 | return error.ToError(); |
418 | } |
419 | if (status != 0) { |
420 | // xcrun didn't find a matching SDK. Not an error, we'll try |
421 | // different spellings. |
422 | LLDB_LOG(log, "xcrun returned exit code {0}" , status); |
423 | if (!output_str.empty()) |
424 | LLDB_LOG(log, "xcrun output was:\n{0}" , output_str); |
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(C: '\n'); |
441 | if (last_newline != llvm::StringRef::npos) |
442 | output = output.substr(Start: last_newline + 1); |
443 | |
444 | return output.str(); |
445 | } |
446 | |
447 | static llvm::Expected<std::string> GetXcodeSDK(XcodeSDK sdk) { |
448 | XcodeSDK::Info info = sdk.Parse(); |
449 | std::string sdk_name = XcodeSDK::GetCanonicalName(info); |
450 | if (sdk_name.empty()) |
451 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
452 | S: "Unrecognized SDK type: " + sdk.GetString()); |
453 | |
454 | Log *log = GetLog(mask: LLDBLog::Host); |
455 | |
456 | auto find_sdk = |
457 | [](const std::string &sdk_name) -> llvm::Expected<std::string> { |
458 | llvm::SmallVector<llvm::StringRef, 1> show_sdk_path = {"--show-sdk-path" }; |
459 | // Invoke xcrun with the developer dir specified in the environment. |
460 | std::string developer_dir = GetEnvDeveloperDir(); |
461 | if (!developer_dir.empty()) { |
462 | // Don't fallback if DEVELOPER_DIR was set. |
463 | return xcrun(sdk: sdk_name, arguments: show_sdk_path, developer_dir); |
464 | } |
465 | |
466 | // Invoke xcrun with the shlib dir. |
467 | if (FileSpec fspec = HostInfo::GetShlibDir()) { |
468 | if (FileSystem::Instance().Exists(file_spec: fspec)) { |
469 | llvm::SmallString<0> shlib_developer_dir( |
470 | XcodeSDK::FindXcodeContentsDirectoryInPath(path: fspec.GetPath())); |
471 | llvm::sys::path::append(path&: shlib_developer_dir, a: "Developer" ); |
472 | if (FileSystem::Instance().Exists(path: shlib_developer_dir)) { |
473 | auto sdk = xcrun(sdk: sdk_name, arguments: show_sdk_path, developer_dir: shlib_developer_dir); |
474 | if (!sdk) |
475 | return sdk.takeError(); |
476 | if (!sdk->empty()) |
477 | return sdk; |
478 | } |
479 | } |
480 | } |
481 | |
482 | // Invoke xcrun without a developer dir as a last resort. |
483 | return xcrun(sdk: sdk_name, arguments: show_sdk_path); |
484 | }; |
485 | |
486 | auto path_or_err = find_sdk(sdk_name); |
487 | if (!path_or_err) |
488 | return path_or_err.takeError(); |
489 | std::string path = *path_or_err; |
490 | while (path.empty()) { |
491 | // Try an alternate spelling of the name ("macosx10.9internal"). |
492 | if (info.type == XcodeSDK::Type::MacOSX && !info.version.empty() && |
493 | info.internal) { |
494 | llvm::StringRef fixed(sdk_name); |
495 | if (fixed.consume_back(Suffix: ".internal" )) |
496 | sdk_name = fixed.str() + "internal" ; |
497 | path_or_err = find_sdk(sdk_name); |
498 | if (!path_or_err) |
499 | return path_or_err.takeError(); |
500 | path = *path_or_err; |
501 | if (!path.empty()) |
502 | break; |
503 | } |
504 | LLDB_LOG(log, "Couldn't find SDK {0} on host" , sdk_name); |
505 | |
506 | // Try without the version. |
507 | if (!info.version.empty()) { |
508 | info.version = {}; |
509 | sdk_name = XcodeSDK::GetCanonicalName(info); |
510 | path_or_err = find_sdk(sdk_name); |
511 | if (!path_or_err) |
512 | return path_or_err.takeError(); |
513 | path = *path_or_err; |
514 | if (!path.empty()) |
515 | break; |
516 | } |
517 | |
518 | LLDB_LOG(log, "Couldn't find any matching SDK on host" ); |
519 | return "" ; |
520 | } |
521 | |
522 | // Whatever is left in output should be a valid path. |
523 | if (!FileSystem::Instance().Exists(path)) { |
524 | LLDB_LOG(log, "SDK returned by xcrun doesn't exist" ); |
525 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
526 | Msg: "SDK returned by xcrun doesn't exist" ); |
527 | } |
528 | return path; |
529 | } |
530 | |
531 | namespace { |
532 | struct ErrorOrPath { |
533 | std::string str; |
534 | bool is_error; |
535 | }; |
536 | } // namespace |
537 | |
538 | static llvm::Expected<llvm::StringRef> |
539 | find_cached_path(llvm::StringMap<ErrorOrPath> &cache, std::mutex &mutex, |
540 | llvm::StringRef key, |
541 | std::function<llvm::Expected<std::string>(void)> compute) { |
542 | std::lock_guard<std::mutex> guard(mutex); |
543 | LLDB_SCOPED_TIMER(); |
544 | |
545 | auto it = cache.find(Key: key); |
546 | if (it != cache.end()) { |
547 | if (it->second.is_error) |
548 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
549 | S: it->second.str); |
550 | return it->second.str; |
551 | } |
552 | auto path_or_err = compute(); |
553 | if (!path_or_err) { |
554 | std::string error = toString(E: path_or_err.takeError()); |
555 | cache.insert(KV: {key, {.str: error, .is_error: true}}); |
556 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), S: error); |
557 | } |
558 | auto it_new = cache.insert(KV: {key, {.str: *path_or_err, .is_error: false}}); |
559 | return it_new.first->second.str; |
560 | } |
561 | |
562 | llvm::Expected<llvm::StringRef> HostInfoMacOSX::GetSDKRoot(SDKOptions options) { |
563 | static llvm::StringMap<ErrorOrPath> g_sdk_path; |
564 | static std::mutex g_sdk_path_mutex; |
565 | if (!options.XcodeSDKSelection) |
566 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
567 | Msg: "XcodeSDK not specified" ); |
568 | XcodeSDK sdk = *options.XcodeSDKSelection; |
569 | auto key = sdk.GetString(); |
570 | return find_cached_path(cache&: g_sdk_path, mutex&: g_sdk_path_mutex, key, compute: [&](){ |
571 | return GetXcodeSDK(sdk); |
572 | }); |
573 | } |
574 | |
575 | llvm::Expected<llvm::StringRef> |
576 | HostInfoMacOSX::FindSDKTool(XcodeSDK sdk, llvm::StringRef tool) { |
577 | static llvm::StringMap<ErrorOrPath> g_tool_path; |
578 | static std::mutex g_tool_path_mutex; |
579 | std::string key; |
580 | llvm::raw_string_ostream(key) << sdk.GetString() << ":" << tool; |
581 | return find_cached_path( |
582 | cache&: g_tool_path, mutex&: g_tool_path_mutex, key, |
583 | compute: [&]() -> llvm::Expected<std::string> { |
584 | std::string sdk_name = XcodeSDK::GetCanonicalName(info: sdk.Parse()); |
585 | if (sdk_name.empty()) |
586 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
587 | S: "Unrecognized SDK type: " + |
588 | sdk.GetString()); |
589 | llvm::SmallVector<llvm::StringRef, 2> find = {"-find" , tool}; |
590 | return xcrun(sdk: sdk_name, arguments: find); |
591 | }); |
592 | } |
593 | |
594 | namespace { |
595 | struct dyld_shared_cache_dylib_text_info { |
596 | uint64_t version; // current version 1 |
597 | // following fields all exist in version 1 |
598 | uint64_t loadAddressUnslid; |
599 | uint64_t textSegmentSize; |
600 | uuid_t dylibUuid; |
601 | const char *path; // pointer invalid at end of iterations |
602 | // following fields all exist in version 2 |
603 | uint64_t textSegmentOffset; // offset from start of cache |
604 | }; |
605 | typedef struct dyld_shared_cache_dylib_text_info |
606 | dyld_shared_cache_dylib_text_info; |
607 | } |
608 | |
609 | extern "C" int dyld_shared_cache_iterate_text( |
610 | const uuid_t cacheUuid, |
611 | void (^callback)(const dyld_shared_cache_dylib_text_info *info)); |
612 | extern "C" uint8_t *_dyld_get_shared_cache_range(size_t *length); |
613 | extern "C" bool _dyld_get_shared_cache_uuid(uuid_t uuid); |
614 | |
615 | namespace { |
616 | class SharedCacheInfo { |
617 | public: |
618 | const UUID &GetUUID() const { return m_uuid; } |
619 | const llvm::StringMap<SharedCacheImageInfo> &GetImages() const { |
620 | return m_images; |
621 | } |
622 | |
623 | SharedCacheInfo(); |
624 | |
625 | private: |
626 | bool CreateSharedCacheInfoWithInstrospectionSPIs(); |
627 | |
628 | llvm::StringMap<SharedCacheImageInfo> m_images; |
629 | UUID m_uuid; |
630 | }; |
631 | } |
632 | |
633 | bool SharedCacheInfo::CreateSharedCacheInfoWithInstrospectionSPIs() { |
634 | #if defined(SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS) |
635 | dyld_process_t dyld_process = dyld_process_create_for_current_task(); |
636 | if (!dyld_process) |
637 | return false; |
638 | |
639 | dyld_process_snapshot_t snapshot = |
640 | dyld_process_snapshot_create_for_process(dyld_process, nullptr); |
641 | if (!snapshot) |
642 | return false; |
643 | |
644 | auto on_exit = |
645 | llvm::make_scope_exit([&]() { dyld_process_snapshot_dispose(snapshot); }); |
646 | |
647 | dyld_shared_cache_t shared_cache = |
648 | dyld_process_snapshot_get_shared_cache(snapshot); |
649 | if (!shared_cache) |
650 | return false; |
651 | |
652 | dyld_shared_cache_for_each_image(shared_cache, ^(dyld_image_t image) { |
653 | __block uint64_t minVmAddr = UINT64_MAX; |
654 | __block uint64_t maxVmAddr = 0; |
655 | uuid_t uuidStore; |
656 | __block uuid_t *uuid = &uuidStore; |
657 | |
658 | dyld_image_for_each_segment_info( |
659 | image, |
660 | ^(const char *segmentName, uint64_t vmAddr, uint64_t vmSize, int perm) { |
661 | minVmAddr = std::min(minVmAddr, vmAddr); |
662 | maxVmAddr = std::max(maxVmAddr, vmAddr + vmSize); |
663 | dyld_image_copy_uuid(image, uuid); |
664 | }); |
665 | assert(minVmAddr != UINT_MAX); |
666 | assert(maxVmAddr != 0); |
667 | m_images[dyld_image_get_installname(image)] = SharedCacheImageInfo{ |
668 | UUID(uuid, 16), std::make_shared<DataBufferUnowned>( |
669 | (uint8_t *)minVmAddr, maxVmAddr - minVmAddr)}; |
670 | }); |
671 | return true; |
672 | #endif |
673 | return false; |
674 | } |
675 | |
676 | SharedCacheInfo::SharedCacheInfo() { |
677 | if (CreateSharedCacheInfoWithInstrospectionSPIs()) |
678 | return; |
679 | |
680 | size_t shared_cache_size; |
681 | uint8_t *shared_cache_start = |
682 | _dyld_get_shared_cache_range(length: &shared_cache_size); |
683 | uuid_t dsc_uuid; |
684 | _dyld_get_shared_cache_uuid(uuid: dsc_uuid); |
685 | m_uuid = UUID(dsc_uuid); |
686 | |
687 | dyld_shared_cache_iterate_text( |
688 | cacheUuid: dsc_uuid, callback: ^(const dyld_shared_cache_dylib_text_info *info) { |
689 | m_images[info->path] = SharedCacheImageInfo{ |
690 | .uuid: UUID(info->dylibUuid, 16), |
691 | .data_sp: std::make_shared<DataBufferUnowned>( |
692 | args: shared_cache_start + info->textSegmentOffset, |
693 | args: shared_cache_size - info->textSegmentOffset)}; |
694 | }); |
695 | } |
696 | |
697 | SharedCacheImageInfo |
698 | HostInfoMacOSX::GetSharedCacheImageInfo(llvm::StringRef image_name) { |
699 | static SharedCacheInfo g_shared_cache_info; |
700 | return g_shared_cache_info.GetImages().lookup(Key: image_name); |
701 | } |
702 | |