112115Sdyson//===-- PlatformPOSIX.cpp -------------------------------------------------===//
212115Sdyson//
312115Sdyson// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
412115Sdyson// See https://llvm.org/LICENSE.txt for license information.
512115Sdyson// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
612115Sdyson//
712115Sdyson//===----------------------------------------------------------------------===//
812115Sdyson
912115Sdyson#include "PlatformPOSIX.h"
1012115Sdyson
1112115Sdyson#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
1212115Sdyson#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
1312115Sdyson#include "lldb/Core/Debugger.h"
1412115Sdyson#include "lldb/Core/Module.h"
1512115Sdyson#include "lldb/Core/ValueObject.h"
1612115Sdyson#include "lldb/Expression/DiagnosticManager.h"
1712115Sdyson#include "lldb/Expression/FunctionCaller.h"
1812115Sdyson#include "lldb/Expression/UserExpression.h"
1912115Sdyson#include "lldb/Expression/UtilityFunction.h"
2012115Sdyson#include "lldb/Host/File.h"
2112115Sdyson#include "lldb/Host/FileCache.h"
2212115Sdyson#include "lldb/Host/FileSystem.h"
2312115Sdyson#include "lldb/Host/Host.h"
2412115Sdyson#include "lldb/Host/HostInfo.h"
2512115Sdyson#include "lldb/Host/ProcessLaunchInfo.h"
2612115Sdyson#include "lldb/Target/DynamicLoader.h"
2712115Sdyson#include "lldb/Target/ExecutionContext.h"
2812115Sdyson#include "lldb/Target/Process.h"
2912115Sdyson#include "lldb/Target/Thread.h"
3012115Sdyson#include "lldb/Utility/DataBufferHeap.h"
3112115Sdyson#include "lldb/Utility/FileSpec.h"
3212115Sdyson#include "lldb/Utility/LLDBLog.h"
3312115Sdyson#include "lldb/Utility/Log.h"
3412115Sdyson#include "lldb/Utility/StreamString.h"
3512115Sdyson#include "llvm/ADT/ScopeExit.h"
3612115Sdyson#include <optional>
3712115Sdyson
3812115Sdysonusing namespace lldb;
3912115Sdysonusing namespace lldb_private;
4012115Sdyson
4112115Sdyson/// Default Constructor
4212115SdysonPlatformPOSIX::PlatformPOSIX(bool is_host)
4312115Sdyson    : RemoteAwarePlatform(is_host), // This is the local host platform
4412115Sdyson      m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
4593016Sbde      m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
4612115Sdyson      m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
4712115Sdyson
4812115Sdyson/// Destructor.
4912159Sbde///
5012115Sdyson/// The destructor is virtual since this class is designed to be
5160041Sphk/// inherited from by the plug-in instance.
5212115SdysonPlatformPOSIX::~PlatformPOSIX() = default;
5312115Sdyson
5412115Sdysonlldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
5512115Sdyson    lldb_private::CommandInterpreter &interpreter) {
5612115Sdyson  auto iter = m_options.find(&interpreter), end = m_options.end();
5796753Siedowse  if (iter == end) {
5812115Sdyson    std::unique_ptr<lldb_private::OptionGroupOptions> options(
5912115Sdyson        new OptionGroupOptions());
6012115Sdyson    options->Append(m_option_group_platform_rsync.get());
6196749Siedowse    options->Append(m_option_group_platform_ssh.get());
6296749Siedowse    options->Append(m_option_group_platform_caching.get());
6312115Sdyson    m_options[&interpreter] = std::move(options);
6412115Sdyson  }
6512115Sdyson
6612115Sdyson  return m_options.at(&interpreter).get();
6796753Siedowse}
6896753Siedowse
6996753Siedowsestatic uint32_t chown_file(Platform *platform, const char *path,
7096753Siedowse                           uint32_t uid = UINT32_MAX,
7196753Siedowse                           uint32_t gid = UINT32_MAX) {
7296753Siedowse  if (!platform || !path || *path == 0)
7396753Siedowse    return UINT32_MAX;
7496753Siedowse
7596753Siedowse  if (uid == UINT32_MAX && gid == UINT32_MAX)
76111742Sdes    return 0; // pretend I did chown correctly - actually I just didn't care
7712115Sdyson
7812115Sdyson  StreamString command;
7912115Sdyson  command.PutCString("chown ");
8012115Sdyson  if (uid != UINT32_MAX)
8112115Sdyson    command.Printf("%d", uid);
8212115Sdyson  if (gid != UINT32_MAX)
8355477Sbde    command.Printf(":%d", gid);
8455477Sbde  command.Printf("%s", path);
8555477Sbde  int status;
8655477Sbde  platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
8755477Sbde                            nullptr, std::chrono::seconds(10));
8855477Sbde  return status;
8955477Sbde}
9055477Sbde
9155477Sbdelldb_private::Status
9255477SbdePlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
9355477Sbde                       const lldb_private::FileSpec &destination, uint32_t uid,
9455477Sbde                       uint32_t gid) {
9555477Sbde  Log *log = GetLog(LLDBLog::Platform);
9655477Sbde
9755477Sbde  if (IsHost()) {
9855477Sbde    if (source == destination)
9955477Sbde      return Status();
10055477Sbde    // cp src dst
10155477Sbde    // chown uid:gid dst
10255477Sbde    std::string src_path(source.GetPath());
10355477Sbde    if (src_path.empty())
10455477Sbde      return Status("unable to get file path for source");
10555477Sbde    std::string dst_path(destination.GetPath());
10655477Sbde    if (dst_path.empty())
10755477Sbde      return Status("unable to get file path for destination");
10855477Sbde    StreamString command;
10955477Sbde    command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
11055477Sbde    int status;
11155477Sbde    RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
11255477Sbde                    std::chrono::seconds(10));
11355477Sbde    if (status != 0)
11455477Sbde      return Status("unable to perform copy");
11555477Sbde    if (uid == UINT32_MAX && gid == UINT32_MAX)
11655477Sbde      return Status();
11755477Sbde    if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
11893014Sbde      return Status("unable to perform chown");
11993014Sbde    return Status();
12012159Sbde  } else if (m_remote_platform_sp) {
12112115Sdyson    if (GetSupportsRSync()) {
12212115Sdyson      std::string src_path(source.GetPath());
12312115Sdyson      if (src_path.empty())
12412115Sdyson        return Status("unable to get file path for source");
12512115Sdyson      std::string dst_path(destination.GetPath());
12612115Sdyson      if (dst_path.empty())
12712115Sdyson        return Status("unable to get file path for destination");
12812115Sdyson      StreamString command;
12912115Sdyson      if (GetIgnoresRemoteHostname()) {
13012115Sdyson        if (!GetRSyncPrefix())
13112115Sdyson          command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
13212115Sdyson                         dst_path.c_str());
13312115Sdyson        else
134111742Sdes          command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
13512115Sdyson                         GetRSyncPrefix(), dst_path.c_str());
13612115Sdyson      } else
13712115Sdyson        command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
13812115Sdyson                       GetHostname(), dst_path.c_str());
13912115Sdyson      LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData());
14012115Sdyson      int retcode;
141111742Sdes      Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
142111742Sdes                            nullptr, std::chrono::minutes(1));
143111742Sdes      if (retcode == 0) {
144111742Sdes        // Don't chown a local file for a remote system
145111742Sdes        //                if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
14612115Sdyson        //                    return Status("unable to perform chown");
147111742Sdes        return Status();
148111742Sdes      }
14912115Sdyson      // if we are still here rsync has failed - let's try the slow way before
15055477Sbde      // giving up
15124649Sdfr    }
15212115Sdyson  }
15312115Sdyson  return Platform::PutFile(source, destination, uid, gid);
15412115Sdyson}
15512115Sdyson
15665780Sbdelldb_private::Status PlatformPOSIX::GetFile(
15712115Sdyson    const lldb_private::FileSpec &source,      // remote file path
15865780Sbde    const lldb_private::FileSpec &destination) // local file path
15912115Sdyson{
16065780Sbde  Log *log = GetLog(LLDBLog::Platform);
16165780Sbde
16265780Sbde  // Check the args, first.
16365780Sbde  std::string src_path(source.GetPath());
16465780Sbde  if (src_path.empty())
16565780Sbde    return Status("unable to get file path for source");
16665780Sbde  std::string dst_path(destination.GetPath());
16765780Sbde  if (dst_path.empty())
16865780Sbde    return Status("unable to get file path for destination");
16965780Sbde  if (IsHost()) {
17065780Sbde    if (source == destination)
17165780Sbde      return Status("local scenario->source and destination are the same file "
17212115Sdyson                    "path: no operation performed");
17365780Sbde    // cp src dst
174111742Sdes    StreamString cp_command;
17565780Sbde    cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
17612115Sdyson    int status;
17712115Sdyson    RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
17812115Sdyson                    std::chrono::seconds(10));
17912115Sdyson    if (status != 0)
18012115Sdyson      return Status("unable to perform copy");
18165780Sbde    return Status();
18212115Sdyson  } else if (m_remote_platform_sp) {
18312115Sdyson    if (GetSupportsRSync()) {
184111119Simp      StreamString command;
18512115Sdyson      if (GetIgnoresRemoteHostname()) {
18612115Sdyson        if (!GetRSyncPrefix())
18712115Sdyson          command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
18812115Sdyson                         dst_path.c_str());
18955477Sbde        else
19024649Sdfr          command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
19155477Sbde                         src_path.c_str(), dst_path.c_str());
192111742Sdes      } else
19355477Sbde        command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
19455477Sbde                       m_remote_platform_sp->GetHostname(), src_path.c_str(),
19555477Sbde                       dst_path.c_str());
19655477Sbde      LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData());
19755477Sbde      int retcode;
19855477Sbde      Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
19955477Sbde                            nullptr, std::chrono::minutes(1));
20055477Sbde      if (retcode == 0)
20155477Sbde        return Status();
20255477Sbde      // If we are here, rsync has failed - let's try the slow way before
20355477Sbde      // giving up
20455477Sbde    }
20555477Sbde    // open src and dst
20655477Sbde    // read/write, read/write, read/write, ...
20755477Sbde    // close src
20855477Sbde    // close dst
20955477Sbde    LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n");
21055477Sbde    Status error;
21155477Sbde    user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly,
21255477Sbde                                lldb::eFilePermissionsFileDefault, error);
21355477Sbde
21455477Sbde    if (fd_src == UINT64_MAX)
21555477Sbde      return Status("unable to open source file");
21655477Sbde
21755477Sbde    uint32_t permissions = 0;
21855477Sbde    error = GetFilePermissions(source, permissions);
21912115Sdyson
22012115Sdyson    if (permissions == 0)
22112115Sdyson      permissions = lldb::eFilePermissionsFileDefault;
22255477Sbde
223111742Sdes    user_id_t fd_dst = FileCache::GetInstance().OpenFile(
224111742Sdes        destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly |
225111741Sdes                         File::eOpenOptionTruncate,
22624649Sdfr        permissions, error);
22724649Sdfr
22812115Sdyson    if (fd_dst == UINT64_MAX) {
22912115Sdyson      if (error.Success())
23012115Sdyson        error.SetErrorString("unable to open destination file");
23112115Sdyson    }
23212115Sdyson
23312115Sdyson    if (error.Success()) {
23412115Sdyson      lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
23512115Sdyson      uint64_t offset = 0;
23612115Sdyson      error.Clear();
23724649Sdfr      while (error.Success()) {
23824649Sdfr        const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
23965780Sbde                                         buffer_sp->GetByteSize(), error);
24024649Sdfr        if (error.Fail())
24124649Sdfr          break;
24224649Sdfr        if (n_read == 0)
24324649Sdfr          break;
24424649Sdfr        if (FileCache::GetInstance().WriteFile(fd_dst, offset,
245111119Simp                                               buffer_sp->GetBytes(), n_read,
24624649Sdfr                                               error) != n_read) {
24765780Sbde          if (!error.Fail())
24865780Sbde            error.SetErrorString("unable to write to destination file");
24965780Sbde          break;
25055477Sbde        }
25124649Sdfr        offset += n_read;
25224649Sdfr      }
25324649Sdfr    }
25424649Sdfr    // Ignore the close error of src.
25524649Sdfr    if (fd_src != UINT64_MAX)
25624649Sdfr      CloseFile(fd_src, error);
25712115Sdyson    // And close the dst file descriptot.
25812115Sdyson    if (fd_dst != UINT64_MAX &&
25924649Sdfr        !FileCache::GetInstance().CloseFile(fd_dst, error)) {
26024649Sdfr      if (!error.Fail())
261111742Sdes        error.SetErrorString("unable to close destination file");
26212115Sdyson    }
26312115Sdyson    return error;
26412115Sdyson  }
26512115Sdyson  return Platform::GetFile(source, destination);
26612115Sdyson}
26712115Sdyson
26812115Sdysonstd::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
26912115Sdyson  StreamString stream;
27012115Sdyson  if (GetSupportsRSync()) {
27112115Sdyson    stream.PutCString("rsync");
27212115Sdyson    if ((GetRSyncOpts() && *GetRSyncOpts()) ||
27312115Sdyson        (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
27412115Sdyson      stream.Printf(", options: ");
27512115Sdyson      if (GetRSyncOpts() && *GetRSyncOpts())
27612115Sdyson        stream.Printf("'%s' ", GetRSyncOpts());
27712115Sdyson      stream.Printf(", prefix: ");
27812115Sdyson      if (GetRSyncPrefix() && *GetRSyncPrefix())
27912115Sdyson        stream.Printf("'%s' ", GetRSyncPrefix());
28012115Sdyson      if (GetIgnoresRemoteHostname())
28112115Sdyson        stream.Printf("ignore remote-hostname ");
28212115Sdyson    }
28312115Sdyson  }
28412115Sdyson  if (GetSupportsSSH()) {
28512115Sdyson    stream.PutCString("ssh");
28612115Sdyson    if (GetSSHOpts() && *GetSSHOpts())
28712115Sdyson      stream.Printf(", options: '%s' ", GetSSHOpts());
28812115Sdyson  }
28912115Sdyson  if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
29012115Sdyson    stream.Printf("cache dir: %s", GetLocalCacheDirectory());
29112115Sdyson  if (stream.GetSize())
29212115Sdyson    return std::string(stream.GetString());
29312115Sdyson  else
29412115Sdyson    return "";
29512115Sdyson}
29628787Sphk
29712115Sdysonconst lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
29812115Sdyson  if (IsRemote() && m_remote_platform_sp)
29912115Sdyson    return m_remote_platform_sp->GetRemoteUnixSignals();
30012115Sdyson  return Platform::GetRemoteUnixSignals();
30112115Sdyson}
30296752Siedowse
30396752SiedowseStatus PlatformPOSIX::ConnectRemote(Args &args) {
30412115Sdyson  Status error;
30596752Siedowse  if (IsHost()) {
30612115Sdyson    error.SetErrorStringWithFormatv(
30712115Sdyson        "can't connect to the host platform '{0}', always connected",
30812115Sdyson        GetPluginName());
30912115Sdyson  } else {
31012115Sdyson    if (!m_remote_platform_sp)
31112115Sdyson      m_remote_platform_sp =
31212115Sdyson          platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
31312115Sdyson              /*force=*/true, nullptr);
31412115Sdyson
31512115Sdyson    if (m_remote_platform_sp && error.Success())
31612115Sdyson      error = m_remote_platform_sp->ConnectRemote(args);
31712115Sdyson    else
31812115Sdyson      error.SetErrorString("failed to create a 'remote-gdb-server' platform");
31912115Sdyson
32012115Sdyson    if (error.Fail())
32112115Sdyson      m_remote_platform_sp.reset();
32212115Sdyson  }
32312115Sdyson
32412115Sdyson  if (error.Success() && m_remote_platform_sp) {
32512115Sdyson    if (m_option_group_platform_rsync.get() &&
32612115Sdyson        m_option_group_platform_ssh.get() &&
32783366Sjulian        m_option_group_platform_caching.get()) {
32812115Sdyson      if (m_option_group_platform_rsync->m_rsync) {
32912115Sdyson        SetSupportsRSync(true);
33012115Sdyson        SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
33112115Sdyson        SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
33212115Sdyson        SetIgnoresRemoteHostname(
33312115Sdyson            m_option_group_platform_rsync->m_ignores_remote_hostname);
33412115Sdyson      }
33512115Sdyson      if (m_option_group_platform_ssh->m_ssh) {
33612115Sdyson        SetSupportsSSH(true);
33712115Sdyson        SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
33812115Sdyson      }
33912115Sdyson      SetLocalCacheDirectory(
34012115Sdyson          m_option_group_platform_caching->m_cache_dir.c_str());
34112115Sdyson    }
34212115Sdyson  }
34312115Sdyson
34412115Sdyson  return error;
34512115Sdyson}
34612115Sdyson
34712115SdysonStatus PlatformPOSIX::DisconnectRemote() {
34812115Sdyson  Status error;
34912115Sdyson
35012115Sdyson  if (IsHost()) {
35112115Sdyson    error.SetErrorStringWithFormatv(
35212115Sdyson        "can't disconnect from the host platform '{0}', always connected",
35312115Sdyson        GetPluginName());
354111742Sdes  } else {
35512115Sdyson    if (m_remote_platform_sp)
35612115Sdyson      error = m_remote_platform_sp->DisconnectRemote();
35712115Sdyson    else
35812115Sdyson      error.SetErrorString("the platform is not currently connected");
35912115Sdyson  }
36012115Sdyson  return error;
36112115Sdyson}
36212115Sdyson
36312115Sdysonlldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
36412115Sdyson                                      Debugger &debugger, Target *target,
36512115Sdyson                                      Status &error) {
36612115Sdyson  lldb::ProcessSP process_sp;
36712115Sdyson  Log *log = GetLog(LLDBLog::Platform);
36812115Sdyson
36912115Sdyson  if (IsHost()) {
37012115Sdyson    if (target == nullptr) {
37196749Siedowse      TargetSP new_target_sp;
37212115Sdyson
37312115Sdyson      error = debugger.GetTargetList().CreateTarget(
37412115Sdyson          debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
37512115Sdyson      target = new_target_sp.get();
37612115Sdyson      LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__);
37712115Sdyson    } else {
37812115Sdyson      error.Clear();
37912115Sdyson      LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target",
38096749Siedowse                __FUNCTION__);
38196749Siedowse    }
38212115Sdyson
38312115Sdyson    if (target && error.Success()) {
38412115Sdyson      if (log) {
38512115Sdyson        ModuleSP exe_module_sp = target->GetExecutableModule();
38612115Sdyson        LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s",
38712115Sdyson                  __FUNCTION__, (void *)target,
38812115Sdyson                  exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()
38912115Sdyson                                : "<null>");
39012115Sdyson      }
39112115Sdyson
39212115Sdyson      process_sp =
39312115Sdyson          target->CreateProcess(attach_info.GetListenerForProcess(debugger),
39412115Sdyson                                "gdb-remote", nullptr, true);
39512115Sdyson
39612115Sdyson      if (process_sp) {
39712115Sdyson        ListenerSP listener_sp = attach_info.GetHijackListener();
39843301Sdillon        if (listener_sp == nullptr) {
39996749Siedowse          listener_sp =
40096749Siedowse              Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
40112115Sdyson          attach_info.SetHijackListener(listener_sp);
40212115Sdyson        }
40312115Sdyson        process_sp->HijackProcessEvents(listener_sp);
40412115Sdyson        process_sp->SetShadowListener(attach_info.GetShadowListener());
40512115Sdyson        error = process_sp->Attach(attach_info);
40612115Sdyson      }
40712115Sdyson    }
40812115Sdyson  } else {
40912115Sdyson    if (m_remote_platform_sp)
41012115Sdyson      process_sp =
41112115Sdyson          m_remote_platform_sp->Attach(attach_info, debugger, target, error);
41212115Sdyson    else
41312115Sdyson      error.SetErrorString("the platform is not currently connected");
41412115Sdyson  }
41512115Sdyson  return process_sp;
41612115Sdyson}
41796753Siedowse
41896753Siedowselldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
41912115Sdyson                                            Debugger &debugger, Target &target,
42055477Sbde                                            Status &error) {
42112115Sdyson  Log *log = GetLog(LLDBLog::Platform);
42212115Sdyson  LLDB_LOG(log, "target {0}", &target);
42312147Sdyson
42412115Sdyson  ProcessSP process_sp;
42596749Siedowse
42612115Sdyson  if (!IsHost()) {
42712115Sdyson    if (m_remote_platform_sp)
42812115Sdyson      process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
42912115Sdyson                                                      target, error);
43012115Sdyson    else
43112115Sdyson      error.SetErrorString("the platform is not currently connected");
43212115Sdyson    return process_sp;
43312115Sdyson  }
43412115Sdyson
43512115Sdyson  //
43612115Sdyson  // For local debugging, we'll insist on having ProcessGDBRemote create the
43712115Sdyson  // process.
43812115Sdyson  //
43912115Sdyson
44012115Sdyson  // Make sure we stop at the entry point
44112115Sdyson  launch_info.GetFlags().Set(eLaunchFlagDebug);
44212115Sdyson
44312115Sdyson  // We always launch the process we are going to debug in a separate process
44412115Sdyson  // group, since then we can handle ^C interrupts ourselves w/o having to
44512115Sdyson  // worry about the target getting them as well.
44612115Sdyson  launch_info.SetLaunchInSeparateProcessGroup(true);
44712115Sdyson
44812115Sdyson  // Now create the gdb-remote process.
44912115Sdyson  LLDB_LOG(log, "having target create process with gdb-remote plugin");
45012115Sdyson  process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
45112115Sdyson                                    nullptr, true);
45212115Sdyson
45312115Sdyson  if (!process_sp) {
45412115Sdyson    error.SetErrorString("CreateProcess() failed for gdb-remote process");
45512115Sdyson    LLDB_LOG(log, "error: {0}", error);
45612115Sdyson    return process_sp;
45712115Sdyson  }
45812115Sdyson
45912115Sdyson  LLDB_LOG(log, "successfully created process");
46012115Sdyson
46112115Sdyson  process_sp->HijackProcessEvents(launch_info.GetHijackListener());
46212115Sdyson  process_sp->SetShadowListener(launch_info.GetShadowListener());
46312115Sdyson
46412115Sdyson  // Log file actions.
46512115Sdyson  if (log) {
46612115Sdyson    LLDB_LOG(log, "launching process with the following file actions:");
46712115Sdyson    StreamString stream;
46812115Sdyson    size_t i = 0;
46912115Sdyson    const FileAction *file_action;
47012115Sdyson    while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
47112115Sdyson      file_action->Dump(stream);
47212115Sdyson      LLDB_LOG(log, "{0}", stream.GetData());
47312115Sdyson      stream.Clear();
47412115Sdyson    }
47512115Sdyson  }
47612115Sdyson
47712115Sdyson  // Do the launch.
47812115Sdyson  error = process_sp->Launch(launch_info);
47912115Sdyson  if (error.Success()) {
48012115Sdyson    // Hook up process PTY if we have one (which we should for local debugging
48112115Sdyson    // with llgs).
48212115Sdyson    int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
48312115Sdyson    if (pty_fd != PseudoTerminal::invalid_fd) {
48412115Sdyson      process_sp->SetSTDIOFileDescriptor(pty_fd);
48512115Sdyson      LLDB_LOG(log, "hooked up STDIO pty to process");
48612115Sdyson    } else
48712115Sdyson      LLDB_LOG(log, "not using process STDIO pty");
48812115Sdyson  } else {
48912115Sdyson    LLDB_LOG(log, "{0}", error);
49012115Sdyson    // FIXME figure out appropriate cleanup here. Do we delete the process?
49112115Sdyson    // Does our caller do that?
49212115Sdyson  }
49312115Sdyson
49412115Sdyson  return process_sp;
49512115Sdyson}
49612115Sdyson
49712115Sdysonvoid PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
49812115Sdyson  m_trap_handlers.push_back(ConstString("_sigtramp"));
49912115Sdyson}
50012115Sdyson
50112115SdysonStatus PlatformPOSIX::EvaluateLibdlExpression(
50212115Sdyson    lldb_private::Process *process, const char *expr_cstr,
50312115Sdyson    llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
50412115Sdyson  DynamicLoader *loader = process->GetDynamicLoader();
50512115Sdyson  if (loader) {
50612115Sdyson    Status error = loader->CanLoadImage();
50712115Sdyson    if (error.Fail())
50812115Sdyson      return error;
50983366Sjulian  }
51012115Sdyson
51112115Sdyson  ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
51212115Sdyson  if (!thread_sp)
51312115Sdyson    return Status("Selected thread isn't valid");
51412115Sdyson
51512115Sdyson  StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
51612115Sdyson  if (!frame_sp)
51712115Sdyson    return Status("Frame 0 isn't valid");
51812115Sdyson
51912115Sdyson  ExecutionContext exe_ctx;
52012115Sdyson  frame_sp->CalculateExecutionContext(exe_ctx);
52112115Sdyson  EvaluateExpressionOptions expr_options;
52212115Sdyson  expr_options.SetUnwindOnError(true);
52312115Sdyson  expr_options.SetIgnoreBreakpoints(true);
52412115Sdyson  expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
52512115Sdyson  expr_options.SetLanguage(eLanguageTypeC_plus_plus);
52612115Sdyson  expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
52712115Sdyson                                         // don't do the work to trap them.
52812115Sdyson  expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
52912115Sdyson
53012115Sdyson  Status expr_error;
53112115Sdyson  ExpressionResults result =
53212115Sdyson      UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
53312115Sdyson                               result_valobj_sp, expr_error);
53412115Sdyson  if (result != eExpressionCompleted)
53512115Sdyson    return expr_error;
53612115Sdyson
53712115Sdyson  if (result_valobj_sp->GetError().Fail())
53812115Sdyson    return result_valobj_sp->GetError();
53912115Sdyson  return Status();
54012115Sdyson}
54112115Sdyson
54212115Sdysonstd::unique_ptr<UtilityFunction>
54312115SdysonPlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
54412115Sdyson                                            Status &error) {
54512115Sdyson  // Remember to prepend this with the prefix from
54612115Sdyson  // GetLibdlFunctionDeclarations. The returned values are all in
54783366Sjulian  // __lldb_dlopen_result for consistency. The wrapper returns a void * but
54812115Sdyson  // doesn't use it because UtilityFunctions don't work with void returns at
54912115Sdyson  // present.
55012115Sdyson  //
55112115Sdyson  // Use lazy binding so as to not make dlopen()'s success conditional on
55212115Sdyson  // forcing every symbol in the library.
55312115Sdyson  //
55412115Sdyson  // In general, the debugger should allow programs to load & run with
55512115Sdyson  // libraries as far as they can, instead of defaulting to being super-picky
55612115Sdyson  // about unavailable symbols.
55712115Sdyson  //
55812115Sdyson  // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
55912115Sdyson  // and other POSIX OSes.
56012115Sdyson  static const char *dlopen_wrapper_code = R"(
56112115Sdyson  const int RTLD_LAZY = 1;
56212115Sdyson
56312115Sdyson  struct __lldb_dlopen_result {
56412115Sdyson    void *image_ptr;
56512115Sdyson    const char *error_str;
56696749Siedowse  };
56712115Sdyson
56812115Sdyson  extern "C" void *memcpy(void *, const void *, size_t size);
56912115Sdyson  extern "C" size_t strlen(const char *);
570105420Sbde
57112115Sdyson
57212115Sdyson  void * __lldb_dlopen_wrapper (const char *name,
57312115Sdyson                                const char *path_strings,
57412115Sdyson                                char *buffer,
57512115Sdyson                                __lldb_dlopen_result *result_ptr)
57612115Sdyson  {
57712115Sdyson    // This is the case where the name is the full path:
57812115Sdyson    if (!path_strings) {
57912115Sdyson      result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
58012115Sdyson      if (result_ptr->image_ptr)
58112115Sdyson        result_ptr->error_str = nullptr;
58212115Sdyson      else
58312115Sdyson        result_ptr->error_str = dlerror();
58412115Sdyson      return nullptr;
58512115Sdyson    }
58612115Sdyson
58712115Sdyson    // This is the case where we have a list of paths:
58812115Sdyson    size_t name_len = strlen(name);
58912115Sdyson    while (path_strings && path_strings[0] != '\0') {
59012115Sdyson      size_t path_len = strlen(path_strings);
59183366Sjulian      memcpy((void *) buffer, (void *) path_strings, path_len);
59212115Sdyson      buffer[path_len] = '/';
59312115Sdyson      char *target_ptr = buffer+path_len+1;
59412115Sdyson      memcpy((void *) target_ptr, (void *) name, name_len + 1);
59512115Sdyson      result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
59612115Sdyson      if (result_ptr->image_ptr) {
59712115Sdyson        result_ptr->error_str = nullptr;
59812115Sdyson        break;
59912115Sdyson      }
60012115Sdyson      result_ptr->error_str = dlerror();
60112115Sdyson      path_strings = path_strings + path_len + 1;
60212115Sdyson    }
60312115Sdyson    return nullptr;
60412115Sdyson  }
60512115Sdyson  )";
60612115Sdyson
60712115Sdyson  static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
60892462Smckusick  Process *process = exe_ctx.GetProcessSP().get();
60992462Smckusick  // Insert the dlopen shim defines into our generic expression:
61012115Sdyson  std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
61112115Sdyson  expr.append(dlopen_wrapper_code);
61212115Sdyson  Status utility_error;
61312115Sdyson  DiagnosticManager diagnostics;
61412115Sdyson
61512115Sdyson  auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
61612115Sdyson      std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
61712115Sdyson  if (!utility_fn_or_error) {
61812115Sdyson    std::string error_str = llvm::toString(utility_fn_or_error.takeError());
61912115Sdyson    error.SetErrorStringWithFormat(
62012115Sdyson        "dlopen error: could not create utility function: %s",
62112115Sdyson        error_str.c_str());
62212115Sdyson    return nullptr;
62312115Sdyson  }
62412115Sdyson  std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
62512115Sdyson      std::move(*utility_fn_or_error);
62683366Sjulian
62712115Sdyson  Value value;
62812115Sdyson  ValueList arguments;
62912115Sdyson  FunctionCaller *do_dlopen_function = nullptr;
63012115Sdyson
63112115Sdyson  // Fetch the clang types we will need:
63212115Sdyson  TypeSystemClangSP scratch_ts_sp =
63312115Sdyson      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
63412115Sdyson  if (!scratch_ts_sp)
63512115Sdyson    return nullptr;
63612115Sdyson
63712115Sdyson  CompilerType clang_void_pointer_type =
63883366Sjulian      scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
63912115Sdyson  CompilerType clang_char_pointer_type =
64012115Sdyson      scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType();
64112115Sdyson
64212115Sdyson  // We are passing four arguments, the basename, the list of places to look,
64312115Sdyson  // a buffer big enough for all the path + name combos, and
64412115Sdyson  // a pointer to the storage we've made for the result:
64512115Sdyson  value.SetValueType(Value::ValueType::Scalar);
64692462Smckusick  value.SetCompilerType(clang_void_pointer_type);
64792462Smckusick  arguments.PushValue(value);
64812115Sdyson  value.SetCompilerType(clang_char_pointer_type);
64912115Sdyson  arguments.PushValue(value);
65012115Sdyson  arguments.PushValue(value);
65112115Sdyson  arguments.PushValue(value);
65283366Sjulian
65312115Sdyson  do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
65412115Sdyson      clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
65512115Sdyson  if (utility_error.Fail()) {
65612115Sdyson    error.SetErrorStringWithFormat(
65712115Sdyson        "dlopen error: could not make function caller: %s",
65812115Sdyson        utility_error.AsCString());
65912115Sdyson    return nullptr;
66012115Sdyson  }
66112115Sdyson
66212115Sdyson  do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
66312115Sdyson  if (!do_dlopen_function) {
66412115Sdyson    error.SetErrorString("dlopen error: could not get function caller.");
66512115Sdyson    return nullptr;
66612115Sdyson  }
66712115Sdyson
66812115Sdyson  // We made a good utility function, so cache it in the process:
66912115Sdyson  return dlopen_utility_func_up;
67012115Sdyson}
67112115Sdyson
67212115Sdysonuint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
67312115Sdyson                                    const lldb_private::FileSpec &remote_file,
67412115Sdyson                                    const std::vector<std::string> *paths,
67512115Sdyson                                    lldb_private::Status &error,
67612115Sdyson                                    lldb_private::FileSpec *loaded_image) {
67783366Sjulian  if (loaded_image)
67892462Smckusick    loaded_image->Clear();
67992462Smckusick
68083366Sjulian  std::string path;
68112115Sdyson  path = remote_file.GetPath();
68212115Sdyson
68312115Sdyson  ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
68483366Sjulian  if (!thread_sp) {
68512115Sdyson    error.SetErrorString("dlopen error: no thread available to call dlopen.");
68612115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
68712115Sdyson  }
68812115Sdyson
68912115Sdyson  DiagnosticManager diagnostics;
69012115Sdyson
69112115Sdyson  ExecutionContext exe_ctx;
69212115Sdyson  thread_sp->CalculateExecutionContext(exe_ctx);
69392462Smckusick
69492462Smckusick  Status utility_error;
69512115Sdyson  UtilityFunction *dlopen_utility_func;
69612115Sdyson  ValueList arguments;
69783366Sjulian  FunctionCaller *do_dlopen_function = nullptr;
69812115Sdyson
69912115Sdyson  // The UtilityFunction is held in the Process.  Platforms don't track the
70012115Sdyson  // lifespan of the Targets that use them, we can't put this in the Platform.
70112115Sdyson  dlopen_utility_func = process->GetLoadImageUtilityFunction(
70212115Sdyson      this, [&]() -> std::unique_ptr<UtilityFunction> {
70312115Sdyson        return MakeLoadImageUtilityFunction(exe_ctx, error);
70412115Sdyson      });
70512115Sdyson  // If we couldn't make it, the error will be in error, so we can exit here.
70612115Sdyson  if (!dlopen_utility_func)
70712115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
70812115Sdyson
70996749Siedowse  do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
71096749Siedowse  if (!do_dlopen_function) {
71196749Siedowse    error.SetErrorString("dlopen error: could not get function caller.");
71296749Siedowse    return LLDB_INVALID_IMAGE_TOKEN;
71396749Siedowse  }
71496749Siedowse  arguments = do_dlopen_function->GetArgumentValues();
71596749Siedowse
71696749Siedowse  // Now insert the path we are searching for and the result structure into the
71796749Siedowse  // target.
71896749Siedowse  uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
71996749Siedowse  size_t path_len = path.size() + 1;
72096749Siedowse  lldb::addr_t path_addr = process->AllocateMemory(path_len,
72196749Siedowse                                                   permissions,
72296749Siedowse                                                   utility_error);
72396749Siedowse  if (path_addr == LLDB_INVALID_ADDRESS) {
72412115Sdyson    error.SetErrorStringWithFormat(
72512115Sdyson        "dlopen error: could not allocate memory for path: %s",
72612115Sdyson        utility_error.AsCString());
72712115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
72812115Sdyson  }
72912115Sdyson
73012115Sdyson  // Make sure we deallocate the input string memory:
73112115Sdyson  auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
73212115Sdyson    // Deallocate the buffer.
73312115Sdyson    process->DeallocateMemory(path_addr);
73412115Sdyson  });
73512159Sbde
73612115Sdyson  process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
73712115Sdyson  if (utility_error.Fail()) {
73896752Siedowse    error.SetErrorStringWithFormat(
73912115Sdyson        "dlopen error: could not write path string: %s",
74012115Sdyson        utility_error.AsCString());
74112115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
74212115Sdyson  }
743111742Sdes
74412115Sdyson  // Make space for our return structure.  It is two pointers big: the token
745111742Sdes  // and the error string.
746111742Sdes  const uint32_t addr_size = process->GetAddressByteSize();
747111742Sdes  lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
748111742Sdes                                                      permissions,
749111742Sdes                                                      utility_error);
750111742Sdes  if (utility_error.Fail()) {
751111742Sdes    error.SetErrorStringWithFormat(
752111742Sdes        "dlopen error: could not allocate memory for path: %s",
753111742Sdes        utility_error.AsCString());
75412115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
755111742Sdes  }
75612115Sdyson
75712115Sdyson  // Make sure we deallocate the result structure memory
758111742Sdes  auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
759111742Sdes    // Deallocate the buffer
760111742Sdes    process->DeallocateMemory(return_addr);
76124492Sbde  });
76224492Sbde
763111742Sdes  // This will be the address of the storage for paths, if we are using them,
764111742Sdes  // or nullptr to signal we aren't.
76512115Sdyson  lldb::addr_t path_array_addr = 0x0;
76612115Sdyson  std::optional<llvm::detail::scope_exit<std::function<void()>>>
76712115Sdyson      path_array_cleanup;
76812115Sdyson
76912115Sdyson  // This is the address to a buffer large enough to hold the largest path
77012115Sdyson  // conjoined with the library name we're passing in.  This is a convenience
77112115Sdyson  // to avoid having to call malloc in the dlopen function.
77212115Sdyson  lldb::addr_t buffer_addr = 0x0;
77312115Sdyson  std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup;
77412115Sdyson
77512115Sdyson  // Set the values into our args and write them to the target:
77612115Sdyson  if (paths != nullptr) {
77712115Sdyson    // First insert the paths into the target.  This is expected to be a
77812115Sdyson    // continuous buffer with the strings laid out null terminated and
77996752Siedowse    // end to end with an empty string terminating the buffer.
78012115Sdyson    // We also compute the buffer's required size as we go.
78196752Siedowse    size_t buffer_size = 0;
78296752Siedowse    std::string path_array;
78312115Sdyson    for (auto path : *paths) {
78455477Sbde      // Don't insert empty paths, they will make us abort the path
78512115Sdyson      // search prematurely.
78612115Sdyson      if (path.empty())
78712115Sdyson        continue;
78812115Sdyson      size_t path_size = path.size();
78912115Sdyson      path_array.append(path);
79012115Sdyson      path_array.push_back('\0');
79112115Sdyson      if (path_size > buffer_size)
79212115Sdyson        buffer_size = path_size;
79312115Sdyson    }
79412115Sdyson    path_array.push_back('\0');
79512115Sdyson
79612115Sdyson    path_array_addr = process->AllocateMemory(path_array.size(),
79712115Sdyson                                              permissions,
79812115Sdyson                                              utility_error);
79912115Sdyson    if (path_array_addr == LLDB_INVALID_ADDRESS) {
80055477Sbde      error.SetErrorStringWithFormat(
80155477Sbde          "dlopen error: could not allocate memory for path array: %s",
80255477Sbde          utility_error.AsCString());
80355477Sbde      return LLDB_INVALID_IMAGE_TOKEN;
80455477Sbde    }
80512115Sdyson
80612115Sdyson    // Make sure we deallocate the paths array.
80712115Sdyson    path_array_cleanup.emplace([process, path_array_addr]() {
80812115Sdyson      // Deallocate the path array.
80912115Sdyson      process->DeallocateMemory(path_array_addr);
81012115Sdyson    });
81112115Sdyson
81212115Sdyson    process->WriteMemory(path_array_addr, path_array.data(),
81312115Sdyson                         path_array.size(), utility_error);
81412115Sdyson
81512115Sdyson    if (utility_error.Fail()) {
81612115Sdyson      error.SetErrorStringWithFormat(
81712115Sdyson          "dlopen error: could not write path array: %s",
81812115Sdyson          utility_error.AsCString());
81912115Sdyson      return LLDB_INVALID_IMAGE_TOKEN;
82012115Sdyson    }
82112115Sdyson    // Now make spaces in the target for the buffer.  We need to add one for
82212115Sdyson    // the '/' that the utility function will insert and one for the '\0':
82312115Sdyson    buffer_size += path.size() + 2;
82412115Sdyson
82583366Sjulian    buffer_addr = process->AllocateMemory(buffer_size,
82612115Sdyson                                          permissions,
82712115Sdyson                                          utility_error);
82896749Siedowse    if (buffer_addr == LLDB_INVALID_ADDRESS) {
82912115Sdyson      error.SetErrorStringWithFormat(
83012115Sdyson          "dlopen error: could not allocate memory for buffer: %s",
83112115Sdyson          utility_error.AsCString());
83212115Sdyson      return LLDB_INVALID_IMAGE_TOKEN;
83312115Sdyson    }
83412115Sdyson
83512115Sdyson    // Make sure we deallocate the buffer memory:
83612115Sdyson    buffer_cleanup.emplace([process, buffer_addr]() {
83712115Sdyson      // Deallocate the buffer.
83812115Sdyson      process->DeallocateMemory(buffer_addr);
83912115Sdyson    });
84012115Sdyson  }
84112115Sdyson
84212115Sdyson  arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
84312115Sdyson  arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
84412115Sdyson  arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
84512115Sdyson  arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
84612115Sdyson
84712115Sdyson  lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
84812115Sdyson
84912115Sdyson  diagnostics.Clear();
85012115Sdyson  if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
85112115Sdyson                                                 func_args_addr,
85212115Sdyson                                                 arguments,
85312115Sdyson                                                 diagnostics)) {
85412115Sdyson    error.SetErrorStringWithFormat(
85512115Sdyson        "dlopen error: could not write function arguments: %s",
85612115Sdyson        diagnostics.GetString().c_str());
85712115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
85812115Sdyson  }
85996749Siedowse
86096749Siedowse  // Make sure we clean up the args structure.  We can't reuse it because the
86112115Sdyson  // Platform lives longer than the process and the Platforms don't get a
86212115Sdyson  // signal to clean up cached data when a process goes away.
86312115Sdyson  auto args_cleanup =
86412115Sdyson      llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
86512115Sdyson        do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
86612115Sdyson      });
86712115Sdyson
86812115Sdyson  // Now run the caller:
86955477Sbde  EvaluateExpressionOptions options;
87012115Sdyson  options.SetExecutionPolicy(eExecutionPolicyAlways);
87112115Sdyson  options.SetLanguage(eLanguageTypeC_plus_plus);
87212115Sdyson  options.SetIgnoreBreakpoints(true);
87355477Sbde  options.SetUnwindOnError(true);
87412115Sdyson  options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
87512115Sdyson                                    // don't do the work to trap them.
87612115Sdyson  options.SetTimeout(process->GetUtilityExpressionTimeout());
87755477Sbde  options.SetIsForUtilityExpr(true);
87812115Sdyson
87912115Sdyson  Value return_value;
88012115Sdyson  // Fetch the clang types we will need:
88112115Sdyson  TypeSystemClangSP scratch_ts_sp =
88219541Sbde      ScratchTypeSystemClang::GetForTarget(process->GetTarget());
88312115Sdyson  if (!scratch_ts_sp) {
88412115Sdyson    error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
88512115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
88612115Sdyson  }
88712115Sdyson
88812115Sdyson  CompilerType clang_void_pointer_type =
88912115Sdyson      scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType();
89012115Sdyson
89112115Sdyson  return_value.SetCompilerType(clang_void_pointer_type);
89212115Sdyson
89312115Sdyson  ExpressionResults results = do_dlopen_function->ExecuteFunction(
89412115Sdyson      exe_ctx, &func_args_addr, options, diagnostics, return_value);
89512115Sdyson  if (results != eExpressionCompleted) {
89612115Sdyson    error.SetErrorStringWithFormat(
89712115Sdyson        "dlopen error: failed executing dlopen wrapper function: %s",
89812115Sdyson        diagnostics.GetString().c_str());
89912115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
90055477Sbde  }
90112115Sdyson
90212115Sdyson  // Read the dlopen token from the return area:
90358349Sphk  lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
90412115Sdyson                                                      utility_error);
90512115Sdyson  if (utility_error.Fail()) {
90696749Siedowse    error.SetErrorStringWithFormat(
90783366Sjulian        "dlopen error: could not read the return struct: %s",
90812115Sdyson        utility_error.AsCString());
90912115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
91012115Sdyson  }
91112115Sdyson
91212115Sdyson  // The dlopen succeeded!
91312115Sdyson  if (token != 0x0) {
91412115Sdyson    if (loaded_image && buffer_addr != 0x0)
91512115Sdyson    {
91612115Sdyson      // Capture the image which was loaded.  We leave it in the buffer on
91712115Sdyson      // exit from the dlopen function, so we can just read it from there:
91812115Sdyson      std::string name_string;
91912115Sdyson      process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
92012115Sdyson      if (utility_error.Success())
92112115Sdyson        loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
92212115Sdyson    }
92312115Sdyson    return process->AddImageToken(token);
92412115Sdyson  }
92512115Sdyson
92612115Sdyson  // We got an error, lets read in the error string:
92712115Sdyson  std::string dlopen_error_str;
92896752Siedowse  lldb::addr_t error_addr
92955477Sbde    = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
93012115Sdyson  if (utility_error.Fail()) {
93112115Sdyson    error.SetErrorStringWithFormat(
932111742Sdes        "dlopen error: could not read error string: %s",
93312115Sdyson        utility_error.AsCString());
93412115Sdyson    return LLDB_INVALID_IMAGE_TOKEN;
93512115Sdyson  }
93612115Sdyson
93712115Sdyson  size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
93843301Sdillon                                                    dlopen_error_str,
93996749Siedowse                                                    utility_error);
94096749Siedowse  if (utility_error.Success() && num_chars > 0)
94112115Sdyson    error.SetErrorStringWithFormat("dlopen error: %s",
94212115Sdyson                                   dlopen_error_str.c_str());
94358349Sphk  else
94412115Sdyson    error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
94512115Sdyson
94612115Sdyson  return LLDB_INVALID_IMAGE_TOKEN;
94712115Sdyson}
94812115Sdyson
94912115SdysonStatus PlatformPOSIX::UnloadImage(lldb_private::Process *process,
95096749Siedowse                                  uint32_t image_token) {
95143301Sdillon  const addr_t image_addr = process->GetImagePtrFromToken(image_token);
95212115Sdyson  if (image_addr == LLDB_INVALID_IMAGE_TOKEN)
95312115Sdyson    return Status("Invalid image token");
95458349Sphk
95512115Sdyson  StreamString expr;
95612115Sdyson  expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr);
95712115Sdyson  llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
95812115Sdyson  lldb::ValueObjectSP result_valobj_sp;
95912115Sdyson  Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
96012115Sdyson                                         result_valobj_sp);
96112115Sdyson  if (error.Fail())
96212115Sdyson    return error;
96312115Sdyson
96412115Sdyson  if (result_valobj_sp->GetError().Fail())
96512115Sdyson    return result_valobj_sp->GetError();
96612115Sdyson
96712115Sdyson  Scalar scalar;
96812115Sdyson  if (result_valobj_sp->ResolveValue(scalar)) {
96912115Sdyson    if (scalar.UInt(1))
97055477Sbde      return Status("expression failed: \"%s\"", expr.GetData());
97112115Sdyson    process->ResetImageToken(image_token);
97212115Sdyson  }
97312115Sdyson  return Status();
97496749Siedowse}
97596749Siedowse
97612115Sdysonllvm::StringRef
97712115SdysonPlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
97855477Sbde  return R"(
97955477Sbde              extern "C" void* dlopen(const char*, int);
98055477Sbde              extern "C" void* dlsym(void*, const char*);
98155477Sbde              extern "C" int   dlclose(void*);
98255477Sbde              extern "C" char* dlerror(void);
98358349Sphk             )";
98412115Sdyson}
98512115Sdyson
98612115SdysonConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
98712115Sdyson  if (basename.IsEmpty())
98812115Sdyson    return basename;
98912115Sdyson
99012115Sdyson  StreamString stream;
99112115Sdyson  stream.Printf("lib%s.so", basename.GetCString());
99212115Sdyson  return ConstString(stream.GetString());
99312115Sdyson}
99412115Sdyson