diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Platform')
13 files changed, 3493 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp new file mode 100644 index 000000000000..d7584be2b95e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -0,0 +1,284 @@ +//===-- PlatformFreeBSD.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PlatformFreeBSD.h" +#include "lldb/Host/Config.h" + +#include <cstdio> +#if LLDB_ENABLE_POSIX +#include <sys/utsname.h> +#endif + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/Triple.h" + +// Define these constants from FreeBSD mman.h for use when targeting remote +// FreeBSD systems even when host has different values. +#define MAP_PRIVATE 0x0002 +#define MAP_ANON 0x1000 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_freebsd; + +LLDB_PLUGIN_DEFINE(PlatformFreeBSD) + +static uint32_t g_initialize_count = 0; + + +PlatformSP PlatformFreeBSD::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force, + arch ? arch->GetArchitectureName() : "<null>", + arch ? arch->GetTriple().getTriple() : "<null>"); + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::FreeBSD: + create = true; + break; + +#if defined(__FreeBSD__) + // Only accept "unknown" for the OS if the host is BSD and it "unknown" + // wasn't specified (it was just returned because it was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + break; + } + } + LLDB_LOG(log, "create = {0}", create); + if (create) { + return PlatformSP(new PlatformFreeBSD(false)); + } + return PlatformSP(); +} + +llvm::StringRef PlatformFreeBSD::GetPluginDescriptionStatic(bool is_host) { + if (is_host) + return "Local FreeBSD user platform plug-in."; + return "Remote FreeBSD user platform plug-in."; +} + +void PlatformFreeBSD::Initialize() { + Platform::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__FreeBSD__) + PlatformSP default_platform_sp(new PlatformFreeBSD(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformFreeBSD::GetPluginNameStatic(false), + PlatformFreeBSD::GetPluginDescriptionStatic(false), + PlatformFreeBSD::CreateInstance, nullptr); + } +} + +void PlatformFreeBSD::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformFreeBSD::CreateInstance); + } + } + + PlatformPOSIX::Terminate(); +} + +/// Default Constructor +PlatformFreeBSD::PlatformFreeBSD(bool is_host) + : PlatformPOSIX(is_host) // This is the local host platform +{ + if (is_host) { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + m_supported_architectures.push_back(hostArch); + if (hostArch.GetTriple().isArch64Bit()) { + m_supported_architectures.push_back( + HostInfo::GetArchitecture(HostInfo::eArchKind32)); + } + } else { + m_supported_architectures = CreateArchList( + {llvm::Triple::x86_64, llvm::Triple::x86, llvm::Triple::aarch64, + llvm::Triple::arm, llvm::Triple::mips64, llvm::Triple::ppc64, + llvm::Triple::ppc}, + llvm::Triple::FreeBSD); + } +} + +std::vector<ArchSpec> +PlatformFreeBSD::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); + return m_supported_architectures; +} + +void PlatformFreeBSD::GetStatus(Stream &strm) { + Platform::GetStatus(strm); + +#if LLDB_ENABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-FreeBSD information (when running + // on Mac OS for example). + if (IsHost()) { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf(" Kernel: %s\n", un.sysname); + strm.Printf(" Release: %s\n", un.release); + strm.Printf(" Version: %s\n", un.version); + } +#endif +} + +bool PlatformFreeBSD::CanDebugProcess() { + if (IsHost()) { + return true; + } else { + // If we're connected, we can debug. + return IsConnected(); + } +} + +void PlatformFreeBSD::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); +} + +MmapArgList PlatformFreeBSD::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { + uint64_t flags_platform = 0; + + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= MAP_ANON; + + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + if (arch.GetTriple().getArch() == llvm::Triple::x86) + args.push_back(0); + return args; +} + +CompilerType PlatformFreeBSD::GetSiginfoType(const llvm::Triple &triple) { + { + std::lock_guard<std::mutex> guard(m_mutex); + if (!m_type_system) + m_type_system = std::make_shared<TypeSystemClang>("siginfo", triple); + } + TypeSystemClang *ast = m_type_system.get(); + + // generic types + CompilerType int_type = ast->GetBasicType(eBasicTypeInt); + CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); + CompilerType long_type = ast->GetBasicType(eBasicTypeLong); + CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); + + // platform-specific types + CompilerType &pid_type = int_type; + CompilerType &uid_type = uint_type; + + CompilerType sigval_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigval_type); + ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigval_type); + + // siginfo_t + CompilerType siginfo_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", + llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(siginfo_type); + ast->AddFieldToRecordType(siginfo_type, "si_signo", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_errno", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_code", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_pid", pid_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_uid", uid_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_status", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_addr", voidp_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(siginfo_type, "si_value", sigval_type, + lldb::eAccessPublic, 0); + + // union used to hold the signal data + CompilerType union_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(union_type); + + ast->AddFieldToRecordType( + union_type, "_fault", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_trapno", int_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_timer", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_timerid", int_type}, + {"_overrun", int_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_mesgq", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_mqd", int_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_poll", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_band", long_type}, + }), + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(union_type); + ast->AddFieldToRecordType(siginfo_type, "_reason", union_type, + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(siginfo_type); + return siginfo_type; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h new file mode 100644 index 000000000000..1e92bb4a1e14 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h @@ -0,0 +1,70 @@ +//===-- PlatformFreeBSD.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_FREEBSD_PLATFORMFREEBSD_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_FREEBSD_PLATFORMFREEBSD_H + +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +namespace lldb_private { +namespace platform_freebsd { + +class PlatformFreeBSD : public PlatformPOSIX { +public: + PlatformFreeBSD(bool is_host); + + static void Initialize(); + + static void Terminate(); + + // lldb_private::PluginInterface functions + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-freebsd"; + } + + static llvm::StringRef GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override { + return GetPluginNameStatic(IsHost()); + } + + // lldb_private::Platform functions + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + bool CanDebugProcess() override; + + void CalculateTrapHandlerSymbolNames() override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + + CompilerType GetSiginfoType(const llvm::Triple &triple) override; + + std::vector<ArchSpec> m_supported_architectures; + +private: + std::mutex m_mutex; + std::shared_ptr<TypeSystemClang> m_type_system; +}; + +} // namespace platform_freebsd +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_FREEBSD_PLATFORMFREEBSD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp new file mode 100644 index 000000000000..ce81aab55706 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.cpp @@ -0,0 +1,350 @@ +//===-- PlatformNetBSD.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PlatformNetBSD.h" +#include "lldb/Host/Config.h" + +#include <cstdio> +#if LLDB_ENABLE_POSIX +#include <sys/utsname.h> +#endif + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +// Define these constants from NetBSD mman.h for use when targeting remote +// netbsd systems even when host has different values. +#define MAP_PRIVATE 0x0002 +#define MAP_ANON 0x1000 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_netbsd; + +LLDB_PLUGIN_DEFINE(PlatformNetBSD) + +static uint32_t g_initialize_count = 0; + + +PlatformSP PlatformNetBSD::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force, + arch ? arch->GetArchitectureName() : "<null>", + arch ? arch->GetTriple().getTriple() : "<null>"); + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::NetBSD: + create = true; + break; + + default: + break; + } + } + + LLDB_LOG(log, "create = {0}", create); + if (create) { + return PlatformSP(new PlatformNetBSD(false)); + } + return PlatformSP(); +} + +llvm::StringRef PlatformNetBSD::GetPluginDescriptionStatic(bool is_host) { + if (is_host) + return "Local NetBSD user platform plug-in."; + return "Remote NetBSD user platform plug-in."; +} + +void PlatformNetBSD::Initialize() { + PlatformPOSIX::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__NetBSD__) + PlatformSP default_platform_sp(new PlatformNetBSD(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformNetBSD::GetPluginNameStatic(false), + PlatformNetBSD::GetPluginDescriptionStatic(false), + PlatformNetBSD::CreateInstance, nullptr); + } +} + +void PlatformNetBSD::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformNetBSD::CreateInstance); + } + } + + PlatformPOSIX::Terminate(); +} + +/// Default Constructor +PlatformNetBSD::PlatformNetBSD(bool is_host) + : PlatformPOSIX(is_host) // This is the local host platform +{ + if (is_host) { + ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); + m_supported_architectures.push_back(hostArch); + if (hostArch.GetTriple().isArch64Bit()) { + m_supported_architectures.push_back( + HostInfo::GetArchitecture(HostInfo::eArchKind32)); + } + } else { + m_supported_architectures = CreateArchList( + {llvm::Triple::x86_64, llvm::Triple::x86}, llvm::Triple::NetBSD); + } +} + +std::vector<ArchSpec> +PlatformNetBSD::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); + return m_supported_architectures; +} + +void PlatformNetBSD::GetStatus(Stream &strm) { + Platform::GetStatus(strm); + +#if LLDB_ENABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-NetBSD information (when running + // on Mac OS for example). + if (IsHost()) { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf(" Kernel: %s\n", un.sysname); + strm.Printf(" Release: %s\n", un.release); + strm.Printf(" Version: %s\n", un.version); + } +#endif +} + +uint32_t +PlatformNetBSD::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) { + uint32_t resume_count = 0; + + // Always resume past the initial stop when we use eLaunchFlagDebug + if (launch_info.GetFlags().Test(eLaunchFlagDebug)) { + // Resume past the stop for the final exec into the true inferior. + ++resume_count; + } + + // If we're not launching a shell, we're done. + const FileSpec &shell = launch_info.GetShell(); + if (!shell) + return resume_count; + + std::string shell_string = shell.GetPath(); + // We're in a shell, so for sure we have to resume past the shell exec. + ++resume_count; + + // Figure out what shell we're planning on using. + const char *shell_name = strrchr(shell_string.c_str(), '/'); + if (shell_name == nullptr) + shell_name = shell_string.c_str(); + else + shell_name++; + + if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 || + strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) { + // These shells seem to re-exec themselves. Add another resume. + ++resume_count; + } + + return resume_count; +} + +bool PlatformNetBSD::CanDebugProcess() { + if (IsHost()) { + return true; + } else { + // If we're connected, we can debug. + return IsConnected(); + } +} + +void PlatformNetBSD::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); +} + +MmapArgList PlatformNetBSD::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { + uint64_t flags_platform = 0; + + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= MAP_ANON; + + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} + +CompilerType PlatformNetBSD::GetSiginfoType(const llvm::Triple &triple) { + { + std::lock_guard<std::mutex> guard(m_mutex); + if (!m_type_system) + m_type_system = std::make_shared<TypeSystemClang>("siginfo", triple); + } + TypeSystemClang *ast = m_type_system.get(); + + // generic types + CompilerType int_type = ast->GetBasicType(eBasicTypeInt); + CompilerType uint_type = ast->GetBasicType(eBasicTypeUnsignedInt); + CompilerType long_type = ast->GetBasicType(eBasicTypeLong); + CompilerType long_long_type = ast->GetBasicType(eBasicTypeLongLong); + CompilerType voidp_type = ast->GetBasicType(eBasicTypeVoid).GetPointerType(); + + // platform-specific types + CompilerType &pid_type = int_type; + CompilerType &uid_type = uint_type; + CompilerType &clock_type = uint_type; + CompilerType &lwpid_type = int_type; + + CompilerType sigval_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_sigval_t", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(sigval_type); + ast->AddFieldToRecordType(sigval_type, "sival_int", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(sigval_type, "sival_ptr", voidp_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(sigval_type); + + CompilerType ptrace_option_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(ptrace_option_type); + ast->AddFieldToRecordType(ptrace_option_type, "_pe_other_pid", pid_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(ptrace_option_type, "_pe_lwp", lwpid_type, + lldb::eAccessPublic, 0); + ast->CompleteTagDeclarationDefinition(ptrace_option_type); + + // siginfo_t + CompilerType siginfo_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "__lldb_siginfo_t", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(siginfo_type); + + // struct _ksiginfo + CompilerType ksiginfo_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Struct), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(ksiginfo_type); + ast->AddFieldToRecordType(ksiginfo_type, "_signo", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(ksiginfo_type, "_code", int_type, + lldb::eAccessPublic, 0); + ast->AddFieldToRecordType(ksiginfo_type, "_errno", int_type, + lldb::eAccessPublic, 0); + + // the structure is padded on 64-bit arches to fix alignment + if (triple.isArch64Bit()) + ast->AddFieldToRecordType(ksiginfo_type, "__pad0", int_type, + lldb::eAccessPublic, 0); + + // union used to hold the signal data + CompilerType union_type = ast->CreateRecordType( + nullptr, OptionalClangModuleID(), lldb::eAccessPublic, "", + llvm::to_underlying(clang::TagTypeKind::Union), lldb::eLanguageTypeC); + ast->StartTagDeclarationDefinition(union_type); + + ast->AddFieldToRecordType( + union_type, "_rt", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_pid", pid_type}, + {"_uid", uid_type}, + {"_value", sigval_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_child", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_pid", pid_type}, + {"_uid", uid_type}, + {"_status", int_type}, + {"_utime", clock_type}, + {"_stime", clock_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_fault", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_addr", voidp_type}, + {"_trap", int_type}, + {"_trap2", int_type}, + {"_trap3", int_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_poll", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_band", long_type}, + {"_fd", int_type}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType(union_type, "_syscall", + ast->CreateStructForIdentifier( + llvm::StringRef(), + { + {"_sysnum", int_type}, + {"_retval", int_type.GetArrayType(2)}, + {"_error", int_type}, + {"_args", long_long_type.GetArrayType(8)}, + }), + lldb::eAccessPublic, 0); + + ast->AddFieldToRecordType( + union_type, "_ptrace_state", + ast->CreateStructForIdentifier(llvm::StringRef(), + { + {"_pe_report_event", int_type}, + {"_option", ptrace_option_type}, + }), + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(union_type); + ast->AddFieldToRecordType(ksiginfo_type, "_reason", union_type, + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(ksiginfo_type); + ast->AddFieldToRecordType(siginfo_type, "_info", ksiginfo_type, + lldb::eAccessPublic, 0); + + ast->CompleteTagDeclarationDefinition(siginfo_type); + return siginfo_type; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h new file mode 100644 index 000000000000..3437d7e5eb51 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/NetBSD/PlatformNetBSD.h @@ -0,0 +1,72 @@ +//===-- PlatformNetBSD.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_NETBSD_PLATFORMNETBSD_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_NETBSD_PLATFORMNETBSD_H + +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" + +namespace lldb_private { +namespace platform_netbsd { + +class PlatformNetBSD : public PlatformPOSIX { +public: + PlatformNetBSD(bool is_host); + + static void Initialize(); + + static void Terminate(); + + // lldb_private::PluginInterface functions + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-netbsd"; + } + + static llvm::StringRef GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override { + return GetPluginNameStatic(IsHost()); + } + + // lldb_private::Platform functions + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + uint32_t GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) override; + + bool CanDebugProcess() override; + + void CalculateTrapHandlerSymbolNames() override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + + CompilerType GetSiginfoType(const llvm::Triple &triple) override; + + std::vector<ArchSpec> m_supported_architectures; + +private: + std::mutex m_mutex; + std::shared_ptr<TypeSystemClang> m_type_system; +}; + +} // namespace platform_netbsd +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_NETBSD_PLATFORMNETBSD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp new file mode 100644 index 000000000000..3946092276fd --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.cpp @@ -0,0 +1,170 @@ +//===-- PlatformOpenBSD.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PlatformOpenBSD.h" +#include "lldb/Host/Config.h" + +#include <cstdio> +#if LLDB_ENABLE_POSIX +#include <sys/utsname.h> +#endif + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" + +// Define these constants from OpenBSD mman.h for use when targeting remote +// openbsd systems even when host has different values. +#define MAP_PRIVATE 0x0002 +#define MAP_ANON 0x1000 + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_openbsd; + +LLDB_PLUGIN_DEFINE(PlatformOpenBSD) + +static uint32_t g_initialize_count = 0; + + +PlatformSP PlatformOpenBSD::CreateInstance(bool force, const ArchSpec *arch) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force, + arch ? arch->GetArchitectureName() : "<null>", + arch ? arch->GetTriple().getTriple() : "<null>"); + + bool create = force; + if (!create && arch && arch->IsValid()) { + const llvm::Triple &triple = arch->GetTriple(); + switch (triple.getOS()) { + case llvm::Triple::OpenBSD: + create = true; + break; + +#if defined(__OpenBSD__) + // Only accept "unknown" for the OS if the host is BSD and it "unknown" + // wasn't specified (it was just returned because it was NOT specified) + case llvm::Triple::OSType::UnknownOS: + create = !arch->TripleOSWasSpecified(); + break; +#endif + default: + break; + } + } + LLDB_LOG(log, "create = {0}", create); + if (create) { + return PlatformSP(new PlatformOpenBSD(false)); + } + return PlatformSP(); +} + +llvm::StringRef PlatformOpenBSD::GetPluginDescriptionStatic(bool is_host) { + if (is_host) + return "Local OpenBSD user platform plug-in."; + return "Remote OpenBSD user platform plug-in."; +} + +void PlatformOpenBSD::Initialize() { + Platform::Initialize(); + + if (g_initialize_count++ == 0) { +#if defined(__OpenBSD__) + PlatformSP default_platform_sp(new PlatformOpenBSD(true)); + default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(default_platform_sp); +#endif + PluginManager::RegisterPlugin( + PlatformOpenBSD::GetPluginNameStatic(false), + PlatformOpenBSD::GetPluginDescriptionStatic(false), + PlatformOpenBSD::CreateInstance, nullptr); + } +} + +void PlatformOpenBSD::Terminate() { + if (g_initialize_count > 0) { + if (--g_initialize_count == 0) { + PluginManager::UnregisterPlugin(PlatformOpenBSD::CreateInstance); + } + } + + PlatformPOSIX::Terminate(); +} + +/// Default Constructor +PlatformOpenBSD::PlatformOpenBSD(bool is_host) + : PlatformPOSIX(is_host) // This is the local host platform +{ + if (is_host) { + m_supported_architectures.push_back(HostInfo::GetArchitecture()); + } else { + m_supported_architectures = + CreateArchList({llvm::Triple::x86_64, llvm::Triple::x86, + llvm::Triple::aarch64, llvm::Triple::arm}, + llvm::Triple::OpenBSD); + } +} + +std::vector<ArchSpec> +PlatformOpenBSD::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetSupportedArchitectures(process_host_arch); + return m_supported_architectures; +} + +void PlatformOpenBSD::GetStatus(Stream &strm) { + Platform::GetStatus(strm); + +#if LLDB_ENABLE_POSIX + // Display local kernel information only when we are running in host mode. + // Otherwise, we would end up printing non-OpenBSD information (when running + // on Mac OS for example). + if (IsHost()) { + struct utsname un; + + if (uname(&un)) + return; + + strm.Printf(" Kernel: %s\n", un.sysname); + strm.Printf(" Release: %s\n", un.release); + strm.Printf(" Version: %s\n", un.version); + } +#endif +} + +// OpenBSD processes cannot yet be launched by spawning and attaching. +bool PlatformOpenBSD::CanDebugProcess() { + return false; +} + +void PlatformOpenBSD::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); +} + +MmapArgList PlatformOpenBSD::GetMmapArgumentList(const ArchSpec &arch, + addr_t addr, addr_t length, + unsigned prot, unsigned flags, + addr_t fd, addr_t offset) { + uint64_t flags_platform = 0; + + if (flags & eMmapFlagsPrivate) + flags_platform |= MAP_PRIVATE; + if (flags & eMmapFlagsAnon) + flags_platform |= MAP_ANON; + + MmapArgList args({addr, length, prot, flags_platform, fd, offset}); + return args; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h new file mode 100644 index 000000000000..fcdc03576711 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/OpenBSD/PlatformOpenBSD.h @@ -0,0 +1,63 @@ +//===-- PlatformOpenBSD.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_OPENBSD_PLATFORMOPENBSD_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_OPENBSD_PLATFORMOPENBSD_H + +#include "Plugins/Platform/POSIX/PlatformPOSIX.h" + +namespace lldb_private { +namespace platform_openbsd { + +class PlatformOpenBSD : public PlatformPOSIX { +public: + PlatformOpenBSD(bool is_host); + + static void Initialize(); + + static void Terminate(); + + // lldb_private::PluginInterface functions + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic(bool is_host) { + return is_host ? Platform::GetHostPlatformName() : "remote-openbsd"; + } + + static llvm::StringRef GetPluginDescriptionStatic(bool is_host); + + llvm::StringRef GetPluginName() override { + return GetPluginNameStatic(IsHost()); + } + + // lldb_private::Platform functions + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(IsHost()); + } + + void GetStatus(Stream &strm) override; + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + bool CanDebugProcess() override; + + void CalculateTrapHandlerSymbolNames() override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override; + + std::vector<ArchSpec> m_supported_architectures; +}; + +} // namespace platform_openbsd +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_OPENBSD_PLATFORMOPENBSD_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp new file mode 100644 index 000000000000..b4f1b76c39db --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -0,0 +1,993 @@ +//===-- PlatformPOSIX.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PlatformPOSIX.h" + +#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" +#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/FunctionCaller.h" +#include "lldb/Expression/UserExpression.h" +#include "lldb/Expression/UtilityFunction.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileCache.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/ADT/ScopeExit.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; + +/// Default Constructor +PlatformPOSIX::PlatformPOSIX(bool is_host) + : RemoteAwarePlatform(is_host), // This is the local host platform + m_option_group_platform_rsync(new OptionGroupPlatformRSync()), + m_option_group_platform_ssh(new OptionGroupPlatformSSH()), + m_option_group_platform_caching(new OptionGroupPlatformCaching()) {} + +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +PlatformPOSIX::~PlatformPOSIX() = default; + +lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions( + lldb_private::CommandInterpreter &interpreter) { + auto iter = m_options.find(&interpreter), end = m_options.end(); + if (iter == end) { + std::unique_ptr<lldb_private::OptionGroupOptions> options( + new OptionGroupOptions()); + options->Append(m_option_group_platform_rsync.get()); + options->Append(m_option_group_platform_ssh.get()); + options->Append(m_option_group_platform_caching.get()); + m_options[&interpreter] = std::move(options); + } + + return m_options.at(&interpreter).get(); +} + +static uint32_t chown_file(Platform *platform, const char *path, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) { + if (!platform || !path || *path == 0) + return UINT32_MAX; + + if (uid == UINT32_MAX && gid == UINT32_MAX) + return 0; // pretend I did chown correctly - actually I just didn't care + + StreamString command; + command.PutCString("chown "); + if (uid != UINT32_MAX) + command.Printf("%d", uid); + if (gid != UINT32_MAX) + command.Printf(":%d", gid); + command.Printf("%s", path); + int status; + platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, + nullptr, std::chrono::seconds(10)); + return status; +} + +lldb_private::Status +PlatformPOSIX::PutFile(const lldb_private::FileSpec &source, + const lldb_private::FileSpec &destination, uint32_t uid, + uint32_t gid) { + Log *log = GetLog(LLDBLog::Platform); + + if (IsHost()) { + if (source == destination) + return Status(); + // cp src dst + // chown uid:gid dst + std::string src_path(source.GetPath()); + if (src_path.empty()) + return Status("unable to get file path for source"); + std::string dst_path(destination.GetPath()); + if (dst_path.empty()) + return Status("unable to get file path for destination"); + StreamString command; + command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); + int status; + RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr, + std::chrono::seconds(10)); + if (status != 0) + return Status("unable to perform copy"); + if (uid == UINT32_MAX && gid == UINT32_MAX) + return Status(); + if (chown_file(this, dst_path.c_str(), uid, gid) != 0) + return Status("unable to perform chown"); + return Status(); + } else if (m_remote_platform_sp) { + if (GetSupportsRSync()) { + std::string src_path(source.GetPath()); + if (src_path.empty()) + return Status("unable to get file path for source"); + std::string dst_path(destination.GetPath()); + if (dst_path.empty()) + return Status("unable to get file path for destination"); + StreamString command; + if (GetIgnoresRemoteHostname()) { + if (!GetRSyncPrefix()) + command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(), + dst_path.c_str()); + else + command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(), + GetRSyncPrefix(), dst_path.c_str()); + } else + command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(), + GetHostname(), dst_path.c_str()); + LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData()); + int retcode; + Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr, + nullptr, std::chrono::minutes(1)); + if (retcode == 0) { + // Don't chown a local file for a remote system + // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) + // return Status("unable to perform chown"); + return Status(); + } + // if we are still here rsync has failed - let's try the slow way before + // giving up + } + } + return Platform::PutFile(source, destination, uid, gid); +} + +lldb_private::Status PlatformPOSIX::GetFile( + const lldb_private::FileSpec &source, // remote file path + const lldb_private::FileSpec &destination) // local file path +{ + Log *log = GetLog(LLDBLog::Platform); + + // Check the args, first. + std::string src_path(source.GetPath()); + if (src_path.empty()) + return Status("unable to get file path for source"); + std::string dst_path(destination.GetPath()); + if (dst_path.empty()) + return Status("unable to get file path for destination"); + if (IsHost()) { + if (source == destination) + return Status("local scenario->source and destination are the same file " + "path: no operation performed"); + // cp src dst + StreamString cp_command; + cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); + int status; + RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr, + std::chrono::seconds(10)); + if (status != 0) + return Status("unable to perform copy"); + return Status(); + } else if (m_remote_platform_sp) { + if (GetSupportsRSync()) { + StreamString command; + if (GetIgnoresRemoteHostname()) { + if (!GetRSyncPrefix()) + command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(), + dst_path.c_str()); + else + command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(), + src_path.c_str(), dst_path.c_str()); + } else + command.Printf("rsync %s %s:%s %s", GetRSyncOpts(), + m_remote_platform_sp->GetHostname(), src_path.c_str(), + dst_path.c_str()); + LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData()); + int retcode; + Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr, + nullptr, std::chrono::minutes(1)); + if (retcode == 0) + return Status(); + // If we are here, rsync has failed - let's try the slow way before + // giving up + } + // open src and dst + // read/write, read/write, read/write, ... + // close src + // close dst + LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n"); + Status error; + user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly, + lldb::eFilePermissionsFileDefault, error); + + if (fd_src == UINT64_MAX) + return Status("unable to open source file"); + + uint32_t permissions = 0; + error = GetFilePermissions(source, permissions); + + if (permissions == 0) + permissions = lldb::eFilePermissionsFileDefault; + + user_id_t fd_dst = FileCache::GetInstance().OpenFile( + destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly | + File::eOpenOptionTruncate, + permissions, error); + + if (fd_dst == UINT64_MAX) { + if (error.Success()) + error.SetErrorString("unable to open destination file"); + } + + if (error.Success()) { + lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); + uint64_t offset = 0; + error.Clear(); + while (error.Success()) { + const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), error); + if (error.Fail()) + break; + if (n_read == 0) + break; + if (FileCache::GetInstance().WriteFile(fd_dst, offset, + buffer_sp->GetBytes(), n_read, + error) != n_read) { + if (!error.Fail()) + error.SetErrorString("unable to write to destination file"); + break; + } + offset += n_read; + } + } + // Ignore the close error of src. + if (fd_src != UINT64_MAX) + CloseFile(fd_src, error); + // And close the dst file descriptot. + if (fd_dst != UINT64_MAX && + !FileCache::GetInstance().CloseFile(fd_dst, error)) { + if (!error.Fail()) + error.SetErrorString("unable to close destination file"); + } + return error; + } + return Platform::GetFile(source, destination); +} + +std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() { + StreamString stream; + if (GetSupportsRSync()) { + stream.PutCString("rsync"); + if ((GetRSyncOpts() && *GetRSyncOpts()) || + (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) { + stream.Printf(", options: "); + if (GetRSyncOpts() && *GetRSyncOpts()) + stream.Printf("'%s' ", GetRSyncOpts()); + stream.Printf(", prefix: "); + if (GetRSyncPrefix() && *GetRSyncPrefix()) + stream.Printf("'%s' ", GetRSyncPrefix()); + if (GetIgnoresRemoteHostname()) + stream.Printf("ignore remote-hostname "); + } + } + if (GetSupportsSSH()) { + stream.PutCString("ssh"); + if (GetSSHOpts() && *GetSSHOpts()) + stream.Printf(", options: '%s' ", GetSSHOpts()); + } + if (GetLocalCacheDirectory() && *GetLocalCacheDirectory()) + stream.Printf("cache dir: %s", GetLocalCacheDirectory()); + if (stream.GetSize()) + return std::string(stream.GetString()); + else + return ""; +} + +const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() { + if (IsRemote() && m_remote_platform_sp) + return m_remote_platform_sp->GetRemoteUnixSignals(); + return Platform::GetRemoteUnixSignals(); +} + +Status PlatformPOSIX::ConnectRemote(Args &args) { + Status error; + if (IsHost()) { + error.SetErrorStringWithFormatv( + "can't connect to the host platform '{0}', always connected", + GetPluginName()); + } else { + if (!m_remote_platform_sp) + m_remote_platform_sp = + platform_gdb_server::PlatformRemoteGDBServer::CreateInstance( + /*force=*/true, nullptr); + + if (m_remote_platform_sp && error.Success()) + error = m_remote_platform_sp->ConnectRemote(args); + else + error.SetErrorString("failed to create a 'remote-gdb-server' platform"); + + if (error.Fail()) + m_remote_platform_sp.reset(); + } + + if (error.Success() && m_remote_platform_sp) { + if (m_option_group_platform_rsync.get() && + m_option_group_platform_ssh.get() && + m_option_group_platform_caching.get()) { + if (m_option_group_platform_rsync->m_rsync) { + SetSupportsRSync(true); + SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str()); + SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str()); + SetIgnoresRemoteHostname( + m_option_group_platform_rsync->m_ignores_remote_hostname); + } + if (m_option_group_platform_ssh->m_ssh) { + SetSupportsSSH(true); + SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str()); + } + SetLocalCacheDirectory( + m_option_group_platform_caching->m_cache_dir.c_str()); + } + } + + return error; +} + +Status PlatformPOSIX::DisconnectRemote() { + Status error; + + if (IsHost()) { + error.SetErrorStringWithFormatv( + "can't disconnect from the host platform '{0}', always connected", + GetPluginName()); + } else { + if (m_remote_platform_sp) + error = m_remote_platform_sp->DisconnectRemote(); + else + error.SetErrorString("the platform is not currently connected"); + } + return error; +} + +lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info, + Debugger &debugger, Target *target, + Status &error) { + lldb::ProcessSP process_sp; + Log *log = GetLog(LLDBLog::Platform); + + if (IsHost()) { + if (target == nullptr) { + TargetSP new_target_sp; + + error = debugger.GetTargetList().CreateTarget( + debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); + target = new_target_sp.get(); + LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__); + } else { + error.Clear(); + LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target", + __FUNCTION__); + } + + if (target && error.Success()) { + if (log) { + ModuleSP exe_module_sp = target->GetExecutableModule(); + LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s", + __FUNCTION__, (void *)target, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str() + : "<null>"); + } + + process_sp = + target->CreateProcess(attach_info.GetListenerForProcess(debugger), + "gdb-remote", nullptr, true); + + if (process_sp) { + ListenerSP listener_sp = attach_info.GetHijackListener(); + if (listener_sp == nullptr) { + listener_sp = + Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack"); + attach_info.SetHijackListener(listener_sp); + } + process_sp->HijackProcessEvents(listener_sp); + process_sp->SetShadowListener(attach_info.GetShadowListener()); + error = process_sp->Attach(attach_info); + } + } + } else { + if (m_remote_platform_sp) + process_sp = + m_remote_platform_sp->Attach(attach_info, debugger, target, error); + else + error.SetErrorString("the platform is not currently connected"); + } + return process_sp; +} + +lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) { + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOG(log, "target {0}", &target); + + ProcessSP process_sp; + + if (!IsHost()) { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger, + target, error); + else + error.SetErrorString("the platform is not currently connected"); + return process_sp; + } + + // + // For local debugging, we'll insist on having ProcessGDBRemote create the + // process. + // + + // Make sure we stop at the entry point + launch_info.GetFlags().Set(eLaunchFlagDebug); + + // We always launch the process we are going to debug in a separate process + // group, since then we can handle ^C interrupts ourselves w/o having to + // worry about the target getting them as well. + launch_info.SetLaunchInSeparateProcessGroup(true); + + // Now create the gdb-remote process. + LLDB_LOG(log, "having target create process with gdb-remote plugin"); + process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote", + nullptr, true); + + if (!process_sp) { + error.SetErrorString("CreateProcess() failed for gdb-remote process"); + LLDB_LOG(log, "error: {0}", error); + return process_sp; + } + + LLDB_LOG(log, "successfully created process"); + + process_sp->HijackProcessEvents(launch_info.GetHijackListener()); + process_sp->SetShadowListener(launch_info.GetShadowListener()); + + // Log file actions. + if (log) { + LLDB_LOG(log, "launching process with the following file actions:"); + StreamString stream; + size_t i = 0; + const FileAction *file_action; + while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) { + file_action->Dump(stream); + LLDB_LOG(log, "{0}", stream.GetData()); + stream.Clear(); + } + } + + // Do the launch. + error = process_sp->Launch(launch_info); + if (error.Success()) { + // Hook up process PTY if we have one (which we should for local debugging + // with llgs). + int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor(); + if (pty_fd != PseudoTerminal::invalid_fd) { + process_sp->SetSTDIOFileDescriptor(pty_fd); + LLDB_LOG(log, "hooked up STDIO pty to process"); + } else + LLDB_LOG(log, "not using process STDIO pty"); + } else { + LLDB_LOG(log, "{0}", error); + // FIXME figure out appropriate cleanup here. Do we delete the process? + // Does our caller do that? + } + + return process_sp; +} + +void PlatformPOSIX::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); +} + +Status PlatformPOSIX::EvaluateLibdlExpression( + lldb_private::Process *process, const char *expr_cstr, + llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) { + DynamicLoader *loader = process->GetDynamicLoader(); + if (loader) { + Status error = loader->CanLoadImage(); + if (error.Fail()) + return error; + } + + ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread()); + if (!thread_sp) + return Status("Selected thread isn't valid"); + + StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0)); + if (!frame_sp) + return Status("Frame 0 isn't valid"); + + ExecutionContext exe_ctx; + frame_sp->CalculateExecutionContext(exe_ctx); + EvaluateExpressionOptions expr_options; + expr_options.SetUnwindOnError(true); + expr_options.SetIgnoreBreakpoints(true); + expr_options.SetExecutionPolicy(eExecutionPolicyAlways); + expr_options.SetLanguage(eLanguageTypeC_plus_plus); + expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so + // don't do the work to trap them. + expr_options.SetTimeout(process->GetUtilityExpressionTimeout()); + + Status expr_error; + ExpressionResults result = + UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix, + result_valobj_sp, expr_error); + if (result != eExpressionCompleted) + return expr_error; + + if (result_valobj_sp->GetError().Fail()) + return result_valobj_sp->GetError(); + return Status(); +} + +std::unique_ptr<UtilityFunction> +PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx, + Status &error) { + // Remember to prepend this with the prefix from + // GetLibdlFunctionDeclarations. The returned values are all in + // __lldb_dlopen_result for consistency. The wrapper returns a void * but + // doesn't use it because UtilityFunctions don't work with void returns at + // present. + // + // Use lazy binding so as to not make dlopen()'s success conditional on + // forcing every symbol in the library. + // + // In general, the debugger should allow programs to load & run with + // libraries as far as they can, instead of defaulting to being super-picky + // about unavailable symbols. + // + // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin + // and other POSIX OSes. + static const char *dlopen_wrapper_code = R"( + const int RTLD_LAZY = 1; + + struct __lldb_dlopen_result { + void *image_ptr; + const char *error_str; + }; + + extern "C" void *memcpy(void *, const void *, size_t size); + extern "C" size_t strlen(const char *); + + + void * __lldb_dlopen_wrapper (const char *name, + const char *path_strings, + char *buffer, + __lldb_dlopen_result *result_ptr) + { + // This is the case where the name is the full path: + if (!path_strings) { + result_ptr->image_ptr = dlopen(name, RTLD_LAZY); + if (result_ptr->image_ptr) + result_ptr->error_str = nullptr; + else + result_ptr->error_str = dlerror(); + return nullptr; + } + + // This is the case where we have a list of paths: + size_t name_len = strlen(name); + while (path_strings && path_strings[0] != '\0') { + size_t path_len = strlen(path_strings); + memcpy((void *) buffer, (void *) path_strings, path_len); + buffer[path_len] = '/'; + char *target_ptr = buffer+path_len+1; + memcpy((void *) target_ptr, (void *) name, name_len + 1); + result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY); + if (result_ptr->image_ptr) { + result_ptr->error_str = nullptr; + break; + } + result_ptr->error_str = dlerror(); + path_strings = path_strings + path_len + 1; + } + return nullptr; + } + )"; + + static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper"; + Process *process = exe_ctx.GetProcessSP().get(); + // Insert the dlopen shim defines into our generic expression: + std::string expr(std::string(GetLibdlFunctionDeclarations(process))); + expr.append(dlopen_wrapper_code); + Status utility_error; + DiagnosticManager diagnostics; + + auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction( + std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx); + if (!utility_fn_or_error) { + std::string error_str = llvm::toString(utility_fn_or_error.takeError()); + error.SetErrorStringWithFormat( + "dlopen error: could not create utility function: %s", + error_str.c_str()); + return nullptr; + } + std::unique_ptr<UtilityFunction> dlopen_utility_func_up = + std::move(*utility_fn_or_error); + + Value value; + ValueList arguments; + FunctionCaller *do_dlopen_function = nullptr; + + // Fetch the clang types we will need: + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(process->GetTarget()); + if (!scratch_ts_sp) + return nullptr; + + CompilerType clang_void_pointer_type = + scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); + CompilerType clang_char_pointer_type = + scratch_ts_sp->GetBasicType(eBasicTypeChar).GetPointerType(); + + // We are passing four arguments, the basename, the list of places to look, + // a buffer big enough for all the path + name combos, and + // a pointer to the storage we've made for the result: + value.SetValueType(Value::ValueType::Scalar); + value.SetCompilerType(clang_void_pointer_type); + arguments.PushValue(value); + value.SetCompilerType(clang_char_pointer_type); + arguments.PushValue(value); + arguments.PushValue(value); + arguments.PushValue(value); + + do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller( + clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error); + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not make function caller: %s", + utility_error.AsCString()); + return nullptr; + } + + do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller(); + if (!do_dlopen_function) { + error.SetErrorString("dlopen error: could not get function caller."); + return nullptr; + } + + // We made a good utility function, so cache it in the process: + return dlopen_utility_func_up; +} + +uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process, + const lldb_private::FileSpec &remote_file, + const std::vector<std::string> *paths, + lldb_private::Status &error, + lldb_private::FileSpec *loaded_image) { + if (loaded_image) + loaded_image->Clear(); + + std::string path; + path = remote_file.GetPath(); + + ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread(); + if (!thread_sp) { + error.SetErrorString("dlopen error: no thread available to call dlopen."); + return LLDB_INVALID_IMAGE_TOKEN; + } + + DiagnosticManager diagnostics; + + ExecutionContext exe_ctx; + thread_sp->CalculateExecutionContext(exe_ctx); + + Status utility_error; + UtilityFunction *dlopen_utility_func; + ValueList arguments; + FunctionCaller *do_dlopen_function = nullptr; + + // The UtilityFunction is held in the Process. Platforms don't track the + // lifespan of the Targets that use them, we can't put this in the Platform. + dlopen_utility_func = process->GetLoadImageUtilityFunction( + this, [&]() -> std::unique_ptr<UtilityFunction> { + return MakeLoadImageUtilityFunction(exe_ctx, error); + }); + // If we couldn't make it, the error will be in error, so we can exit here. + if (!dlopen_utility_func) + return LLDB_INVALID_IMAGE_TOKEN; + + do_dlopen_function = dlopen_utility_func->GetFunctionCaller(); + if (!do_dlopen_function) { + error.SetErrorString("dlopen error: could not get function caller."); + return LLDB_INVALID_IMAGE_TOKEN; + } + arguments = do_dlopen_function->GetArgumentValues(); + + // Now insert the path we are searching for and the result structure into the + // target. + uint32_t permissions = ePermissionsReadable|ePermissionsWritable; + size_t path_len = path.size() + 1; + lldb::addr_t path_addr = process->AllocateMemory(path_len, + permissions, + utility_error); + if (path_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorStringWithFormat( + "dlopen error: could not allocate memory for path: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make sure we deallocate the input string memory: + auto path_cleanup = llvm::make_scope_exit([process, path_addr] { + // Deallocate the buffer. + process->DeallocateMemory(path_addr); + }); + + process->WriteMemory(path_addr, path.c_str(), path_len, utility_error); + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not write path string: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make space for our return structure. It is two pointers big: the token + // and the error string. + const uint32_t addr_size = process->GetAddressByteSize(); + lldb::addr_t return_addr = process->CallocateMemory(2*addr_size, + permissions, + utility_error); + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not allocate memory for path: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make sure we deallocate the result structure memory + auto return_cleanup = llvm::make_scope_exit([process, return_addr] { + // Deallocate the buffer + process->DeallocateMemory(return_addr); + }); + + // This will be the address of the storage for paths, if we are using them, + // or nullptr to signal we aren't. + lldb::addr_t path_array_addr = 0x0; + std::optional<llvm::detail::scope_exit<std::function<void()>>> + path_array_cleanup; + + // This is the address to a buffer large enough to hold the largest path + // conjoined with the library name we're passing in. This is a convenience + // to avoid having to call malloc in the dlopen function. + lldb::addr_t buffer_addr = 0x0; + std::optional<llvm::detail::scope_exit<std::function<void()>>> buffer_cleanup; + + // Set the values into our args and write them to the target: + if (paths != nullptr) { + // First insert the paths into the target. This is expected to be a + // continuous buffer with the strings laid out null terminated and + // end to end with an empty string terminating the buffer. + // We also compute the buffer's required size as we go. + size_t buffer_size = 0; + std::string path_array; + for (auto path : *paths) { + // Don't insert empty paths, they will make us abort the path + // search prematurely. + if (path.empty()) + continue; + size_t path_size = path.size(); + path_array.append(path); + path_array.push_back('\0'); + if (path_size > buffer_size) + buffer_size = path_size; + } + path_array.push_back('\0'); + + path_array_addr = process->AllocateMemory(path_array.size(), + permissions, + utility_error); + if (path_array_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorStringWithFormat( + "dlopen error: could not allocate memory for path array: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make sure we deallocate the paths array. + path_array_cleanup.emplace([process, path_array_addr]() { + // Deallocate the path array. + process->DeallocateMemory(path_array_addr); + }); + + process->WriteMemory(path_array_addr, path_array.data(), + path_array.size(), utility_error); + + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not write path array: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + // Now make spaces in the target for the buffer. We need to add one for + // the '/' that the utility function will insert and one for the '\0': + buffer_size += path.size() + 2; + + buffer_addr = process->AllocateMemory(buffer_size, + permissions, + utility_error); + if (buffer_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorStringWithFormat( + "dlopen error: could not allocate memory for buffer: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make sure we deallocate the buffer memory: + buffer_cleanup.emplace([process, buffer_addr]() { + // Deallocate the buffer. + process->DeallocateMemory(buffer_addr); + }); + } + + arguments.GetValueAtIndex(0)->GetScalar() = path_addr; + arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr; + arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr; + arguments.GetValueAtIndex(3)->GetScalar() = return_addr; + + lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS; + + diagnostics.Clear(); + if (!do_dlopen_function->WriteFunctionArguments(exe_ctx, + func_args_addr, + arguments, + diagnostics)) { + error.SetErrorStringWithFormat( + "dlopen error: could not write function arguments: %s", + diagnostics.GetString().c_str()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Make sure we clean up the args structure. We can't reuse it because the + // Platform lives longer than the process and the Platforms don't get a + // signal to clean up cached data when a process goes away. + auto args_cleanup = + llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] { + do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr); + }); + + // Now run the caller: + EvaluateExpressionOptions options; + options.SetExecutionPolicy(eExecutionPolicyAlways); + options.SetLanguage(eLanguageTypeC_plus_plus); + options.SetIgnoreBreakpoints(true); + options.SetUnwindOnError(true); + options.SetTrapExceptions(false); // dlopen can't throw exceptions, so + // don't do the work to trap them. + options.SetTimeout(process->GetUtilityExpressionTimeout()); + options.SetIsForUtilityExpr(true); + + Value return_value; + // Fetch the clang types we will need: + TypeSystemClangSP scratch_ts_sp = + ScratchTypeSystemClang::GetForTarget(process->GetTarget()); + if (!scratch_ts_sp) { + error.SetErrorString("dlopen error: Unable to get TypeSystemClang"); + return LLDB_INVALID_IMAGE_TOKEN; + } + + CompilerType clang_void_pointer_type = + scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); + + return_value.SetCompilerType(clang_void_pointer_type); + + ExpressionResults results = do_dlopen_function->ExecuteFunction( + exe_ctx, &func_args_addr, options, diagnostics, return_value); + if (results != eExpressionCompleted) { + error.SetErrorStringWithFormat( + "dlopen error: failed executing dlopen wrapper function: %s", + diagnostics.GetString().c_str()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // Read the dlopen token from the return area: + lldb::addr_t token = process->ReadPointerFromMemory(return_addr, + utility_error); + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not read the return struct: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + // The dlopen succeeded! + if (token != 0x0) { + if (loaded_image && buffer_addr != 0x0) + { + // Capture the image which was loaded. We leave it in the buffer on + // exit from the dlopen function, so we can just read it from there: + std::string name_string; + process->ReadCStringFromMemory(buffer_addr, name_string, utility_error); + if (utility_error.Success()) + loaded_image->SetFile(name_string, llvm::sys::path::Style::posix); + } + return process->AddImageToken(token); + } + + // We got an error, lets read in the error string: + std::string dlopen_error_str; + lldb::addr_t error_addr + = process->ReadPointerFromMemory(return_addr + addr_size, utility_error); + if (utility_error.Fail()) { + error.SetErrorStringWithFormat( + "dlopen error: could not read error string: %s", + utility_error.AsCString()); + return LLDB_INVALID_IMAGE_TOKEN; + } + + size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size, + dlopen_error_str, + utility_error); + if (utility_error.Success() && num_chars > 0) + error.SetErrorStringWithFormat("dlopen error: %s", + dlopen_error_str.c_str()); + else + error.SetErrorStringWithFormat("dlopen failed for unknown reasons."); + + return LLDB_INVALID_IMAGE_TOKEN; +} + +Status PlatformPOSIX::UnloadImage(lldb_private::Process *process, + uint32_t image_token) { + const addr_t image_addr = process->GetImagePtrFromToken(image_token); + if (image_addr == LLDB_INVALID_IMAGE_TOKEN) + return Status("Invalid image token"); + + StreamString expr; + expr.Printf("dlclose((void *)0x%" PRIx64 ")", image_addr); + llvm::StringRef prefix = GetLibdlFunctionDeclarations(process); + lldb::ValueObjectSP result_valobj_sp; + Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix, + result_valobj_sp); + if (error.Fail()) + return error; + + if (result_valobj_sp->GetError().Fail()) + return result_valobj_sp->GetError(); + + Scalar scalar; + if (result_valobj_sp->ResolveValue(scalar)) { + if (scalar.UInt(1)) + return Status("expression failed: \"%s\"", expr.GetData()); + process->ResetImageToken(image_token); + } + return Status(); +} + +llvm::StringRef +PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) { + return R"( + extern "C" void* dlopen(const char*, int); + extern "C" void* dlsym(void*, const char*); + extern "C" int dlclose(void*); + extern "C" char* dlerror(void); + )"; +} + +ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) { + if (basename.IsEmpty()) + return basename; + + StreamString stream; + stream.Printf("lib%s.so", basename.GetCString()); + return ConstString(stream.GetString()); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h new file mode 100644 index 000000000000..511797ce6bb7 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -0,0 +1,101 @@ +//===-- PlatformPOSIX.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_POSIX_PLATFORMPOSIX_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_POSIX_PLATFORMPOSIX_H + +#include <map> +#include <memory> + +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/RemoteAwarePlatform.h" + +class PlatformPOSIX : public lldb_private::RemoteAwarePlatform { +public: + PlatformPOSIX(bool is_host); + + ~PlatformPOSIX() override; + + // lldb_private::Platform functions + + lldb_private::OptionGroupOptions * + GetConnectionOptions(lldb_private::CommandInterpreter &interpreter) override; + + lldb_private::Status PutFile(const lldb_private::FileSpec &source, + const lldb_private::FileSpec &destination, + uint32_t uid = UINT32_MAX, + uint32_t gid = UINT32_MAX) override; + + lldb_private::Status + GetFile(const lldb_private::FileSpec &source, + const lldb_private::FileSpec &destination) override; + + const lldb::UnixSignalsSP &GetRemoteUnixSignals() override; + + lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo &attach_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, // Can be nullptr, if + // nullptr create a new + // target, else use + // existing one + lldb_private::Status &error) override; + + lldb::ProcessSP DebugProcess(lldb_private::ProcessLaunchInfo &launch_info, + lldb_private::Debugger &debugger, + lldb_private::Target &target, + lldb_private::Status &error) override; + + std::string GetPlatformSpecificConnectionInformation() override; + + void CalculateTrapHandlerSymbolNames() override; + + lldb_private::Status ConnectRemote(lldb_private::Args &args) override; + + lldb_private::Status DisconnectRemote() override; + + uint32_t DoLoadImage(lldb_private::Process *process, + const lldb_private::FileSpec &remote_file, + const std::vector<std::string> *paths, + lldb_private::Status &error, + lldb_private::FileSpec *loaded_image) override; + + lldb_private::Status UnloadImage(lldb_private::Process *process, + uint32_t image_token) override; + + lldb_private::ConstString GetFullNameForDylib(lldb_private::ConstString basename) override; + +protected: + std::unique_ptr<lldb_private::OptionGroupPlatformRSync> + m_option_group_platform_rsync; + std::unique_ptr<lldb_private::OptionGroupPlatformSSH> + m_option_group_platform_ssh; + std::unique_ptr<lldb_private::OptionGroupPlatformCaching> + m_option_group_platform_caching; + + std::map<lldb_private::CommandInterpreter *, + std::unique_ptr<lldb_private::OptionGroupOptions>> + m_options; + + lldb_private::Status + EvaluateLibdlExpression(lldb_private::Process *process, const char *expr_cstr, + llvm::StringRef expr_prefix, + lldb::ValueObjectSP &result_valobj_sp); + + std::unique_ptr<lldb_private::UtilityFunction> + MakeLoadImageUtilityFunction(lldb_private::ExecutionContext &exe_ctx, + lldb_private::Status &error); + + virtual + llvm::StringRef GetLibdlFunctionDeclarations(lldb_private::Process *process); + +private: + PlatformPOSIX(const PlatformPOSIX &) = delete; + const PlatformPOSIX &operator=(const PlatformPOSIX &) = delete; +}; + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_POSIX_PLATFORMPOSIX_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp new file mode 100644 index 000000000000..460f5560573d --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.cpp @@ -0,0 +1,251 @@ +//===-- PlatformQemuUser.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Plugins/Platform/QemuUser/PlatformQemuUser.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Listener.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(PlatformQemuUser) + +namespace { +#define LLDB_PROPERTIES_platformqemuuser +#include "PlatformQemuUserProperties.inc" + +enum { +#define LLDB_PROPERTIES_platformqemuuser +#include "PlatformQemuUserPropertiesEnum.inc" +}; + +class PluginProperties : public Properties { +public: + PluginProperties() { + m_collection_sp = std::make_shared<OptionValueProperties>( + PlatformQemuUser::GetPluginNameStatic()); + m_collection_sp->Initialize(g_platformqemuuser_properties); + } + + llvm::StringRef GetArchitecture() { + return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyArchitecture, ""); + } + + FileSpec GetEmulatorPath() { + return GetPropertyAtIndexAs<FileSpec>(ePropertyEmulatorPath, {}); + } + + Args GetEmulatorArgs() { + Args result; + m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorArgs, result); + return result; + } + + Environment GetEmulatorEnvVars() { + Args args; + m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyEmulatorEnvVars, args); + return Environment(args); + } + + Environment GetTargetEnvVars() { + Args args; + m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyTargetEnvVars, args); + return Environment(args); + } +}; + +} // namespace + +static PluginProperties &GetGlobalProperties() { + static PluginProperties g_settings; + return g_settings; +} + +llvm::StringRef PlatformQemuUser::GetPluginDescriptionStatic() { + return "Platform for debugging binaries under user mode qemu"; +} + +void PlatformQemuUser::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), GetPluginDescriptionStatic(), + PlatformQemuUser::CreateInstance, PlatformQemuUser::DebuggerInitialize); +} + +void PlatformQemuUser::Terminate() { + PluginManager::UnregisterPlugin(PlatformQemuUser::CreateInstance); +} + +void PlatformQemuUser::DebuggerInitialize(Debugger &debugger) { + if (!PluginManager::GetSettingForPlatformPlugin(debugger, + GetPluginNameStatic())) { + PluginManager::CreateSettingForPlatformPlugin( + debugger, GetGlobalProperties().GetValueProperties(), + "Properties for the qemu-user platform plugin.", + /*is_global_property=*/true); + } +} + +PlatformSP PlatformQemuUser::CreateInstance(bool force, const ArchSpec *arch) { + if (force) + return PlatformSP(new PlatformQemuUser()); + return nullptr; +} + +std::vector<ArchSpec> +PlatformQemuUser::GetSupportedArchitectures(const ArchSpec &process_host_arch) { + llvm::Triple triple = HostInfo::GetArchitecture().GetTriple(); + triple.setEnvironment(llvm::Triple::UnknownEnvironment); + triple.setArchName(GetGlobalProperties().GetArchitecture()); + if (triple.getArch() != llvm::Triple::UnknownArch) + return {ArchSpec(triple)}; + return {}; +} + +static auto get_arg_range(const Args &args) { + return llvm::make_range(args.GetArgumentArrayRef().begin(), + args.GetArgumentArrayRef().end()); +} + +// Returns the emulator environment which result in the desired environment +// being presented to the emulated process. We want to be careful about +// preserving the host environment, as it may contain entries (LD_LIBRARY_PATH, +// for example) needed for the operation of the emulator itself. +static Environment ComputeLaunchEnvironment(Environment target, + Environment host) { + std::vector<std::string> set_env; + for (const auto &KV : target) { + // If the host value differs from the target (or is unset), then set it + // through QEMU_SET_ENV. Identical entries will be forwarded automatically. + auto host_it = host.find(KV.first()); + if (host_it == host.end() || host_it->second != KV.second) + set_env.push_back(Environment::compose(KV)); + } + llvm::sort(set_env); + + std::vector<llvm::StringRef> unset_env; + for (const auto &KV : host) { + // If the target is missing some host entries, then unset them through + // QEMU_UNSET_ENV. + if (target.count(KV.first()) == 0) + unset_env.push_back(KV.first()); + } + llvm::sort(unset_env); + + // The actual QEMU_(UN)SET_ENV variables should not be forwarded to the + // target. + if (!set_env.empty()) { + host["QEMU_SET_ENV"] = llvm::join(set_env, ","); + unset_env.push_back("QEMU_SET_ENV"); + } + if (!unset_env.empty()) { + unset_env.push_back("QEMU_UNSET_ENV"); + host["QEMU_UNSET_ENV"] = llvm::join(unset_env, ","); + } + return host; +} + +lldb::ProcessSP PlatformQemuUser::DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target &target, Status &error) { + Log *log = GetLog(LLDBLog::Platform); + + // If platform.plugin.qemu-user.emulator-path is set, use it. + FileSpec qemu = GetGlobalProperties().GetEmulatorPath(); + // If platform.plugin.qemu-user.emulator-path is not set, build the + // executable name from platform.plugin.qemu-user.architecture. + if (!qemu) { + llvm::StringRef arch = GetGlobalProperties().GetArchitecture(); + // If platform.plugin.qemu-user.architecture is not set, build the + // executable name from the target Triple's ArchName + if (arch.empty()) + arch = target.GetArchitecture().GetTriple().getArchName(); + qemu.SetPath(("qemu-" + arch).str()); + } + FileSystem::Instance().ResolveExecutableLocation(qemu); + + llvm::SmallString<0> socket_model, socket_path; + HostInfo::GetProcessTempDir().GetPath(socket_model); + llvm::sys::path::append(socket_model, "qemu-%%%%%%%%.socket"); + do { + llvm::sys::fs::createUniquePath(socket_model, socket_path, false); + } while (FileSystem::Instance().Exists(socket_path)); + + Args args({qemu.GetPath(), "-g", socket_path}); + if (!launch_info.GetArg0().empty()) { + args.AppendArgument("-0"); + args.AppendArgument(launch_info.GetArg0()); + } + args.AppendArguments(GetGlobalProperties().GetEmulatorArgs()); + args.AppendArgument("--"); + args.AppendArgument(launch_info.GetExecutableFile().GetPath()); + for (size_t i = 1; i < launch_info.GetArguments().size(); ++i) + args.AppendArgument(launch_info.GetArguments()[i].ref()); + + LLDB_LOG(log, "{0} -> {1}", get_arg_range(launch_info.GetArguments()), + get_arg_range(args)); + + launch_info.SetArguments(args, true); + + Environment emulator_env = Host::GetEnvironment(); + if (const std::string &sysroot = GetSDKRootDirectory(); !sysroot.empty()) + emulator_env["QEMU_LD_PREFIX"] = sysroot; + for (const auto &KV : GetGlobalProperties().GetEmulatorEnvVars()) + emulator_env[KV.first()] = KV.second; + launch_info.GetEnvironment() = ComputeLaunchEnvironment( + std::move(launch_info.GetEnvironment()), std::move(emulator_env)); + + launch_info.SetLaunchInSeparateProcessGroup(true); + launch_info.GetFlags().Clear(eLaunchFlagDebug); + launch_info.SetMonitorProcessCallback(ProcessLaunchInfo::NoOpMonitorCallback); + + // This is automatically done for host platform in + // Target::FinalizeFileActions, but we're not a host platform. + llvm::Error Err = launch_info.SetUpPtyRedirection(); + LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}"); + + error = Host::LaunchProcess(launch_info); + if (error.Fail()) + return nullptr; + + ProcessSP process_sp = target.CreateProcess( + launch_info.GetListener(), + process_gdb_remote::ProcessGDBRemote::GetPluginNameStatic(), nullptr, + true); + if (!process_sp) { + error.SetErrorString("Failed to create GDB process"); + return nullptr; + } + + process_sp->HijackProcessEvents(launch_info.GetHijackListener()); + + error = process_sp->ConnectRemote(("unix-connect://" + socket_path).str()); + if (error.Fail()) + return nullptr; + + if (launch_info.GetPTY().GetPrimaryFileDescriptor() != + PseudoTerminal::invalid_fd) + process_sp->SetSTDIOFileDescriptor( + launch_info.GetPTY().ReleasePrimaryFileDescriptor()); + + return process_sp; +} + +Environment PlatformQemuUser::GetEnvironment() { + Environment env = Host::GetEnvironment(); + for (const auto &KV : GetGlobalProperties().GetTargetEnvVars()) + env[KV.first()] = KV.second; + return env; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.h b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.h new file mode 100644 index 000000000000..596cf75b591f --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUser.h @@ -0,0 +1,81 @@ +//===-- PlatformQemuUser.h ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_QEMUUSER_PLATFORMQEMUUSER_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_QEMUUSER_PLATFORMQEMUUSER_H + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Platform.h" + +namespace lldb_private { + +class PlatformQemuUser : public Platform { +public: + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "qemu-user"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + llvm::StringRef GetDescription() override { + return GetPluginDescriptionStatic(); + } + + UserIDResolver &GetUserIDResolver() override { + return HostInfo::GetUserIDResolver(); + } + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override; + + lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) override; + + lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, + Target *target, Status &status) override { + status.SetErrorString("Not supported"); + return nullptr; + } + + uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos) override { + return 0; + } + + bool GetProcessInfo(lldb::pid_t pid, + ProcessInstanceInfo &proc_info) override { + return false; + } + + bool IsConnected() const override { return true; } + + void CalculateTrapHandlerSymbolNames() override {} + + Environment GetEnvironment() override; + + MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr, + lldb::addr_t length, unsigned prot, + unsigned flags, lldb::addr_t fd, + lldb::addr_t offset) override { + return Platform::GetHostPlatform()->GetMmapArgumentList( + arch, addr, length, prot, flags, fd, offset); + } + +private: + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + static void DebuggerInitialize(Debugger &debugger); + + PlatformQemuUser() : Platform(/*is_host=*/true) {} +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_QEMUUSER_PLATFORMQEMUUSER_H diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUserProperties.td b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUserProperties.td new file mode 100644 index 000000000000..c7ec4bbc6e78 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/QemuUser/PlatformQemuUserProperties.td @@ -0,0 +1,24 @@ +include "../../../../include/lldb/Core/PropertiesBase.td" + +let Definition = "platformqemuuser" in { + def Architecture: Property<"architecture", "String">, + Global, + DefaultStringValue<"">, + Desc<"Architecture to emulate.">; + def EmulatorPath: Property<"emulator-path", "FileSpec">, + Global, + DefaultStringValue<"">, + Desc<"Path to the emulator binary. If the path does not contain a directory separator, the filename is looked up in the PATH environment variable. If empty, the filename is derived from the architecture setting.">; + def EmulatorArgs: Property<"emulator-args", "Args">, + Global, + DefaultStringValue<"">, + Desc<"Extra arguments to pass to the emulator.">; + def EmulatorEnvVars: Property<"emulator-env-vars", "Dictionary">, + Global, + ElementType<"String">, + Desc<"Extra variables to add to the emulator environment.">; + def TargetEnvVars: Property<"target-env-vars", "Dictionary">, + Global, + ElementType<"String">, + Desc<"Extra variables to add to emulated target environment.">; +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp new file mode 100644 index 000000000000..88f1ad15b6b4 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -0,0 +1,836 @@ +//===-- PlatformRemoteGDBServer.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PlatformRemoteGDBServer.h" +#include "lldb/Host/Config.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UriParser.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/FormatAdapters.h" + +#include "Plugins/Process/Utility/GDBRemoteSignals.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include <mutex> +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::platform_gdb_server; + +LLDB_PLUGIN_DEFINE_ADV(PlatformRemoteGDBServer, PlatformGDB) + +static bool g_initialized = false; +// UnixSignals does not store the signal names or descriptions itself. +// It holds onto StringRefs. Becaue we may get signal information dynamically +// from the remote, these strings need persistent storage client-side. +static std::mutex g_signal_string_mutex; +static llvm::StringSet<> g_signal_string_storage; + +void PlatformRemoteGDBServer::Initialize() { + Platform::Initialize(); + + if (!g_initialized) { + g_initialized = true; + PluginManager::RegisterPlugin( + PlatformRemoteGDBServer::GetPluginNameStatic(), + PlatformRemoteGDBServer::GetDescriptionStatic(), + PlatformRemoteGDBServer::CreateInstance); + } +} + +void PlatformRemoteGDBServer::Terminate() { + if (g_initialized) { + g_initialized = false; + PluginManager::UnregisterPlugin(PlatformRemoteGDBServer::CreateInstance); + } + + Platform::Terminate(); +} + +PlatformSP PlatformRemoteGDBServer::CreateInstance(bool force, + const ArchSpec *arch) { + bool create = force; + if (!create) { + create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); + } + if (create) + return PlatformSP(new PlatformRemoteGDBServer()); + return PlatformSP(); +} + +llvm::StringRef PlatformRemoteGDBServer::GetDescriptionStatic() { + return "A platform that uses the GDB remote protocol as the communication " + "transport."; +} + +llvm::StringRef PlatformRemoteGDBServer::GetDescription() { + if (m_platform_description.empty()) { + if (IsConnected()) { + // Send the get description packet + } + } + + if (!m_platform_description.empty()) + return m_platform_description.c_str(); + return GetDescriptionStatic(); +} + +bool PlatformRemoteGDBServer::GetModuleSpec(const FileSpec &module_file_spec, + const ArchSpec &arch, + ModuleSpec &module_spec) { + Log *log = GetLog(LLDBLog::Platform); + + const auto module_path = module_file_spec.GetPath(false); + + if (!m_gdb_client_up || + !m_gdb_client_up->GetModuleInfo(module_file_spec, arch, module_spec)) { + LLDB_LOGF( + log, + "PlatformRemoteGDBServer::%s - failed to get module info for %s:%s", + __FUNCTION__, module_path.c_str(), + arch.GetTriple().getTriple().c_str()); + return false; + } + + if (log) { + StreamString stream; + module_spec.Dump(stream); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::%s - got module info for (%s:%s) : %s", + __FUNCTION__, module_path.c_str(), + arch.GetTriple().getTriple().c_str(), stream.GetData()); + } + + return true; +} + +Status PlatformRemoteGDBServer::GetFileWithUUID(const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file) { + // Default to the local case + local_file = platform_file; + return Status(); +} + +/// Default Constructor +PlatformRemoteGDBServer::PlatformRemoteGDBServer() + : Platform(/*is_host=*/false) {} + +/// Destructor. +/// +/// The destructor is virtual since this class is designed to be +/// inherited from by the plug-in instance. +PlatformRemoteGDBServer::~PlatformRemoteGDBServer() = default; + +size_t PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode( + Target &target, BreakpointSite *bp_site) { + // This isn't needed if the z/Z packets are supported in the GDB remote + // server. But we might need a packet to detect this. + return 0; +} + +bool PlatformRemoteGDBServer::GetRemoteOSVersion() { + if (m_gdb_client_up) + m_os_version = m_gdb_client_up->GetOSVersion(); + return !m_os_version.empty(); +} + +std::optional<std::string> PlatformRemoteGDBServer::GetRemoteOSBuildString() { + if (!m_gdb_client_up) + return std::nullopt; + return m_gdb_client_up->GetOSBuildString(); +} + +std::optional<std::string> +PlatformRemoteGDBServer::GetRemoteOSKernelDescription() { + if (!m_gdb_client_up) + return std::nullopt; + return m_gdb_client_up->GetOSKernelDescription(); +} + +// Remote Platform subclasses need to override this function +ArchSpec PlatformRemoteGDBServer::GetRemoteSystemArchitecture() { + if (!m_gdb_client_up) + return ArchSpec(); + return m_gdb_client_up->GetSystemArchitecture(); +} + +FileSpec PlatformRemoteGDBServer::GetRemoteWorkingDirectory() { + if (IsConnected()) { + Log *log = GetLog(LLDBLog::Platform); + FileSpec working_dir; + if (m_gdb_client_up->GetWorkingDir(working_dir) && log) + LLDB_LOGF(log, + "PlatformRemoteGDBServer::GetRemoteWorkingDirectory() -> '%s'", + working_dir.GetPath().c_str()); + return working_dir; + } else { + return Platform::GetRemoteWorkingDirectory(); + } +} + +bool PlatformRemoteGDBServer::SetRemoteWorkingDirectory( + const FileSpec &working_dir) { + if (IsConnected()) { + // Clear the working directory it case it doesn't get set correctly. This + // will for use to re-read it + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, "PlatformRemoteGDBServer::SetRemoteWorkingDirectory('%s')", + working_dir.GetPath().c_str()); + return m_gdb_client_up->SetWorkingDir(working_dir) == 0; + } else + return Platform::SetRemoteWorkingDirectory(working_dir); +} + +bool PlatformRemoteGDBServer::IsConnected() const { + if (m_gdb_client_up) { + assert(m_gdb_client_up->IsConnected()); + return true; + } + return false; +} + +Status PlatformRemoteGDBServer::ConnectRemote(Args &args) { + Status error; + if (IsConnected()) { + error.SetErrorStringWithFormat("the platform is already connected to '%s', " + "execute 'platform disconnect' to close the " + "current connection", + GetHostname()); + return error; + } + + if (args.GetArgumentCount() != 1) { + error.SetErrorString( + "\"platform connect\" takes a single argument: <connect-url>"); + return error; + } + + const char *url = args.GetArgumentAtIndex(0); + if (!url) + return Status("URL is null."); + + std::optional<URI> parsed_url = URI::Parse(url); + if (!parsed_url) + return Status("Invalid URL: %s", url); + + // We're going to reuse the hostname when we connect to the debugserver. + m_platform_scheme = parsed_url->scheme.str(); + m_platform_hostname = parsed_url->hostname.str(); + + auto client_up = + std::make_unique<process_gdb_remote::GDBRemoteCommunicationClient>(); + client_up->SetPacketTimeout( + process_gdb_remote::ProcessGDBRemote::GetPacketTimeout()); + client_up->SetConnection(std::make_unique<ConnectionFileDescriptor>()); + client_up->Connect(url, &error); + + if (error.Fail()) + return error; + + if (client_up->HandshakeWithServer(&error)) { + m_gdb_client_up = std::move(client_up); + m_gdb_client_up->GetHostInfo(); + // If a working directory was set prior to connecting, send it down + // now. + if (m_working_dir) + m_gdb_client_up->SetWorkingDir(m_working_dir); + + m_supported_architectures.clear(); + ArchSpec remote_arch = m_gdb_client_up->GetSystemArchitecture(); + if (remote_arch) { + m_supported_architectures.push_back(remote_arch); + if (remote_arch.GetTriple().isArch64Bit()) + m_supported_architectures.push_back( + ArchSpec(remote_arch.GetTriple().get32BitArchVariant())); + } + } else { + client_up->Disconnect(); + if (error.Success()) + error.SetErrorString("handshake failed"); + } + return error; +} + +Status PlatformRemoteGDBServer::DisconnectRemote() { + Status error; + m_gdb_client_up.reset(); + m_remote_signals_sp.reset(); + return error; +} + +const char *PlatformRemoteGDBServer::GetHostname() { + if (m_gdb_client_up) + m_gdb_client_up->GetHostname(m_hostname); + if (m_hostname.empty()) + return nullptr; + return m_hostname.c_str(); +} + +std::optional<std::string> +PlatformRemoteGDBServer::DoGetUserName(UserIDResolver::id_t uid) { + std::string name; + if (m_gdb_client_up && m_gdb_client_up->GetUserName(uid, name)) + return std::move(name); + return std::nullopt; +} + +std::optional<std::string> +PlatformRemoteGDBServer::DoGetGroupName(UserIDResolver::id_t gid) { + std::string name; + if (m_gdb_client_up && m_gdb_client_up->GetGroupName(gid, name)) + return std::move(name); + return std::nullopt; +} + +uint32_t PlatformRemoteGDBServer::FindProcesses( + const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) { + if (m_gdb_client_up) + return m_gdb_client_up->FindProcesses(match_info, process_infos); + return 0; +} + +bool PlatformRemoteGDBServer::GetProcessInfo( + lldb::pid_t pid, ProcessInstanceInfo &process_info) { + if (m_gdb_client_up) + return m_gdb_client_up->GetProcessInfo(pid, process_info); + return false; +} + +Status PlatformRemoteGDBServer::LaunchProcess(ProcessLaunchInfo &launch_info) { + Log *log = GetLog(LLDBLog::Platform); + Status error; + + LLDB_LOGF(log, "PlatformRemoteGDBServer::%s() called", __FUNCTION__); + + if (!IsConnected()) + return Status("Not connected."); + auto num_file_actions = launch_info.GetNumFileActions(); + for (decltype(num_file_actions) i = 0; i < num_file_actions; ++i) { + const auto file_action = launch_info.GetFileActionAtIndex(i); + if (file_action->GetAction() != FileAction::eFileActionOpen) + continue; + switch (file_action->GetFD()) { + case STDIN_FILENO: + m_gdb_client_up->SetSTDIN(file_action->GetFileSpec()); + break; + case STDOUT_FILENO: + m_gdb_client_up->SetSTDOUT(file_action->GetFileSpec()); + break; + case STDERR_FILENO: + m_gdb_client_up->SetSTDERR(file_action->GetFileSpec()); + break; + } + } + + m_gdb_client_up->SetDisableASLR( + launch_info.GetFlags().Test(eLaunchFlagDisableASLR)); + m_gdb_client_up->SetDetachOnError( + launch_info.GetFlags().Test(eLaunchFlagDetachOnError)); + + FileSpec working_dir = launch_info.GetWorkingDirectory(); + if (working_dir) { + m_gdb_client_up->SetWorkingDir(working_dir); + } + + // Send the environment and the program + arguments after we connect + m_gdb_client_up->SendEnvironment(launch_info.GetEnvironment()); + + ArchSpec arch_spec = launch_info.GetArchitecture(); + const char *arch_triple = arch_spec.GetTriple().str().c_str(); + + m_gdb_client_up->SendLaunchArchPacket(arch_triple); + LLDB_LOGF( + log, + "PlatformRemoteGDBServer::%s() set launch architecture triple to '%s'", + __FUNCTION__, arch_triple ? arch_triple : "<NULL>"); + + { + // Scope for the scoped timeout object + process_gdb_remote::GDBRemoteCommunication::ScopedTimeout timeout( + *m_gdb_client_up, std::chrono::seconds(5)); + // Since we can't send argv0 separate from the executable path, we need to + // make sure to use the actual executable path found in the launch_info... + Args args = launch_info.GetArguments(); + if (FileSpec exe_file = launch_info.GetExecutableFile()) + args.ReplaceArgumentAtIndex(0, exe_file.GetPath(false)); + if (llvm::Error err = m_gdb_client_up->LaunchProcess(args)) { + error.SetErrorStringWithFormatv("Cannot launch '{0}': {1}", + args.GetArgumentAtIndex(0), + llvm::fmt_consume(std::move(err))); + return error; + } + } + + const auto pid = m_gdb_client_up->GetCurrentProcessID(false); + if (pid != LLDB_INVALID_PROCESS_ID) { + launch_info.SetProcessID(pid); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::%s() pid %" PRIu64 + " launched successfully", + __FUNCTION__, pid); + } else { + LLDB_LOGF(log, + "PlatformRemoteGDBServer::%s() launch succeeded but we " + "didn't get a valid process id back!", + __FUNCTION__); + error.SetErrorString("failed to get PID"); + } + return error; +} + +Status PlatformRemoteGDBServer::KillProcess(const lldb::pid_t pid) { + if (!KillSpawnedProcess(pid)) + return Status("failed to kill remote spawned process"); + return Status(); +} + +lldb::ProcessSP +PlatformRemoteGDBServer::DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) { + lldb::ProcessSP process_sp; + if (IsRemote()) { + if (IsConnected()) { + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + std::string connect_url; + if (!LaunchGDBServer(debugserver_pid, connect_url)) { + error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", + GetHostname()); + } else { + // The darwin always currently uses the GDB remote debugger plug-in + // so even when debugging locally we are debugging remotely! + process_sp = target.CreateProcess(launch_info.GetListener(), + "gdb-remote", nullptr, true); + + if (process_sp) { + process_sp->HijackProcessEvents(launch_info.GetHijackListener()); + process_sp->SetShadowListener(launch_info.GetShadowListener()); + + error = process_sp->ConnectRemote(connect_url.c_str()); + // Retry the connect remote one time... + if (error.Fail()) + error = process_sp->ConnectRemote(connect_url.c_str()); + if (error.Success()) + error = process_sp->Launch(launch_info); + else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) { + printf("error: connect remote failed (%s)\n", error.AsCString()); + KillSpawnedProcess(debugserver_pid); + } + } + } + } else { + error.SetErrorString("not connected to remote gdb server"); + } + } + return process_sp; +} + +bool PlatformRemoteGDBServer::LaunchGDBServer(lldb::pid_t &pid, + std::string &connect_url) { + assert(IsConnected()); + + ArchSpec remote_arch = GetRemoteSystemArchitecture(); + llvm::Triple &remote_triple = remote_arch.GetTriple(); + + uint16_t port = 0; + std::string socket_name; + bool launch_result = false; + if (remote_triple.getVendor() == llvm::Triple::Apple && + remote_triple.getOS() == llvm::Triple::IOS) { + // When remote debugging to iOS, we use a USB mux that always talks to + // localhost, so we will need the remote debugserver to accept connections + // only from localhost, no matter what our current hostname is + launch_result = + m_gdb_client_up->LaunchGDBServer("127.0.0.1", pid, port, socket_name); + } else { + // All other hosts should use their actual hostname + launch_result = + m_gdb_client_up->LaunchGDBServer(nullptr, pid, port, socket_name); + } + + if (!launch_result) + return false; + + connect_url = + MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port, + (socket_name.empty()) ? nullptr : socket_name.c_str()); + return true; +} + +bool PlatformRemoteGDBServer::KillSpawnedProcess(lldb::pid_t pid) { + assert(IsConnected()); + return m_gdb_client_up->KillSpawnedProcess(pid); +} + +lldb::ProcessSP PlatformRemoteGDBServer::Attach( + ProcessAttachInfo &attach_info, Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use + // existing one + Status &error) { + lldb::ProcessSP process_sp; + if (IsRemote()) { + if (IsConnected()) { + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + std::string connect_url; + if (!LaunchGDBServer(debugserver_pid, connect_url)) { + error.SetErrorStringWithFormat("unable to launch a GDB server on '%s'", + GetHostname()); + } else { + if (target == nullptr) { + TargetSP new_target_sp; + + error = debugger.GetTargetList().CreateTarget( + debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp); + target = new_target_sp.get(); + } else + error.Clear(); + + if (target && error.Success()) { + // The darwin always currently uses the GDB remote debugger plug-in + // so even when debugging locally we are debugging remotely! + process_sp = + target->CreateProcess(attach_info.GetListenerForProcess(debugger), + "gdb-remote", nullptr, true); + if (process_sp) { + error = process_sp->ConnectRemote(connect_url.c_str()); + if (error.Success()) { + ListenerSP listener_sp = attach_info.GetHijackListener(); + if (listener_sp) + process_sp->HijackProcessEvents(listener_sp); + process_sp->SetShadowListener(attach_info.GetShadowListener()); + error = process_sp->Attach(attach_info); + } + + if (error.Fail() && debugserver_pid != LLDB_INVALID_PROCESS_ID) { + KillSpawnedProcess(debugserver_pid); + } + } + } + } + } else { + error.SetErrorString("not connected to remote gdb server"); + } + } + return process_sp; +} + +Status PlatformRemoteGDBServer::MakeDirectory(const FileSpec &file_spec, + uint32_t mode) { + if (!IsConnected()) + return Status("Not connected."); + Status error = m_gdb_client_up->MakeDirectory(file_spec, mode); + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::MakeDirectory(path='%s', mode=%o) " + "error = %u (%s)", + file_spec.GetPath().c_str(), mode, error.GetError(), + error.AsCString()); + return error; +} + +Status PlatformRemoteGDBServer::GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) { + if (!IsConnected()) + return Status("Not connected."); + Status error = + m_gdb_client_up->GetFilePermissions(file_spec, file_permissions); + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::GetFilePermissions(path='%s', " + "file_permissions=%o) error = %u (%s)", + file_spec.GetPath().c_str(), file_permissions, error.GetError(), + error.AsCString()); + return error; +} + +Status PlatformRemoteGDBServer::SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) { + if (!IsConnected()) + return Status("Not connected."); + Status error = + m_gdb_client_up->SetFilePermissions(file_spec, file_permissions); + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::SetFilePermissions(path='%s', " + "file_permissions=%o) error = %u (%s)", + file_spec.GetPath().c_str(), file_permissions, error.GetError(), + error.AsCString()); + return error; +} + +lldb::user_id_t PlatformRemoteGDBServer::OpenFile(const FileSpec &file_spec, + File::OpenOptions flags, + uint32_t mode, + Status &error) { + if (IsConnected()) + return m_gdb_client_up->OpenFile(file_spec, flags, mode, error); + return LLDB_INVALID_UID; +} + +bool PlatformRemoteGDBServer::CloseFile(lldb::user_id_t fd, Status &error) { + if (IsConnected()) + return m_gdb_client_up->CloseFile(fd, error); + error = Status("Not connected."); + return false; +} + +lldb::user_id_t +PlatformRemoteGDBServer::GetFileSize(const FileSpec &file_spec) { + if (IsConnected()) + return m_gdb_client_up->GetFileSize(file_spec); + return LLDB_INVALID_UID; +} + +void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory( + CompletionRequest &request, bool only_dir) { + if (IsConnected()) + m_gdb_client_up->AutoCompleteDiskFileOrDirectory(request, only_dir); +} + +uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, + void *dst, uint64_t dst_len, + Status &error) { + if (IsConnected()) + return m_gdb_client_up->ReadFile(fd, offset, dst, dst_len, error); + error = Status("Not connected."); + return 0; +} + +uint64_t PlatformRemoteGDBServer::WriteFile(lldb::user_id_t fd, uint64_t offset, + const void *src, uint64_t src_len, + Status &error) { + if (IsConnected()) + return m_gdb_client_up->WriteFile(fd, offset, src, src_len, error); + error = Status("Not connected."); + return 0; +} + +Status PlatformRemoteGDBServer::PutFile(const FileSpec &source, + const FileSpec &destination, + uint32_t uid, uint32_t gid) { + return Platform::PutFile(source, destination, uid, gid); +} + +Status PlatformRemoteGDBServer::CreateSymlink( + const FileSpec &src, // The name of the link is in src + const FileSpec &dst) // The symlink points to dst +{ + if (!IsConnected()) + return Status("Not connected."); + Status error = m_gdb_client_up->CreateSymlink(src, dst); + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, + "PlatformRemoteGDBServer::CreateSymlink(src='%s', dst='%s') " + "error = %u (%s)", + src.GetPath().c_str(), dst.GetPath().c_str(), error.GetError(), + error.AsCString()); + return error; +} + +Status PlatformRemoteGDBServer::Unlink(const FileSpec &file_spec) { + if (!IsConnected()) + return Status("Not connected."); + Status error = m_gdb_client_up->Unlink(file_spec); + Log *log = GetLog(LLDBLog::Platform); + LLDB_LOGF(log, "PlatformRemoteGDBServer::Unlink(path='%s') error = %u (%s)", + file_spec.GetPath().c_str(), error.GetError(), error.AsCString()); + return error; +} + +bool PlatformRemoteGDBServer::GetFileExists(const FileSpec &file_spec) { + if (IsConnected()) + return m_gdb_client_up->GetFileExists(file_spec); + return false; +} + +Status PlatformRemoteGDBServer::RunShellCommand( + llvm::StringRef shell, llvm::StringRef command, + const FileSpec & + working_dir, // Pass empty FileSpec to use the current working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass NULL if you don't want the command output + const Timeout<std::micro> &timeout) { + if (!IsConnected()) + return Status("Not connected."); + return m_gdb_client_up->RunShellCommand(command, working_dir, status_ptr, + signo_ptr, command_output, timeout); +} + +void PlatformRemoteGDBServer::CalculateTrapHandlerSymbolNames() { + m_trap_handlers.push_back(ConstString("_sigtramp")); +} + +const UnixSignalsSP &PlatformRemoteGDBServer::GetRemoteUnixSignals() { + if (!IsConnected()) + return Platform::GetRemoteUnixSignals(); + + if (m_remote_signals_sp) + return m_remote_signals_sp; + + // If packet not implemented or JSON failed to parse, we'll guess the signal + // set based on the remote architecture. + m_remote_signals_sp = UnixSignals::Create(GetRemoteSystemArchitecture()); + + StringExtractorGDBRemote response; + auto result = + m_gdb_client_up->SendPacketAndWaitForResponse("jSignalsInfo", response); + + if (result != decltype(result)::Success || + response.GetResponseType() != response.eResponse) + return m_remote_signals_sp; + + auto object_sp = StructuredData::ParseJSON(response.GetStringRef()); + if (!object_sp || !object_sp->IsValid()) + return m_remote_signals_sp; + + auto array_sp = object_sp->GetAsArray(); + if (!array_sp || !array_sp->IsValid()) + return m_remote_signals_sp; + + auto remote_signals_sp = std::make_shared<lldb_private::GDBRemoteSignals>(); + + bool done = array_sp->ForEach( + [&remote_signals_sp](StructuredData::Object *object) -> bool { + if (!object || !object->IsValid()) + return false; + + auto dict = object->GetAsDictionary(); + if (!dict || !dict->IsValid()) + return false; + + // Signal number and signal name are required. + uint64_t signo; + if (!dict->GetValueForKeyAsInteger("signo", signo)) + return false; + + llvm::StringRef name; + if (!dict->GetValueForKeyAsString("name", name)) + return false; + + // We can live without short_name, description, etc. + bool suppress{false}; + auto object_sp = dict->GetValueForKey("suppress"); + if (object_sp && object_sp->IsValid()) + suppress = object_sp->GetBooleanValue(); + + bool stop{false}; + object_sp = dict->GetValueForKey("stop"); + if (object_sp && object_sp->IsValid()) + stop = object_sp->GetBooleanValue(); + + bool notify{false}; + object_sp = dict->GetValueForKey("notify"); + if (object_sp && object_sp->IsValid()) + notify = object_sp->GetBooleanValue(); + + std::string description; + object_sp = dict->GetValueForKey("description"); + if (object_sp && object_sp->IsValid()) + description = std::string(object_sp->GetStringValue()); + + llvm::StringRef name_backed, description_backed; + { + std::lock_guard<std::mutex> guard(g_signal_string_mutex); + name_backed = + g_signal_string_storage.insert(name).first->getKeyData(); + if (!description.empty()) + description_backed = + g_signal_string_storage.insert(description).first->getKeyData(); + } + + remote_signals_sp->AddSignal(signo, name_backed, suppress, stop, notify, + description_backed); + return true; + }); + + if (done) + m_remote_signals_sp = std::move(remote_signals_sp); + + return m_remote_signals_sp; +} + +std::string PlatformRemoteGDBServer::MakeGdbServerUrl( + const std::string &platform_scheme, const std::string &platform_hostname, + uint16_t port, const char *socket_name) { + const char *override_scheme = + getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_SCHEME"); + const char *override_hostname = + getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_HOSTNAME"); + const char *port_offset_c_str = + getenv("LLDB_PLATFORM_REMOTE_GDB_SERVER_PORT_OFFSET"); + int port_offset = port_offset_c_str ? ::atoi(port_offset_c_str) : 0; + + return MakeUrl(override_scheme ? override_scheme : platform_scheme.c_str(), + override_hostname ? override_hostname + : platform_hostname.c_str(), + port + port_offset, socket_name); +} + +std::string PlatformRemoteGDBServer::MakeUrl(const char *scheme, + const char *hostname, + uint16_t port, const char *path) { + StreamString result; + result.Printf("%s://[%s]", scheme, hostname); + if (port != 0) + result.Printf(":%u", port); + if (path) + result.Write(path, strlen(path)); + return std::string(result.GetString()); +} + +size_t PlatformRemoteGDBServer::ConnectToWaitingProcesses(Debugger &debugger, + Status &error) { + std::vector<std::string> connection_urls; + GetPendingGdbServerList(connection_urls); + + for (size_t i = 0; i < connection_urls.size(); ++i) { + ConnectProcess(connection_urls[i].c_str(), "gdb-remote", debugger, nullptr, error); + if (error.Fail()) + return i; // We already connected to i process successfully + } + return connection_urls.size(); +} + +size_t PlatformRemoteGDBServer::GetPendingGdbServerList( + std::vector<std::string> &connection_urls) { + std::vector<std::pair<uint16_t, std::string>> remote_servers; + if (!IsConnected()) + return 0; + m_gdb_client_up->QueryGDBServer(remote_servers); + for (const auto &gdbserver : remote_servers) { + const char *socket_name_cstr = + gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); + connection_urls.emplace_back( + MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, + gdbserver.first, socket_name_cstr)); + } + return connection_urls.size(); +} diff --git a/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h new file mode 100644 index 000000000000..638f7db5ef80 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -0,0 +1,198 @@ +//===-- PlatformRemoteGDBServer.h ----------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEGDBSERVER_H +#define LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEGDBSERVER_H + +#include <optional> +#include <string> + +#include "Plugins/Process/Utility/GDBRemoteSignals.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h" +#include "lldb/Target/Platform.h" + +namespace lldb_private { +namespace platform_gdb_server { + +class PlatformRemoteGDBServer : public Platform, private UserIDResolver { +public: + static void Initialize(); + + static void Terminate(); + + static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); + + static llvm::StringRef GetPluginNameStatic() { return "remote-gdb-server"; } + + static llvm::StringRef GetDescriptionStatic(); + + PlatformRemoteGDBServer(); + + ~PlatformRemoteGDBServer() override; + + // lldb_private::PluginInterface functions + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + // lldb_private::Platform functions + bool GetModuleSpec(const FileSpec &module_file_spec, const ArchSpec &arch, + ModuleSpec &module_spec) override; + + llvm::StringRef GetDescription() override; + + Status GetFileWithUUID(const FileSpec &platform_file, const UUID *uuid_ptr, + FileSpec &local_file) override; + + bool GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &proc_info) override; + + uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &process_infos) override; + + Status LaunchProcess(ProcessLaunchInfo &launch_info) override; + + Status KillProcess(const lldb::pid_t pid) override; + + lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info, + Debugger &debugger, Target &target, + Status &error) override; + + lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new + // target, else use existing one + Status &error) override; + + std::vector<ArchSpec> + GetSupportedArchitectures(const ArchSpec &process_host_arch) override { + return m_supported_architectures; + } + + size_t GetSoftwareBreakpointTrapOpcode(Target &target, + BreakpointSite *bp_site) override; + + bool GetRemoteOSVersion() override; + + std::optional<std::string> GetRemoteOSBuildString() override; + + std::optional<std::string> GetRemoteOSKernelDescription() override; + + // Remote Platform subclasses need to override this function + ArchSpec GetRemoteSystemArchitecture() override; + + FileSpec GetRemoteWorkingDirectory() override; + + bool SetRemoteWorkingDirectory(const FileSpec &working_dir) override; + + // Remote subclasses should override this and return a valid instance + // name if connected. + const char *GetHostname() override; + + UserIDResolver &GetUserIDResolver() override { return *this; } + + bool IsConnected() const override; + + Status ConnectRemote(Args &args) override; + + Status DisconnectRemote() override; + + Status MakeDirectory(const FileSpec &file_spec, + uint32_t file_permissions) override; + + Status GetFilePermissions(const FileSpec &file_spec, + uint32_t &file_permissions) override; + + Status SetFilePermissions(const FileSpec &file_spec, + uint32_t file_permissions) override; + + lldb::user_id_t OpenFile(const FileSpec &file_spec, File::OpenOptions flags, + uint32_t mode, Status &error) override; + + bool CloseFile(lldb::user_id_t fd, Status &error) override; + + uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *data_ptr, + uint64_t len, Status &error) override; + + uint64_t WriteFile(lldb::user_id_t fd, uint64_t offset, const void *data, + uint64_t len, Status &error) override; + + lldb::user_id_t GetFileSize(const FileSpec &file_spec) override; + + void AutoCompleteDiskFileOrDirectory(CompletionRequest &request, + bool only_dir) override; + + Status PutFile(const FileSpec &source, const FileSpec &destination, + uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override; + + Status CreateSymlink(const FileSpec &src, const FileSpec &dst) override; + + bool GetFileExists(const FileSpec &file_spec) override; + + Status Unlink(const FileSpec &path) override; + + Status RunShellCommand( + llvm::StringRef shell, llvm::StringRef command, + const FileSpec &working_dir, // Pass empty FileSpec to use the current + // working directory + int *status_ptr, // Pass NULL if you don't want the process exit status + int *signo_ptr, // Pass NULL if you don't want the signal that caused the + // process to exit + std::string + *command_output, // Pass NULL if you don't want the command output + const lldb_private::Timeout<std::micro> &timeout) override; + + void CalculateTrapHandlerSymbolNames() override; + + const lldb::UnixSignalsSP &GetRemoteUnixSignals() override; + + size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger, + lldb_private::Status &error) override; + + virtual size_t + GetPendingGdbServerList(std::vector<std::string> &connection_urls); + +protected: + std::unique_ptr<process_gdb_remote::GDBRemoteCommunicationClient> + m_gdb_client_up; + std::string m_platform_description; // After we connect we can get a more + // complete description of what we are + // connected to + std::string m_platform_scheme; + std::string m_platform_hostname; + + lldb::UnixSignalsSP m_remote_signals_sp; + + // Launch the debug server on the remote host - caller connects to launched + // debug server using connect_url. + // Subclasses should override this method if they want to do extra actions + // before or + // after launching the debug server. + virtual bool LaunchGDBServer(lldb::pid_t &pid, std::string &connect_url); + + virtual bool KillSpawnedProcess(lldb::pid_t pid); + + virtual std::string MakeUrl(const char *scheme, const char *hostname, + uint16_t port, const char *path); + +private: + std::string MakeGdbServerUrl(const std::string &platform_scheme, + const std::string &platform_hostname, + uint16_t port, const char *socket_name); + + std::optional<std::string> DoGetUserName(UserIDResolver::id_t uid) override; + std::optional<std::string> DoGetGroupName(UserIDResolver::id_t uid) override; + + std::vector<ArchSpec> m_supported_architectures; + + PlatformRemoteGDBServer(const PlatformRemoteGDBServer &) = delete; + const PlatformRemoteGDBServer & + operator=(const PlatformRemoteGDBServer &) = delete; +}; + +} // namespace platform_gdb_server +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEGDBSERVER_H |