1 //===-- PlatformRemoteiOS.cpp -----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "PlatformRemoteiOS.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Breakpoint/BreakpointLocation.h"
17 #include "lldb/Core/ArchSpec.h"
18 #include "lldb/Core/Error.h"
19 #include "lldb/Core/Module.h"
20 #include "lldb/Core/ModuleList.h"
21 #include "lldb/Core/PluginManager.h"
22 #include "lldb/Core/StreamString.h"
23 #include "lldb/Host/FileSpec.h"
24 #include "lldb/Host/Host.h"
25 #include "lldb/Target/Process.h"
26 #include "lldb/Target/Target.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 PlatformRemoteiOS::SDKDirectoryInfo::SDKDirectoryInfo (const lldb_private::FileSpec &sdk_dir) :
32     directory(sdk_dir),
33     build(),
34     version_major(0),
35     version_minor(0),
36     version_update(0),
37     user_cached(false)
38 {
39     const char *dirname_cstr = sdk_dir.GetFilename().GetCString();
40     const char *pos = Args::StringToVersion (dirname_cstr,
41                                              version_major,
42                                              version_minor,
43                                              version_update);
44 
45     if (pos && pos[0] == ' ' && pos[1] == '(')
46     {
47         const char *build_start = pos + 2;
48         const char *end_paren = strchr (build_start, ')');
49         if (end_paren && build_start < end_paren)
50             build.SetCStringWithLength(build_start, end_paren - build_start);
51     }
52 }
53 
54 //------------------------------------------------------------------
55 // Static Variables
56 //------------------------------------------------------------------
57 static uint32_t g_initialize_count = 0;
58 
59 //------------------------------------------------------------------
60 // Static Functions
61 //------------------------------------------------------------------
62 void
63 PlatformRemoteiOS::Initialize ()
64 {
65     if (g_initialize_count++ == 0)
66     {
67         PluginManager::RegisterPlugin (PlatformRemoteiOS::GetShortPluginNameStatic(),
68                                        PlatformRemoteiOS::GetDescriptionStatic(),
69                                        PlatformRemoteiOS::CreateInstance);
70     }
71 }
72 
73 void
74 PlatformRemoteiOS::Terminate ()
75 {
76     if (g_initialize_count > 0)
77     {
78         if (--g_initialize_count == 0)
79         {
80             PluginManager::UnregisterPlugin (PlatformRemoteiOS::CreateInstance);
81         }
82     }
83 }
84 
85 Platform*
86 PlatformRemoteiOS::CreateInstance (bool force, const ArchSpec *arch)
87 {
88     bool create = force;
89     if (create == false && arch && arch->IsValid())
90     {
91         switch (arch->GetMachine())
92         {
93         case llvm::Triple::arm:
94         case llvm::Triple::thumb:
95             {
96                 const llvm::Triple &triple = arch->GetTriple();
97                 const llvm::Triple::OSType os = triple.getOS();
98                 const llvm::Triple::VendorType vendor = triple.getVendor();
99                 if (os == llvm::Triple::Darwin && vendor == llvm::Triple::Apple)
100                     create = true;
101             }
102             break;
103         default:
104             break;
105         }
106     }
107 
108     if (create)
109         return new PlatformRemoteiOS ();
110     return NULL;
111 }
112 
113 
114 const char *
115 PlatformRemoteiOS::GetPluginNameStatic ()
116 {
117     return "PlatformRemoteiOS";
118 }
119 
120 const char *
121 PlatformRemoteiOS::GetShortPluginNameStatic()
122 {
123     return "remote-ios";
124 }
125 
126 const char *
127 PlatformRemoteiOS::GetDescriptionStatic()
128 {
129     return "Remote iOS platform plug-in.";
130 }
131 
132 
133 //------------------------------------------------------------------
134 /// Default Constructor
135 //------------------------------------------------------------------
136 PlatformRemoteiOS::PlatformRemoteiOS () :
137     PlatformDarwin (false),    // This is a remote platform
138     m_device_support_directory_for_os_version ()
139 {
140 }
141 
142 //------------------------------------------------------------------
143 /// Destructor.
144 ///
145 /// The destructor is virtual since this class is designed to be
146 /// inherited from by the plug-in instance.
147 //------------------------------------------------------------------
148 PlatformRemoteiOS::~PlatformRemoteiOS()
149 {
150 }
151 
152 
153 void
154 PlatformRemoteiOS::GetStatus (Stream &strm)
155 {
156     Platform::GetStatus (strm);
157     const char *sdk_directory = GetDeviceSupportDirectoryForOSVersion();
158     if (sdk_directory)
159         strm.Printf ("  SDK Path: \"%s\"\n", sdk_directory);
160     else
161         strm.PutCString ("  SDK Path: error: unable to locate SDK\n");
162 
163 //    const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
164 //    for (uint32_t i=0; i<num_sdk_infos; ++i)
165 //    {
166 //        const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i];
167 //        strm.Printf (" SDK Roots: [%2u] \"%s/%s\"\n",
168 //                     i,
169 //                     sdk_dir_info.directory.GetDirectory().GetCString(),
170 //                     sdk_dir_info.directory.GetFilename().GetCString());
171 //    }
172 }
173 
174 
175 Error
176 PlatformRemoteiOS::ResolveExecutable (const FileSpec &exe_file,
177                                       const ArchSpec &exe_arch,
178                                       lldb::ModuleSP &exe_module_sp,
179                                       const FileSpecList *module_search_paths_ptr)
180 {
181     Error error;
182     // Nothing special to do here, just use the actual file and architecture
183 
184     FileSpec resolved_exe_file (exe_file);
185 
186     // If we have "ls" as the exe_file, resolve the executable loation based on
187     // the current path variables
188     // TODO: resolve bare executables in the Platform SDK
189 //    if (!resolved_exe_file.Exists())
190 //        resolved_exe_file.ResolveExecutableLocation ();
191 
192     // Resolve any executable within a bundle on MacOSX
193     // TODO: verify that this handles shallow bundles, if not then implement one ourselves
194     Host::ResolveExecutableInBundle (resolved_exe_file);
195 
196     if (resolved_exe_file.Exists())
197     {
198         if (exe_arch.IsValid())
199         {
200             ModuleSpec module_spec (resolved_exe_file, exe_arch);
201             error = ModuleList::GetSharedModule (module_spec,
202                                                  exe_module_sp,
203                                                  NULL,
204                                                  NULL,
205                                                  NULL);
206 
207             if (exe_module_sp && exe_module_sp->GetObjectFile())
208                 return error;
209             exe_module_sp.reset();
210         }
211         // No valid architecture was specified or the exact ARM slice wasn't
212         // found so ask the platform for the architectures that we should be
213         // using (in the correct order) and see if we can find a match that way
214         StreamString arch_names;
215         ArchSpec platform_arch;
216         for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, platform_arch); ++idx)
217         {
218             ModuleSpec module_spec (resolved_exe_file, platform_arch);
219             error = ModuleList::GetSharedModule (module_spec,
220                                                  exe_module_sp,
221                                                  NULL,
222                                                  NULL,
223                                                  NULL);
224             // Did we find an executable using one of the
225             if (error.Success())
226             {
227                 if (exe_module_sp && exe_module_sp->GetObjectFile())
228                     break;
229                 else
230                     error.SetErrorToGenericError();
231             }
232 
233             if (idx > 0)
234                 arch_names.PutCString (", ");
235             arch_names.PutCString (platform_arch.GetArchitectureName());
236         }
237 
238         if (error.Fail() || !exe_module_sp)
239         {
240             error.SetErrorStringWithFormat ("'%s%s%s' doesn't contain any '%s' platform architectures: %s",
241                                             exe_file.GetDirectory().AsCString(""),
242                                             exe_file.GetDirectory() ? "/" : "",
243                                             exe_file.GetFilename().AsCString(""),
244                                             GetShortPluginName(),
245                                             arch_names.GetString().c_str());
246         }
247     }
248     else
249     {
250         error.SetErrorStringWithFormat ("'%s%s%s' does not exist",
251                                         exe_file.GetDirectory().AsCString(""),
252                                         exe_file.GetDirectory() ? "/" : "",
253                                         exe_file.GetFilename().AsCString(""));
254     }
255 
256     return error;
257 }
258 
259 FileSpec::EnumerateDirectoryResult
260 PlatformRemoteiOS::GetContainedFilesIntoVectorOfStringsCallback (void *baton,
261                                                                  FileSpec::FileType file_type,
262                                                                  const FileSpec &file_spec)
263 {
264     ((PlatformRemoteiOS::SDKDirectoryInfoCollection *)baton)->push_back(PlatformRemoteiOS::SDKDirectoryInfo(file_spec));
265     return FileSpec::eEnumerateDirectoryResultNext;
266 }
267 
268 bool
269 PlatformRemoteiOS::UpdateSDKDirectoryInfosInNeeded()
270 {
271     if (m_sdk_directory_infos.empty())
272     {
273         const char *device_support_dir = GetDeviceSupportDirectory();
274         if (device_support_dir)
275         {
276             const bool find_directories = true;
277             const bool find_files = false;
278             const bool find_other = false;
279             FileSpec::EnumerateDirectory (m_device_support_directory.c_str(),
280                                           find_directories,
281                                           find_files,
282                                           find_other,
283                                           GetContainedFilesIntoVectorOfStringsCallback,
284                                           &m_sdk_directory_infos);
285 
286             const uint32_t num_installed = m_sdk_directory_infos.size();
287             FileSpec local_sdk_cache("~/Library/Developer/Xcode/iOS DeviceSupport", true);
288             if (local_sdk_cache.Exists())
289             {
290                 char path[PATH_MAX];
291                 if (local_sdk_cache.GetPath(path, sizeof(path)))
292                 {
293                     FileSpec::EnumerateDirectory (path,
294                                                   find_directories,
295                                                   find_files,
296                                                   find_other,
297                                                   GetContainedFilesIntoVectorOfStringsCallback,
298                                                   &m_sdk_directory_infos);
299                     const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
300                     // First try for an exact match of major, minor and update
301                     for (uint32_t i=num_installed; i<num_sdk_infos; ++i)
302                     {
303                         m_sdk_directory_infos[i].user_cached = true;
304                     }
305                 }
306             }
307         }
308     }
309     return !m_sdk_directory_infos.empty();
310 }
311 
312 const PlatformRemoteiOS::SDKDirectoryInfo *
313 PlatformRemoteiOS::GetSDKDirectoryForCurrentOSVersion ()
314 {
315     uint32_t i;
316     if (UpdateSDKDirectoryInfosInNeeded())
317     {
318         const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
319 
320         // Check to see if the user specified a build string. If they did, then
321         // be sure to match it.
322         std::vector<bool> check_sdk_info(num_sdk_infos, true);
323         ConstString build(m_sdk_build);
324         if (build)
325         {
326             for (i=0; i<num_sdk_infos; ++i)
327                 check_sdk_info[i] = m_sdk_directory_infos[i].build == build;
328         }
329 
330         // If we are connected we can find the version of the OS the platform
331         // us running on and select the right SDK
332         uint32_t major, minor, update;
333         if (GetOSVersion(major, minor, update))
334         {
335             if (UpdateSDKDirectoryInfosInNeeded())
336             {
337                 // First try for an exact match of major, minor and update
338                 for (i=0; i<num_sdk_infos; ++i)
339                 {
340                     if (check_sdk_info[i])
341                     {
342                         if (m_sdk_directory_infos[i].version_major == major &&
343                             m_sdk_directory_infos[i].version_minor == minor &&
344                             m_sdk_directory_infos[i].version_update == update)
345                         {
346                             return &m_sdk_directory_infos[i];
347                         }
348                     }
349                 }
350                 // First try for an exact match of major and minor
351                 for (i=0; i<num_sdk_infos; ++i)
352                 {
353                     if (check_sdk_info[i])
354                     {
355                         if (m_sdk_directory_infos[i].version_major == major &&
356                             m_sdk_directory_infos[i].version_minor == minor)
357                         {
358                             return &m_sdk_directory_infos[i];
359                         }
360                     }
361                 }
362                 // Lastly try to match of major version only..
363                 for (i=0; i<num_sdk_infos; ++i)
364                 {
365                     if (check_sdk_info[i])
366                     {
367                         if (m_sdk_directory_infos[i].version_major == major)
368                         {
369                             return &m_sdk_directory_infos[i];
370                         }
371                     }
372                 }
373             }
374         }
375         else if (build)
376         {
377             // No version, just a build number, search for the first one that matches
378             for (i=0; i<num_sdk_infos; ++i)
379                 if (check_sdk_info[i])
380                     return &m_sdk_directory_infos[i];
381         }
382     }
383     return NULL;
384 }
385 
386 const PlatformRemoteiOS::SDKDirectoryInfo *
387 PlatformRemoteiOS::GetSDKDirectoryForLatestOSVersion ()
388 {
389     const PlatformRemoteiOS::SDKDirectoryInfo *result = NULL;
390     if (UpdateSDKDirectoryInfosInNeeded())
391     {
392         const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
393         // First try for an exact match of major, minor and update
394         for (uint32_t i=0; i<num_sdk_infos; ++i)
395         {
396             const SDKDirectoryInfo &sdk_dir_info = m_sdk_directory_infos[i];
397             if (sdk_dir_info.version_major != UINT32_MAX)
398             {
399                 if (result == NULL || sdk_dir_info.version_major > result->version_major)
400                 {
401                     result = &sdk_dir_info;
402                 }
403                 else if (sdk_dir_info.version_major == result->version_major)
404                 {
405                     if (sdk_dir_info.version_minor > result->version_minor)
406                     {
407                         result = &sdk_dir_info;
408                     }
409                     else if (sdk_dir_info.version_minor == result->version_minor)
410                     {
411                         if (sdk_dir_info.version_update > result->version_update)
412                         {
413                             result = &sdk_dir_info;
414                         }
415                     }
416                 }
417             }
418         }
419     }
420     return result;
421 }
422 
423 
424 
425 const char *
426 PlatformRemoteiOS::GetDeviceSupportDirectory()
427 {
428     if (m_device_support_directory.empty())
429     {
430         const char *device_support_dir = GetDeveloperDirectory();
431         if (device_support_dir)
432         {
433             m_device_support_directory.assign (device_support_dir);
434             m_device_support_directory.append ("/Platforms/iPhoneOS.platform/DeviceSupport");
435         }
436         else
437         {
438             // Assign a single NULL character so we know we tried to find the device
439             // support directory and we don't keep trying to find it over and over.
440             m_device_support_directory.assign (1, '\0');
441         }
442     }
443     // We should have put a single NULL character into m_device_support_directory
444     // or it should have a valid path if the code gets here
445     assert (m_device_support_directory.empty() == false);
446     if (m_device_support_directory[0])
447         return m_device_support_directory.c_str();
448     return NULL;
449 }
450 
451 
452 const char *
453 PlatformRemoteiOS::GetDeviceSupportDirectoryForOSVersion()
454 {
455     if (m_sdk_sysroot)
456         return m_sdk_sysroot.GetCString();
457 
458     if (m_device_support_directory_for_os_version.empty())
459     {
460         const PlatformRemoteiOS::SDKDirectoryInfo *sdk_dir_info = GetSDKDirectoryForCurrentOSVersion ();
461         if (sdk_dir_info == NULL)
462             sdk_dir_info = GetSDKDirectoryForLatestOSVersion ();
463         if (sdk_dir_info)
464         {
465             char path[PATH_MAX];
466             if (sdk_dir_info->directory.GetPath(path, sizeof(path)))
467             {
468                 m_device_support_directory_for_os_version = path;
469                 return m_device_support_directory_for_os_version.c_str();
470             }
471         }
472         else
473         {
474             // Assign a single NULL character so we know we tried to find the device
475             // support directory and we don't keep trying to find it over and over.
476             m_device_support_directory_for_os_version.assign (1, '\0');
477         }
478     }
479     // We should have put a single NULL character into m_device_support_directory_for_os_version
480     // or it should have a valid path if the code gets here
481     assert (m_device_support_directory_for_os_version.empty() == false);
482     if (m_device_support_directory_for_os_version[0])
483         return m_device_support_directory_for_os_version.c_str();
484     return NULL;
485 }
486 
487 Error
488 PlatformRemoteiOS::GetFile (const FileSpec &platform_file,
489                             const UUID *uuid_ptr,
490                             FileSpec &local_file)
491 {
492     Error error;
493     char platform_file_path[PATH_MAX];
494     if (platform_file.GetPath(platform_file_path, sizeof(platform_file_path)))
495     {
496         char resolved_path[PATH_MAX];
497 
498         const char * os_version_dir = GetDeviceSupportDirectoryForOSVersion();
499         if (os_version_dir)
500         {
501             ::snprintf (resolved_path,
502                         sizeof(resolved_path),
503                         "%s/%s",
504                         os_version_dir,
505                         platform_file_path);
506 
507             local_file.SetFile(resolved_path, true);
508             if (local_file.Exists())
509                 return error;
510 
511             ::snprintf (resolved_path,
512                         sizeof(resolved_path),
513                         "%s/Symbols.Internal/%s",
514                         os_version_dir,
515                         platform_file_path);
516 
517             local_file.SetFile(resolved_path, true);
518             if (local_file.Exists())
519                 return error;
520             ::snprintf (resolved_path,
521                         sizeof(resolved_path),
522                         "%s/Symbols/%s",
523                         os_version_dir,
524                         platform_file_path);
525 
526             local_file.SetFile(resolved_path, true);
527             if (local_file.Exists())
528                 return error;
529 
530         }
531         local_file = platform_file;
532         if (local_file.Exists())
533             return error;
534 
535         error.SetErrorStringWithFormat ("unable to locate a platform file for '%s' in platform '%s'",
536                                         platform_file_path,
537                                         GetPluginName());
538     }
539     else
540     {
541         error.SetErrorString ("invalid platform file argument");
542     }
543     return error;
544 }
545 
546 Error
547 PlatformRemoteiOS::GetSharedModule (const ModuleSpec &module_spec,
548                                     ModuleSP &module_sp,
549                                     const FileSpecList *module_search_paths_ptr,
550                                     ModuleSP *old_module_sp_ptr,
551                                     bool *did_create_ptr)
552 {
553     // For iOS, the SDK files are all cached locally on the host
554     // system. So first we ask for the file in the cached SDK,
555     // then we attempt to get a shared module for the right architecture
556     // with the right UUID.
557     const FileSpec &platform_file = module_spec.GetFileSpec();
558 
559     FileSpec local_file;
560     Error error (GetFile (platform_file, module_spec.GetUUIDPtr(), local_file));
561     if (error.Success())
562     {
563         error = ResolveExecutable (local_file, module_spec.GetArchitecture(), module_sp, module_search_paths_ptr);
564     }
565     else
566     {
567         const bool always_create = false;
568         error = ModuleList::GetSharedModule (module_spec,
569                                              module_sp,
570                                              module_search_paths_ptr,
571                                              old_module_sp_ptr,
572                                              did_create_ptr,
573                                              always_create);
574 
575     }
576     if (module_sp)
577         module_sp->SetPlatformFileSpec(platform_file);
578 
579     return error;
580 }
581 
582 
583 uint32_t
584 PlatformRemoteiOS::FindProcesses (const ProcessInstanceInfoMatch &match_info,
585                                   ProcessInstanceInfoList &process_infos)
586 {
587     // TODO: if connected, send a packet to get the remote process infos by name
588     process_infos.Clear();
589     return 0;
590 }
591 
592 bool
593 PlatformRemoteiOS::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
594 {
595     // TODO: if connected, send a packet to get the remote process info
596     process_info.Clear();
597     return false;
598 }
599 
600 bool
601 PlatformRemoteiOS::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
602 {
603     return ARMGetSupportedArchitectureAtIndex (idx, arch);
604 }
605