1 //===-- PlatformPOSIX.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 "PlatformPOSIX.h"
10 
11 #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
12 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
13 #include "lldb/Core/Debugger.h"
14 #include "lldb/Core/Module.h"
15 #include "lldb/Core/ValueObject.h"
16 #include "lldb/Expression/DiagnosticManager.h"
17 #include "lldb/Expression/FunctionCaller.h"
18 #include "lldb/Expression/UserExpression.h"
19 #include "lldb/Expression/UtilityFunction.h"
20 #include "lldb/Host/File.h"
21 #include "lldb/Host/FileCache.h"
22 #include "lldb/Host/FileSystem.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Host/HostInfo.h"
25 #include "lldb/Host/ProcessLaunchInfo.h"
26 #include "lldb/Target/DynamicLoader.h"
27 #include "lldb/Target/ExecutionContext.h"
28 #include "lldb/Target/Process.h"
29 #include "lldb/Target/Thread.h"
30 #include "lldb/Utility/DataBufferHeap.h"
31 #include "lldb/Utility/FileSpec.h"
32 #include "lldb/Utility/LLDBLog.h"
33 #include "lldb/Utility/Log.h"
34 #include "lldb/Utility/StreamString.h"
35 #include "llvm/ADT/ScopeExit.h"
36 #include <optional>
37 
38 using namespace lldb;
39 using namespace lldb_private;
40 
41 /// Default Constructor
PlatformPOSIX(bool is_host)42 PlatformPOSIX::PlatformPOSIX(bool is_host)
43     : RemoteAwarePlatform(is_host), // This is the local host platform
44       m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
45       m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
46       m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
47 
48 /// Destructor.
49 ///
50 /// The destructor is virtual since this class is designed to be
51 /// inherited from by the plug-in instance.
52 PlatformPOSIX::~PlatformPOSIX() = default;
53 
GetConnectionOptions(lldb_private::CommandInterpreter & interpreter)54 lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
55     lldb_private::CommandInterpreter &interpreter) {
56   auto iter = m_options.find(&interpreter), end = m_options.end();
57   if (iter == end) {
58     std::unique_ptr<lldb_private::OptionGroupOptions> options(
59         new OptionGroupOptions());
60     options->Append(m_option_group_platform_rsync.get());
61     options->Append(m_option_group_platform_ssh.get());
62     options->Append(m_option_group_platform_caching.get());
63     m_options[&interpreter] = std::move(options);
64   }
65 
66   return m_options.at(&interpreter).get();
67 }
68 
chown_file(Platform * platform,const char * path,uint32_t uid=UINT32_MAX,uint32_t gid=UINT32_MAX)69 static uint32_t chown_file(Platform *platform, const char *path,
70                            uint32_t uid = UINT32_MAX,
71                            uint32_t gid = UINT32_MAX) {
72   if (!platform || !path || *path == 0)
73     return UINT32_MAX;
74 
75   if (uid == UINT32_MAX && gid == UINT32_MAX)
76     return 0; // pretend I did chown correctly - actually I just didn't care
77 
78   StreamString command;
79   command.PutCString("chown ");
80   if (uid != UINT32_MAX)
81     command.Printf("%d", uid);
82   if (gid != UINT32_MAX)
83     command.Printf(":%d", gid);
84   command.Printf("%s", path);
85   int status;
86   platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
87                             nullptr, std::chrono::seconds(10));
88   return status;
89 }
90 
91 lldb_private::Status
PutFile(const lldb_private::FileSpec & source,const lldb_private::FileSpec & destination,uint32_t uid,uint32_t gid)92 PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
93                        const lldb_private::FileSpec &destination, uint32_t uid,
94                        uint32_t gid) {
95   Log *log = GetLog(LLDBLog::Platform);
96 
97   if (IsHost()) {
98     if (source == destination)
99       return Status();
100     // cp src dst
101     // chown uid:gid dst
102     std::string src_path(source.GetPath());
103     if (src_path.empty())
104       return Status("unable to get file path for source");
105     std::string dst_path(destination.GetPath());
106     if (dst_path.empty())
107       return Status("unable to get file path for destination");
108     StreamString command;
109     command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
110     int status;
111     RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
112                     std::chrono::seconds(10));
113     if (status != 0)
114       return Status("unable to perform copy");
115     if (uid == UINT32_MAX && gid == UINT32_MAX)
116       return Status();
117     if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
118       return Status("unable to perform chown");
119     return Status();
120   } else if (m_remote_platform_sp) {
121     if (GetSupportsRSync()) {
122       std::string src_path(source.GetPath());
123       if (src_path.empty())
124         return Status("unable to get file path for source");
125       std::string dst_path(destination.GetPath());
126       if (dst_path.empty())
127         return Status("unable to get file path for destination");
128       StreamString command;
129       if (GetIgnoresRemoteHostname()) {
130         if (!GetRSyncPrefix())
131           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
132                          dst_path.c_str());
133         else
134           command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
135                          GetRSyncPrefix(), dst_path.c_str());
136       } else
137         command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
138                        GetHostname(), dst_path.c_str());
139       LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData());
140       int retcode;
141       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
142                             nullptr, std::chrono::minutes(1));
143       if (retcode == 0) {
144         // Don't chown a local file for a remote system
145         //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
146         //                    return Status("unable to perform chown");
147         return Status();
148       }
149       // if we are still here rsync has failed - let's try the slow way before
150       // giving up
151     }
152   }
153   return Platform::PutFile(source, destination, uid, gid);
154 }
155 
GetFile(const lldb_private::FileSpec & source,const lldb_private::FileSpec & destination)156 lldb_private::Status PlatformPOSIX::GetFile(
157     const lldb_private::FileSpec &source,      // remote file path
158     const lldb_private::FileSpec &destination) // local file path
159 {
160   Log *log = GetLog(LLDBLog::Platform);
161 
162   // Check the args, first.
163   std::string src_path(source.GetPath());
164   if (src_path.empty())
165     return Status("unable to get file path for source");
166   std::string dst_path(destination.GetPath());
167   if (dst_path.empty())
168     return Status("unable to get file path for destination");
169   if (IsHost()) {
170     if (source == destination)
171       return Status("local scenario->source and destination are the same file "
172                     "path: no operation performed");
173     // cp src dst
174     StreamString cp_command;
175     cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
176     int status;
177     RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
178                     std::chrono::seconds(10));
179     if (status != 0)
180       return Status("unable to perform copy");
181     return Status();
182   } else if (m_remote_platform_sp) {
183     if (GetSupportsRSync()) {
184       StreamString command;
185       if (GetIgnoresRemoteHostname()) {
186         if (!GetRSyncPrefix())
187           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
188                          dst_path.c_str());
189         else
190           command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
191                          src_path.c_str(), dst_path.c_str());
192       } else
193         command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
194                        m_remote_platform_sp->GetHostname(), src_path.c_str(),
195                        dst_path.c_str());
196       LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData());
197       int retcode;
198       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
199                             nullptr, std::chrono::minutes(1));
200       if (retcode == 0)
201         return Status();
202       // If we are here, rsync has failed - let's try the slow way before
203       // giving up
204     }
205     // open src and dst
206     // read/write, read/write, read/write, ...
207     // close src
208     // close dst
209     LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n");
210     Status error;
211     user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly,
212                                 lldb::eFilePermissionsFileDefault, error);
213 
214     if (fd_src == UINT64_MAX)
215       return Status("unable to open source file");
216 
217     uint32_t permissions = 0;
218     error = GetFilePermissions(source, permissions);
219 
220     if (permissions == 0)
221       permissions = lldb::eFilePermissionsFileDefault;
222 
223     user_id_t fd_dst = FileCache::GetInstance().OpenFile(
224         destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly |
225                          File::eOpenOptionTruncate,
226         permissions, error);
227 
228     if (fd_dst == UINT64_MAX) {
229       if (error.Success())
230         error.SetErrorString("unable to open destination file");
231     }
232 
233     if (error.Success()) {
234       lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
235       uint64_t offset = 0;
236       error.Clear();
237       while (error.Success()) {
238         const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
239                                          buffer_sp->GetByteSize(), error);
240         if (error.Fail())
241           break;
242         if (n_read == 0)
243           break;
244         if (FileCache::GetInstance().WriteFile(fd_dst, offset,
245                                                buffer_sp->GetBytes(), n_read,
246                                                error) != n_read) {
247           if (!error.Fail())
248             error.SetErrorString("unable to write to destination file");
249           break;
250         }
251         offset += n_read;
252       }
253     }
254     // Ignore the close error of src.
255     if (fd_src != UINT64_MAX)
256       CloseFile(fd_src, error);
257     // And close the dst file descriptot.
258     if (fd_dst != UINT64_MAX &&
259         !FileCache::GetInstance().CloseFile(fd_dst, error)) {
260       if (!error.Fail())
261         error.SetErrorString("unable to close destination file");
262     }
263     return error;
264   }
265   return Platform::GetFile(source, destination);
266 }
267 
GetPlatformSpecificConnectionInformation()268 std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
269   StreamString stream;
270   if (GetSupportsRSync()) {
271     stream.PutCString("rsync");
272     if ((GetRSyncOpts() && *GetRSyncOpts()) ||
273         (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
274       stream.Printf(", options: ");
275       if (GetRSyncOpts() && *GetRSyncOpts())
276         stream.Printf("'%s' ", GetRSyncOpts());
277       stream.Printf(", prefix: ");
278       if (GetRSyncPrefix() && *GetRSyncPrefix())
279         stream.Printf("'%s' ", GetRSyncPrefix());
280       if (GetIgnoresRemoteHostname())
281         stream.Printf("ignore remote-hostname ");
282     }
283   }
284   if (GetSupportsSSH()) {
285     stream.PutCString("ssh");
286     if (GetSSHOpts() && *GetSSHOpts())
287       stream.Printf(", options: '%s' ", GetSSHOpts());
288   }
289   if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
290     stream.Printf("cache dir: %s", GetLocalCacheDirectory());
291   if (stream.GetSize())
292     return std::string(stream.GetString());
293   else
294     return "";
295 }
296 
GetRemoteUnixSignals()297 const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
298   if (IsRemote() && m_remote_platform_sp)
299     return m_remote_platform_sp->GetRemoteUnixSignals();
300   return Platform::GetRemoteUnixSignals();
301 }
302 
ConnectRemote(Args & args)303 Status PlatformPOSIX::ConnectRemote(Args &args) {
304   Status error;
305   if (IsHost()) {
306     error.SetErrorStringWithFormatv(
307         "can't connect to the host platform '{0}', always connected",
308         GetPluginName());
309   } else {
310     if (!m_remote_platform_sp)
311       m_remote_platform_sp =
312           platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
313               /*force=*/true, nullptr);
314 
315     if (m_remote_platform_sp && error.Success())
316       error = m_remote_platform_sp->ConnectRemote(args);
317     else
318       error.SetErrorString("failed to create a 'remote-gdb-server' platform");
319 
320     if (error.Fail())
321       m_remote_platform_sp.reset();
322   }
323 
324   if (error.Success() && m_remote_platform_sp) {
325     if (m_option_group_platform_rsync.get() &&
326         m_option_group_platform_ssh.get() &&
327         m_option_group_platform_caching.get()) {
328       if (m_option_group_platform_rsync->m_rsync) {
329         SetSupportsRSync(true);
330         SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
331         SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
332         SetIgnoresRemoteHostname(
333             m_option_group_platform_rsync->m_ignores_remote_hostname);
334       }
335       if (m_option_group_platform_ssh->m_ssh) {
336         SetSupportsSSH(true);
337         SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
338       }
339       SetLocalCacheDirectory(
340           m_option_group_platform_caching->m_cache_dir.c_str());
341     }
342   }
343 
344   return error;
345 }
346 
DisconnectRemote()347 Status PlatformPOSIX::DisconnectRemote() {
348   Status error;
349 
350   if (IsHost()) {
351     error.SetErrorStringWithFormatv(
352         "can't disconnect from the host platform '{0}', always connected",
353         GetPluginName());
354   } else {
355     if (m_remote_platform_sp)
356       error = m_remote_platform_sp->DisconnectRemote();
357     else
358       error.SetErrorString("the platform is not currently connected");
359   }
360   return error;
361 }
362 
Attach(ProcessAttachInfo & attach_info,Debugger & debugger,Target * target,Status & error)363 lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
364                                       Debugger &debugger, Target *target,
365                                       Status &error) {
366   lldb::ProcessSP process_sp;
367   Log *log = GetLog(LLDBLog::Platform);
368 
369   if (IsHost()) {
370     if (target == nullptr) {
371       TargetSP new_target_sp;
372 
373       error = debugger.GetTargetList().CreateTarget(
374           debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
375       target = new_target_sp.get();
376       LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__);
377     } else {
378       error.Clear();
379       LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target",
380                 __FUNCTION__);
381     }
382 
383     if (target && error.Success()) {
384       if (log) {
385         ModuleSP exe_module_sp = target->GetExecutableModule();
386         LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s",
387                   __FUNCTION__, (void *)target,
388                   exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
389                                 : "<null>");
390       }
391 
392       process_sp =
393           target->CreateProcess(attach_info.GetListenerForProcess(debugger),
394                                 "gdb-remote", nullptr, true);
395 
396       if (process_sp) {
397         ListenerSP listener_sp = attach_info.GetHijackListener();
398         if (listener_sp == nullptr) {
399           listener_sp =
400               Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
401           attach_info.SetHijackListener(listener_sp);
402         }
403         process_sp->HijackProcessEvents(listener_sp);
404         error = process_sp->Attach(attach_info);
405       }
406     }
407   } else {
408     if (m_remote_platform_sp)
409       process_sp =
410           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
411     else
412       error.SetErrorString("the platform is not currently connected");
413   }
414   return process_sp;
415 }
416 
DebugProcess(ProcessLaunchInfo & launch_info,Debugger & debugger,Target & target,Status & error)417 lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
418                                             Debugger &debugger, Target &target,
419                                             Status &error) {
420   Log *log = GetLog(LLDBLog::Platform);
421   LLDB_LOG(log, "target {0}", &target);
422 
423   ProcessSP process_sp;
424 
425   if (!IsHost()) {
426     if (m_remote_platform_sp)
427       process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
428                                                       target, error);
429     else
430       error.SetErrorString("the platform is not currently connected");
431     return process_sp;
432   }
433 
434   //
435   // For local debugging, we'll insist on having ProcessGDBRemote create the
436   // process.
437   //
438 
439   // Make sure we stop at the entry point
440   launch_info.GetFlags().Set(eLaunchFlagDebug);
441 
442   // We always launch the process we are going to debug in a separate process
443   // group, since then we can handle ^C interrupts ourselves w/o having to
444   // worry about the target getting them as well.
445   launch_info.SetLaunchInSeparateProcessGroup(true);
446 
447   // Now create the gdb-remote process.
448   LLDB_LOG(log, "having target create process with gdb-remote plugin");
449   process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
450                                     nullptr, true);
451 
452   if (!process_sp) {
453     error.SetErrorString("CreateProcess() failed for gdb-remote process");
454     LLDB_LOG(log, "error: {0}", error);
455     return process_sp;
456   }
457 
458   LLDB_LOG(log, "successfully created process");
459 
460   process_sp->HijackProcessEvents(launch_info.GetHijackListener());
461 
462   // Log file actions.
463   if (log) {
464     LLDB_LOG(log, "launching process with the following file actions:");
465     StreamString stream;
466     size_t i = 0;
467     const FileAction *file_action;
468     while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
469       file_action->Dump(stream);
470       LLDB_LOG(log, "{0}", stream.GetData());
471       stream.Clear();
472     }
473   }
474 
475   // Do the launch.
476   error = process_sp->Launch(launch_info);
477   if (error.Success()) {
478     // Hook up process PTY if we have one (which we should for local debugging
479     // with llgs).
480     int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
481     if (pty_fd != PseudoTerminal::invalid_fd) {
482       process_sp->SetSTDIOFileDescriptor(pty_fd);
483       LLDB_LOG(log, "hooked up STDIO pty to process");
484     } else
485       LLDB_LOG(log, "not using process STDIO pty");
486   } else {
487     LLDB_LOG(log, "{0}", error);
488     // FIXME figure out appropriate cleanup here. Do we delete the process?
489     // Does our caller do that?
490   }
491 
492   return process_sp;
493 }
494 
CalculateTrapHandlerSymbolNames()495 void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
496   m_trap_handlers.push_back(ConstString("_sigtramp"));
497 }
498 
EvaluateLibdlExpression(lldb_private::Process * process,const char * expr_cstr,llvm::StringRef expr_prefix,lldb::ValueObjectSP & result_valobj_sp)499 Status PlatformPOSIX::EvaluateLibdlExpression(
500     lldb_private::Process *process, const char *expr_cstr,
501     llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
502   DynamicLoader *loader = process->GetDynamicLoader();
503   if (loader) {
504     Status error = loader->CanLoadImage();
505     if (error.Fail())
506       return error;
507   }
508 
509   ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
510   if (!thread_sp)
511     return Status("Selected thread isn't valid");
512 
513   StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
514   if (!frame_sp)
515     return Status("Frame 0 isn't valid");
516 
517   ExecutionContext exe_ctx;
518   frame_sp->CalculateExecutionContext(exe_ctx);
519   EvaluateExpressionOptions expr_options;
520   expr_options.SetUnwindOnError(true);
521   expr_options.SetIgnoreBreakpoints(true);
522   expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
523   expr_options.SetLanguage(eLanguageTypeC_plus_plus);
524   expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
525                                          // don't do the work to trap them.
526   expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
527 
528   Status expr_error;
529   ExpressionResults result =
530       UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
531                                result_valobj_sp, expr_error);
532   if (result != eExpressionCompleted)
533     return expr_error;
534 
535   if (result_valobj_sp->GetError().Fail())
536     return result_valobj_sp->GetError();
537   return Status();
538 }
539 
540 std::unique_ptr<UtilityFunction>
MakeLoadImageUtilityFunction(ExecutionContext & exe_ctx,Status & error)541 PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
542                                             Status &error) {
543   // Remember to prepend this with the prefix from
544   // GetLibdlFunctionDeclarations. The returned values are all in
545   // __lldb_dlopen_result for consistency. The wrapper returns a void * but
546   // doesn't use it because UtilityFunctions don't work with void returns at
547   // present.
548   //
549   // Use lazy binding so as to not make dlopen()'s success conditional on
550   // forcing every symbol in the library.
551   //
552   // In general, the debugger should allow programs to load & run with
553   // libraries as far as they can, instead of defaulting to being super-picky
554   // about unavailable symbols.
555   //
556   // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
557   // and other POSIX OSes.
558   static const char *dlopen_wrapper_code = R"(
559   const int RTLD_LAZY = 1;
560 
561   struct __lldb_dlopen_result {
562     void *image_ptr;
563     const char *error_str;
564   };
565 
566   extern "C" void *memcpy(void *, const void *, size_t size);
567   extern "C" size_t strlen(const char *);
568 
569 
570   void * __lldb_dlopen_wrapper (const char *name,
571                                 const char *path_strings,
572                                 char *buffer,
573                                 __lldb_dlopen_result *result_ptr)
574   {
575     // This is the case where the name is the full path:
576     if (!path_strings) {
577       result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
578       if (result_ptr->image_ptr)
579         result_ptr->error_str = nullptr;
580       else
581         result_ptr->error_str = dlerror();
582       return nullptr;
583     }
584 
585     // This is the case where we have a list of paths:
586     size_t name_len = strlen(name);
587     while (path_strings && path_strings[0] != '\0') {
588       size_t path_len = strlen(path_strings);
589       memcpy((void *) buffer, (void *) path_strings, path_len);
590       buffer[path_len] = '/';
591       char *target_ptr = buffer+path_len+1;
592       memcpy((void *) target_ptr, (void *) name, name_len + 1);
593       result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
594       if (result_ptr->image_ptr) {
595         result_ptr->error_str = nullptr;
596         break;
597       }
598       result_ptr->error_str = dlerror();
599       path_strings = path_strings + path_len + 1;
600     }
601     return nullptr;
602   }
603   )";
604 
605   static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
606   Process *process = exe_ctx.GetProcessSP().get();
607   // Insert the dlopen shim defines into our generic expression:
608   std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
609   expr.append(dlopen_wrapper_code);
610   Status utility_error;
611   DiagnosticManager diagnostics;
612 
613   auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
614       std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
615   if (!utility_fn_or_error) {
616     std::string error_str = llvm::toString(utility_fn_or_error.takeError());
617     error.SetErrorStringWithFormat(
618         "dlopen error: could not create utility function: %s",
619         error_str.c_str());
620     return nullptr;
621   }
622   std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
623       std::move(*utility_fn_or_error);
624 
625   Value value;
626   ValueList arguments;
627   FunctionCaller *do_dlopen_function = nullptr;
628 
629   // Fetch the clang types we will need:
630   TypeSystemClangSP scratch_ts_sp =
631       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
632   if (!scratch_ts_sp)
633     return nullptr;
634 
635   CompilerType clang_void_pointer_type =
636       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
637   CompilerType clang_char_pointer_type =
638       scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
639 
640   // We are passing four arguments, the basename, the list of places to look,
641   // a buffer big enough for all the path + name combos, and
642   // a pointer to the storage we've made for the result:
643   value.SetValueType(Value::ValueType::Scalar);
644   value.SetCompilerType(clang_void_pointer_type);
645   arguments.PushValue(value);
646   value.SetCompilerType(clang_char_pointer_type);
647   arguments.PushValue(value);
648   arguments.PushValue(value);
649   arguments.PushValue(value);
650 
651   do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
652       clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
653   if (utility_error.Fail()) {
654     error.SetErrorStringWithFormat(
655         "dlopen error: could not make function caller: %s",
656         utility_error.AsCString());
657     return nullptr;
658   }
659 
660   do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
661   if (!do_dlopen_function) {
662     error.SetErrorString("dlopen error: could not get function caller.");
663     return nullptr;
664   }
665 
666   // We made a good utility function, so cache it in the process:
667   return dlopen_utility_func_up;
668 }
669 
DoLoadImage(lldb_private::Process * process,const lldb_private::FileSpec & remote_file,const std::vector<std::string> * paths,lldb_private::Status & error,lldb_private::FileSpec * loaded_image)670 uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
671                                     const lldb_private::FileSpec &remote_file,
672                                     const std::vector<std::string> *paths,
673                                     lldb_private::Status &error,
674                                     lldb_private::FileSpec *loaded_image) {
675   if (loaded_image)
676     loaded_image->Clear();
677 
678   std::string path;
679   path = remote_file.GetPath();
680 
681   ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
682   if (!thread_sp) {
683     error.SetErrorString("dlopen error: no thread available to call dlopen.");
684     return LLDB_INVALID_IMAGE_TOKEN;
685   }
686 
687   DiagnosticManager diagnostics;
688 
689   ExecutionContext exe_ctx;
690   thread_sp->CalculateExecutionContext(exe_ctx);
691 
692   Status utility_error;
693   UtilityFunction *dlopen_utility_func;
694   ValueList arguments;
695   FunctionCaller *do_dlopen_function = nullptr;
696 
697   // The UtilityFunction is held in the Process.  Platforms don't track the
698   // lifespan of the Targets that use them, we can't put this in the Platform.
699   dlopen_utility_func = process->GetLoadImageUtilityFunction(
700       this, [&]() -> std::unique_ptr<UtilityFunction> {
701         return MakeLoadImageUtilityFunction(exe_ctx, error);
702       });
703   // If we couldn't make it, the error will be in error, so we can exit here.
704   if (!dlopen_utility_func)
705     return LLDB_INVALID_IMAGE_TOKEN;
706 
707   do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
708   if (!do_dlopen_function) {
709     error.SetErrorString("dlopen error: could not get function caller.");
710     return LLDB_INVALID_IMAGE_TOKEN;
711   }
712   arguments = do_dlopen_function->GetArgumentValues();
713 
714   // Now insert the path we are searching for and the result structure into the
715   // target.
716   uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
717   size_t path_len = path.size() + 1;
718   lldb::addr_t path_addr = process->AllocateMemory(path_len,
719                                                    permissions,
720                                                    utility_error);
721   if (path_addr == LLDB_INVALID_ADDRESS) {
722     error.SetErrorStringWithFormat(
723         "dlopen error: could not allocate memory for path: %s",
724         utility_error.AsCString());
725     return LLDB_INVALID_IMAGE_TOKEN;
726   }
727 
728   // Make sure we deallocate the input string memory:
729   auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
730     // Deallocate the buffer.
731     process->DeallocateMemory(path_addr);
732   });
733 
734   process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
735   if (utility_error.Fail()) {
736     error.SetErrorStringWithFormat(
737         "dlopen error: could not write path string: %s",
738         utility_error.AsCString());
739     return LLDB_INVALID_IMAGE_TOKEN;
740   }
741 
742   // Make space for our return structure.  It is two pointers big: the token
743   // and the error string.
744   const uint32_t addr_size = process->GetAddressByteSize();
745   lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
746                                                       permissions,
747                                                       utility_error);
748   if (utility_error.Fail()) {
749     error.SetErrorStringWithFormat(
750         "dlopen error: could not allocate memory for path: %s",
751         utility_error.AsCString());
752     return LLDB_INVALID_IMAGE_TOKEN;
753   }
754 
755   // Make sure we deallocate the result structure memory
756   auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
757     // Deallocate the buffer
758     process->DeallocateMemory(return_addr);
759   });
760 
761   // This will be the address of the storage for paths, if we are using them,
762   // or nullptr to signal we aren't.
763   lldb::addr_t path_array_addr = 0x0;
764   std::optional<llvm::detail::scope_exit<std::function<void()>>>
765       path_array_cleanup;
766 
767   // This is the address to a buffer large enough to hold the largest path
768   // conjoined with the library name we're passing in.  This is a convenience
769   // to avoid having to call malloc in the dlopen function.
770   lldb::addr_t buffer_addr = 0x0;
771   std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup;
772 
773   // Set the values into our args and write them to the target:
774   if (paths != nullptr) {
775     // First insert the paths into the target.  This is expected to be a
776     // continuous buffer with the strings laid out null terminated and
777     // end to end with an empty string terminating the buffer.
778     // We also compute the buffer's required size as we go.
779     size_t buffer_size = 0;
780     std::string path_array;
781     for (auto path : *paths) {
782       // Don't insert empty paths, they will make us abort the path
783       // search prematurely.
784       if (path.empty())
785         continue;
786       size_t path_size = path.size();
787       path_array.append(path);
788       path_array.push_back('\0');
789       if (path_size > buffer_size)
790         buffer_size = path_size;
791     }
792     path_array.push_back('\0');
793 
794     path_array_addr = process->AllocateMemory(path_array.size(),
795                                               permissions,
796                                               utility_error);
797     if (path_array_addr == LLDB_INVALID_ADDRESS) {
798       error.SetErrorStringWithFormat(
799           "dlopen error: could not allocate memory for path array: %s",
800           utility_error.AsCString());
801       return LLDB_INVALID_IMAGE_TOKEN;
802     }
803 
804     // Make sure we deallocate the paths array.
805     path_array_cleanup.emplace([process, path_array_addr]() {
806       // Deallocate the path array.
807       process->DeallocateMemory(path_array_addr);
808     });
809 
810     process->WriteMemory(path_array_addr, path_array.data(),
811                          path_array.size(), utility_error);
812 
813     if (utility_error.Fail()) {
814       error.SetErrorStringWithFormat(
815           "dlopen error: could not write path array: %s",
816           utility_error.AsCString());
817       return LLDB_INVALID_IMAGE_TOKEN;
818     }
819     // Now make spaces in the target for the buffer.  We need to add one for
820     // the '/' that the utility function will insert and one for the '\0':
821     buffer_size += path.size() + 2;
822 
823     buffer_addr = process->AllocateMemory(buffer_size,
824                                           permissions,
825                                           utility_error);
826     if (buffer_addr == LLDB_INVALID_ADDRESS) {
827       error.SetErrorStringWithFormat(
828           "dlopen error: could not allocate memory for buffer: %s",
829           utility_error.AsCString());
830       return LLDB_INVALID_IMAGE_TOKEN;
831     }
832 
833     // Make sure we deallocate the buffer memory:
834     buffer_cleanup.emplace([process, buffer_addr]() {
835       // Deallocate the buffer.
836       process->DeallocateMemory(buffer_addr);
837     });
838   }
839 
840   arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
841   arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
842   arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
843   arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
844 
845   lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
846 
847   diagnostics.Clear();
848   if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
849                                                  func_args_addr,
850                                                  arguments,
851                                                  diagnostics)) {
852     error.SetErrorStringWithFormat(
853         "dlopen error: could not write function arguments: %s",
854         diagnostics.GetString().c_str());
855     return LLDB_INVALID_IMAGE_TOKEN;
856   }
857 
858   // Make sure we clean up the args structure.  We can't reuse it because the
859   // Platform lives longer than the process and the Platforms don't get a
860   // signal to clean up cached data when a process goes away.
861   auto args_cleanup =
862       llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
863         do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
864       });
865 
866   // Now run the caller:
867   EvaluateExpressionOptions options;
868   options.SetExecutionPolicy(eExecutionPolicyAlways);
869   options.SetLanguage(eLanguageTypeC_plus_plus);
870   options.SetIgnoreBreakpoints(true);
871   options.SetUnwindOnError(true);
872   options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
873                                     // don't do the work to trap them.
874   options.SetTimeout(process->GetUtilityExpressionTimeout());
875   options.SetIsForUtilityExpr(true);
876 
877   Value return_value;
878   // Fetch the clang types we will need:
879   TypeSystemClangSP scratch_ts_sp =
880       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
881   if (!scratch_ts_sp) {
882     error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
883     return LLDB_INVALID_IMAGE_TOKEN;
884   }
885 
886   CompilerType clang_void_pointer_type =
887       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
888 
889   return_value.SetCompilerType(clang_void_pointer_type);
890 
891   ExpressionResults results = do_dlopen_function->ExecuteFunction(
892       exe_ctx, &func_args_addr, options, diagnostics, return_value);
893   if (results != eExpressionCompleted) {
894     error.SetErrorStringWithFormat(
895         "dlopen error: failed executing dlopen wrapper function: %s",
896         diagnostics.GetString().c_str());
897     return LLDB_INVALID_IMAGE_TOKEN;
898   }
899 
900   // Read the dlopen token from the return area:
901   lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
902                                                       utility_error);
903   if (utility_error.Fail()) {
904     error.SetErrorStringWithFormat(
905         "dlopen error: could not read the return struct: %s",
906         utility_error.AsCString());
907     return LLDB_INVALID_IMAGE_TOKEN;
908   }
909 
910   // The dlopen succeeded!
911   if (token != 0x0) {
912     if (loaded_image && buffer_addr != 0x0)
913     {
914       // Capture the image which was loaded.  We leave it in the buffer on
915       // exit from the dlopen function, so we can just read it from there:
916       std::string name_string;
917       process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
918       if (utility_error.Success())
919         loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
920     }
921     return process->AddImageToken(token);
922   }
923 
924   // We got an error, lets read in the error string:
925   std::string dlopen_error_str;
926   lldb::addr_t error_addr
927     = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
928   if (utility_error.Fail()) {
929     error.SetErrorStringWithFormat(
930         "dlopen error: could not read error string: %s",
931         utility_error.AsCString());
932     return LLDB_INVALID_IMAGE_TOKEN;
933   }
934 
935   size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
936                                                     dlopen_error_str,
937                                                     utility_error);
938   if (utility_error.Success() && num_chars > 0)
939     error.SetErrorStringWithFormat("dlopen error: %s",
940                                    dlopen_error_str.c_str());
941   else
942     error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
943 
944   return LLDB_INVALID_IMAGE_TOKEN;
945 }
946 
UnloadImage(lldb_private::Process * process,uint32_t image_token)947 Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
948                                   uint32_t image_token) {
949   const addr_t image_addr = process->GetImagePtrFromToken(image_token);
950   if (image_addr == LLDB_INVALID_ADDRESS)
951     return Status("Invalid image token");
952 
953   StreamString expr;
954   expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
955   llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
956   lldb::ValueObjectSP result_valobj_sp;
957   Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
958                                          result_valobj_sp);
959   if (error.Fail())
960     return error;
961 
962   if (result_valobj_sp->GetError().Fail())
963     return result_valobj_sp->GetError();
964 
965   Scalar scalar;
966   if (result_valobj_sp->ResolveValue(scalar)) {
967     if (scalar.UInt(1))
968       return Status("expression failed: \"%s\"", expr.GetData());
969     process->ResetImageToken(image_token);
970   }
971   return Status();
972 }
973 
974 llvm::StringRef
GetLibdlFunctionDeclarations(lldb_private::Process * process)975 PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
976   return R"(
977               extern "C" void* dlopen(const char*, int);
978               extern "C" void* dlsym(void*, const char*);
979               extern "C" int   dlclose(void*);
980               extern "C" char* dlerror(void);
981              )";
982 }
983 
GetFullNameForDylib(ConstString basename)984 ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
985   if (basename.IsEmpty())
986     return basename;
987 
988   StreamString stream;
989   stream.Printf("lib%s.so", basename.GetCString());
990   return ConstString(stream.GetString());
991 }
992