1 //===-- PlatformPOSIX.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 "PlatformPOSIX.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/Module.h"
19 #include "lldb/Core/ModuleSpec.h"
20 #include "lldb/Core/ValueObject.h"
21 #include "lldb/Expression/UserExpression.h"
22 #include "lldb/Host/File.h"
23 #include "lldb/Host/FileCache.h"
24 #include "lldb/Host/FileSystem.h"
25 #include "lldb/Host/Host.h"
26 #include "lldb/Host/HostInfo.h"
27 #include "lldb/Target/DynamicLoader.h"
28 #include "lldb/Target/ExecutionContext.h"
29 #include "lldb/Target/Process.h"
30 #include "lldb/Target/ProcessLaunchInfo.h"
31 #include "lldb/Target/Thread.h"
32 #include "lldb/Utility/DataBufferHeap.h"
33 #include "lldb/Utility/FileSpec.h"
34 #include "lldb/Utility/Log.h"
35 #include "lldb/Utility/StreamString.h"
36 
37 using namespace lldb;
38 using namespace lldb_private;
39 
40 //------------------------------------------------------------------
41 /// Default Constructor
42 //------------------------------------------------------------------
43 PlatformPOSIX::PlatformPOSIX(bool is_host)
44     : Platform(is_host), // This is the local host platform
45       m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
46       m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
47       m_option_group_platform_caching(new OptionGroupPlatformCaching()),
48       m_remote_platform_sp() {}
49 
50 //------------------------------------------------------------------
51 /// Destructor.
52 ///
53 /// The destructor is virtual since this class is designed to be
54 /// inherited from by the plug-in instance.
55 //------------------------------------------------------------------
56 PlatformPOSIX::~PlatformPOSIX() {}
57 
58 bool PlatformPOSIX::GetModuleSpec(const FileSpec &module_file_spec,
59                                   const ArchSpec &arch,
60                                   ModuleSpec &module_spec) {
61   if (m_remote_platform_sp)
62     return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch,
63                                                module_spec);
64 
65   return Platform::GetModuleSpec(module_file_spec, arch, module_spec);
66 }
67 
68 lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
69     lldb_private::CommandInterpreter &interpreter) {
70   auto iter = m_options.find(&interpreter), end = m_options.end();
71   if (iter == end) {
72     std::unique_ptr<lldb_private::OptionGroupOptions> options(
73         new OptionGroupOptions());
74     options->Append(m_option_group_platform_rsync.get());
75     options->Append(m_option_group_platform_ssh.get());
76     options->Append(m_option_group_platform_caching.get());
77     m_options[&interpreter] = std::move(options);
78   }
79 
80   return m_options.at(&interpreter).get();
81 }
82 
83 bool PlatformPOSIX::IsConnected() const {
84   if (IsHost())
85     return true;
86   else if (m_remote_platform_sp)
87     return m_remote_platform_sp->IsConnected();
88   return false;
89 }
90 
91 lldb_private::Status PlatformPOSIX::RunShellCommand(
92     const char *command, // Shouldn't be NULL
93     const FileSpec &
94         working_dir, // Pass empty FileSpec to use the current working directory
95     int *status_ptr, // Pass NULL if you don't want the process exit status
96     int *signo_ptr,  // Pass NULL if you don't want the signal that caused the
97                      // process to exit
98     std::string
99         *command_output, // Pass NULL if you don't want the command output
100     uint32_t
101         timeout_sec) // Timeout in seconds to wait for shell program to finish
102 {
103   if (IsHost())
104     return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr,
105                                  command_output, timeout_sec);
106   else {
107     if (m_remote_platform_sp)
108       return m_remote_platform_sp->RunShellCommand(command, working_dir,
109                                                    status_ptr, signo_ptr,
110                                                    command_output, timeout_sec);
111     else
112       return Status("unable to run a remote command without a platform");
113   }
114 }
115 
116 Status
117 PlatformPOSIX::ResolveExecutable(const ModuleSpec &module_spec,
118                                  lldb::ModuleSP &exe_module_sp,
119                                  const FileSpecList *module_search_paths_ptr) {
120   Status error;
121   // Nothing special to do here, just use the actual file and architecture
122 
123   char exe_path[PATH_MAX];
124   ModuleSpec resolved_module_spec(module_spec);
125 
126   if (IsHost()) {
127     // If we have "ls" as the exe_file, resolve the executable location based on
128     // the current path variables
129     if (!resolved_module_spec.GetFileSpec().Exists()) {
130       resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
131       resolved_module_spec.GetFileSpec().SetFile(exe_path, true);
132     }
133 
134     if (!resolved_module_spec.GetFileSpec().Exists())
135       resolved_module_spec.GetFileSpec().ResolveExecutableLocation();
136 
137     // Resolve any executable within a bundle on MacOSX
138     Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
139 
140     if (resolved_module_spec.GetFileSpec().Exists())
141       error.Clear();
142     else {
143       const uint32_t permissions =
144           resolved_module_spec.GetFileSpec().GetPermissions();
145       if (permissions && (permissions & eFilePermissionsEveryoneR) == 0)
146         error.SetErrorStringWithFormat(
147             "executable '%s' is not readable",
148             resolved_module_spec.GetFileSpec().GetPath().c_str());
149       else
150         error.SetErrorStringWithFormat(
151             "unable to find executable for '%s'",
152             resolved_module_spec.GetFileSpec().GetPath().c_str());
153     }
154   } else {
155     if (m_remote_platform_sp) {
156       error =
157           GetCachedExecutable(resolved_module_spec, exe_module_sp,
158                               module_search_paths_ptr, *m_remote_platform_sp);
159     } else {
160       // We may connect to a process and use the provided executable (Don't use
161       // local $PATH).
162 
163       // Resolve any executable within a bundle on MacOSX
164       Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec());
165 
166       if (resolved_module_spec.GetFileSpec().Exists())
167         error.Clear();
168       else
169         error.SetErrorStringWithFormat("the platform is not currently "
170                                        "connected, and '%s' doesn't exist in "
171                                        "the system root.",
172                                        exe_path);
173     }
174   }
175 
176   if (error.Success()) {
177     if (resolved_module_spec.GetArchitecture().IsValid()) {
178       error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
179                                           module_search_paths_ptr, nullptr, nullptr);
180       if (error.Fail()) {
181         // If we failed, it may be because the vendor and os aren't known. If
182 	// that is the case, try setting them to the host architecture and give
183 	// it another try.
184         llvm::Triple &module_triple =
185             resolved_module_spec.GetArchitecture().GetTriple();
186         bool is_vendor_specified =
187             (module_triple.getVendor() != llvm::Triple::UnknownVendor);
188         bool is_os_specified =
189             (module_triple.getOS() != llvm::Triple::UnknownOS);
190         if (!is_vendor_specified || !is_os_specified) {
191           const llvm::Triple &host_triple =
192               HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple();
193 
194           if (!is_vendor_specified)
195             module_triple.setVendorName(host_triple.getVendorName());
196           if (!is_os_specified)
197             module_triple.setOSName(host_triple.getOSName());
198 
199           error = ModuleList::GetSharedModule(resolved_module_spec,
200                                               exe_module_sp, module_search_paths_ptr, nullptr, nullptr);
201         }
202       }
203 
204       // TODO find out why exe_module_sp might be NULL
205       if (error.Fail() || !exe_module_sp || !exe_module_sp->GetObjectFile()) {
206         exe_module_sp.reset();
207         error.SetErrorStringWithFormat(
208             "'%s' doesn't contain the architecture %s",
209             resolved_module_spec.GetFileSpec().GetPath().c_str(),
210             resolved_module_spec.GetArchitecture().GetArchitectureName());
211       }
212     } else {
213       // No valid architecture was specified, ask the platform for
214       // the architectures that we should be using (in the correct order)
215       // and see if we can find a match that way
216       StreamString arch_names;
217       for (uint32_t idx = 0; GetSupportedArchitectureAtIndex(
218                idx, resolved_module_spec.GetArchitecture());
219            ++idx) {
220         error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp,
221                                             module_search_paths_ptr, nullptr, nullptr);
222         // Did we find an executable using one of the
223         if (error.Success()) {
224           if (exe_module_sp && exe_module_sp->GetObjectFile())
225             break;
226           else
227             error.SetErrorToGenericError();
228         }
229 
230         if (idx > 0)
231           arch_names.PutCString(", ");
232         arch_names.PutCString(
233             resolved_module_spec.GetArchitecture().GetArchitectureName());
234       }
235 
236       if (error.Fail() || !exe_module_sp) {
237         if (resolved_module_spec.GetFileSpec().Readable()) {
238           error.SetErrorStringWithFormat(
239               "'%s' doesn't contain any '%s' platform architectures: %s",
240               resolved_module_spec.GetFileSpec().GetPath().c_str(),
241               GetPluginName().GetCString(), arch_names.GetData());
242         } else {
243           error.SetErrorStringWithFormat(
244               "'%s' is not readable",
245               resolved_module_spec.GetFileSpec().GetPath().c_str());
246         }
247       }
248     }
249   }
250 
251   return error;
252 }
253 
254 Status PlatformPOSIX::GetFileWithUUID(const FileSpec &platform_file,
255                                       const UUID *uuid_ptr,
256                                       FileSpec &local_file) {
257   if (IsRemote() && m_remote_platform_sp)
258       return m_remote_platform_sp->GetFileWithUUID(platform_file, uuid_ptr,
259                                                    local_file);
260 
261   // Default to the local case
262   local_file = platform_file;
263   return Status();
264 }
265 
266 bool PlatformPOSIX::GetProcessInfo(lldb::pid_t pid,
267                                      ProcessInstanceInfo &process_info) {
268   if (IsHost())
269     return Platform::GetProcessInfo(pid, process_info);
270   if (m_remote_platform_sp)
271     return m_remote_platform_sp->GetProcessInfo(pid, process_info);
272   return false;
273 }
274 
275 uint32_t
276 PlatformPOSIX::FindProcesses(const ProcessInstanceInfoMatch &match_info,
277                                ProcessInstanceInfoList &process_infos) {
278   if (IsHost())
279     return Platform::FindProcesses(match_info, process_infos);
280   if (m_remote_platform_sp)
281     return
282       m_remote_platform_sp->FindProcesses(match_info, process_infos);
283   return 0;
284 }
285 
286 Status PlatformPOSIX::MakeDirectory(const FileSpec &file_spec,
287                                     uint32_t file_permissions) {
288   if (m_remote_platform_sp)
289     return m_remote_platform_sp->MakeDirectory(file_spec, file_permissions);
290   else
291     return Platform::MakeDirectory(file_spec, file_permissions);
292 }
293 
294 Status PlatformPOSIX::GetFilePermissions(const FileSpec &file_spec,
295                                          uint32_t &file_permissions) {
296   if (m_remote_platform_sp)
297     return m_remote_platform_sp->GetFilePermissions(file_spec,
298                                                     file_permissions);
299   else
300     return Platform::GetFilePermissions(file_spec, file_permissions);
301 }
302 
303 Status PlatformPOSIX::SetFilePermissions(const FileSpec &file_spec,
304                                          uint32_t file_permissions) {
305   if (m_remote_platform_sp)
306     return m_remote_platform_sp->SetFilePermissions(file_spec,
307                                                     file_permissions);
308   else
309     return Platform::SetFilePermissions(file_spec, file_permissions);
310 }
311 
312 lldb::user_id_t PlatformPOSIX::OpenFile(const FileSpec &file_spec,
313                                         uint32_t flags, uint32_t mode,
314                                         Status &error) {
315   if (IsHost())
316     return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error);
317   else if (m_remote_platform_sp)
318     return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
319   else
320     return Platform::OpenFile(file_spec, flags, mode, error);
321 }
322 
323 bool PlatformPOSIX::CloseFile(lldb::user_id_t fd, Status &error) {
324   if (IsHost())
325     return FileCache::GetInstance().CloseFile(fd, error);
326   else if (m_remote_platform_sp)
327     return m_remote_platform_sp->CloseFile(fd, error);
328   else
329     return Platform::CloseFile(fd, error);
330 }
331 
332 uint64_t PlatformPOSIX::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst,
333                                  uint64_t dst_len, Status &error) {
334   if (IsHost())
335     return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error);
336   else if (m_remote_platform_sp)
337     return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
338   else
339     return Platform::ReadFile(fd, offset, dst, dst_len, error);
340 }
341 
342 uint64_t PlatformPOSIX::WriteFile(lldb::user_id_t fd, uint64_t offset,
343                                   const void *src, uint64_t src_len,
344                                   Status &error) {
345   if (IsHost())
346     return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error);
347   else if (m_remote_platform_sp)
348     return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
349   else
350     return Platform::WriteFile(fd, offset, src, src_len, error);
351 }
352 
353 static uint32_t chown_file(Platform *platform, const char *path,
354                            uint32_t uid = UINT32_MAX,
355                            uint32_t gid = UINT32_MAX) {
356   if (!platform || !path || *path == 0)
357     return UINT32_MAX;
358 
359   if (uid == UINT32_MAX && gid == UINT32_MAX)
360     return 0; // pretend I did chown correctly - actually I just didn't care
361 
362   StreamString command;
363   command.PutCString("chown ");
364   if (uid != UINT32_MAX)
365     command.Printf("%d", uid);
366   if (gid != UINT32_MAX)
367     command.Printf(":%d", gid);
368   command.Printf("%s", path);
369   int status;
370   platform->RunShellCommand(command.GetData(), NULL, &status, NULL, NULL, 10);
371   return status;
372 }
373 
374 lldb_private::Status
375 PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
376                        const lldb_private::FileSpec &destination, uint32_t uid,
377                        uint32_t gid) {
378   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
379 
380   if (IsHost()) {
381     if (FileSpec::Equal(source, destination, true))
382       return Status();
383     // cp src dst
384     // chown uid:gid dst
385     std::string src_path(source.GetPath());
386     if (src_path.empty())
387       return Status("unable to get file path for source");
388     std::string dst_path(destination.GetPath());
389     if (dst_path.empty())
390       return Status("unable to get file path for destination");
391     StreamString command;
392     command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
393     int status;
394     RunShellCommand(command.GetData(), NULL, &status, NULL, NULL, 10);
395     if (status != 0)
396       return Status("unable to perform copy");
397     if (uid == UINT32_MAX && gid == UINT32_MAX)
398       return Status();
399     if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
400       return Status("unable to perform chown");
401     return Status();
402   } else if (m_remote_platform_sp) {
403     if (GetSupportsRSync()) {
404       std::string src_path(source.GetPath());
405       if (src_path.empty())
406         return Status("unable to get file path for source");
407       std::string dst_path(destination.GetPath());
408       if (dst_path.empty())
409         return Status("unable to get file path for destination");
410       StreamString command;
411       if (GetIgnoresRemoteHostname()) {
412         if (!GetRSyncPrefix())
413           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
414                          dst_path.c_str());
415         else
416           command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
417                          GetRSyncPrefix(), dst_path.c_str());
418       } else
419         command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
420                        GetHostname(), dst_path.c_str());
421       if (log)
422         log->Printf("[PutFile] Running command: %s\n", command.GetData());
423       int retcode;
424       Host::RunShellCommand(command.GetData(), NULL, &retcode, NULL, NULL, 60);
425       if (retcode == 0) {
426         // Don't chown a local file for a remote system
427         //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
428         //                    return Status("unable to perform chown");
429         return Status();
430       }
431       // if we are still here rsync has failed - let's try the slow way before
432       // giving up
433     }
434   }
435   return Platform::PutFile(source, destination, uid, gid);
436 }
437 
438 lldb::user_id_t PlatformPOSIX::GetFileSize(const FileSpec &file_spec) {
439   if (IsHost()) {
440     uint64_t Size;
441     if (llvm::sys::fs::file_size(file_spec.GetPath(), Size))
442       return 0;
443     return Size;
444   } else if (m_remote_platform_sp)
445     return m_remote_platform_sp->GetFileSize(file_spec);
446   else
447     return Platform::GetFileSize(file_spec);
448 }
449 
450 Status PlatformPOSIX::CreateSymlink(const FileSpec &src, const FileSpec &dst) {
451   if (IsHost())
452     return FileSystem::Symlink(src, dst);
453   else if (m_remote_platform_sp)
454     return m_remote_platform_sp->CreateSymlink(src, dst);
455   else
456     return Platform::CreateSymlink(src, dst);
457 }
458 
459 bool PlatformPOSIX::GetFileExists(const FileSpec &file_spec) {
460   if (IsHost())
461     return file_spec.Exists();
462   else if (m_remote_platform_sp)
463     return m_remote_platform_sp->GetFileExists(file_spec);
464   else
465     return Platform::GetFileExists(file_spec);
466 }
467 
468 Status PlatformPOSIX::Unlink(const FileSpec &file_spec) {
469   if (IsHost())
470     return llvm::sys::fs::remove(file_spec.GetPath());
471   else if (m_remote_platform_sp)
472     return m_remote_platform_sp->Unlink(file_spec);
473   else
474     return Platform::Unlink(file_spec);
475 }
476 
477 lldb_private::Status PlatformPOSIX::GetFile(
478     const lldb_private::FileSpec &source,      // remote file path
479     const lldb_private::FileSpec &destination) // local file path
480 {
481   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
482 
483   // Check the args, first.
484   std::string src_path(source.GetPath());
485   if (src_path.empty())
486     return Status("unable to get file path for source");
487   std::string dst_path(destination.GetPath());
488   if (dst_path.empty())
489     return Status("unable to get file path for destination");
490   if (IsHost()) {
491     if (FileSpec::Equal(source, destination, true))
492       return Status("local scenario->source and destination are the same file "
493                     "path: no operation performed");
494     // cp src dst
495     StreamString cp_command;
496     cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
497     int status;
498     RunShellCommand(cp_command.GetData(), NULL, &status, NULL, NULL, 10);
499     if (status != 0)
500       return Status("unable to perform copy");
501     return Status();
502   } else if (m_remote_platform_sp) {
503     if (GetSupportsRSync()) {
504       StreamString command;
505       if (GetIgnoresRemoteHostname()) {
506         if (!GetRSyncPrefix())
507           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
508                          dst_path.c_str());
509         else
510           command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
511                          src_path.c_str(), dst_path.c_str());
512       } else
513         command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
514                        m_remote_platform_sp->GetHostname(), src_path.c_str(),
515                        dst_path.c_str());
516       if (log)
517         log->Printf("[GetFile] Running command: %s\n", command.GetData());
518       int retcode;
519       Host::RunShellCommand(command.GetData(), NULL, &retcode, NULL, NULL, 60);
520       if (retcode == 0)
521         return Status();
522       // If we are here, rsync has failed - let's try the slow way before giving
523       // up
524     }
525     // open src and dst
526     // read/write, read/write, read/write, ...
527     // close src
528     // close dst
529     if (log)
530       log->Printf("[GetFile] Using block by block transfer....\n");
531     Status error;
532     user_id_t fd_src = OpenFile(source, File::eOpenOptionRead,
533                                 lldb::eFilePermissionsFileDefault, error);
534 
535     if (fd_src == UINT64_MAX)
536       return Status("unable to open source file");
537 
538     uint32_t permissions = 0;
539     error = GetFilePermissions(source, permissions);
540 
541     if (permissions == 0)
542       permissions = lldb::eFilePermissionsFileDefault;
543 
544     user_id_t fd_dst = FileCache::GetInstance().OpenFile(
545         destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite |
546                          File::eOpenOptionTruncate,
547         permissions, error);
548 
549     if (fd_dst == UINT64_MAX) {
550       if (error.Success())
551         error.SetErrorString("unable to open destination file");
552     }
553 
554     if (error.Success()) {
555       lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
556       uint64_t offset = 0;
557       error.Clear();
558       while (error.Success()) {
559         const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
560                                          buffer_sp->GetByteSize(), error);
561         if (error.Fail())
562           break;
563         if (n_read == 0)
564           break;
565         if (FileCache::GetInstance().WriteFile(fd_dst, offset,
566                                                buffer_sp->GetBytes(), n_read,
567                                                error) != n_read) {
568           if (!error.Fail())
569             error.SetErrorString("unable to write to destination file");
570           break;
571         }
572         offset += n_read;
573       }
574     }
575     // Ignore the close error of src.
576     if (fd_src != UINT64_MAX)
577       CloseFile(fd_src, error);
578     // And close the dst file descriptot.
579     if (fd_dst != UINT64_MAX &&
580         !FileCache::GetInstance().CloseFile(fd_dst, error)) {
581       if (!error.Fail())
582         error.SetErrorString("unable to close destination file");
583     }
584     return error;
585   }
586   return Platform::GetFile(source, destination);
587 }
588 
589 std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
590   StreamString stream;
591   if (GetSupportsRSync()) {
592     stream.PutCString("rsync");
593     if ((GetRSyncOpts() && *GetRSyncOpts()) ||
594         (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
595       stream.Printf(", options: ");
596       if (GetRSyncOpts() && *GetRSyncOpts())
597         stream.Printf("'%s' ", GetRSyncOpts());
598       stream.Printf(", prefix: ");
599       if (GetRSyncPrefix() && *GetRSyncPrefix())
600         stream.Printf("'%s' ", GetRSyncPrefix());
601       if (GetIgnoresRemoteHostname())
602         stream.Printf("ignore remote-hostname ");
603     }
604   }
605   if (GetSupportsSSH()) {
606     stream.PutCString("ssh");
607     if (GetSSHOpts() && *GetSSHOpts())
608       stream.Printf(", options: '%s' ", GetSSHOpts());
609   }
610   if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
611     stream.Printf("cache dir: %s", GetLocalCacheDirectory());
612   if (stream.GetSize())
613     return stream.GetString();
614   else
615     return "";
616 }
617 
618 bool PlatformPOSIX::CalculateMD5(const FileSpec &file_spec, uint64_t &low,
619                                  uint64_t &high) {
620   if (IsHost())
621     return Platform::CalculateMD5(file_spec, low, high);
622   if (m_remote_platform_sp)
623     return m_remote_platform_sp->CalculateMD5(file_spec, low, high);
624   return false;
625 }
626 
627 const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
628   if (IsRemote() && m_remote_platform_sp)
629     return m_remote_platform_sp->GetRemoteUnixSignals();
630   return Platform::GetRemoteUnixSignals();
631 }
632 
633 FileSpec PlatformPOSIX::GetRemoteWorkingDirectory() {
634   if (IsRemote() && m_remote_platform_sp)
635     return m_remote_platform_sp->GetRemoteWorkingDirectory();
636   else
637     return Platform::GetRemoteWorkingDirectory();
638 }
639 
640 bool PlatformPOSIX::SetRemoteWorkingDirectory(const FileSpec &working_dir) {
641   if (IsRemote() && m_remote_platform_sp)
642     return m_remote_platform_sp->SetRemoteWorkingDirectory(working_dir);
643   else
644     return Platform::SetRemoteWorkingDirectory(working_dir);
645 }
646 
647 bool PlatformPOSIX::GetRemoteOSVersion() {
648   if (m_remote_platform_sp)
649     return m_remote_platform_sp->GetOSVersion(
650         m_major_os_version, m_minor_os_version, m_update_os_version);
651   return false;
652 }
653 
654 bool PlatformPOSIX::GetRemoteOSBuildString(std::string &s) {
655   if (m_remote_platform_sp)
656     return m_remote_platform_sp->GetRemoteOSBuildString(s);
657   s.clear();
658   return false;
659 }
660 
661 Environment PlatformPOSIX::GetEnvironment() {
662   if (IsRemote()) {
663     if (m_remote_platform_sp)
664       return m_remote_platform_sp->GetEnvironment();
665     return Environment();
666   }
667   return Host::GetEnvironment();
668 }
669 
670 bool PlatformPOSIX::GetRemoteOSKernelDescription(std::string &s) {
671   if (m_remote_platform_sp)
672     return m_remote_platform_sp->GetRemoteOSKernelDescription(s);
673   s.clear();
674   return false;
675 }
676 
677 // Remote Platform subclasses need to override this function
678 ArchSpec PlatformPOSIX::GetRemoteSystemArchitecture() {
679   if (m_remote_platform_sp)
680     return m_remote_platform_sp->GetRemoteSystemArchitecture();
681   return ArchSpec();
682 }
683 
684 const char *PlatformPOSIX::GetHostname() {
685   if (IsHost())
686     return Platform::GetHostname();
687 
688   if (m_remote_platform_sp)
689     return m_remote_platform_sp->GetHostname();
690   return NULL;
691 }
692 
693 const char *PlatformPOSIX::GetUserName(uint32_t uid) {
694   // Check the cache in Platform in case we have already looked this uid up
695   const char *user_name = Platform::GetUserName(uid);
696   if (user_name)
697     return user_name;
698 
699   if (IsRemote() && m_remote_platform_sp)
700     return m_remote_platform_sp->GetUserName(uid);
701   return NULL;
702 }
703 
704 const char *PlatformPOSIX::GetGroupName(uint32_t gid) {
705   const char *group_name = Platform::GetGroupName(gid);
706   if (group_name)
707     return group_name;
708 
709   if (IsRemote() && m_remote_platform_sp)
710     return m_remote_platform_sp->GetGroupName(gid);
711   return NULL;
712 }
713 
714 Status PlatformPOSIX::ConnectRemote(Args &args) {
715   Status error;
716   if (IsHost()) {
717     error.SetErrorStringWithFormat(
718         "can't connect to the host platform '%s', always connected",
719         GetPluginName().GetCString());
720   } else {
721     if (!m_remote_platform_sp)
722       m_remote_platform_sp =
723           Platform::Create(ConstString("remote-gdb-server"), error);
724 
725     if (m_remote_platform_sp && error.Success())
726       error = m_remote_platform_sp->ConnectRemote(args);
727     else
728       error.SetErrorString("failed to create a 'remote-gdb-server' platform");
729 
730     if (error.Fail())
731       m_remote_platform_sp.reset();
732   }
733 
734   if (error.Success() && m_remote_platform_sp) {
735     if (m_option_group_platform_rsync.get() &&
736         m_option_group_platform_ssh.get() &&
737         m_option_group_platform_caching.get()) {
738       if (m_option_group_platform_rsync->m_rsync) {
739         SetSupportsRSync(true);
740         SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
741         SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
742         SetIgnoresRemoteHostname(
743             m_option_group_platform_rsync->m_ignores_remote_hostname);
744       }
745       if (m_option_group_platform_ssh->m_ssh) {
746         SetSupportsSSH(true);
747         SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
748       }
749       SetLocalCacheDirectory(
750           m_option_group_platform_caching->m_cache_dir.c_str());
751     }
752   }
753 
754   return error;
755 }
756 
757 Status PlatformPOSIX::DisconnectRemote() {
758   Status error;
759 
760   if (IsHost()) {
761     error.SetErrorStringWithFormat(
762         "can't disconnect from the host platform '%s', always connected",
763         GetPluginName().GetCString());
764   } else {
765     if (m_remote_platform_sp)
766       error = m_remote_platform_sp->DisconnectRemote();
767     else
768       error.SetErrorString("the platform is not currently connected");
769   }
770   return error;
771 }
772 
773 Status PlatformPOSIX::LaunchProcess(ProcessLaunchInfo &launch_info) {
774   Status error;
775 
776   if (IsHost()) {
777     error = Platform::LaunchProcess(launch_info);
778   } else {
779     if (m_remote_platform_sp)
780       error = m_remote_platform_sp->LaunchProcess(launch_info);
781     else
782       error.SetErrorString("the platform is not currently connected");
783   }
784   return error;
785 }
786 
787 lldb_private::Status PlatformPOSIX::KillProcess(const lldb::pid_t pid) {
788   if (IsHost())
789     return Platform::KillProcess(pid);
790 
791   if (m_remote_platform_sp)
792     return m_remote_platform_sp->KillProcess(pid);
793 
794   return Status("the platform is not currently connected");
795 }
796 
797 lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
798                                       Debugger &debugger, Target *target,
799                                       Status &error) {
800   lldb::ProcessSP process_sp;
801   Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
802 
803   if (IsHost()) {
804     if (target == NULL) {
805       TargetSP new_target_sp;
806 
807       error = debugger.GetTargetList().CreateTarget(debugger, "", "", false,
808                                                     NULL, new_target_sp);
809       target = new_target_sp.get();
810       if (log)
811         log->Printf("PlatformPOSIX::%s created new target", __FUNCTION__);
812     } else {
813       error.Clear();
814       if (log)
815         log->Printf("PlatformPOSIX::%s target already existed, setting target",
816                     __FUNCTION__);
817     }
818 
819     if (target && error.Success()) {
820       debugger.GetTargetList().SetSelectedTarget(target);
821       if (log) {
822         ModuleSP exe_module_sp = target->GetExecutableModule();
823         log->Printf("PlatformPOSIX::%s set selected target to %p %s",
824                     __FUNCTION__, (void *)target,
825                     exe_module_sp
826                         ? exe_module_sp->GetFileSpec().GetPath().c_str()
827                         : "<null>");
828       }
829 
830       process_sp =
831           target->CreateProcess(attach_info.GetListenerForProcess(debugger),
832                                 attach_info.GetProcessPluginName(), NULL);
833 
834       if (process_sp) {
835         ListenerSP listener_sp = attach_info.GetHijackListener();
836         if (listener_sp == nullptr) {
837           listener_sp =
838               Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
839           attach_info.SetHijackListener(listener_sp);
840         }
841         process_sp->HijackProcessEvents(listener_sp);
842         error = process_sp->Attach(attach_info);
843       }
844     }
845   } else {
846     if (m_remote_platform_sp)
847       process_sp =
848           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
849     else
850       error.SetErrorString("the platform is not currently connected");
851   }
852   return process_sp;
853 }
854 
855 lldb::ProcessSP
856 PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
857                             Target *target, // Can be NULL, if NULL create a new
858                                             // target, else use existing one
859                             Status &error) {
860   ProcessSP process_sp;
861 
862   if (IsHost()) {
863     // We are going to hand this process off to debugserver which will be in
864     // charge of setting the exit status.
865     // We still need to reap it from lldb but if we let the monitor thread also
866     // set the exit status, we set up a
867     // race between debugserver & us for who will find out about the debugged
868     // process's death.
869     launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus);
870     process_sp = Platform::DebugProcess(launch_info, debugger, target, error);
871   } else {
872     if (m_remote_platform_sp)
873       process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
874                                                       target, error);
875     else
876       error.SetErrorString("the platform is not currently connected");
877   }
878   return process_sp;
879 }
880 
881 void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
882   m_trap_handlers.push_back(ConstString("_sigtramp"));
883 }
884 
885 Status PlatformPOSIX::EvaluateLibdlExpression(
886     lldb_private::Process *process, const char *expr_cstr,
887     llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
888   DynamicLoader *loader = process->GetDynamicLoader();
889   if (loader) {
890     Status error = loader->CanLoadImage();
891     if (error.Fail())
892       return error;
893   }
894 
895   ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
896   if (!thread_sp)
897     return Status("Selected thread isn't valid");
898 
899   StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
900   if (!frame_sp)
901     return Status("Frame 0 isn't valid");
902 
903   ExecutionContext exe_ctx;
904   frame_sp->CalculateExecutionContext(exe_ctx);
905   EvaluateExpressionOptions expr_options;
906   expr_options.SetUnwindOnError(true);
907   expr_options.SetIgnoreBreakpoints(true);
908   expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
909   expr_options.SetLanguage(eLanguageTypeC_plus_plus);
910   expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
911                                          // don't do the work to trap them.
912   expr_options.SetTimeout(std::chrono::seconds(2));
913 
914   Status expr_error;
915   ExpressionResults result =
916       UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
917                                result_valobj_sp, expr_error);
918   if (result != eExpressionCompleted)
919     return expr_error;
920 
921   if (result_valobj_sp->GetError().Fail())
922     return result_valobj_sp->GetError();
923   return Status();
924 }
925 
926 uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
927                                     const lldb_private::FileSpec &remote_file,
928                                     lldb_private::Status &error) {
929   char path[PATH_MAX];
930   remote_file.GetPath(path, sizeof(path));
931 
932   StreamString expr;
933   expr.Printf(R"(
934                    struct __lldb_dlopen_result { void *image_ptr; const char *error_str; } the_result;
935                    the_result.image_ptr = dlopen ("%s", 2);
936                    if (the_result.image_ptr == (void *) 0x0)
937                    {
938                        the_result.error_str = dlerror();
939                    }
940                    else
941                    {
942                        the_result.error_str = (const char *) 0x0;
943                    }
944                    the_result;
945                   )",
946               path);
947   llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
948   lldb::ValueObjectSP result_valobj_sp;
949   error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
950                                   result_valobj_sp);
951   if (error.Fail())
952     return LLDB_INVALID_IMAGE_TOKEN;
953 
954   error = result_valobj_sp->GetError();
955   if (error.Fail())
956     return LLDB_INVALID_IMAGE_TOKEN;
957 
958   Scalar scalar;
959   ValueObjectSP image_ptr_sp = result_valobj_sp->GetChildAtIndex(0, true);
960   if (!image_ptr_sp || !image_ptr_sp->ResolveValue(scalar)) {
961     error.SetErrorStringWithFormat("unable to load '%s'", path);
962     return LLDB_INVALID_IMAGE_TOKEN;
963   }
964 
965   addr_t image_ptr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
966   if (image_ptr != 0 && image_ptr != LLDB_INVALID_ADDRESS)
967     return process->AddImageToken(image_ptr);
968 
969   if (image_ptr == 0) {
970     ValueObjectSP error_str_sp = result_valobj_sp->GetChildAtIndex(1, true);
971     if (error_str_sp) {
972       DataBufferSP buffer_sp(new DataBufferHeap(10240, 0));
973       size_t num_chars =
974           error_str_sp->ReadPointedString(buffer_sp, error, 10240).first;
975       if (error.Success() && num_chars > 0)
976         error.SetErrorStringWithFormat("dlopen error: %s",
977                                        buffer_sp->GetBytes());
978       else
979         error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
980       return LLDB_INVALID_IMAGE_TOKEN;
981     }
982   }
983   error.SetErrorStringWithFormat("unable to load '%s'", path);
984   return LLDB_INVALID_IMAGE_TOKEN;
985 }
986 
987 Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
988                                   uint32_t image_token) {
989   const addr_t image_addr = process->GetImagePtrFromToken(image_token);
990   if (image_addr == LLDB_INVALID_ADDRESS)
991     return Status("Invalid image token");
992 
993   StreamString expr;
994   expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
995   llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
996   lldb::ValueObjectSP result_valobj_sp;
997   Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
998                                          result_valobj_sp);
999   if (error.Fail())
1000     return error;
1001 
1002   if (result_valobj_sp->GetError().Fail())
1003     return result_valobj_sp->GetError();
1004 
1005   Scalar scalar;
1006   if (result_valobj_sp->ResolveValue(scalar)) {
1007     if (scalar.UInt(1))
1008       return Status("expression failed: \"%s\"", expr.GetData());
1009     process->ResetImageToken(image_token);
1010   }
1011   return Status();
1012 }
1013 
1014 lldb::ProcessSP PlatformPOSIX::ConnectProcess(llvm::StringRef connect_url,
1015                                               llvm::StringRef plugin_name,
1016                                               lldb_private::Debugger &debugger,
1017                                               lldb_private::Target *target,
1018                                               lldb_private::Status &error) {
1019   if (m_remote_platform_sp)
1020     return m_remote_platform_sp->ConnectProcess(connect_url, plugin_name,
1021                                                 debugger, target, error);
1022 
1023   return Platform::ConnectProcess(connect_url, plugin_name, debugger, target,
1024                                   error);
1025 }
1026 
1027 llvm::StringRef
1028 PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
1029   return R"(
1030               extern "C" void* dlopen(const char*, int);
1031               extern "C" void* dlsym(void*, const char*);
1032               extern "C" int   dlclose(void*);
1033               extern "C" char* dlerror(void);
1034              )";
1035 }
1036 
1037 size_t PlatformPOSIX::ConnectToWaitingProcesses(Debugger &debugger,
1038                                                 Status &error) {
1039   if (m_remote_platform_sp)
1040     return m_remote_platform_sp->ConnectToWaitingProcesses(debugger, error);
1041   return Platform::ConnectToWaitingProcesses(debugger, error);
1042 }
1043 
1044 ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
1045   if (basename.IsEmpty())
1046     return basename;
1047 
1048   StreamString stream;
1049   stream.Printf("lib%s.so", basename.GetCString());
1050   return ConstString(stream.GetString());
1051 }
1052