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