1//===-- PlatformDarwinDevice.cpp ------------------------------------------===//
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 "PlatformDarwinDevice.h"
10#include "lldb/Core/Module.h"
11#include "lldb/Core/ModuleList.h"
12#include "lldb/Core/ModuleSpec.h"
13#include "lldb/Host/HostInfo.h"
14#include "lldb/Utility/FileSpec.h"
15#include "lldb/Utility/LLDBLog.h"
16#include "lldb/Utility/Log.h"
17#include <optional>
18
19using namespace lldb;
20using namespace lldb_private;
21
22PlatformDarwinDevice::~PlatformDarwinDevice() = default;
23
24FileSystem::EnumerateDirectoryResult
25PlatformDarwinDevice::GetContainedFilesIntoVectorOfStringsCallback(
26 void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
27 ((PlatformDarwinDevice::SDKDirectoryInfoCollection *)baton)
28 ->push_back(x: PlatformDarwinDevice::SDKDirectoryInfo(FileSpec(path)));
29 return FileSystem::eEnumerateDirectoryResultNext;
30}
31
32bool PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded() {
33 Log *log = GetLog(mask: LLDBLog::Host);
34 std::lock_guard<std::mutex> guard(m_sdk_dir_mutex);
35 if (m_sdk_directory_infos.empty()) {
36 // A --sysroot option was supplied - add it to our list of SDKs to check
37 if (!m_sdk_sysroot.empty()) {
38 FileSpec sdk_sysroot_fspec(m_sdk_sysroot.c_str());
39 FileSystem::Instance().Resolve(file_spec&: sdk_sysroot_fspec);
40 const SDKDirectoryInfo sdk_sysroot_directory_info(sdk_sysroot_fspec);
41 m_sdk_directory_infos.push_back(x: sdk_sysroot_directory_info);
42 if (log) {
43 LLDB_LOGF(log,
44 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded added "
45 "--sysroot SDK directory %s",
46 m_sdk_sysroot.c_str());
47 }
48 return true;
49 }
50 const char *device_support_dir = GetDeviceSupportDirectory();
51 if (log) {
52 LLDB_LOGF(log,
53 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded Got "
54 "DeviceSupport directory %s",
55 device_support_dir);
56 }
57 if (device_support_dir) {
58 const bool find_directories = true;
59 const bool find_files = false;
60 const bool find_other = false;
61
62 SDKDirectoryInfoCollection builtin_sdk_directory_infos;
63 FileSystem::Instance().EnumerateDirectory(
64 path: m_device_support_directory, find_directories, find_files, find_other,
65 callback: GetContainedFilesIntoVectorOfStringsCallback,
66 callback_baton: &builtin_sdk_directory_infos);
67
68 // Only add SDK directories that have symbols in them, some SDKs only
69 // contain developer disk images and no symbols, so they aren't useful to
70 // us.
71 FileSpec sdk_symbols_symlink_fspec;
72 for (const auto &sdk_directory_info : builtin_sdk_directory_infos) {
73 sdk_symbols_symlink_fspec = sdk_directory_info.directory;
74 sdk_symbols_symlink_fspec.AppendPathComponent(component: "Symbols");
75 if (FileSystem::Instance().Exists(file_spec: sdk_symbols_symlink_fspec)) {
76 m_sdk_directory_infos.push_back(x: sdk_directory_info);
77 if (log) {
78 LLDB_LOGF(log,
79 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
80 "added builtin SDK directory %s",
81 sdk_symbols_symlink_fspec.GetPath().c_str());
82 }
83 }
84 }
85
86 const uint32_t num_installed = m_sdk_directory_infos.size();
87 llvm::StringRef dirname = GetDeviceSupportDirectoryName();
88 std::string local_sdk_cache_str = "~/Library/Developer/Xcode/";
89 local_sdk_cache_str += std::string(dirname);
90 FileSpec local_sdk_cache(local_sdk_cache_str.c_str());
91 FileSystem::Instance().Resolve(file_spec&: local_sdk_cache);
92 if (FileSystem::Instance().Exists(file_spec: local_sdk_cache)) {
93 if (log) {
94 LLDB_LOGF(log,
95 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
96 "searching %s for additional SDKs",
97 local_sdk_cache.GetPath().c_str());
98 }
99 char path[PATH_MAX];
100 if (local_sdk_cache.GetPath(path, max_path_length: sizeof(path))) {
101 FileSystem::Instance().EnumerateDirectory(
102 path, find_directories, find_files, find_other,
103 callback: GetContainedFilesIntoVectorOfStringsCallback,
104 callback_baton: &m_sdk_directory_infos);
105 const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
106 // First try for an exact match of major, minor and update
107 for (uint32_t i = num_installed; i < num_sdk_infos; ++i) {
108 m_sdk_directory_infos[i].user_cached = true;
109 if (log) {
110 LLDB_LOGF(log,
111 "PlatformDarwinDevice::"
112 "UpdateSDKDirectoryInfosIfNeeded "
113 "user SDK directory %s",
114 m_sdk_directory_infos[i].directory.GetPath().c_str());
115 }
116 }
117 }
118 }
119
120 const char *addtional_platform_dirs = getenv(name: "PLATFORM_SDK_DIRECTORY");
121 if (addtional_platform_dirs) {
122 SDKDirectoryInfoCollection env_var_sdk_directory_infos;
123 FileSystem::Instance().EnumerateDirectory(
124 path: addtional_platform_dirs, find_directories, find_files, find_other,
125 callback: GetContainedFilesIntoVectorOfStringsCallback,
126 callback_baton: &env_var_sdk_directory_infos);
127 FileSpec sdk_symbols_symlink_fspec;
128 for (const auto &sdk_directory_info : env_var_sdk_directory_infos) {
129 sdk_symbols_symlink_fspec = sdk_directory_info.directory;
130 sdk_symbols_symlink_fspec.AppendPathComponent(component: "Symbols");
131 if (FileSystem::Instance().Exists(file_spec: sdk_symbols_symlink_fspec)) {
132 m_sdk_directory_infos.push_back(x: sdk_directory_info);
133 if (log) {
134 LLDB_LOGF(log,
135 "PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
136 "added env var SDK directory %s",
137 sdk_symbols_symlink_fspec.GetPath().c_str());
138 }
139 }
140 }
141 }
142 }
143 }
144 return !m_sdk_directory_infos.empty();
145}
146
147const PlatformDarwinDevice::SDKDirectoryInfo *
148PlatformDarwinDevice::GetSDKDirectoryForCurrentOSVersion() {
149 uint32_t i;
150 if (UpdateSDKDirectoryInfosIfNeeded()) {
151 const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
152 std::vector<bool> check_sdk_info(num_sdk_infos, true);
153
154 // Prefer the user SDK build string.
155 std::string build = GetSDKBuild();
156
157 // Fall back to the platform's build string.
158 if (build.empty()) {
159 if (std::optional<std::string> os_build_str = GetOSBuildString())
160 build.assign(str: *os_build_str);
161 }
162
163 // If we have a build string, only check platforms for which the build
164 // string matches.
165 if (!build.empty()) {
166 for (i = 0; i < num_sdk_infos; ++i)
167 check_sdk_info[i] = m_sdk_directory_infos[i].build.GetStringRef() ==
168 llvm::StringRef(build);
169 }
170
171 // If we are connected we can find the version of the OS the platform us
172 // running on and select the right SDK
173 llvm::VersionTuple version = GetOSVersion();
174 if (!version.empty()) {
175 if (UpdateSDKDirectoryInfosIfNeeded()) {
176 // First try for an exact match of major, minor and update.
177 for (i = 0; i < num_sdk_infos; ++i) {
178 if (check_sdk_info[i]) {
179 if (m_sdk_directory_infos[i].version == version)
180 return &m_sdk_directory_infos[i];
181 }
182 }
183 // Try for an exact match of major and minor.
184 for (i = 0; i < num_sdk_infos; ++i) {
185 if (check_sdk_info[i]) {
186 if (m_sdk_directory_infos[i].version.getMajor() ==
187 version.getMajor() &&
188 m_sdk_directory_infos[i].version.getMinor() ==
189 version.getMinor()) {
190 return &m_sdk_directory_infos[i];
191 }
192 }
193 }
194 // Lastly try to match of major version only.
195 for (i = 0; i < num_sdk_infos; ++i) {
196 if (check_sdk_info[i]) {
197 if (m_sdk_directory_infos[i].version.getMajor() ==
198 version.getMajor()) {
199 return &m_sdk_directory_infos[i];
200 }
201 }
202 }
203 }
204 } else if (!build.empty()) {
205 // No version, just a build number, return the first one that matches.
206 for (i = 0; i < num_sdk_infos; ++i)
207 if (check_sdk_info[i])
208 return &m_sdk_directory_infos[i];
209 }
210 }
211 return nullptr;
212}
213
214const PlatformDarwinDevice::SDKDirectoryInfo *
215PlatformDarwinDevice::GetSDKDirectoryForLatestOSVersion() {
216 const PlatformDarwinDevice::SDKDirectoryInfo *result = nullptr;
217 if (UpdateSDKDirectoryInfosIfNeeded()) {
218 auto max = std::max_element(
219 first: m_sdk_directory_infos.begin(), last: m_sdk_directory_infos.end(),
220 comp: [](const SDKDirectoryInfo &a, const SDKDirectoryInfo &b) {
221 return a.version < b.version;
222 });
223 if (max != m_sdk_directory_infos.end())
224 result = &*max;
225 }
226 return result;
227}
228
229const char *PlatformDarwinDevice::GetDeviceSupportDirectory() {
230 std::string platform_dir =
231 ("/Platforms/" + GetPlatformName() + "/DeviceSupport").str();
232 if (m_device_support_directory.empty()) {
233 if (FileSpec fspec = HostInfo::GetXcodeDeveloperDirectory()) {
234 m_device_support_directory = fspec.GetPath();
235 m_device_support_directory.append(s: platform_dir.c_str());
236 } else {
237 // Assign a single NULL character so we know we tried to find the device
238 // support directory and we don't keep trying to find it over and over.
239 m_device_support_directory.assign(n: 1, c: '\0');
240 }
241 }
242 // We should have put a single NULL character into m_device_support_directory
243 // or it should have a valid path if the code gets here
244 assert(m_device_support_directory.empty() == false);
245 if (m_device_support_directory[0])
246 return m_device_support_directory.c_str();
247 return nullptr;
248}
249
250const char *PlatformDarwinDevice::GetDeviceSupportDirectoryForOSVersion() {
251 if (!m_sdk_sysroot.empty())
252 return m_sdk_sysroot.c_str();
253
254 if (m_device_support_directory_for_os_version.empty()) {
255 const PlatformDarwinDevice::SDKDirectoryInfo *sdk_dir_info =
256 GetSDKDirectoryForCurrentOSVersion();
257 if (sdk_dir_info == nullptr)
258 sdk_dir_info = GetSDKDirectoryForLatestOSVersion();
259 if (sdk_dir_info) {
260 char path[PATH_MAX];
261 if (sdk_dir_info->directory.GetPath(path, max_path_length: sizeof(path))) {
262 m_device_support_directory_for_os_version = path;
263 return m_device_support_directory_for_os_version.c_str();
264 }
265 } else {
266 // Assign a single NULL character so we know we tried to find the device
267 // support directory and we don't keep trying to find it over and over.
268 m_device_support_directory_for_os_version.assign(n: 1, c: '\0');
269 }
270 }
271 // We should have put a single NULL character into
272 // m_device_support_directory_for_os_version or it should have a valid path
273 // if the code gets here
274 assert(m_device_support_directory_for_os_version.empty() == false);
275 if (m_device_support_directory_for_os_version[0])
276 return m_device_support_directory_for_os_version.c_str();
277 return nullptr;
278}
279
280static lldb_private::Status
281MakeCacheFolderForFile(const FileSpec &module_cache_spec) {
282 FileSpec module_cache_folder =
283 module_cache_spec.CopyByRemovingLastPathComponent();
284 return llvm::sys::fs::create_directory(path: module_cache_folder.GetPath());
285}
286
287static lldb_private::Status
288BringInRemoteFile(Platform *platform,
289 const lldb_private::ModuleSpec &module_spec,
290 const FileSpec &module_cache_spec) {
291 MakeCacheFolderForFile(module_cache_spec);
292 Status err = platform->GetFile(source: module_spec.GetFileSpec(), destination: module_cache_spec);
293 return err;
294}
295
296lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache(
297 const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp,
298 const lldb_private::FileSpecList *module_search_paths_ptr,
299 llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) {
300
301 Log *log = GetLog(mask: LLDBLog::Platform);
302 LLDB_LOGF(log,
303 "[%s] Trying to find module %s/%s - platform path %s/%s symbol "
304 "path %s/%s",
305 (IsHost() ? "host" : "remote"),
306 module_spec.GetFileSpec().GetDirectory().AsCString(),
307 module_spec.GetFileSpec().GetFilename().AsCString(),
308 module_spec.GetPlatformFileSpec().GetDirectory().AsCString(),
309 module_spec.GetPlatformFileSpec().GetFilename().AsCString(),
310 module_spec.GetSymbolFileSpec().GetDirectory().AsCString(),
311 module_spec.GetSymbolFileSpec().GetFilename().AsCString());
312
313 Status err;
314
315 if (CheckLocalSharedCache()) {
316 // When debugging on the host, we are most likely using the same shared
317 // cache as our inferior. The dylibs from the shared cache might not
318 // exist on the filesystem, so let's use the images in our own memory
319 // to create the modules.
320
321 // Check if the requested image is in our shared cache.
322 SharedCacheImageInfo image_info =
323 HostInfo::GetSharedCacheImageInfo(image_name: module_spec.GetFileSpec().GetPath());
324
325 // If we found it and it has the correct UUID, let's proceed with
326 // creating a module from the memory contents.
327 if (image_info.uuid &&
328 (!module_spec.GetUUID() || module_spec.GetUUID() == image_info.uuid)) {
329 ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid,
330 image_info.data_sp);
331 err = ModuleList::GetSharedModule(module_spec: shared_cache_spec, module_sp,
332 module_search_paths_ptr, old_modules,
333 did_create_ptr);
334 if (module_sp) {
335 LLDB_LOGF(log, "[%s] module %s was found in the in-memory shared cache",
336 (IsHost() ? "host" : "remote"),
337 module_spec.GetFileSpec().GetPath().c_str());
338 return err;
339 }
340 }
341
342 // We failed to find the module in our shared cache. Let's see if we have a
343 // copy in our device support directory.
344 FileSpec device_support_spec(GetDeviceSupportDirectoryForOSVersion());
345 device_support_spec.AppendPathComponent(component: "Symbols");
346 device_support_spec.AppendPathComponent(
347 component: module_spec.GetFileSpec().GetPath());
348 FileSystem::Instance().Resolve(file_spec&: device_support_spec);
349 if (FileSystem::Instance().Exists(file_spec: device_support_spec)) {
350 ModuleSpec local_spec(device_support_spec, module_spec.GetUUID());
351 err = ModuleList::GetSharedModule(module_spec: local_spec, module_sp,
352 module_search_paths_ptr, old_modules,
353 did_create_ptr);
354 if (module_sp) {
355 LLDB_LOGF(log,
356 "[%s] module %s was found in Device Support "
357 "directory: %s",
358 (IsHost() ? "host" : "remote"),
359 module_spec.GetFileSpec().GetPath().c_str(),
360 local_spec.GetFileSpec().GetPath().c_str());
361 return err;
362 }
363 }
364 }
365
366 err = ModuleList::GetSharedModule(module_spec, module_sp,
367 module_search_paths_ptr, old_modules,
368 did_create_ptr);
369 if (module_sp)
370 return err;
371
372 if (!IsHost()) {
373 std::string cache_path(GetLocalCacheDirectory());
374 // Only search for a locally cached file if we have a valid cache path
375 if (!cache_path.empty()) {
376 std::string module_path(module_spec.GetFileSpec().GetPath());
377 cache_path.append(str: module_path);
378 FileSpec module_cache_spec(cache_path);
379
380 // if rsync is supported, always bring in the file - rsync will be very
381 // efficient when files are the same on the local and remote end of the
382 // connection
383 if (this->GetSupportsRSync()) {
384 err = BringInRemoteFile(platform: this, module_spec, module_cache_spec);
385 if (err.Fail())
386 return err;
387 if (FileSystem::Instance().Exists(file_spec: module_cache_spec)) {
388 Log *log = GetLog(mask: LLDBLog::Platform);
389 LLDB_LOGF(log, "[%s] module %s/%s was rsynced and is now there",
390 (IsHost() ? "host" : "remote"),
391 module_spec.GetFileSpec().GetDirectory().AsCString(),
392 module_spec.GetFileSpec().GetFilename().AsCString());
393 ModuleSpec local_spec(module_cache_spec,
394 module_spec.GetArchitecture());
395 module_sp = std::make_shared<Module>(args&: local_spec);
396 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
397 return Status();
398 }
399 }
400
401 // try to find the module in the cache
402 if (FileSystem::Instance().Exists(file_spec: module_cache_spec)) {
403 // get the local and remote MD5 and compare
404 if (m_remote_platform_sp) {
405 // when going over the *slow* GDB remote transfer mechanism we first
406 // check the hashes of the files - and only do the actual transfer if
407 // they differ
408 uint64_t high_local, high_remote, low_local, low_remote;
409 auto MD5 = llvm::sys::fs::md5_contents(Path: module_cache_spec.GetPath());
410 if (!MD5)
411 return Status(MD5.getError());
412 std::tie(args&: high_local, args&: low_local) = MD5->words();
413
414 m_remote_platform_sp->CalculateMD5(file_spec: module_spec.GetFileSpec(),
415 low&: low_remote, high&: high_remote);
416 if (low_local != low_remote || high_local != high_remote) {
417 // bring in the remote file
418 Log *log = GetLog(mask: LLDBLog::Platform);
419 LLDB_LOGF(log,
420 "[%s] module %s/%s needs to be replaced from remote copy",
421 (IsHost() ? "host" : "remote"),
422 module_spec.GetFileSpec().GetDirectory().AsCString(),
423 module_spec.GetFileSpec().GetFilename().AsCString());
424 Status err =
425 BringInRemoteFile(platform: this, module_spec, module_cache_spec);
426 if (err.Fail())
427 return err;
428 }
429 }
430
431 ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture());
432 module_sp = std::make_shared<Module>(args&: local_spec);
433 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
434 Log *log = GetLog(mask: LLDBLog::Platform);
435 LLDB_LOGF(log, "[%s] module %s/%s was found in the cache",
436 (IsHost() ? "host" : "remote"),
437 module_spec.GetFileSpec().GetDirectory().AsCString(),
438 module_spec.GetFileSpec().GetFilename().AsCString());
439 return Status();
440 }
441
442 // bring in the remote module file
443 LLDB_LOGF(log, "[%s] module %s/%s needs to come in remotely",
444 (IsHost() ? "host" : "remote"),
445 module_spec.GetFileSpec().GetDirectory().AsCString(),
446 module_spec.GetFileSpec().GetFilename().AsCString());
447 Status err = BringInRemoteFile(platform: this, module_spec, module_cache_spec);
448 if (err.Fail())
449 return err;
450 if (FileSystem::Instance().Exists(file_spec: module_cache_spec)) {
451 Log *log = GetLog(mask: LLDBLog::Platform);
452 LLDB_LOGF(log, "[%s] module %s/%s is now cached and fine",
453 (IsHost() ? "host" : "remote"),
454 module_spec.GetFileSpec().GetDirectory().AsCString(),
455 module_spec.GetFileSpec().GetFilename().AsCString());
456 ModuleSpec local_spec(module_cache_spec, module_spec.GetArchitecture());
457 module_sp = std::make_shared<Module>(args&: local_spec);
458 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
459 return Status();
460 } else
461 return Status("unable to obtain valid module file");
462 } else
463 return Status("no cache path");
464 } else
465 return Status("unable to resolve module");
466}
467

source code of lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.cpp