xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===-- PlatformPOSIX.cpp -------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "PlatformPOSIX.h"
100b57cec5SDimitry Andric 
1181ad6265SDimitry Andric #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
125ffd83dbSDimitry Andric #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
130b57cec5SDimitry Andric #include "lldb/Core/Debugger.h"
140b57cec5SDimitry Andric #include "lldb/Core/Module.h"
150b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h"
160b57cec5SDimitry Andric #include "lldb/Expression/DiagnosticManager.h"
170b57cec5SDimitry Andric #include "lldb/Expression/FunctionCaller.h"
180b57cec5SDimitry Andric #include "lldb/Expression/UserExpression.h"
190b57cec5SDimitry Andric #include "lldb/Expression/UtilityFunction.h"
200b57cec5SDimitry Andric #include "lldb/Host/File.h"
210b57cec5SDimitry Andric #include "lldb/Host/FileCache.h"
220b57cec5SDimitry Andric #include "lldb/Host/FileSystem.h"
230b57cec5SDimitry Andric #include "lldb/Host/Host.h"
240b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h"
250b57cec5SDimitry Andric #include "lldb/Host/ProcessLaunchInfo.h"
260b57cec5SDimitry Andric #include "lldb/Target/DynamicLoader.h"
270b57cec5SDimitry Andric #include "lldb/Target/ExecutionContext.h"
280b57cec5SDimitry Andric #include "lldb/Target/Process.h"
290b57cec5SDimitry Andric #include "lldb/Target/Thread.h"
300b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
310b57cec5SDimitry Andric #include "lldb/Utility/FileSpec.h"
3281ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
330b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
340b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
359dba64beSDimitry Andric #include "llvm/ADT/ScopeExit.h"
36bdd1243dSDimitry Andric #include <optional>
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric using namespace lldb;
390b57cec5SDimitry Andric using namespace lldb_private;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric /// Default Constructor
420b57cec5SDimitry Andric PlatformPOSIX::PlatformPOSIX(bool is_host)
430b57cec5SDimitry Andric     : RemoteAwarePlatform(is_host), // This is the local host platform
440b57cec5SDimitry Andric       m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
450b57cec5SDimitry Andric       m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
460b57cec5SDimitry Andric       m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric /// Destructor.
490b57cec5SDimitry Andric ///
500b57cec5SDimitry Andric /// The destructor is virtual since this class is designed to be
510b57cec5SDimitry Andric /// inherited from by the plug-in instance.
52fe6060f1SDimitry Andric PlatformPOSIX::~PlatformPOSIX() = default;
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
550b57cec5SDimitry Andric     lldb_private::CommandInterpreter &interpreter) {
560b57cec5SDimitry Andric   auto iter = m_options.find(&interpreter), end = m_options.end();
570b57cec5SDimitry Andric   if (iter == end) {
580b57cec5SDimitry Andric     std::unique_ptr<lldb_private::OptionGroupOptions> options(
590b57cec5SDimitry Andric         new OptionGroupOptions());
600b57cec5SDimitry Andric     options->Append(m_option_group_platform_rsync.get());
610b57cec5SDimitry Andric     options->Append(m_option_group_platform_ssh.get());
620b57cec5SDimitry Andric     options->Append(m_option_group_platform_caching.get());
630b57cec5SDimitry Andric     m_options[&interpreter] = std::move(options);
640b57cec5SDimitry Andric   }
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   return m_options.at(&interpreter).get();
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric static uint32_t chown_file(Platform *platform, const char *path,
700b57cec5SDimitry Andric                            uint32_t uid = UINT32_MAX,
710b57cec5SDimitry Andric                            uint32_t gid = UINT32_MAX) {
720b57cec5SDimitry Andric   if (!platform || !path || *path == 0)
730b57cec5SDimitry Andric     return UINT32_MAX;
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   if (uid == UINT32_MAX && gid == UINT32_MAX)
760b57cec5SDimitry Andric     return 0; // pretend I did chown correctly - actually I just didn't care
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric   StreamString command;
790b57cec5SDimitry Andric   command.PutCString("chown ");
800b57cec5SDimitry Andric   if (uid != UINT32_MAX)
810b57cec5SDimitry Andric     command.Printf("%d", uid);
820b57cec5SDimitry Andric   if (gid != UINT32_MAX)
830b57cec5SDimitry Andric     command.Printf(":%d", gid);
840b57cec5SDimitry Andric   command.Printf("%s", path);
850b57cec5SDimitry Andric   int status;
86480093f4SDimitry Andric   platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
870b57cec5SDimitry Andric                             nullptr, std::chrono::seconds(10));
880b57cec5SDimitry Andric   return status;
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric lldb_private::Status
920b57cec5SDimitry Andric PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
930b57cec5SDimitry Andric                        const lldb_private::FileSpec &destination, uint32_t uid,
940b57cec5SDimitry Andric                        uint32_t gid) {
9581ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Platform);
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   if (IsHost()) {
98480093f4SDimitry Andric     if (source == destination)
990b57cec5SDimitry Andric       return Status();
1000b57cec5SDimitry Andric     // cp src dst
1010b57cec5SDimitry Andric     // chown uid:gid dst
1020b57cec5SDimitry Andric     std::string src_path(source.GetPath());
1030b57cec5SDimitry Andric     if (src_path.empty())
1040b57cec5SDimitry Andric       return Status("unable to get file path for source");
1050b57cec5SDimitry Andric     std::string dst_path(destination.GetPath());
1060b57cec5SDimitry Andric     if (dst_path.empty())
1070b57cec5SDimitry Andric       return Status("unable to get file path for destination");
1080b57cec5SDimitry Andric     StreamString command;
1090b57cec5SDimitry Andric     command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
1100b57cec5SDimitry Andric     int status;
111480093f4SDimitry Andric     RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
1120b57cec5SDimitry Andric                     std::chrono::seconds(10));
1130b57cec5SDimitry Andric     if (status != 0)
1140b57cec5SDimitry Andric       return Status("unable to perform copy");
1150b57cec5SDimitry Andric     if (uid == UINT32_MAX && gid == UINT32_MAX)
1160b57cec5SDimitry Andric       return Status();
1170b57cec5SDimitry Andric     if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
1180b57cec5SDimitry Andric       return Status("unable to perform chown");
1190b57cec5SDimitry Andric     return Status();
1200b57cec5SDimitry Andric   } else if (m_remote_platform_sp) {
1210b57cec5SDimitry Andric     if (GetSupportsRSync()) {
1220b57cec5SDimitry Andric       std::string src_path(source.GetPath());
1230b57cec5SDimitry Andric       if (src_path.empty())
1240b57cec5SDimitry Andric         return Status("unable to get file path for source");
1250b57cec5SDimitry Andric       std::string dst_path(destination.GetPath());
1260b57cec5SDimitry Andric       if (dst_path.empty())
1270b57cec5SDimitry Andric         return Status("unable to get file path for destination");
1280b57cec5SDimitry Andric       StreamString command;
1290b57cec5SDimitry Andric       if (GetIgnoresRemoteHostname()) {
1300b57cec5SDimitry Andric         if (!GetRSyncPrefix())
1310b57cec5SDimitry Andric           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
1320b57cec5SDimitry Andric                          dst_path.c_str());
1330b57cec5SDimitry Andric         else
1340b57cec5SDimitry Andric           command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
1350b57cec5SDimitry Andric                          GetRSyncPrefix(), dst_path.c_str());
1360b57cec5SDimitry Andric       } else
1370b57cec5SDimitry Andric         command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
1380b57cec5SDimitry Andric                        GetHostname(), dst_path.c_str());
1399dba64beSDimitry Andric       LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData());
1400b57cec5SDimitry Andric       int retcode;
141480093f4SDimitry Andric       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
1420b57cec5SDimitry Andric                             nullptr, std::chrono::minutes(1));
1430b57cec5SDimitry Andric       if (retcode == 0) {
1440b57cec5SDimitry Andric         // Don't chown a local file for a remote system
1450b57cec5SDimitry Andric         //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
1460b57cec5SDimitry Andric         //                    return Status("unable to perform chown");
1470b57cec5SDimitry Andric         return Status();
1480b57cec5SDimitry Andric       }
1490b57cec5SDimitry Andric       // if we are still here rsync has failed - let's try the slow way before
1500b57cec5SDimitry Andric       // giving up
1510b57cec5SDimitry Andric     }
1520b57cec5SDimitry Andric   }
1530b57cec5SDimitry Andric   return Platform::PutFile(source, destination, uid, gid);
1540b57cec5SDimitry Andric }
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric lldb_private::Status PlatformPOSIX::GetFile(
1570b57cec5SDimitry Andric     const lldb_private::FileSpec &source,      // remote file path
1580b57cec5SDimitry Andric     const lldb_private::FileSpec &destination) // local file path
1590b57cec5SDimitry Andric {
16081ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Platform);
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric   // Check the args, first.
1630b57cec5SDimitry Andric   std::string src_path(source.GetPath());
1640b57cec5SDimitry Andric   if (src_path.empty())
1650b57cec5SDimitry Andric     return Status("unable to get file path for source");
1660b57cec5SDimitry Andric   std::string dst_path(destination.GetPath());
1670b57cec5SDimitry Andric   if (dst_path.empty())
1680b57cec5SDimitry Andric     return Status("unable to get file path for destination");
1690b57cec5SDimitry Andric   if (IsHost()) {
170480093f4SDimitry Andric     if (source == destination)
1710b57cec5SDimitry Andric       return Status("local scenario->source and destination are the same file "
1720b57cec5SDimitry Andric                     "path: no operation performed");
1730b57cec5SDimitry Andric     // cp src dst
1740b57cec5SDimitry Andric     StreamString cp_command;
1750b57cec5SDimitry Andric     cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
1760b57cec5SDimitry Andric     int status;
177480093f4SDimitry Andric     RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
1780b57cec5SDimitry Andric                     std::chrono::seconds(10));
1790b57cec5SDimitry Andric     if (status != 0)
1800b57cec5SDimitry Andric       return Status("unable to perform copy");
1810b57cec5SDimitry Andric     return Status();
1820b57cec5SDimitry Andric   } else if (m_remote_platform_sp) {
1830b57cec5SDimitry Andric     if (GetSupportsRSync()) {
1840b57cec5SDimitry Andric       StreamString command;
1850b57cec5SDimitry Andric       if (GetIgnoresRemoteHostname()) {
1860b57cec5SDimitry Andric         if (!GetRSyncPrefix())
1870b57cec5SDimitry Andric           command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
1880b57cec5SDimitry Andric                          dst_path.c_str());
1890b57cec5SDimitry Andric         else
1900b57cec5SDimitry Andric           command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
1910b57cec5SDimitry Andric                          src_path.c_str(), dst_path.c_str());
1920b57cec5SDimitry Andric       } else
1930b57cec5SDimitry Andric         command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
1940b57cec5SDimitry Andric                        m_remote_platform_sp->GetHostname(), src_path.c_str(),
1950b57cec5SDimitry Andric                        dst_path.c_str());
1969dba64beSDimitry Andric       LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData());
1970b57cec5SDimitry Andric       int retcode;
198480093f4SDimitry Andric       Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
1990b57cec5SDimitry Andric                             nullptr, std::chrono::minutes(1));
2000b57cec5SDimitry Andric       if (retcode == 0)
2010b57cec5SDimitry Andric         return Status();
2020b57cec5SDimitry Andric       // If we are here, rsync has failed - let's try the slow way before
2030b57cec5SDimitry Andric       // giving up
2040b57cec5SDimitry Andric     }
2050b57cec5SDimitry Andric     // open src and dst
2060b57cec5SDimitry Andric     // read/write, read/write, read/write, ...
2070b57cec5SDimitry Andric     // close src
2080b57cec5SDimitry Andric     // close dst
2099dba64beSDimitry Andric     LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n");
2100b57cec5SDimitry Andric     Status error;
211349cc55cSDimitry Andric     user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly,
2120b57cec5SDimitry Andric                                 lldb::eFilePermissionsFileDefault, error);
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric     if (fd_src == UINT64_MAX)
2150b57cec5SDimitry Andric       return Status("unable to open source file");
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric     uint32_t permissions = 0;
2180b57cec5SDimitry Andric     error = GetFilePermissions(source, permissions);
2190b57cec5SDimitry Andric 
2200b57cec5SDimitry Andric     if (permissions == 0)
2210b57cec5SDimitry Andric       permissions = lldb::eFilePermissionsFileDefault;
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric     user_id_t fd_dst = FileCache::GetInstance().OpenFile(
224349cc55cSDimitry Andric         destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly |
2250b57cec5SDimitry Andric                          File::eOpenOptionTruncate,
2260b57cec5SDimitry Andric         permissions, error);
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric     if (fd_dst == UINT64_MAX) {
2290b57cec5SDimitry Andric       if (error.Success())
2300b57cec5SDimitry Andric         error.SetErrorString("unable to open destination file");
2310b57cec5SDimitry Andric     }
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric     if (error.Success()) {
23481ad6265SDimitry Andric       lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
2350b57cec5SDimitry Andric       uint64_t offset = 0;
2360b57cec5SDimitry Andric       error.Clear();
2370b57cec5SDimitry Andric       while (error.Success()) {
2380b57cec5SDimitry Andric         const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
2390b57cec5SDimitry Andric                                          buffer_sp->GetByteSize(), error);
2400b57cec5SDimitry Andric         if (error.Fail())
2410b57cec5SDimitry Andric           break;
2420b57cec5SDimitry Andric         if (n_read == 0)
2430b57cec5SDimitry Andric           break;
2440b57cec5SDimitry Andric         if (FileCache::GetInstance().WriteFile(fd_dst, offset,
2450b57cec5SDimitry Andric                                                buffer_sp->GetBytes(), n_read,
2460b57cec5SDimitry Andric                                                error) != n_read) {
2470b57cec5SDimitry Andric           if (!error.Fail())
2480b57cec5SDimitry Andric             error.SetErrorString("unable to write to destination file");
2490b57cec5SDimitry Andric           break;
2500b57cec5SDimitry Andric         }
2510b57cec5SDimitry Andric         offset += n_read;
2520b57cec5SDimitry Andric       }
2530b57cec5SDimitry Andric     }
2540b57cec5SDimitry Andric     // Ignore the close error of src.
2550b57cec5SDimitry Andric     if (fd_src != UINT64_MAX)
2560b57cec5SDimitry Andric       CloseFile(fd_src, error);
2570b57cec5SDimitry Andric     // And close the dst file descriptot.
2580b57cec5SDimitry Andric     if (fd_dst != UINT64_MAX &&
2590b57cec5SDimitry Andric         !FileCache::GetInstance().CloseFile(fd_dst, error)) {
2600b57cec5SDimitry Andric       if (!error.Fail())
2610b57cec5SDimitry Andric         error.SetErrorString("unable to close destination file");
2620b57cec5SDimitry Andric     }
2630b57cec5SDimitry Andric     return error;
2640b57cec5SDimitry Andric   }
2650b57cec5SDimitry Andric   return Platform::GetFile(source, destination);
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric 
2680b57cec5SDimitry Andric std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
2690b57cec5SDimitry Andric   StreamString stream;
2700b57cec5SDimitry Andric   if (GetSupportsRSync()) {
2710b57cec5SDimitry Andric     stream.PutCString("rsync");
2720b57cec5SDimitry Andric     if ((GetRSyncOpts() && *GetRSyncOpts()) ||
2730b57cec5SDimitry Andric         (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
2740b57cec5SDimitry Andric       stream.Printf(", options: ");
2750b57cec5SDimitry Andric       if (GetRSyncOpts() && *GetRSyncOpts())
2760b57cec5SDimitry Andric         stream.Printf("'%s' ", GetRSyncOpts());
2770b57cec5SDimitry Andric       stream.Printf(", prefix: ");
2780b57cec5SDimitry Andric       if (GetRSyncPrefix() && *GetRSyncPrefix())
2790b57cec5SDimitry Andric         stream.Printf("'%s' ", GetRSyncPrefix());
2800b57cec5SDimitry Andric       if (GetIgnoresRemoteHostname())
2810b57cec5SDimitry Andric         stream.Printf("ignore remote-hostname ");
2820b57cec5SDimitry Andric     }
2830b57cec5SDimitry Andric   }
2840b57cec5SDimitry Andric   if (GetSupportsSSH()) {
2850b57cec5SDimitry Andric     stream.PutCString("ssh");
2860b57cec5SDimitry Andric     if (GetSSHOpts() && *GetSSHOpts())
2870b57cec5SDimitry Andric       stream.Printf(", options: '%s' ", GetSSHOpts());
2880b57cec5SDimitry Andric   }
2890b57cec5SDimitry Andric   if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
2900b57cec5SDimitry Andric     stream.Printf("cache dir: %s", GetLocalCacheDirectory());
2910b57cec5SDimitry Andric   if (stream.GetSize())
2925ffd83dbSDimitry Andric     return std::string(stream.GetString());
2930b57cec5SDimitry Andric   else
2940b57cec5SDimitry Andric     return "";
2950b57cec5SDimitry Andric }
2960b57cec5SDimitry Andric 
2970b57cec5SDimitry Andric const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
2980b57cec5SDimitry Andric   if (IsRemote() && m_remote_platform_sp)
2990b57cec5SDimitry Andric     return m_remote_platform_sp->GetRemoteUnixSignals();
3000b57cec5SDimitry Andric   return Platform::GetRemoteUnixSignals();
3010b57cec5SDimitry Andric }
3020b57cec5SDimitry Andric 
3030b57cec5SDimitry Andric Status PlatformPOSIX::ConnectRemote(Args &args) {
3040b57cec5SDimitry Andric   Status error;
3050b57cec5SDimitry Andric   if (IsHost()) {
306349cc55cSDimitry Andric     error.SetErrorStringWithFormatv(
307349cc55cSDimitry Andric         "can't connect to the host platform '{0}', always connected",
308349cc55cSDimitry Andric         GetPluginName());
3090b57cec5SDimitry Andric   } else {
3100b57cec5SDimitry Andric     if (!m_remote_platform_sp)
3110b57cec5SDimitry Andric       m_remote_platform_sp =
31281ad6265SDimitry Andric           platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
31381ad6265SDimitry Andric               /*force=*/true, nullptr);
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric     if (m_remote_platform_sp && error.Success())
3160b57cec5SDimitry Andric       error = m_remote_platform_sp->ConnectRemote(args);
3170b57cec5SDimitry Andric     else
3180b57cec5SDimitry Andric       error.SetErrorString("failed to create a 'remote-gdb-server' platform");
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric     if (error.Fail())
3210b57cec5SDimitry Andric       m_remote_platform_sp.reset();
3220b57cec5SDimitry Andric   }
3230b57cec5SDimitry Andric 
3240b57cec5SDimitry Andric   if (error.Success() && m_remote_platform_sp) {
3250b57cec5SDimitry Andric     if (m_option_group_platform_rsync.get() &&
3260b57cec5SDimitry Andric         m_option_group_platform_ssh.get() &&
3270b57cec5SDimitry Andric         m_option_group_platform_caching.get()) {
3280b57cec5SDimitry Andric       if (m_option_group_platform_rsync->m_rsync) {
3290b57cec5SDimitry Andric         SetSupportsRSync(true);
3300b57cec5SDimitry Andric         SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
3310b57cec5SDimitry Andric         SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
3320b57cec5SDimitry Andric         SetIgnoresRemoteHostname(
3330b57cec5SDimitry Andric             m_option_group_platform_rsync->m_ignores_remote_hostname);
3340b57cec5SDimitry Andric       }
3350b57cec5SDimitry Andric       if (m_option_group_platform_ssh->m_ssh) {
3360b57cec5SDimitry Andric         SetSupportsSSH(true);
3370b57cec5SDimitry Andric         SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
3380b57cec5SDimitry Andric       }
3390b57cec5SDimitry Andric       SetLocalCacheDirectory(
3400b57cec5SDimitry Andric           m_option_group_platform_caching->m_cache_dir.c_str());
3410b57cec5SDimitry Andric     }
3420b57cec5SDimitry Andric   }
3430b57cec5SDimitry Andric 
3440b57cec5SDimitry Andric   return error;
3450b57cec5SDimitry Andric }
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric Status PlatformPOSIX::DisconnectRemote() {
3480b57cec5SDimitry Andric   Status error;
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   if (IsHost()) {
351349cc55cSDimitry Andric     error.SetErrorStringWithFormatv(
352349cc55cSDimitry Andric         "can't disconnect from the host platform '{0}', always connected",
353349cc55cSDimitry Andric         GetPluginName());
3540b57cec5SDimitry Andric   } else {
3550b57cec5SDimitry Andric     if (m_remote_platform_sp)
3560b57cec5SDimitry Andric       error = m_remote_platform_sp->DisconnectRemote();
3570b57cec5SDimitry Andric     else
3580b57cec5SDimitry Andric       error.SetErrorString("the platform is not currently connected");
3590b57cec5SDimitry Andric   }
3600b57cec5SDimitry Andric   return error;
3610b57cec5SDimitry Andric }
3620b57cec5SDimitry Andric 
3630b57cec5SDimitry Andric lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
3640b57cec5SDimitry Andric                                       Debugger &debugger, Target *target,
3650b57cec5SDimitry Andric                                       Status &error) {
3660b57cec5SDimitry Andric   lldb::ProcessSP process_sp;
36781ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Platform);
3680b57cec5SDimitry Andric 
3690b57cec5SDimitry Andric   if (IsHost()) {
3700b57cec5SDimitry Andric     if (target == nullptr) {
3710b57cec5SDimitry Andric       TargetSP new_target_sp;
3720b57cec5SDimitry Andric 
3730b57cec5SDimitry Andric       error = debugger.GetTargetList().CreateTarget(
3740b57cec5SDimitry Andric           debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
3750b57cec5SDimitry Andric       target = new_target_sp.get();
3769dba64beSDimitry Andric       LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__);
3770b57cec5SDimitry Andric     } else {
3780b57cec5SDimitry Andric       error.Clear();
3799dba64beSDimitry Andric       LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target",
3800b57cec5SDimitry Andric                 __FUNCTION__);
3810b57cec5SDimitry Andric     }
3820b57cec5SDimitry Andric 
3830b57cec5SDimitry Andric     if (target && error.Success()) {
3840b57cec5SDimitry Andric       if (log) {
3850b57cec5SDimitry Andric         ModuleSP exe_module_sp = target->GetExecutableModule();
3869dba64beSDimitry Andric         LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s",
3870b57cec5SDimitry Andric                   __FUNCTION__, (void *)target,
3889dba64beSDimitry Andric                   exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
3890b57cec5SDimitry Andric                                 : "<null>");
3900b57cec5SDimitry Andric       }
3910b57cec5SDimitry Andric 
3920b57cec5SDimitry Andric       process_sp =
3930b57cec5SDimitry Andric           target->CreateProcess(attach_info.GetListenerForProcess(debugger),
394e8d8bef9SDimitry Andric                                 "gdb-remote", nullptr, true);
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric       if (process_sp) {
3970b57cec5SDimitry Andric         ListenerSP listener_sp = attach_info.GetHijackListener();
3980b57cec5SDimitry Andric         if (listener_sp == nullptr) {
3990b57cec5SDimitry Andric           listener_sp =
4000b57cec5SDimitry Andric               Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
4010b57cec5SDimitry Andric           attach_info.SetHijackListener(listener_sp);
4020b57cec5SDimitry Andric         }
4030b57cec5SDimitry Andric         process_sp->HijackProcessEvents(listener_sp);
40406c3fb27SDimitry Andric         process_sp->SetShadowListener(attach_info.GetShadowListener());
4050b57cec5SDimitry Andric         error = process_sp->Attach(attach_info);
4060b57cec5SDimitry Andric       }
4070b57cec5SDimitry Andric     }
4080b57cec5SDimitry Andric   } else {
4090b57cec5SDimitry Andric     if (m_remote_platform_sp)
4100b57cec5SDimitry Andric       process_sp =
4110b57cec5SDimitry Andric           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
4120b57cec5SDimitry Andric     else
4130b57cec5SDimitry Andric       error.SetErrorString("the platform is not currently connected");
4140b57cec5SDimitry Andric   }
4150b57cec5SDimitry Andric   return process_sp;
4160b57cec5SDimitry Andric }
4170b57cec5SDimitry Andric 
418349cc55cSDimitry Andric lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
419349cc55cSDimitry Andric                                             Debugger &debugger, Target &target,
4200b57cec5SDimitry Andric                                             Status &error) {
42181ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Platform);
422349cc55cSDimitry Andric   LLDB_LOG(log, "target {0}", &target);
423e8d8bef9SDimitry Andric 
4240b57cec5SDimitry Andric   ProcessSP process_sp;
4250b57cec5SDimitry Andric 
426e8d8bef9SDimitry Andric   if (!IsHost()) {
4270b57cec5SDimitry Andric     if (m_remote_platform_sp)
4280b57cec5SDimitry Andric       process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
4290b57cec5SDimitry Andric                                                       target, error);
4300b57cec5SDimitry Andric     else
4310b57cec5SDimitry Andric       error.SetErrorString("the platform is not currently connected");
432e8d8bef9SDimitry Andric     return process_sp;
4330b57cec5SDimitry Andric   }
434e8d8bef9SDimitry Andric 
435e8d8bef9SDimitry Andric   //
436e8d8bef9SDimitry Andric   // For local debugging, we'll insist on having ProcessGDBRemote create the
437e8d8bef9SDimitry Andric   // process.
438e8d8bef9SDimitry Andric   //
439e8d8bef9SDimitry Andric 
440e8d8bef9SDimitry Andric   // Make sure we stop at the entry point
441e8d8bef9SDimitry Andric   launch_info.GetFlags().Set(eLaunchFlagDebug);
442e8d8bef9SDimitry Andric 
443e8d8bef9SDimitry Andric   // We always launch the process we are going to debug in a separate process
444e8d8bef9SDimitry Andric   // group, since then we can handle ^C interrupts ourselves w/o having to
445e8d8bef9SDimitry Andric   // worry about the target getting them as well.
446e8d8bef9SDimitry Andric   launch_info.SetLaunchInSeparateProcessGroup(true);
447e8d8bef9SDimitry Andric 
448e8d8bef9SDimitry Andric   // Now create the gdb-remote process.
449e8d8bef9SDimitry Andric   LLDB_LOG(log, "having target create process with gdb-remote plugin");
45081ad6265SDimitry Andric   process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
45181ad6265SDimitry Andric                                     nullptr, true);
452e8d8bef9SDimitry Andric 
453e8d8bef9SDimitry Andric   if (!process_sp) {
454e8d8bef9SDimitry Andric     error.SetErrorString("CreateProcess() failed for gdb-remote process");
455e8d8bef9SDimitry Andric     LLDB_LOG(log, "error: {0}", error);
456e8d8bef9SDimitry Andric     return process_sp;
457e8d8bef9SDimitry Andric   }
458e8d8bef9SDimitry Andric 
459e8d8bef9SDimitry Andric   LLDB_LOG(log, "successfully created process");
46081ad6265SDimitry Andric 
46181ad6265SDimitry Andric   process_sp->HijackProcessEvents(launch_info.GetHijackListener());
46206c3fb27SDimitry Andric   process_sp->SetShadowListener(launch_info.GetShadowListener());
463e8d8bef9SDimitry Andric 
464e8d8bef9SDimitry Andric   // Log file actions.
465e8d8bef9SDimitry Andric   if (log) {
466e8d8bef9SDimitry Andric     LLDB_LOG(log, "launching process with the following file actions:");
467e8d8bef9SDimitry Andric     StreamString stream;
468e8d8bef9SDimitry Andric     size_t i = 0;
469e8d8bef9SDimitry Andric     const FileAction *file_action;
470e8d8bef9SDimitry Andric     while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
471e8d8bef9SDimitry Andric       file_action->Dump(stream);
472e8d8bef9SDimitry Andric       LLDB_LOG(log, "{0}", stream.GetData());
473e8d8bef9SDimitry Andric       stream.Clear();
474e8d8bef9SDimitry Andric     }
475e8d8bef9SDimitry Andric   }
476e8d8bef9SDimitry Andric 
477e8d8bef9SDimitry Andric   // Do the launch.
478e8d8bef9SDimitry Andric   error = process_sp->Launch(launch_info);
479e8d8bef9SDimitry Andric   if (error.Success()) {
480e8d8bef9SDimitry Andric     // Hook up process PTY if we have one (which we should for local debugging
481e8d8bef9SDimitry Andric     // with llgs).
482e8d8bef9SDimitry Andric     int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
483e8d8bef9SDimitry Andric     if (pty_fd != PseudoTerminal::invalid_fd) {
484e8d8bef9SDimitry Andric       process_sp->SetSTDIOFileDescriptor(pty_fd);
485e8d8bef9SDimitry Andric       LLDB_LOG(log, "hooked up STDIO pty to process");
486e8d8bef9SDimitry Andric     } else
487e8d8bef9SDimitry Andric       LLDB_LOG(log, "not using process STDIO pty");
488e8d8bef9SDimitry Andric   } else {
489e8d8bef9SDimitry Andric     LLDB_LOG(log, "{0}", error);
490349cc55cSDimitry Andric     // FIXME figure out appropriate cleanup here. Do we delete the process?
491349cc55cSDimitry Andric     // Does our caller do that?
492e8d8bef9SDimitry Andric   }
493e8d8bef9SDimitry Andric 
4940b57cec5SDimitry Andric   return process_sp;
4950b57cec5SDimitry Andric }
4960b57cec5SDimitry Andric 
4970b57cec5SDimitry Andric void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
4980b57cec5SDimitry Andric   m_trap_handlers.push_back(ConstString("_sigtramp"));
4990b57cec5SDimitry Andric }
5000b57cec5SDimitry Andric 
5010b57cec5SDimitry Andric Status PlatformPOSIX::EvaluateLibdlExpression(
5020b57cec5SDimitry Andric     lldb_private::Process *process, const char *expr_cstr,
5030b57cec5SDimitry Andric     llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
5040b57cec5SDimitry Andric   DynamicLoader *loader = process->GetDynamicLoader();
5050b57cec5SDimitry Andric   if (loader) {
5060b57cec5SDimitry Andric     Status error = loader->CanLoadImage();
5070b57cec5SDimitry Andric     if (error.Fail())
5080b57cec5SDimitry Andric       return error;
5090b57cec5SDimitry Andric   }
5100b57cec5SDimitry Andric 
5110b57cec5SDimitry Andric   ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
5120b57cec5SDimitry Andric   if (!thread_sp)
5130b57cec5SDimitry Andric     return Status("Selected thread isn't valid");
5140b57cec5SDimitry Andric 
5150b57cec5SDimitry Andric   StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
5160b57cec5SDimitry Andric   if (!frame_sp)
5170b57cec5SDimitry Andric     return Status("Frame 0 isn't valid");
5180b57cec5SDimitry Andric 
5190b57cec5SDimitry Andric   ExecutionContext exe_ctx;
5200b57cec5SDimitry Andric   frame_sp->CalculateExecutionContext(exe_ctx);
5210b57cec5SDimitry Andric   EvaluateExpressionOptions expr_options;
5220b57cec5SDimitry Andric   expr_options.SetUnwindOnError(true);
5230b57cec5SDimitry Andric   expr_options.SetIgnoreBreakpoints(true);
5240b57cec5SDimitry Andric   expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
5250b57cec5SDimitry Andric   expr_options.SetLanguage(eLanguageTypeC_plus_plus);
5260b57cec5SDimitry Andric   expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
5270b57cec5SDimitry Andric                                          // don't do the work to trap them.
5280b57cec5SDimitry Andric   expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
5290b57cec5SDimitry Andric 
5300b57cec5SDimitry Andric   Status expr_error;
5310b57cec5SDimitry Andric   ExpressionResults result =
5320b57cec5SDimitry Andric       UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
5330b57cec5SDimitry Andric                                result_valobj_sp, expr_error);
5340b57cec5SDimitry Andric   if (result != eExpressionCompleted)
5350b57cec5SDimitry Andric     return expr_error;
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric   if (result_valobj_sp->GetError().Fail())
5380b57cec5SDimitry Andric     return result_valobj_sp->GetError();
5390b57cec5SDimitry Andric   return Status();
5400b57cec5SDimitry Andric }
5410b57cec5SDimitry Andric 
5420b57cec5SDimitry Andric std::unique_ptr<UtilityFunction>
5430b57cec5SDimitry Andric PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
5440b57cec5SDimitry Andric                                             Status &error) {
5450b57cec5SDimitry Andric   // Remember to prepend this with the prefix from
5460b57cec5SDimitry Andric   // GetLibdlFunctionDeclarations. The returned values are all in
5470b57cec5SDimitry Andric   // __lldb_dlopen_result for consistency. The wrapper returns a void * but
5480b57cec5SDimitry Andric   // doesn't use it because UtilityFunctions don't work with void returns at
5490b57cec5SDimitry Andric   // present.
550fe6060f1SDimitry Andric   //
551fe6060f1SDimitry Andric   // Use lazy binding so as to not make dlopen()'s success conditional on
552fe6060f1SDimitry Andric   // forcing every symbol in the library.
553fe6060f1SDimitry Andric   //
554fe6060f1SDimitry Andric   // In general, the debugger should allow programs to load & run with
555fe6060f1SDimitry Andric   // libraries as far as they can, instead of defaulting to being super-picky
556fe6060f1SDimitry Andric   // about unavailable symbols.
557fe6060f1SDimitry Andric   //
558fe6060f1SDimitry Andric   // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
559fe6060f1SDimitry Andric   // and other POSIX OSes.
5600b57cec5SDimitry Andric   static const char *dlopen_wrapper_code = R"(
561fe6060f1SDimitry Andric   const int RTLD_LAZY = 1;
562fe6060f1SDimitry Andric 
5630b57cec5SDimitry Andric   struct __lldb_dlopen_result {
5640b57cec5SDimitry Andric     void *image_ptr;
5650b57cec5SDimitry Andric     const char *error_str;
5660b57cec5SDimitry Andric   };
5670b57cec5SDimitry Andric 
56881ad6265SDimitry Andric   extern "C" void *memcpy(void *, const void *, size_t size);
56981ad6265SDimitry Andric   extern "C" size_t strlen(const char *);
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric 
5720b57cec5SDimitry Andric   void * __lldb_dlopen_wrapper (const char *name,
5730b57cec5SDimitry Andric                                 const char *path_strings,
5740b57cec5SDimitry Andric                                 char *buffer,
5750b57cec5SDimitry Andric                                 __lldb_dlopen_result *result_ptr)
5760b57cec5SDimitry Andric   {
5770b57cec5SDimitry Andric     // This is the case where the name is the full path:
5780b57cec5SDimitry Andric     if (!path_strings) {
579fe6060f1SDimitry Andric       result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
5800b57cec5SDimitry Andric       if (result_ptr->image_ptr)
5810b57cec5SDimitry Andric         result_ptr->error_str = nullptr;
5820eae32dcSDimitry Andric       else
5830eae32dcSDimitry Andric         result_ptr->error_str = dlerror();
5840b57cec5SDimitry Andric       return nullptr;
5850b57cec5SDimitry Andric     }
5860b57cec5SDimitry Andric 
5870b57cec5SDimitry Andric     // This is the case where we have a list of paths:
5880b57cec5SDimitry Andric     size_t name_len = strlen(name);
5890b57cec5SDimitry Andric     while (path_strings && path_strings[0] != '\0') {
5900b57cec5SDimitry Andric       size_t path_len = strlen(path_strings);
5910b57cec5SDimitry Andric       memcpy((void *) buffer, (void *) path_strings, path_len);
5920b57cec5SDimitry Andric       buffer[path_len] = '/';
5930b57cec5SDimitry Andric       char *target_ptr = buffer+path_len+1;
5940b57cec5SDimitry Andric       memcpy((void *) target_ptr, (void *) name, name_len + 1);
595fe6060f1SDimitry Andric       result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
5960b57cec5SDimitry Andric       if (result_ptr->image_ptr) {
5970b57cec5SDimitry Andric         result_ptr->error_str = nullptr;
5980b57cec5SDimitry Andric         break;
5990b57cec5SDimitry Andric       }
6000b57cec5SDimitry Andric       result_ptr->error_str = dlerror();
6010b57cec5SDimitry Andric       path_strings = path_strings + path_len + 1;
6020b57cec5SDimitry Andric     }
6030b57cec5SDimitry Andric     return nullptr;
6040b57cec5SDimitry Andric   }
6050b57cec5SDimitry Andric   )";
6060b57cec5SDimitry Andric 
6070b57cec5SDimitry Andric   static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
6080b57cec5SDimitry Andric   Process *process = exe_ctx.GetProcessSP().get();
6090b57cec5SDimitry Andric   // Insert the dlopen shim defines into our generic expression:
6105ffd83dbSDimitry Andric   std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
6110b57cec5SDimitry Andric   expr.append(dlopen_wrapper_code);
6120b57cec5SDimitry Andric   Status utility_error;
6130b57cec5SDimitry Andric   DiagnosticManager diagnostics;
6140b57cec5SDimitry Andric 
615e8d8bef9SDimitry Andric   auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
61681ad6265SDimitry Andric       std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
617e8d8bef9SDimitry Andric   if (!utility_fn_or_error) {
618e8d8bef9SDimitry Andric     std::string error_str = llvm::toString(utility_fn_or_error.takeError());
61981ad6265SDimitry Andric     error.SetErrorStringWithFormat(
62081ad6265SDimitry Andric         "dlopen error: could not create utility function: %s",
621e8d8bef9SDimitry Andric         error_str.c_str());
6220b57cec5SDimitry Andric     return nullptr;
6230b57cec5SDimitry Andric   }
624e8d8bef9SDimitry Andric   std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
625e8d8bef9SDimitry Andric       std::move(*utility_fn_or_error);
6260b57cec5SDimitry Andric 
6270b57cec5SDimitry Andric   Value value;
6280b57cec5SDimitry Andric   ValueList arguments;
6290b57cec5SDimitry Andric   FunctionCaller *do_dlopen_function = nullptr;
6300b57cec5SDimitry Andric 
6310b57cec5SDimitry Andric   // Fetch the clang types we will need:
632bdd1243dSDimitry Andric   TypeSystemClangSP scratch_ts_sp =
633e8d8bef9SDimitry Andric       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
634bdd1243dSDimitry Andric   if (!scratch_ts_sp)
635480093f4SDimitry Andric     return nullptr;
6360b57cec5SDimitry Andric 
637bdd1243dSDimitry Andric   CompilerType clang_void_pointer_type =
638bdd1243dSDimitry Andric       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
639bdd1243dSDimitry Andric   CompilerType clang_char_pointer_type =
640bdd1243dSDimitry Andric       scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
6410b57cec5SDimitry Andric 
6420b57cec5SDimitry Andric   // We are passing four arguments, the basename, the list of places to look,
6430b57cec5SDimitry Andric   // a buffer big enough for all the path + name combos, and
6440b57cec5SDimitry Andric   // a pointer to the storage we've made for the result:
645fe6060f1SDimitry Andric   value.SetValueType(Value::ValueType::Scalar);
6460b57cec5SDimitry Andric   value.SetCompilerType(clang_void_pointer_type);
6470b57cec5SDimitry Andric   arguments.PushValue(value);
6480b57cec5SDimitry Andric   value.SetCompilerType(clang_char_pointer_type);
6490b57cec5SDimitry Andric   arguments.PushValue(value);
6500b57cec5SDimitry Andric   arguments.PushValue(value);
6510b57cec5SDimitry Andric   arguments.PushValue(value);
6520b57cec5SDimitry Andric 
6530b57cec5SDimitry Andric   do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
6540b57cec5SDimitry Andric       clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
6550b57cec5SDimitry Andric   if (utility_error.Fail()) {
65681ad6265SDimitry Andric     error.SetErrorStringWithFormat(
65781ad6265SDimitry Andric         "dlopen error: could not make function caller: %s",
65881ad6265SDimitry Andric         utility_error.AsCString());
6590b57cec5SDimitry Andric     return nullptr;
6600b57cec5SDimitry Andric   }
6610b57cec5SDimitry Andric 
6620b57cec5SDimitry Andric   do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
6630b57cec5SDimitry Andric   if (!do_dlopen_function) {
6640b57cec5SDimitry Andric     error.SetErrorString("dlopen error: could not get function caller.");
6650b57cec5SDimitry Andric     return nullptr;
6660b57cec5SDimitry Andric   }
6670b57cec5SDimitry Andric 
6680b57cec5SDimitry Andric   // We made a good utility function, so cache it in the process:
6690b57cec5SDimitry Andric   return dlopen_utility_func_up;
6700b57cec5SDimitry Andric }
6710b57cec5SDimitry Andric 
6720b57cec5SDimitry Andric uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
6730b57cec5SDimitry Andric                                     const lldb_private::FileSpec &remote_file,
6740b57cec5SDimitry Andric                                     const std::vector<std::string> *paths,
6750b57cec5SDimitry Andric                                     lldb_private::Status &error,
6760b57cec5SDimitry Andric                                     lldb_private::FileSpec *loaded_image) {
6770b57cec5SDimitry Andric   if (loaded_image)
6780b57cec5SDimitry Andric     loaded_image->Clear();
6790b57cec5SDimitry Andric 
6800b57cec5SDimitry Andric   std::string path;
681*0fca6ea1SDimitry Andric   path = remote_file.GetPath(false);
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric   ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
6840b57cec5SDimitry Andric   if (!thread_sp) {
6850b57cec5SDimitry Andric     error.SetErrorString("dlopen error: no thread available to call dlopen.");
6860b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
6870b57cec5SDimitry Andric   }
6880b57cec5SDimitry Andric 
6890b57cec5SDimitry Andric   DiagnosticManager diagnostics;
6900b57cec5SDimitry Andric 
6910b57cec5SDimitry Andric   ExecutionContext exe_ctx;
6920b57cec5SDimitry Andric   thread_sp->CalculateExecutionContext(exe_ctx);
6930b57cec5SDimitry Andric 
6940b57cec5SDimitry Andric   Status utility_error;
6950b57cec5SDimitry Andric   UtilityFunction *dlopen_utility_func;
6960b57cec5SDimitry Andric   ValueList arguments;
6970b57cec5SDimitry Andric   FunctionCaller *do_dlopen_function = nullptr;
6980b57cec5SDimitry Andric 
6990b57cec5SDimitry Andric   // The UtilityFunction is held in the Process.  Platforms don't track the
7000b57cec5SDimitry Andric   // lifespan of the Targets that use them, we can't put this in the Platform.
7010b57cec5SDimitry Andric   dlopen_utility_func = process->GetLoadImageUtilityFunction(
7020b57cec5SDimitry Andric       this, [&]() -> std::unique_ptr<UtilityFunction> {
7030b57cec5SDimitry Andric         return MakeLoadImageUtilityFunction(exe_ctx, error);
7040b57cec5SDimitry Andric       });
7050b57cec5SDimitry Andric   // If we couldn't make it, the error will be in error, so we can exit here.
7060b57cec5SDimitry Andric   if (!dlopen_utility_func)
7070b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
7080b57cec5SDimitry Andric 
7090b57cec5SDimitry Andric   do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
7100b57cec5SDimitry Andric   if (!do_dlopen_function) {
7110b57cec5SDimitry Andric     error.SetErrorString("dlopen error: could not get function caller.");
7120b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
7130b57cec5SDimitry Andric   }
7140b57cec5SDimitry Andric   arguments = do_dlopen_function->GetArgumentValues();
7150b57cec5SDimitry Andric 
7160b57cec5SDimitry Andric   // Now insert the path we are searching for and the result structure into the
7170b57cec5SDimitry Andric   // target.
7180b57cec5SDimitry Andric   uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
7190b57cec5SDimitry Andric   size_t path_len = path.size() + 1;
7200b57cec5SDimitry Andric   lldb::addr_t path_addr = process->AllocateMemory(path_len,
7210b57cec5SDimitry Andric                                                    permissions,
7220b57cec5SDimitry Andric                                                    utility_error);
7230b57cec5SDimitry Andric   if (path_addr == LLDB_INVALID_ADDRESS) {
72481ad6265SDimitry Andric     error.SetErrorStringWithFormat(
72581ad6265SDimitry Andric         "dlopen error: could not allocate memory for path: %s",
72681ad6265SDimitry Andric         utility_error.AsCString());
7270b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
7280b57cec5SDimitry Andric   }
7290b57cec5SDimitry Andric 
7300b57cec5SDimitry Andric   // Make sure we deallocate the input string memory:
7319dba64beSDimitry Andric   auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
7329dba64beSDimitry Andric     // Deallocate the buffer.
7330b57cec5SDimitry Andric     process->DeallocateMemory(path_addr);
7340b57cec5SDimitry Andric   });
7350b57cec5SDimitry Andric 
7360b57cec5SDimitry Andric   process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
7370b57cec5SDimitry Andric   if (utility_error.Fail()) {
73881ad6265SDimitry Andric     error.SetErrorStringWithFormat(
73981ad6265SDimitry Andric         "dlopen error: could not write path string: %s",
74081ad6265SDimitry Andric         utility_error.AsCString());
7410b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
7420b57cec5SDimitry Andric   }
7430b57cec5SDimitry Andric 
7440b57cec5SDimitry Andric   // Make space for our return structure.  It is two pointers big: the token
7450b57cec5SDimitry Andric   // and the error string.
7460b57cec5SDimitry Andric   const uint32_t addr_size = process->GetAddressByteSize();
7470b57cec5SDimitry Andric   lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
7480b57cec5SDimitry Andric                                                       permissions,
7490b57cec5SDimitry Andric                                                       utility_error);
7500b57cec5SDimitry Andric   if (utility_error.Fail()) {
75181ad6265SDimitry Andric     error.SetErrorStringWithFormat(
75281ad6265SDimitry Andric         "dlopen error: could not allocate memory for path: %s",
75381ad6265SDimitry Andric         utility_error.AsCString());
7540b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
7550b57cec5SDimitry Andric   }
7560b57cec5SDimitry Andric 
7570b57cec5SDimitry Andric   // Make sure we deallocate the result structure memory
7589dba64beSDimitry Andric   auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
7599dba64beSDimitry Andric     // Deallocate the buffer
7600b57cec5SDimitry Andric     process->DeallocateMemory(return_addr);
7610b57cec5SDimitry Andric   });
7620b57cec5SDimitry Andric 
7630b57cec5SDimitry Andric   // This will be the address of the storage for paths, if we are using them,
7640b57cec5SDimitry Andric   // or nullptr to signal we aren't.
7650b57cec5SDimitry Andric   lldb::addr_t path_array_addr = 0x0;
766bdd1243dSDimitry Andric   std::optional<llvm::detail::scope_exit<std::function<void()>>>
7679dba64beSDimitry Andric       path_array_cleanup;
7680b57cec5SDimitry Andric 
7690b57cec5SDimitry Andric   // This is the address to a buffer large enough to hold the largest path
7700b57cec5SDimitry Andric   // conjoined with the library name we're passing in.  This is a convenience
7710b57cec5SDimitry Andric   // to avoid having to call malloc in the dlopen function.
7720b57cec5SDimitry Andric   lldb::addr_t buffer_addr = 0x0;
773bdd1243dSDimitry Andric   std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup;
7740b57cec5SDimitry Andric 
7750b57cec5SDimitry Andric   // Set the values into our args and write them to the target:
7760b57cec5SDimitry Andric   if (paths != nullptr) {
7770b57cec5SDimitry Andric     // First insert the paths into the target.  This is expected to be a
7780b57cec5SDimitry Andric     // continuous buffer with the strings laid out null terminated and
7790b57cec5SDimitry Andric     // end to end with an empty string terminating the buffer.
7800b57cec5SDimitry Andric     // We also compute the buffer's required size as we go.
7810b57cec5SDimitry Andric     size_t buffer_size = 0;
7820b57cec5SDimitry Andric     std::string path_array;
7830b57cec5SDimitry Andric     for (auto path : *paths) {
7840b57cec5SDimitry Andric       // Don't insert empty paths, they will make us abort the path
7850b57cec5SDimitry Andric       // search prematurely.
7860b57cec5SDimitry Andric       if (path.empty())
7870b57cec5SDimitry Andric         continue;
7880b57cec5SDimitry Andric       size_t path_size = path.size();
7890b57cec5SDimitry Andric       path_array.append(path);
7900b57cec5SDimitry Andric       path_array.push_back('\0');
7910b57cec5SDimitry Andric       if (path_size > buffer_size)
7920b57cec5SDimitry Andric         buffer_size = path_size;
7930b57cec5SDimitry Andric     }
7940b57cec5SDimitry Andric     path_array.push_back('\0');
7950b57cec5SDimitry Andric 
7960b57cec5SDimitry Andric     path_array_addr = process->AllocateMemory(path_array.size(),
7970b57cec5SDimitry Andric                                               permissions,
7980b57cec5SDimitry Andric                                               utility_error);
7990b57cec5SDimitry Andric     if (path_array_addr == LLDB_INVALID_ADDRESS) {
80081ad6265SDimitry Andric       error.SetErrorStringWithFormat(
80181ad6265SDimitry Andric           "dlopen error: could not allocate memory for path array: %s",
8020b57cec5SDimitry Andric           utility_error.AsCString());
8030b57cec5SDimitry Andric       return LLDB_INVALID_IMAGE_TOKEN;
8040b57cec5SDimitry Andric     }
8050b57cec5SDimitry Andric 
8060b57cec5SDimitry Andric     // Make sure we deallocate the paths array.
8079dba64beSDimitry Andric     path_array_cleanup.emplace([process, path_array_addr]() {
8089dba64beSDimitry Andric       // Deallocate the path array.
8090b57cec5SDimitry Andric       process->DeallocateMemory(path_array_addr);
8100b57cec5SDimitry Andric     });
8110b57cec5SDimitry Andric 
8120b57cec5SDimitry Andric     process->WriteMemory(path_array_addr, path_array.data(),
8130b57cec5SDimitry Andric                          path_array.size(), utility_error);
8140b57cec5SDimitry Andric 
8150b57cec5SDimitry Andric     if (utility_error.Fail()) {
81681ad6265SDimitry Andric       error.SetErrorStringWithFormat(
81781ad6265SDimitry Andric           "dlopen error: could not write path array: %s",
81881ad6265SDimitry Andric           utility_error.AsCString());
8190b57cec5SDimitry Andric       return LLDB_INVALID_IMAGE_TOKEN;
8200b57cec5SDimitry Andric     }
8210b57cec5SDimitry Andric     // Now make spaces in the target for the buffer.  We need to add one for
8220b57cec5SDimitry Andric     // the '/' that the utility function will insert and one for the '\0':
8230b57cec5SDimitry Andric     buffer_size += path.size() + 2;
8240b57cec5SDimitry Andric 
8250b57cec5SDimitry Andric     buffer_addr = process->AllocateMemory(buffer_size,
8260b57cec5SDimitry Andric                                           permissions,
8270b57cec5SDimitry Andric                                           utility_error);
8280b57cec5SDimitry Andric     if (buffer_addr == LLDB_INVALID_ADDRESS) {
82981ad6265SDimitry Andric       error.SetErrorStringWithFormat(
83081ad6265SDimitry Andric           "dlopen error: could not allocate memory for buffer: %s",
8310b57cec5SDimitry Andric           utility_error.AsCString());
8320b57cec5SDimitry Andric       return LLDB_INVALID_IMAGE_TOKEN;
8330b57cec5SDimitry Andric     }
8340b57cec5SDimitry Andric 
8350b57cec5SDimitry Andric     // Make sure we deallocate the buffer memory:
8369dba64beSDimitry Andric     buffer_cleanup.emplace([process, buffer_addr]() {
8379dba64beSDimitry Andric       // Deallocate the buffer.
8380b57cec5SDimitry Andric       process->DeallocateMemory(buffer_addr);
8390b57cec5SDimitry Andric     });
8400b57cec5SDimitry Andric   }
8410b57cec5SDimitry Andric 
8420b57cec5SDimitry Andric   arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
8430b57cec5SDimitry Andric   arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
8440b57cec5SDimitry Andric   arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
8450b57cec5SDimitry Andric   arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
8460b57cec5SDimitry Andric 
8470b57cec5SDimitry Andric   lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
8480b57cec5SDimitry Andric 
8490b57cec5SDimitry Andric   diagnostics.Clear();
8500b57cec5SDimitry Andric   if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
8510b57cec5SDimitry Andric                                                  func_args_addr,
8520b57cec5SDimitry Andric                                                  arguments,
8530b57cec5SDimitry Andric                                                  diagnostics)) {
85481ad6265SDimitry Andric     error.SetErrorStringWithFormat(
85581ad6265SDimitry Andric         "dlopen error: could not write function arguments: %s",
8560b57cec5SDimitry Andric         diagnostics.GetString().c_str());
8570b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
8580b57cec5SDimitry Andric   }
8590b57cec5SDimitry Andric 
8600b57cec5SDimitry Andric   // Make sure we clean up the args structure.  We can't reuse it because the
8610b57cec5SDimitry Andric   // Platform lives longer than the process and the Platforms don't get a
8620b57cec5SDimitry Andric   // signal to clean up cached data when a process goes away.
8639dba64beSDimitry Andric   auto args_cleanup =
8649dba64beSDimitry Andric       llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
8650b57cec5SDimitry Andric         do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
8660b57cec5SDimitry Andric       });
8670b57cec5SDimitry Andric 
8680b57cec5SDimitry Andric   // Now run the caller:
8690b57cec5SDimitry Andric   EvaluateExpressionOptions options;
8700b57cec5SDimitry Andric   options.SetExecutionPolicy(eExecutionPolicyAlways);
8710b57cec5SDimitry Andric   options.SetLanguage(eLanguageTypeC_plus_plus);
8720b57cec5SDimitry Andric   options.SetIgnoreBreakpoints(true);
8730b57cec5SDimitry Andric   options.SetUnwindOnError(true);
8740b57cec5SDimitry Andric   options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
8750b57cec5SDimitry Andric                                     // don't do the work to trap them.
8760b57cec5SDimitry Andric   options.SetTimeout(process->GetUtilityExpressionTimeout());
8770b57cec5SDimitry Andric   options.SetIsForUtilityExpr(true);
8780b57cec5SDimitry Andric 
8790b57cec5SDimitry Andric   Value return_value;
8800b57cec5SDimitry Andric   // Fetch the clang types we will need:
881bdd1243dSDimitry Andric   TypeSystemClangSP scratch_ts_sp =
882e8d8bef9SDimitry Andric       ScratchTypeSystemClang::GetForTarget(process->GetTarget());
883bdd1243dSDimitry Andric   if (!scratch_ts_sp) {
8845ffd83dbSDimitry Andric     error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
885480093f4SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
886480093f4SDimitry Andric   }
8870b57cec5SDimitry Andric 
888bdd1243dSDimitry Andric   CompilerType clang_void_pointer_type =
889bdd1243dSDimitry Andric       scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
8900b57cec5SDimitry Andric 
8910b57cec5SDimitry Andric   return_value.SetCompilerType(clang_void_pointer_type);
8920b57cec5SDimitry Andric 
8930b57cec5SDimitry Andric   ExpressionResults results = do_dlopen_function->ExecuteFunction(
8940b57cec5SDimitry Andric       exe_ctx, &func_args_addr, options, diagnostics, return_value);
8950b57cec5SDimitry Andric   if (results != eExpressionCompleted) {
89681ad6265SDimitry Andric     error.SetErrorStringWithFormat(
89781ad6265SDimitry Andric         "dlopen error: failed executing dlopen wrapper function: %s",
8980b57cec5SDimitry Andric         diagnostics.GetString().c_str());
8990b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
9000b57cec5SDimitry Andric   }
9010b57cec5SDimitry Andric 
9020b57cec5SDimitry Andric   // Read the dlopen token from the return area:
9030b57cec5SDimitry Andric   lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
9040b57cec5SDimitry Andric                                                       utility_error);
9050b57cec5SDimitry Andric   if (utility_error.Fail()) {
90681ad6265SDimitry Andric     error.SetErrorStringWithFormat(
90781ad6265SDimitry Andric         "dlopen error: could not read the return struct: %s",
90881ad6265SDimitry Andric         utility_error.AsCString());
9090b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
9100b57cec5SDimitry Andric   }
9110b57cec5SDimitry Andric 
9120b57cec5SDimitry Andric   // The dlopen succeeded!
9130b57cec5SDimitry Andric   if (token != 0x0) {
9140b57cec5SDimitry Andric     if (loaded_image && buffer_addr != 0x0)
9150b57cec5SDimitry Andric     {
9160b57cec5SDimitry Andric       // Capture the image which was loaded.  We leave it in the buffer on
9170b57cec5SDimitry Andric       // exit from the dlopen function, so we can just read it from there:
9180b57cec5SDimitry Andric       std::string name_string;
9190b57cec5SDimitry Andric       process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
9200b57cec5SDimitry Andric       if (utility_error.Success())
9210b57cec5SDimitry Andric         loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
9220b57cec5SDimitry Andric     }
9230b57cec5SDimitry Andric     return process->AddImageToken(token);
9240b57cec5SDimitry Andric   }
9250b57cec5SDimitry Andric 
9260b57cec5SDimitry Andric   // We got an error, lets read in the error string:
9270b57cec5SDimitry Andric   std::string dlopen_error_str;
9280b57cec5SDimitry Andric   lldb::addr_t error_addr
9290b57cec5SDimitry Andric     = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
9300b57cec5SDimitry Andric   if (utility_error.Fail()) {
93181ad6265SDimitry Andric     error.SetErrorStringWithFormat(
93281ad6265SDimitry Andric         "dlopen error: could not read error string: %s",
93381ad6265SDimitry Andric         utility_error.AsCString());
9340b57cec5SDimitry Andric     return LLDB_INVALID_IMAGE_TOKEN;
9350b57cec5SDimitry Andric   }
9360b57cec5SDimitry Andric 
9370b57cec5SDimitry Andric   size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
9380b57cec5SDimitry Andric                                                     dlopen_error_str,
9390b57cec5SDimitry Andric                                                     utility_error);
9400b57cec5SDimitry Andric   if (utility_error.Success() && num_chars > 0)
9410b57cec5SDimitry Andric     error.SetErrorStringWithFormat("dlopen error: %s",
9420b57cec5SDimitry Andric                                    dlopen_error_str.c_str());
9430b57cec5SDimitry Andric   else
9440b57cec5SDimitry Andric     error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
9450b57cec5SDimitry Andric 
9460b57cec5SDimitry Andric   return LLDB_INVALID_IMAGE_TOKEN;
9470b57cec5SDimitry Andric }
9480b57cec5SDimitry Andric 
9490b57cec5SDimitry Andric Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
9500b57cec5SDimitry Andric                                   uint32_t image_token) {
9510b57cec5SDimitry Andric   const addr_t image_addr = process->GetImagePtrFromToken(image_token);
9525f757f3fSDimitry Andric   if (image_addr == LLDB_INVALID_IMAGE_TOKEN)
9530b57cec5SDimitry Andric     return Status("Invalid image token");
9540b57cec5SDimitry Andric 
9550b57cec5SDimitry Andric   StreamString expr;
9560b57cec5SDimitry Andric   expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
9570b57cec5SDimitry Andric   llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
9580b57cec5SDimitry Andric   lldb::ValueObjectSP result_valobj_sp;
9590b57cec5SDimitry Andric   Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
9600b57cec5SDimitry Andric                                          result_valobj_sp);
9610b57cec5SDimitry Andric   if (error.Fail())
9620b57cec5SDimitry Andric     return error;
9630b57cec5SDimitry Andric 
9640b57cec5SDimitry Andric   if (result_valobj_sp->GetError().Fail())
9650b57cec5SDimitry Andric     return result_valobj_sp->GetError();
9660b57cec5SDimitry Andric 
9670b57cec5SDimitry Andric   Scalar scalar;
9680b57cec5SDimitry Andric   if (result_valobj_sp->ResolveValue(scalar)) {
9690b57cec5SDimitry Andric     if (scalar.UInt(1))
9700b57cec5SDimitry Andric       return Status("expression failed: \"%s\"", expr.GetData());
9710b57cec5SDimitry Andric     process->ResetImageToken(image_token);
9720b57cec5SDimitry Andric   }
9730b57cec5SDimitry Andric   return Status();
9740b57cec5SDimitry Andric }
9750b57cec5SDimitry Andric 
9760b57cec5SDimitry Andric llvm::StringRef
9770b57cec5SDimitry Andric PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
9780b57cec5SDimitry Andric   return R"(
9790b57cec5SDimitry Andric               extern "C" void* dlopen(const char*, int);
9800b57cec5SDimitry Andric               extern "C" void* dlsym(void*, const char*);
9810b57cec5SDimitry Andric               extern "C" int   dlclose(void*);
9820b57cec5SDimitry Andric               extern "C" char* dlerror(void);
9830b57cec5SDimitry Andric              )";
9840b57cec5SDimitry Andric }
9850b57cec5SDimitry Andric 
9860b57cec5SDimitry Andric ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
9870b57cec5SDimitry Andric   if (basename.IsEmpty())
9880b57cec5SDimitry Andric     return basename;
9890b57cec5SDimitry Andric 
9900b57cec5SDimitry Andric   StreamString stream;
9910b57cec5SDimitry Andric   stream.Printf("lib%s.so", basename.GetCString());
9920b57cec5SDimitry Andric   return ConstString(stream.GetString());
9930b57cec5SDimitry Andric }
994