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
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 
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 
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
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 
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 
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 
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 
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 
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 
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         process_sp->SetShadowListener(attach_info.GetShadowListener());
405         error = process_sp->Attach(attach_info);
406       }
407     }
408   } else {
409     if (m_remote_platform_sp)
410       process_sp =
411           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
412     else
413       error.SetErrorString("the platform is not currently connected");
414   }
415   return process_sp;
416 }
417 
418 lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
419                                             Debugger &debugger, Target &target,
420                                             Status &error) {
421   Log *log = GetLog(LLDBLog::Platform);
422   LLDB_LOG(log, "target {0}", &target);
423 
424   ProcessSP process_sp;
425 
426   if (!IsHost()) {
427     if (m_remote_platform_sp)
428       process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
429                                                       target, error);
430     else
431       error.SetErrorString("the platform is not currently connected");
432     return process_sp;
433   }
434 
435   //
436   // For local debugging, we'll insist on having ProcessGDBRemote create the
437   // process.
438   //
439 
440   // Make sure we stop at the entry point
441   launch_info.GetFlags().Set(eLaunchFlagDebug);
442 
443   // We always launch the process we are going to debug in a separate process
444   // group, since then we can handle ^C interrupts ourselves w/o having to
445   // worry about the target getting them as well.
446   launch_info.SetLaunchInSeparateProcessGroup(true);
447 
448   // Now create the gdb-remote process.
449   LLDB_LOG(log, "having target create process with gdb-remote plugin");
450   process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
451                                     nullptr, true);
452 
453   if (!process_sp) {
454     error.SetErrorString("CreateProcess() failed for gdb-remote process");
455     LLDB_LOG(log, "error: {0}", error);
456     return process_sp;
457   }
458 
459   LLDB_LOG(log, "successfully created process");
460 
461   process_sp->HijackProcessEvents(launch_info.GetHijackListener());
462   process_sp->SetShadowListener(launch_info.GetShadowListener());
463 
464   // Log file actions.
465   if (log) {
466     LLDB_LOG(log, "launching process with the following file actions:");
467     StreamString stream;
468     size_t i = 0;
469     const FileAction *file_action;
470     while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
471       file_action->Dump(stream);
472       LLDB_LOG(log, "{0}", stream.GetData());
473       stream.Clear();
474     }
475   }
476 
477   // Do the launch.
478   error = process_sp->Launch(launch_info);
479   if (error.Success()) {
480     // Hook up process PTY if we have one (which we should for local debugging
481     // with llgs).
482     int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
483     if (pty_fd != PseudoTerminal::invalid_fd) {
484       process_sp->SetSTDIOFileDescriptor(pty_fd);
485       LLDB_LOG(log, "hooked up STDIO pty to process");
486     } else
487       LLDB_LOG(log, "not using process STDIO pty");
488   } else {
489     LLDB_LOG(log, "{0}", error);
490     // FIXME figure out appropriate cleanup here. Do we delete the process?
491     // Does our caller do that?
492   }
493 
494   return process_sp;
495 }
496 
497 void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
498   m_trap_handlers.push_back(ConstString("_sigtramp"));
499 }
500 
501 Status PlatformPOSIX::EvaluateLibdlExpression(
502     lldb_private::Process *process, const char *expr_cstr,
503     llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
504   DynamicLoader *loader = process->GetDynamicLoader();
505   if (loader) {
506     Status error = loader->CanLoadImage();
507     if (error.Fail())
508       return error;
509   }
510 
511   ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
512   if (!thread_sp)
513     return Status("Selected thread isn't valid");
514 
515   StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
516   if (!frame_sp)
517     return Status("Frame 0 isn't valid");
518 
519   ExecutionContext exe_ctx;
520   frame_sp->CalculateExecutionContext(exe_ctx);
521   EvaluateExpressionOptions expr_options;
522   expr_options.SetUnwindOnError(true);
523   expr_options.SetIgnoreBreakpoints(true);
524   expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
525   expr_options.SetLanguage(eLanguageTypeC_plus_plus);
526   expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
527                                          // don't do the work to trap them.
528   expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
529 
530   Status expr_error;
531   ExpressionResults result =
532       UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
533                                result_valobj_sp, expr_error);
534   if (result != eExpressionCompleted)
535     return expr_error;
536 
537   if (result_valobj_sp->GetError().Fail())
538     return result_valobj_sp->GetError();
539   return Status();
540 }
541 
542 std::unique_ptr<UtilityFunction>
543 PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
544                                             Status &error) {
545   // Remember to prepend this with the prefix from
546   // GetLibdlFunctionDeclarations. The returned values are all in
547   // __lldb_dlopen_result for consistency. The wrapper returns a void * but
548   // doesn't use it because UtilityFunctions don't work with void returns at
549   // present.
550   //
551   // Use lazy binding so as to not make dlopen()'s success conditional on
552   // forcing every symbol in the library.
553   //
554   // In general, the debugger should allow programs to load & run with
555   // libraries as far as they can, instead of defaulting to being super-picky
556   // about unavailable symbols.
557   //
558   // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
559   // and other POSIX OSes.
560   static const char *dlopen_wrapper_code = R"(
561   const int RTLD_LAZY = 1;
562 
563   struct __lldb_dlopen_result {
564     void *image_ptr;
565     const char *error_str;
566   };
567 
568   extern "C" void *memcpy(void *, const void *, size_t size);
569   extern "C" size_t strlen(const char *);
570 
571 
572   void * __lldb_dlopen_wrapper (const char *name,
573                                 const char *path_strings,
574                                 char *buffer,
575                                 __lldb_dlopen_result *result_ptr)
576   {
577     // This is the case where the name is the full path:
578     if (!path_strings) {
579       result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
580       if (result_ptr->image_ptr)
581         result_ptr->error_str = nullptr;
582       else
583         result_ptr->error_str = dlerror();
584       return nullptr;
585     }
586 
587     // This is the case where we have a list of paths:
588     size_t name_len = strlen(name);
589     while (path_strings && path_strings[0] != '\0') {
590       size_t path_len = strlen(path_strings);
591       memcpy((void *) buffer, (void *) path_strings, path_len);
592       buffer[path_len] = '/';
593       char *target_ptr = buffer+path_len+1;
594       memcpy((void *) target_ptr, (void *) name, name_len + 1);
595       result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
596       if (result_ptr->image_ptr) {
597         result_ptr->error_str = nullptr;
598         break;
599       }
600       result_ptr->error_str = dlerror();
601       path_strings = path_strings + path_len + 1;
602     }
603     return nullptr;
604   }
605   )";
606 
607   static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
608   Process *process = exe_ctx.GetProcessSP().get();
609   // Insert the dlopen shim defines into our generic expression:
610   std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
611   expr.append(dlopen_wrapper_code);
612   Status utility_error;
613   DiagnosticManager diagnostics;
614 
615   auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
616       std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
617   if (!utility_fn_or_error) {
618     std::string error_str = llvm::toString(utility_fn_or_error.takeError());
619     error.SetErrorStringWithFormat(
620         "dlopen error: could not create utility function: %s",
621         error_str.c_str());
622     return nullptr;
623   }
624   std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
625       std::move(*utility_fn_or_error);
626 
627   Value value;
628   ValueList arguments;
629   FunctionCaller *do_dlopen_function = nullptr;
630 
631   // Fetch the clang types we will need:
632   TypeSystemClangSP scratch_ts_sp =
633       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
634   if (!scratch_ts_sp)
635     return nullptr;
636 
637   CompilerType clang_void_pointer_type =
638       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
639   CompilerType clang_char_pointer_type =
640       scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
641 
642   // We are passing four arguments, the basename, the list of places to look,
643   // a buffer big enough for all the path + name combos, and
644   // a pointer to the storage we've made for the result:
645   value.SetValueType(Value::ValueType::Scalar);
646   value.SetCompilerType(clang_void_pointer_type);
647   arguments.PushValue(value);
648   value.SetCompilerType(clang_char_pointer_type);
649   arguments.PushValue(value);
650   arguments.PushValue(value);
651   arguments.PushValue(value);
652 
653   do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
654       clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
655   if (utility_error.Fail()) {
656     error.SetErrorStringWithFormat(
657         "dlopen error: could not make function caller: %s",
658         utility_error.AsCString());
659     return nullptr;
660   }
661 
662   do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
663   if (!do_dlopen_function) {
664     error.SetErrorString("dlopen error: could not get function caller.");
665     return nullptr;
666   }
667 
668   // We made a good utility function, so cache it in the process:
669   return dlopen_utility_func_up;
670 }
671 
672 uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
673                                     const lldb_private::FileSpec &remote_file,
674                                     const std::vector<std::string> *paths,
675                                     lldb_private::Status &error,
676                                     lldb_private::FileSpec *loaded_image) {
677   if (loaded_image)
678     loaded_image->Clear();
679 
680   std::string path;
681   path = remote_file.GetPath();
682 
683   ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
684   if (!thread_sp) {
685     error.SetErrorString("dlopen error: no thread available to call dlopen.");
686     return LLDB_INVALID_IMAGE_TOKEN;
687   }
688 
689   DiagnosticManager diagnostics;
690 
691   ExecutionContext exe_ctx;
692   thread_sp->CalculateExecutionContext(exe_ctx);
693 
694   Status utility_error;
695   UtilityFunction *dlopen_utility_func;
696   ValueList arguments;
697   FunctionCaller *do_dlopen_function = nullptr;
698 
699   // The UtilityFunction is held in the Process.  Platforms don't track the
700   // lifespan of the Targets that use them, we can't put this in the Platform.
701   dlopen_utility_func = process->GetLoadImageUtilityFunction(
702       this, [&]() -> std::unique_ptr<UtilityFunction> {
703         return MakeLoadImageUtilityFunction(exe_ctx, error);
704       });
705   // If we couldn't make it, the error will be in error, so we can exit here.
706   if (!dlopen_utility_func)
707     return LLDB_INVALID_IMAGE_TOKEN;
708 
709   do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
710   if (!do_dlopen_function) {
711     error.SetErrorString("dlopen error: could not get function caller.");
712     return LLDB_INVALID_IMAGE_TOKEN;
713   }
714   arguments = do_dlopen_function->GetArgumentValues();
715 
716   // Now insert the path we are searching for and the result structure into the
717   // target.
718   uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
719   size_t path_len = path.size() + 1;
720   lldb::addr_t path_addr = process->AllocateMemory(path_len,
721                                                    permissions,
722                                                    utility_error);
723   if (path_addr == LLDB_INVALID_ADDRESS) {
724     error.SetErrorStringWithFormat(
725         "dlopen error: could not allocate memory for path: %s",
726         utility_error.AsCString());
727     return LLDB_INVALID_IMAGE_TOKEN;
728   }
729 
730   // Make sure we deallocate the input string memory:
731   auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
732     // Deallocate the buffer.
733     process->DeallocateMemory(path_addr);
734   });
735 
736   process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
737   if (utility_error.Fail()) {
738     error.SetErrorStringWithFormat(
739         "dlopen error: could not write path string: %s",
740         utility_error.AsCString());
741     return LLDB_INVALID_IMAGE_TOKEN;
742   }
743 
744   // Make space for our return structure.  It is two pointers big: the token
745   // and the error string.
746   const uint32_t addr_size = process->GetAddressByteSize();
747   lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
748                                                       permissions,
749                                                       utility_error);
750   if (utility_error.Fail()) {
751     error.SetErrorStringWithFormat(
752         "dlopen error: could not allocate memory for path: %s",
753         utility_error.AsCString());
754     return LLDB_INVALID_IMAGE_TOKEN;
755   }
756 
757   // Make sure we deallocate the result structure memory
758   auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
759     // Deallocate the buffer
760     process->DeallocateMemory(return_addr);
761   });
762 
763   // This will be the address of the storage for paths, if we are using them,
764   // or nullptr to signal we aren't.
765   lldb::addr_t path_array_addr = 0x0;
766   std::optional<llvm::detail::scope_exit<std::function<void()>>>
767       path_array_cleanup;
768 
769   // This is the address to a buffer large enough to hold the largest path
770   // conjoined with the library name we're passing in.  This is a convenience
771   // to avoid having to call malloc in the dlopen function.
772   lldb::addr_t buffer_addr = 0x0;
773   std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup;
774 
775   // Set the values into our args and write them to the target:
776   if (paths != nullptr) {
777     // First insert the paths into the target.  This is expected to be a
778     // continuous buffer with the strings laid out null terminated and
779     // end to end with an empty string terminating the buffer.
780     // We also compute the buffer's required size as we go.
781     size_t buffer_size = 0;
782     std::string path_array;
783     for (auto path : *paths) {
784       // Don't insert empty paths, they will make us abort the path
785       // search prematurely.
786       if (path.empty())
787         continue;
788       size_t path_size = path.size();
789       path_array.append(path);
790       path_array.push_back('\0');
791       if (path_size > buffer_size)
792         buffer_size = path_size;
793     }
794     path_array.push_back('\0');
795 
796     path_array_addr = process->AllocateMemory(path_array.size(),
797                                               permissions,
798                                               utility_error);
799     if (path_array_addr == LLDB_INVALID_ADDRESS) {
800       error.SetErrorStringWithFormat(
801           "dlopen error: could not allocate memory for path array: %s",
802           utility_error.AsCString());
803       return LLDB_INVALID_IMAGE_TOKEN;
804     }
805 
806     // Make sure we deallocate the paths array.
807     path_array_cleanup.emplace([process, path_array_addr]() {
808       // Deallocate the path array.
809       process->DeallocateMemory(path_array_addr);
810     });
811 
812     process->WriteMemory(path_array_addr, path_array.data(),
813                          path_array.size(), utility_error);
814 
815     if (utility_error.Fail()) {
816       error.SetErrorStringWithFormat(
817           "dlopen error: could not write path array: %s",
818           utility_error.AsCString());
819       return LLDB_INVALID_IMAGE_TOKEN;
820     }
821     // Now make spaces in the target for the buffer.  We need to add one for
822     // the '/' that the utility function will insert and one for the '\0':
823     buffer_size += path.size() + 2;
824 
825     buffer_addr = process->AllocateMemory(buffer_size,
826                                           permissions,
827                                           utility_error);
828     if (buffer_addr == LLDB_INVALID_ADDRESS) {
829       error.SetErrorStringWithFormat(
830           "dlopen error: could not allocate memory for buffer: %s",
831           utility_error.AsCString());
832       return LLDB_INVALID_IMAGE_TOKEN;
833     }
834 
835     // Make sure we deallocate the buffer memory:
836     buffer_cleanup.emplace([process, buffer_addr]() {
837       // Deallocate the buffer.
838       process->DeallocateMemory(buffer_addr);
839     });
840   }
841 
842   arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
843   arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
844   arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
845   arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
846 
847   lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
848 
849   diagnostics.Clear();
850   if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
851                                                  func_args_addr,
852                                                  arguments,
853                                                  diagnostics)) {
854     error.SetErrorStringWithFormat(
855         "dlopen error: could not write function arguments: %s",
856         diagnostics.GetString().c_str());
857     return LLDB_INVALID_IMAGE_TOKEN;
858   }
859 
860   // Make sure we clean up the args structure.  We can't reuse it because the
861   // Platform lives longer than the process and the Platforms don't get a
862   // signal to clean up cached data when a process goes away.
863   auto args_cleanup =
864       llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
865         do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
866       });
867 
868   // Now run the caller:
869   EvaluateExpressionOptions options;
870   options.SetExecutionPolicy(eExecutionPolicyAlways);
871   options.SetLanguage(eLanguageTypeC_plus_plus);
872   options.SetIgnoreBreakpoints(true);
873   options.SetUnwindOnError(true);
874   options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
875                                     // don't do the work to trap them.
876   options.SetTimeout(process->GetUtilityExpressionTimeout());
877   options.SetIsForUtilityExpr(true);
878 
879   Value return_value;
880   // Fetch the clang types we will need:
881   TypeSystemClangSP scratch_ts_sp =
882       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
883   if (!scratch_ts_sp) {
884     error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
885     return LLDB_INVALID_IMAGE_TOKEN;
886   }
887 
888   CompilerType clang_void_pointer_type =
889       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
890 
891   return_value.SetCompilerType(clang_void_pointer_type);
892 
893   ExpressionResults results = do_dlopen_function->ExecuteFunction(
894       exe_ctx, &func_args_addr, options, diagnostics, return_value);
895   if (results != eExpressionCompleted) {
896     error.SetErrorStringWithFormat(
897         "dlopen error: failed executing dlopen wrapper function: %s",
898         diagnostics.GetString().c_str());
899     return LLDB_INVALID_IMAGE_TOKEN;
900   }
901 
902   // Read the dlopen token from the return area:
903   lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
904                                                       utility_error);
905   if (utility_error.Fail()) {
906     error.SetErrorStringWithFormat(
907         "dlopen error: could not read the return struct: %s",
908         utility_error.AsCString());
909     return LLDB_INVALID_IMAGE_TOKEN;
910   }
911 
912   // The dlopen succeeded!
913   if (token != 0x0) {
914     if (loaded_image && buffer_addr != 0x0)
915     {
916       // Capture the image which was loaded.  We leave it in the buffer on
917       // exit from the dlopen function, so we can just read it from there:
918       std::string name_string;
919       process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
920       if (utility_error.Success())
921         loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
922     }
923     return process->AddImageToken(token);
924   }
925 
926   // We got an error, lets read in the error string:
927   std::string dlopen_error_str;
928   lldb::addr_t error_addr
929     = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
930   if (utility_error.Fail()) {
931     error.SetErrorStringWithFormat(
932         "dlopen error: could not read error string: %s",
933         utility_error.AsCString());
934     return LLDB_INVALID_IMAGE_TOKEN;
935   }
936 
937   size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
938                                                     dlopen_error_str,
939                                                     utility_error);
940   if (utility_error.Success() && num_chars > 0)
941     error.SetErrorStringWithFormat("dlopen error: %s",
942                                    dlopen_error_str.c_str());
943   else
944     error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
945 
946   return LLDB_INVALID_IMAGE_TOKEN;
947 }
948 
949 Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
950                                   uint32_t image_token) {
951   const addr_t image_addr = process->GetImagePtrFromToken(image_token);
952   if (image_addr == LLDB_INVALID_ADDRESS)
953     return Status("Invalid image token");
954 
955   StreamString expr;
956   expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
957   llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
958   lldb::ValueObjectSP result_valobj_sp;
959   Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
960                                          result_valobj_sp);
961   if (error.Fail())
962     return error;
963 
964   if (result_valobj_sp->GetError().Fail())
965     return result_valobj_sp->GetError();
966 
967   Scalar scalar;
968   if (result_valobj_sp->ResolveValue(scalar)) {
969     if (scalar.UInt(1))
970       return Status("expression failed: \"%s\"", expr.GetData());
971     process->ResetImageToken(image_token);
972   }
973   return Status();
974 }
975 
976 llvm::StringRef
977 PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
978   return R"(
979               extern "C" void* dlopen(const char*, int);
980               extern "C" void* dlsym(void*, const char*);
981               extern "C" int   dlclose(void*);
982               extern "C" char* dlerror(void);
983              )";
984 }
985 
986 ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
987   if (basename.IsEmpty())
988     return basename;
989 
990   StreamString stream;
991   stream.Printf("lib%s.so", basename.GetCString());
992   return ConstString(stream.GetString());
993 }
994