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