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