diff options
Diffstat (limited to 'contrib/llvm-project/lldb/tools/lldb-server')
14 files changed, 1487 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.cpp b/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.cpp new file mode 100644 index 000000000000..2037f1e0f62b --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.cpp @@ -0,0 +1,131 @@ +//===-- Acceptor.cpp --------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "Acceptor.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/StreamString.h" +#include "lldb/Utility/UriParser.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace llvm; + +namespace { + +struct SocketScheme { + const char *m_scheme; + const Socket::SocketProtocol m_protocol; +}; + +SocketScheme socket_schemes[] = { + {"tcp", Socket::ProtocolTcp}, + {"udp", Socket::ProtocolUdp}, + {"unix", Socket::ProtocolUnixDomain}, + {"unix-abstract", Socket::ProtocolUnixAbstract}, +}; + +bool FindProtocolByScheme(const char *scheme, + Socket::SocketProtocol &protocol) { + for (auto s : socket_schemes) { + if (!strcmp(s.m_scheme, scheme)) { + protocol = s.m_protocol; + return true; + } + } + return false; +} + +const char *FindSchemeByProtocol(const Socket::SocketProtocol protocol) { + for (auto s : socket_schemes) { + if (s.m_protocol == protocol) + return s.m_scheme; + } + return nullptr; +} +} + +Status Acceptor::Listen(int backlog) { + return m_listener_socket_up->Listen(StringRef(m_name), backlog); +} + +Status Acceptor::Accept(const bool child_processes_inherit, Connection *&conn) { + Socket *conn_socket = nullptr; + auto error = m_listener_socket_up->Accept(conn_socket); + if (error.Success()) + conn = new ConnectionFileDescriptor(conn_socket); + + return error; +} + +Socket::SocketProtocol Acceptor::GetSocketProtocol() const { + return m_listener_socket_up->GetSocketProtocol(); +} + +const char *Acceptor::GetSocketScheme() const { + return FindSchemeByProtocol(GetSocketProtocol()); +} + +std::string Acceptor::GetLocalSocketId() const { return m_local_socket_id(); } + +std::unique_ptr<Acceptor> Acceptor::Create(StringRef name, + const bool child_processes_inherit, + Status &error) { + error.Clear(); + + Socket::SocketProtocol socket_protocol = Socket::ProtocolUnixDomain; + // Try to match socket name as URL - e.g., tcp://localhost:5555 + if (std::optional<URI> res = URI::Parse(name)) { + if (!FindProtocolByScheme(res->scheme.str().c_str(), socket_protocol)) + error.SetErrorStringWithFormat("Unknown protocol scheme \"%s\"", + res->scheme.str().c_str()); + else + name = name.drop_front(res->scheme.size() + strlen("://")); + } else { + // Try to match socket name as $host:port - e.g., localhost:5555 + if (!llvm::errorToBool(Socket::DecodeHostAndPort(name).takeError())) + socket_protocol = Socket::ProtocolTcp; + } + + if (error.Fail()) + return std::unique_ptr<Acceptor>(); + + std::unique_ptr<Socket> listener_socket_up = + Socket::Create(socket_protocol, child_processes_inherit, error); + + LocalSocketIdFunc local_socket_id; + if (error.Success()) { + if (listener_socket_up->GetSocketProtocol() == Socket::ProtocolTcp) { + TCPSocket *tcp_socket = + static_cast<TCPSocket *>(listener_socket_up.get()); + local_socket_id = [tcp_socket]() { + auto local_port = tcp_socket->GetLocalPortNumber(); + return (local_port != 0) ? llvm::to_string(local_port) : ""; + }; + } else { + const std::string socket_name = std::string(name); + local_socket_id = [socket_name]() { return socket_name; }; + } + + return std::unique_ptr<Acceptor>( + new Acceptor(std::move(listener_socket_up), name, local_socket_id)); + } + + return std::unique_ptr<Acceptor>(); +} + +Acceptor::Acceptor(std::unique_ptr<Socket> &&listener_socket, StringRef name, + const LocalSocketIdFunc &local_socket_id) + : m_listener_socket_up(std::move(listener_socket)), m_name(name.str()), + m_local_socket_id(local_socket_id) {} diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.h b/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.h new file mode 100644 index 000000000000..b441e92dcd22 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Acceptor.h @@ -0,0 +1,60 @@ +//===-- Acceptor.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_TOOLS_LLDB_SERVER_ACCEPTOR_H +#define LLDB_TOOLS_LLDB_SERVER_ACCEPTOR_H + +#include "lldb/Host/Socket.h" +#include "lldb/Utility/Connection.h" +#include "lldb/Utility/Status.h" + +#include <functional> +#include <memory> +#include <string> + +namespace llvm { +class StringRef; +} + +namespace lldb_private { +namespace lldb_server { + +class Acceptor { +public: + virtual ~Acceptor() = default; + + Status Listen(int backlog); + + Status Accept(const bool child_processes_inherit, Connection *&conn); + + static std::unique_ptr<Acceptor> Create(llvm::StringRef name, + const bool child_processes_inherit, + Status &error); + + Socket::SocketProtocol GetSocketProtocol() const; + + const char *GetSocketScheme() const; + + // Returns either TCP port number as string or domain socket path. + // Empty string is returned in case of error. + std::string GetLocalSocketId() const; + +private: + typedef std::function<std::string()> LocalSocketIdFunc; + + Acceptor(std::unique_ptr<Socket> &&listener_socket, llvm::StringRef name, + const LocalSocketIdFunc &local_socket_id); + + const std::unique_ptr<Socket> m_listener_socket_up; + const std::string m_name; + const LocalSocketIdFunc m_local_socket_id; +}; + +} // namespace lldb_server +} // namespace lldb_private + +#endif // LLDB_TOOLS_LLDB_SERVER_ACCEPTOR_H diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist new file mode 100644 index 000000000000..58a34ca5e438 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-Info.plist @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleIdentifier</key> + <string>com.apple.lldb-server</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>lldb-server</string> + <key>CFBundleVersion</key> + <string>2</string> + <key>SecTaskAccess</key> + <array> + <string>allowed</string> + <string>debug</string> + </array> +</dict> +</plist> diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist new file mode 100644 index 000000000000..e05a9baa5d0b --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-entitlements.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.springboard.debugapplications</key> + <true/> + <key>com.apple.backboardd.launchapplications</key> + <true/> + <key>com.apple.backboardd.debugapplications</key> + <true/> + <key>com.apple.frontboard.launchapplications</key> + <true/> + <key>com.apple.frontboard.debugapplications</key> + <true/> + <key>run-unsigned-code</key> + <true/> + <key>seatbelt-profiles</key> + <array> + <string>debugserver</string> + </array> + <key>com.apple.private.logging.diagnostic</key> + <true/> + <key>com.apple.security.network.server</key> + <true/> + <key>com.apple.security.network.client</key> + <true/> +</dict> +</plist> diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist new file mode 100644 index 000000000000..edf79b3b3eed --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-macos-entitlements.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.private.logging.diagnostic</key> + <true/> +</dict> +</plist> diff --git a/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs new file mode 100644 index 000000000000..cd5be1700704 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/Darwin/resources/lldb-server-mig.defs @@ -0,0 +1,5 @@ +/* + * nub.defs + */ + +#import <mach/mach_exc.defs> diff --git a/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.cpp b/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.cpp new file mode 100644 index 000000000000..c3a8df19e969 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.cpp @@ -0,0 +1,79 @@ +//===-- LLDBServerUtilities.cpp ---------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "LLDBServerUtilities.h" + +#include "lldb/Utility/Args.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private::lldb_server; +using namespace lldb_private; +using namespace llvm; + +class TestLogHandler : public LogHandler { +public: + TestLogHandler(std::shared_ptr<llvm::raw_ostream> stream_sp) + : m_stream_sp(stream_sp) {} + + void Emit(llvm::StringRef message) override { + (*m_stream_sp) << message; + m_stream_sp->flush(); + } + +private: + std::shared_ptr<raw_ostream> m_stream_sp; +}; + +static std::shared_ptr<TestLogHandler> GetLogStream(StringRef log_file) { + if (!log_file.empty()) { + std::error_code EC; + auto stream_sp = std::make_shared<raw_fd_ostream>( + log_file, EC, sys::fs::OF_TextWithCRLF | sys::fs::OF_Append); + if (!EC) + return std::make_shared<TestLogHandler>(stream_sp); + errs() << llvm::formatv( + "Failed to open log file `{0}`: {1}\nWill log to stderr instead.\n", + log_file, EC.message()); + } + // No need to delete the stderr stream. + return std::make_shared<TestLogHandler>( + std::shared_ptr<raw_ostream>(&errs(), [](raw_ostream *) {})); +} + +bool LLDBServerUtilities::SetupLogging(const std::string &log_file, + const StringRef &log_channels, + uint32_t log_options) { + + auto log_stream_sp = GetLogStream(log_file); + + SmallVector<StringRef, 32> channel_array; + log_channels.split(channel_array, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false); + for (auto channel_with_categories : channel_array) { + std::string error; + llvm::raw_string_ostream error_stream(error); + Args channel_then_categories(channel_with_categories); + std::string channel(channel_then_categories.GetArgumentAtIndex(0)); + channel_then_categories.Shift(); // Shift off the channel + + bool success = Log::EnableLogChannel( + log_stream_sp, log_options, channel, + channel_then_categories.GetArgumentArrayRef(), error_stream); + if (!success) { + errs() << formatv("Unable to setup logging for channel \"{0}\": {1}", + channel, error_stream.str()); + return false; + } + } + return true; +} diff --git a/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.h b/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.h new file mode 100644 index 000000000000..b59d1e411540 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/LLDBServerUtilities.h @@ -0,0 +1,29 @@ +#ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERUTILITIES_H + +#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERUTILITIES_H + +//===-- LLDBServerUtilities.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" + +#include <string> + +namespace lldb_private { +namespace lldb_server { + +class LLDBServerUtilities { +public: + static bool SetupLogging(const std::string &log_file, + const llvm::StringRef &log_channels, + uint32_t log_options); +}; +} +} + +#endif diff --git a/contrib/llvm-project/lldb/tools/lldb-server/LLGSOptions.td b/contrib/llvm-project/lldb/tools/lldb-server/LLGSOptions.td new file mode 100644 index 000000000000..429a4671764f --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/LLGSOptions.td @@ -0,0 +1,62 @@ +include "llvm/Option/OptParser.td" + +class F<string name>: Flag<["--", "-"], name>; +class R<list<string> prefixes, string name> + : Option<prefixes, name, KIND_REMAINING_ARGS>; + +multiclass SJ<string name, string help> { + def NAME: Separate<["--", "-"], name>, + HelpText<help>; + def NAME # _eq: Joined<["--", "-"], name # "=">, + Alias<!cast<Separate>(NAME)>; +} + +def grp_connect : OptionGroup<"connection">, HelpText<"CONNECTION">; + +defm fd: SJ<"fd", "Communicate over the given file descriptor.">, + MetaVarName<"<fd>">, + Group<grp_connect>; + +defm named_pipe: SJ<"named-pipe", "Write port lldb-server will listen on to the given named pipe.">, + MetaVarName<"<name>">, + Group<grp_connect>; + +defm pipe: SJ<"pipe", "Write port lldb-server will listen on to the given file descriptor.">, + MetaVarName<"<fd>">, + Group<grp_connect>; + +def reverse_connect: F<"reverse-connect">, + HelpText<"Connect to the client instead of passively waiting for a connection. In this case [host]:port denotes the remote address to connect to.">, + Group<grp_connect>; + +def grp_general : OptionGroup<"general options">, HelpText<"GENERAL OPTIONS">; + +defm log_channels: SJ<"log-channels", "Channels to log. A colon-separated list of entries. Each entry starts with a channel followed by a space-separated list of categories.">, + MetaVarName<"<channel1 categories...:channel2 categories...>">, + Group<grp_general>; + +defm log_file: SJ<"log-file", "Destination file to log to. If empty, log to stderr.">, + MetaVarName<"<file>">, + Group<grp_general>; + +def setsid: F<"setsid">, HelpText<"Run lldb-server in a new session.">, + Group<grp_general>; +def: Flag<["-"], "S">, Alias<setsid>, + Group<grp_general>; + +def help: F<"help">, HelpText<"Prints out the usage information for lldb-server.">, + Group<grp_general>; +def: Flag<["-"], "h">, Alias<help>, + Group<grp_general>; + +def grp_target : OptionGroup<"target selection">, HelpText<"TARGET SELECTION">; + +defm attach: SJ<"attach", "Attach to the process given by a (numeric) process id or a name.">, + MetaVarName<"<pid-or-name>">, + Group<grp_target>; + +def REM : R<["--"], "">, HelpText<"Launch program for debugging.">, + MetaVarName<"program args">, + Group<grp_target>; + +def: F<"native-regs">; // Noop. Present for backwards compatibility only. diff --git a/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.cpp new file mode 100644 index 000000000000..4233252a84df --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -0,0 +1,101 @@ +//===-- SystemInitializerLLGS.cpp -------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerLLGS.h" + +#if defined(__APPLE__) +#include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" +using HostObjectFile = ObjectFileMachO; +#elif defined(_WIN32) +#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" +using HostObjectFile = ObjectFilePECOFF; +#else +#include "Plugins/ObjectFile/ELF/ObjectFileELF.h" +using HostObjectFile = ObjectFileELF; +#endif + +#if defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define LLDB_TARGET_ARM64 +#endif + +#if defined(__arm__) || defined(__arm) || defined(_ARM) || defined(_M_ARM) || \ + defined(LLDB_TARGET_ARM64) +#define LLDB_TARGET_ARM +#include "Plugins/Instruction/ARM/EmulateInstructionARM.h" +#endif + +#if defined(__loongarch__) +#define LLDB_TARGET_LoongArch +#include "Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h" +#endif + +#if defined(__mips64__) || defined(mips64) || defined(__mips64) || \ + defined(__MIPS64__) || defined(_M_MIPS64) +#define LLDB_TARGET_MIPS64 +#include "Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h" +#endif + +#if defined(__mips__) || defined(mips) || defined(__mips) || \ + defined(__MIPS__) || defined(_M_MIPS) || defined(LLDB_TARGET_MIPS64) +#define LLDB_TARGET_MIPS +#include "Plugins/Instruction/MIPS/EmulateInstructionMIPS.h" +#endif + +#if defined(__riscv) +#define LLDB_TARGET_RISCV +#include "Plugins/Instruction/RISCV/EmulateInstructionRISCV.h" +#endif + +using namespace lldb_private; + +llvm::Error SystemInitializerLLGS::Initialize() { + if (auto e = SystemInitializerCommon::Initialize()) + return e; + + HostObjectFile::Initialize(); + +#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) + EmulateInstructionARM::Initialize(); +#endif +#if defined(LLDB_TARGET_LoongArch) + EmulateInstructionLoongArch::Initialize(); +#endif +#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS::Initialize(); +#endif +#if defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS64::Initialize(); +#endif +#if defined(LLDB_TARGET_RISCV) + EmulateInstructionRISCV::Initialize(); +#endif + + return llvm::Error::success(); +} + +void SystemInitializerLLGS::Terminate() { + HostObjectFile::Terminate(); + +#if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64) + EmulateInstructionARM::Terminate(); +#endif +#if defined(LLDB_TARGET_LoongArch) + EmulateInstructionLoongArch::Terminate(); +#endif +#if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS::Terminate(); +#endif +#if defined(LLDB_TARGET_MIPS64) + EmulateInstructionMIPS64::Terminate(); +#endif +#if defined(LLDB_TARGET_RISCV) + EmulateInstructionRISCV::Terminate(); +#endif + + SystemInitializerCommon::Terminate(); +} diff --git a/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.h b/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.h new file mode 100644 index 000000000000..4469a8ba5f60 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/SystemInitializerLLGS.h @@ -0,0 +1,23 @@ +//===-- SystemInitializerLLGS.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_TOOLS_LLDB_SERVER_SYSTEMINITIALIZERLLGS_H +#define LLDB_TOOLS_LLDB_SERVER_SYSTEMINITIALIZERLLGS_H + +#include "lldb/Initialization/SystemInitializer.h" +#include "lldb/Initialization/SystemInitializerCommon.h" + +class SystemInitializerLLGS : public lldb_private::SystemInitializerCommon { +public: + SystemInitializerLLGS() : SystemInitializerCommon(nullptr) {} + + llvm::Error Initialize() override; + void Terminate() override; +}; + +#endif // LLDB_TOOLS_LLDB_SERVER_SYSTEMINITIALIZERLLGS_H diff --git a/contrib/llvm-project/lldb/tools/lldb-server/lldb-gdbserver.cpp b/contrib/llvm-project/lldb/tools/lldb-server/lldb-gdbserver.cpp new file mode 100644 index 000000000000..563284730bc7 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -0,0 +1,473 @@ +//===-- lldb-gdbserver.cpp --------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#ifndef _WIN32 +#include <csignal> +#include <unistd.h> +#endif + +#include "LLDBServerUtilities.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/Pipe.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Status.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Errno.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/WithColor.h" + +#if defined(__linux__) +#include "Plugins/Process/Linux/NativeProcessLinux.h" +#elif defined(__FreeBSD__) +#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h" +#elif defined(__NetBSD__) +#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h" +#elif defined(_WIN32) +#include "Plugins/Process/Windows/Common/NativeProcessWindows.h" +#endif + +#ifndef LLGS_PROGRAM_NAME +#define LLGS_PROGRAM_NAME "lldb-server" +#endif + +#ifndef LLGS_VERSION_STR +#define LLGS_VERSION_STR "local_build" +#endif + +using namespace llvm; +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; + +namespace { +#if defined(__linux__) +typedef process_linux::NativeProcessLinux::Manager NativeProcessManager; +#elif defined(__FreeBSD__) +typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; +#elif defined(__NetBSD__) +typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; +#elif defined(_WIN32) +typedef NativeProcessWindows::Manager NativeProcessManager; +#else +// Dummy implementation to make sure the code compiles +class NativeProcessManager : public NativeProcessProtocol::Manager { +public: + NativeProcessManager(MainLoop &mainloop) + : NativeProcessProtocol::Manager(mainloop) {} + + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Launch(ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate) override { + llvm_unreachable("Not implemented"); + } + llvm::Expected<std::unique_ptr<NativeProcessProtocol>> + Attach(lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate) override { + llvm_unreachable("Not implemented"); + } +}; +#endif +} + +#ifndef _WIN32 +// Watch for signals +static int g_sighup_received_count = 0; + +static void sighup_handler(MainLoopBase &mainloop) { + ++g_sighup_received_count; + + Log *log = GetLog(LLDBLog::Process); + LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)", + __FUNCTION__, g_sighup_received_count); + + if (g_sighup_received_count >= 2) + mainloop.RequestTermination(); +} +#endif // #ifndef _WIN32 + +void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server, + lldb::pid_t pid) { + Status error = gdb_server.AttachToProcess(pid); + if (error.Fail()) { + fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid, + error.AsCString()); + exit(1); + } +} + +void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server, + const std::string &process_name) { + // FIXME implement. +} + +void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server, + const std::string &attach_target) { + assert(!attach_target.empty() && "attach_target cannot be empty"); + + // First check if the attach_target is convertible to a long. If so, we'll use + // it as a pid. + char *end_p = nullptr; + const long int pid = strtol(attach_target.c_str(), &end_p, 10); + + // We'll call it a match if the entire argument is consumed. + if (end_p && + static_cast<size_t>(end_p - attach_target.c_str()) == + attach_target.size()) + handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid)); + else + handle_attach_to_process_name(gdb_server, attach_target); +} + +void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server, + llvm::ArrayRef<llvm::StringRef> Arguments) { + ProcessLaunchInfo info; + info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | + eLaunchFlagDisableASLR); + info.SetArguments(Args(Arguments), true); + + llvm::SmallString<64> cwd; + if (std::error_code ec = llvm::sys::fs::current_path(cwd)) { + llvm::errs() << "Error getting current directory: " << ec.message() << "\n"; + exit(1); + } + FileSpec cwd_spec(cwd); + FileSystem::Instance().Resolve(cwd_spec); + info.SetWorkingDirectory(cwd_spec); + info.GetEnvironment() = Host::GetEnvironment(); + + gdb_server.SetLaunchInfo(info); + + Status error = gdb_server.LaunchProcess(); + if (error.Fail()) { + llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n", + Arguments[0], error); + exit(1); + } +} + +Status writeSocketIdToPipe(Pipe &port_pipe, llvm::StringRef socket_id) { + size_t bytes_written = 0; + // Write the port number as a C string with the NULL terminator. + return port_pipe.Write(socket_id.data(), socket_id.size() + 1, bytes_written); +} + +Status writeSocketIdToPipe(const char *const named_pipe_path, + llvm::StringRef socket_id) { + Pipe port_name_pipe; + // Wait for 10 seconds for pipe to be opened. + auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false, + std::chrono::seconds{10}); + if (error.Fail()) + return error; + return writeSocketIdToPipe(port_name_pipe, socket_id); +} + +Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe, + llvm::StringRef socket_id) { + Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe}; + return writeSocketIdToPipe(port_pipe, socket_id); +} + +void ConnectToRemote(MainLoop &mainloop, + GDBRemoteCommunicationServerLLGS &gdb_server, + bool reverse_connect, llvm::StringRef host_and_port, + const char *const progname, const char *const subcommand, + const char *const named_pipe_path, pipe_t unnamed_pipe, + int connection_fd) { + Status error; + + std::unique_ptr<Connection> connection_up; + std::string url; + + if (connection_fd != -1) { + url = llvm::formatv("fd://{0}", connection_fd).str(); + + // Create the connection. +#if LLDB_ENABLE_POSIX && !defined _WIN32 + ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC); +#endif + } else if (!host_and_port.empty()) { + llvm::Expected<std::string> url_exp = + LLGSArgToURL(host_and_port, reverse_connect); + if (!url_exp) { + llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': " + "{1}\n", + host_and_port, + llvm::toString(url_exp.takeError())); + exit(-1); + } + + url = std::move(url_exp.get()); + } + + if (!url.empty()) { + // Create the connection or server. + std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{ + new ConnectionFileDescriptor}; + auto connection_result = conn_fd_up->Connect( + url, + [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) { + // If we have a named pipe to write the socket id back to, do that + // now. + if (named_pipe_path && named_pipe_path[0]) { + Status error = writeSocketIdToPipe(named_pipe_path, socket_id); + if (error.Fail()) + llvm::errs() << llvm::formatv( + "failed to write to the named pipe '{0}': {1}\n", + named_pipe_path, error.AsCString()); + } + // If we have an unnamed pipe to write the socket id back to, do + // that now. + else if (unnamed_pipe != LLDB_INVALID_PIPE) { + Status error = writeSocketIdToPipe(unnamed_pipe, socket_id); + if (error.Fail()) + llvm::errs() << llvm::formatv( + "failed to write to the unnamed pipe: {0}\n", error); + } + }, + &error); + + if (error.Fail()) { + llvm::errs() << llvm::formatv( + "error: failed to connect to client at '{0}': {1}\n", url, error); + exit(-1); + } + if (connection_result != eConnectionStatusSuccess) { + llvm::errs() << llvm::formatv( + "error: failed to connect to client at '{0}' " + "(connection status: {1})\n", + url, static_cast<int>(connection_result)); + exit(-1); + } + connection_up = std::move(conn_fd_up); + } + error = gdb_server.InitializeConnection(std::move(connection_up)); + if (error.Fail()) { + llvm::errs() << llvm::formatv("failed to initialize connection\n", error); + exit(-1); + } + llvm::outs() << "Connection established.\n"; +} + +namespace { +using namespace llvm::opt; + +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), +#include "LLGSOptions.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) \ + constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ + constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \ + NAME##_init, std::size(NAME##_init) - 1); +#include "LLGSOptions.inc" +#undef PREFIX + +static constexpr opt::OptTable::Info InfoTable[] = { +#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), +#include "LLGSOptions.inc" +#undef OPTION +}; + +class LLGSOptTable : public opt::GenericOptTable { +public: + LLGSOptTable() : opt::GenericOptTable(InfoTable) {} + + void PrintHelp(llvm::StringRef Name) { + std::string Usage = + (Name + " [options] [[host]:port] [[--] program args...]").str(); + OptTable::printHelp(llvm::outs(), Usage.c_str(), "lldb-server"); + llvm::outs() << R"( +DESCRIPTION + lldb-server connects to the LLDB client, which drives the debugging session. + If no connection options are given, the [host]:port argument must be present + and will denote the address that lldb-server will listen on. [host] defaults + to "localhost" if empty. Port can be zero, in which case the port number will + be chosen dynamically and written to destinations given by --named-pipe and + --pipe arguments. + + If no target is selected at startup, lldb-server can be directed by the LLDB + client to launch or attach to a process. + +)"; + } +}; +} // namespace + +int main_gdbserver(int argc, char *argv[]) { + Status error; + MainLoop mainloop; +#ifndef _WIN32 + // Setup signal handlers first thing. + signal(SIGPIPE, SIG_IGN); + MainLoop::SignalHandleUP sighup_handle = + mainloop.RegisterSignal(SIGHUP, sighup_handler, error); +#endif + + const char *progname = argv[0]; + const char *subcommand = argv[1]; + std::string attach_target; + std::string named_pipe_path; + std::string log_file; + StringRef + log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" + lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE; + bool reverse_connect = false; + int connection_fd = -1; + + // ProcessLaunchInfo launch_info; + ProcessAttachInfo attach_info; + + LLGSOptTable Opts; + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + bool HasError = false; + opt::InputArgList Args = Opts.parseArgs(argc - 1, argv + 1, OPT_UNKNOWN, + Saver, [&](llvm::StringRef Msg) { + WithColor::error() << Msg << "\n"; + HasError = true; + }); + std::string Name = + (llvm::sys::path::filename(argv[0]) + " g[dbserver]").str(); + std::string HelpText = + "Use '" + Name + " --help' for a complete list of options.\n"; + if (HasError) { + llvm::errs() << HelpText; + return 1; + } + + if (Args.hasArg(OPT_help)) { + Opts.PrintHelp(Name); + return 0; + } + +#ifndef _WIN32 + if (Args.hasArg(OPT_setsid)) { + // Put llgs into a new session. Terminals group processes + // into sessions and when a special terminal key sequences + // (like control+c) are typed they can cause signals to go out to + // all processes in a session. Using this --setsid (-S) option + // will cause debugserver to run in its own sessions and be free + // from such issues. + // + // This is useful when llgs is spawned from a command + // line application that uses llgs to do the debugging, + // yet that application doesn't want llgs receiving the + // signals sent to the session (i.e. dying when anyone hits ^C). + { + const ::pid_t new_sid = setsid(); + if (new_sid == -1) { + WithColor::warning() + << llvm::formatv("failed to set new session id for {0} ({1})\n", + LLGS_PROGRAM_NAME, llvm::sys::StrError()); + } + } + } +#endif + + log_file = Args.getLastArgValue(OPT_log_file).str(); + log_channels = Args.getLastArgValue(OPT_log_channels); + named_pipe_path = Args.getLastArgValue(OPT_named_pipe).str(); + reverse_connect = Args.hasArg(OPT_reverse_connect); + attach_target = Args.getLastArgValue(OPT_attach).str(); + if (Args.hasArg(OPT_pipe)) { + uint64_t Arg; + if (!llvm::to_integer(Args.getLastArgValue(OPT_pipe), Arg)) { + WithColor::error() << "invalid '--pipe' argument\n" << HelpText; + return 1; + } + unnamed_pipe = (pipe_t)Arg; + } + if (Args.hasArg(OPT_fd)) { + if (!llvm::to_integer(Args.getLastArgValue(OPT_fd), connection_fd)) { + WithColor::error() << "invalid '--fd' argument\n" << HelpText; + return 1; + } + } + + if (!LLDBServerUtilities::SetupLogging( + log_file, log_channels, + LLDB_LOG_OPTION_PREPEND_TIMESTAMP | + LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) + return -1; + + std::vector<llvm::StringRef> Inputs; + for (opt::Arg *Arg : Args.filtered(OPT_INPUT)) + Inputs.push_back(Arg->getValue()); + if (opt::Arg *Arg = Args.getLastArg(OPT_REM)) { + for (const char *Val : Arg->getValues()) + Inputs.push_back(Val); + } + if (Inputs.empty() && connection_fd == -1) { + WithColor::error() << "no connection arguments\n" << HelpText; + return 1; + } + + NativeProcessManager manager(mainloop); + GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager); + + llvm::StringRef host_and_port; + if (!Inputs.empty()) { + host_and_port = Inputs.front(); + Inputs.erase(Inputs.begin()); + } + + // Any arguments left over are for the program that we need to launch. If + // there + // are no arguments, then the GDB server will start up and wait for an 'A' + // packet + // to launch a program, or a vAttach packet to attach to an existing process, + // unless + // explicitly asked to attach with the --attach={pid|program_name} form. + if (!attach_target.empty()) + handle_attach(gdb_server, attach_target); + else if (!Inputs.empty()) + handle_launch(gdb_server, Inputs); + + // Print version info. + printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR); + + ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, + progname, subcommand, named_pipe_path.c_str(), + unnamed_pipe, connection_fd); + + if (!gdb_server.IsConnected()) { + fprintf(stderr, "no connection information provided, unable to run\n"); + return 1; + } + + Status ret = mainloop.Run(); + if (ret.Fail()) { + fprintf(stderr, "lldb-server terminating due to error: %s\n", + ret.AsCString()); + return 1; + } + fprintf(stderr, "lldb-server exiting...\n"); + + return 0; +} diff --git a/contrib/llvm-project/lldb/tools/lldb-server/lldb-platform.cpp b/contrib/llvm-project/lldb/tools/lldb-server/lldb-platform.cpp new file mode 100644 index 000000000000..7148a1d2a309 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/lldb-platform.cpp @@ -0,0 +1,385 @@ +//===-- lldb-platform.cpp ---------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include <cerrno> +#if defined(__APPLE__) +#include <netinet/in.h> +#endif +#include <csignal> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#if !defined(_WIN32) +#include <sys/wait.h> +#endif +#include <fstream> +#include <optional> + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +#include "Acceptor.h" +#include "LLDBServerUtilities.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/HostGetOpt.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Status.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; +using namespace llvm; + +// option descriptors for getopt_long_only() + +static int g_debug = 0; +static int g_verbose = 0; +static int g_server = 0; + +static struct option g_long_options[] = { + {"debug", no_argument, &g_debug, 1}, + {"verbose", no_argument, &g_verbose, 1}, + {"log-file", required_argument, nullptr, 'l'}, + {"log-channels", required_argument, nullptr, 'c'}, + {"listen", required_argument, nullptr, 'L'}, + {"port-offset", required_argument, nullptr, 'p'}, + {"gdbserver-port", required_argument, nullptr, 'P'}, + {"min-gdbserver-port", required_argument, nullptr, 'm'}, + {"max-gdbserver-port", required_argument, nullptr, 'M'}, + {"socket-file", required_argument, nullptr, 'f'}, + {"server", no_argument, &g_server, 1}, + {nullptr, 0, nullptr, 0}}; + +#if defined(__APPLE__) +#define LOW_PORT (IPPORT_RESERVED) +#define HIGH_PORT (IPPORT_HIFIRSTAUTO) +#else +#define LOW_PORT (1024u) +#define HIGH_PORT (49151u) +#endif + +#if !defined(_WIN32) +// Watch for signals +static void signal_handler(int signo) { + switch (signo) { + case SIGHUP: + // Use SIGINT first, if that does not work, use SIGHUP as a last resort. + // And we should not call exit() here because it results in the global + // destructors to be invoked and wreaking havoc on the threads still + // running. + llvm::errs() << "SIGHUP received, exiting lldb-server...\n"; + abort(); + break; + } +} +#endif + +static void display_usage(const char *progname, const char *subcommand) { + fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " + "log-channel-list] [--port-file port-file-path] --server " + "--listen port\n", + progname, subcommand); + exit(0); +} + +static Status save_socket_id_to_file(const std::string &socket_id, + const FileSpec &file_spec) { + FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef()); + Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); + if (error.Fail()) + return Status("Failed to create directory %s: %s", + temp_file_spec.GetPath().c_str(), error.AsCString()); + + Status status; + if (auto Err = llvm::writeToOutput(file_spec.GetPath(), + [&socket_id](llvm::raw_ostream &OS) { + OS << socket_id; + return llvm::Error::success(); + })) + return Status("Failed to atomically write file %s: %s", + file_spec.GetPath().c_str(), + llvm::toString(std::move(Err)).c_str()); + return status; +} + +// main +int main_platform(int argc, char *argv[]) { + const char *progname = argv[0]; + const char *subcommand = argv[1]; + argc--; + argv++; +#if !defined(_WIN32) + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, signal_handler); +#endif + int long_option_index = 0; + Status error; + std::string listen_host_port; + int ch; + + std::string log_file; + StringRef + log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" + + GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; + int min_gdbserver_port = 0; + int max_gdbserver_port = 0; + uint16_t port_offset = 0; + + FileSpec socket_file; + bool show_usage = false; + int option_error = 0; + int socket_error = -1; + + std::string short_options(OptionParser::GetShortOptionString(g_long_options)); + +#if __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + + while ((ch = getopt_long_only(argc, argv, short_options.c_str(), + g_long_options, &long_option_index)) != -1) { + switch (ch) { + case 0: // Any optional that auto set themselves will return 0 + break; + + case 'L': + listen_host_port.append(optarg); + break; + + case 'l': // Set Log File + if (optarg && optarg[0]) + log_file.assign(optarg); + break; + + case 'c': // Log Channels + if (optarg && optarg[0]) + log_channels = StringRef(optarg); + break; + + case 'f': // Socket file + if (optarg && optarg[0]) + socket_file.SetFile(optarg, FileSpec::Style::native); + break; + + case 'p': { + if (!llvm::to_integer(optarg, port_offset)) { + WithColor::error() << "invalid port offset string " << optarg << "\n"; + option_error = 4; + break; + } + if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { + WithColor::error() << llvm::formatv( + "port offset {0} is not in the " + "valid user port range of {1} - {2}\n", + port_offset, LOW_PORT, HIGH_PORT); + option_error = 5; + } + } break; + + case 'P': + case 'm': + case 'M': { + uint16_t portnum; + if (!llvm::to_integer(optarg, portnum)) { + WithColor::error() << "invalid port number string " << optarg << "\n"; + option_error = 2; + break; + } + if (portnum < LOW_PORT || portnum > HIGH_PORT) { + WithColor::error() << llvm::formatv( + "port number {0} is not in the " + "valid user port range of {1} - {2}\n", + portnum, LOW_PORT, HIGH_PORT); + option_error = 1; + break; + } + if (ch == 'P') + gdbserver_portmap.AllowPort(portnum); + else if (ch == 'm') + min_gdbserver_port = portnum; + else + max_gdbserver_port = portnum; + } break; + + case 'h': /* fall-through is intentional */ + case '?': + show_usage = true; + break; + } + } + + if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) + return -1; + + // Make a port map for a port range that was specified. + if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { + gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap( + min_gdbserver_port, max_gdbserver_port); + } else if (min_gdbserver_port || max_gdbserver_port) { + WithColor::error() << llvm::formatv( + "--min-gdbserver-port ({0}) is not lower than " + "--max-gdbserver-port ({1})\n", + min_gdbserver_port, max_gdbserver_port); + option_error = 3; + } + + // Print usage and exit if no listening port is specified. + if (listen_host_port.empty()) + show_usage = true; + + if (show_usage || option_error) { + display_usage(progname, subcommand); + exit(option_error); + } + + // Skip any options we consumed with getopt_long_only. + argc -= optind; + argv += optind; + lldb_private::Args inferior_arguments; + inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); + + const bool children_inherit_listen_socket = false; + // the test suite makes many connections in parallel, let's not miss any. + // The highest this should get reasonably is a function of the number + // of target CPUs. For now, let's just use 100. + const int backlog = 100; + + std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( + listen_host_port, children_inherit_listen_socket, error)); + if (error.Fail()) { + fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); + exit(socket_error); + } + + error = acceptor_up->Listen(backlog); + if (error.Fail()) { + printf("failed to listen: %s\n", error.AsCString()); + exit(socket_error); + } + if (socket_file) { + error = + save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); + if (error.Fail()) { + fprintf(stderr, "failed to write socket id to %s: %s\n", + socket_file.GetPath().c_str(), error.AsCString()); + return 1; + } + } + + GDBRemoteCommunicationServerPlatform platform( + acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); + if (port_offset > 0) + platform.SetPortOffset(port_offset); + + do { + const bool children_inherit_accept_socket = true; + Connection *conn = nullptr; + error = acceptor_up->Accept(children_inherit_accept_socket, conn); + if (error.Fail()) { + WithColor::error() << error.AsCString() << '\n'; + exit(socket_error); + } + printf("Connection established.\n"); + + if (g_server) { + // Collect child zombie processes. +#if !defined(_WIN32) + ::pid_t waitResult; + while ((waitResult = waitpid(-1, nullptr, WNOHANG)) > 0) { + // waitResult is the child pid + gdbserver_portmap.FreePortForProcess(waitResult); + } +#endif + // TODO: Clean up portmap for Windows when children die + // See https://github.com/llvm/llvm-project/issues/90923 + + // After collecting zombie ports, get the next available + GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child; + llvm::Expected<uint16_t> available_port = + gdbserver_portmap.GetNextAvailablePort(); + if (available_port) { + // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty. + if (*available_port) + portmap_for_child.AllowPort(*available_port); + } else { + llvm::consumeError(available_port.takeError()); + fprintf(stderr, + "no available gdbserver port for connection - dropping...\n"); + delete conn; + continue; + } + platform.SetPortMap(std::move(portmap_for_child)); + + auto childPid = fork(); + if (childPid) { + gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid); + // Parent doesn't need a connection to the lldb client + delete conn; + + // Parent will continue to listen for new connections. + continue; + } else { + // Child process will handle the connection and exit. + g_server = 0; + // Listening socket is owned by parent process. + acceptor_up.release(); + } + } else { + // If not running as a server, this process will not accept + // connections while a connection is active. + acceptor_up.reset(); + + // When not running in server mode, use all available ports + platform.SetPortMap(std::move(gdbserver_portmap)); + } + + platform.SetConnection(std::unique_ptr<Connection>(conn)); + + if (platform.IsConnected()) { + if (inferior_arguments.GetArgumentCount() > 0) { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + std::optional<uint16_t> port; + std::string socket_name; + Status error = platform.LaunchGDBServer(inferior_arguments, + "", // hostname + pid, port, socket_name); + if (error.Success()) + platform.SetPendingGdbServer(pid, *port, socket_name); + else + fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); + } + + bool interrupt = false; + bool done = false; + while (!interrupt && !done) { + if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt, + done) != + GDBRemoteCommunication::PacketResult::Success) + break; + } + + if (error.Fail()) + WithColor::error() << error.AsCString() << '\n'; + } + } while (g_server); + + fprintf(stderr, "lldb-server exiting...\n"); + + return 0; +} diff --git a/contrib/llvm-project/lldb/tools/lldb-server/lldb-server.cpp b/contrib/llvm-project/lldb/tools/lldb-server/lldb-server.cpp new file mode 100644 index 000000000000..e2e6bfcd8645 --- /dev/null +++ b/contrib/llvm-project/lldb/tools/lldb-server/lldb-server.cpp @@ -0,0 +1,82 @@ +//===-- lldb-server.cpp -----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "SystemInitializerLLGS.h" +#include "lldb/Host/Config.h" +#include "lldb/Initialization/SystemLifetimeManager.h" +#include "lldb/Version/Version.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" + +#include <cstdio> +#include <cstdlib> + +static llvm::ManagedStatic<lldb_private::SystemLifetimeManager> + g_debugger_lifetime; + +static void display_usage(const char *progname) { + fprintf(stderr, "Usage:\n" + " %s v[ersion]\n" + " %s g[dbserver] [options]\n" + " %s p[latform] [options]\n" + "Invoke subcommand for additional help\n", + progname, progname, progname); + exit(0); +} + +// Forward declarations of subcommand main methods. +int main_gdbserver(int argc, char *argv[]); +int main_platform(int argc, char *argv[]); + +namespace llgs { +static void initialize() { + if (auto e = g_debugger_lifetime->Initialize( + std::make_unique<SystemInitializerLLGS>(), nullptr)) + llvm::consumeError(std::move(e)); +} + +static void terminate_debugger() { g_debugger_lifetime->Terminate(); } +} // namespace llgs + +// main +int main(int argc, char *argv[]) { + llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); + llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL + " and include the crash backtrace.\n"); + + int option_error = 0; + const char *progname = argv[0]; + if (argc < 2) { + display_usage(progname); + exit(option_error); + } + + switch (argv[1][0]) { + case 'g': + llgs::initialize(); + main_gdbserver(argc, argv); + llgs::terminate_debugger(); + break; + case 'p': + llgs::initialize(); + main_platform(argc, argv); + llgs::terminate_debugger(); + break; + case 'v': + fprintf(stderr, "%s\n", lldb_private::GetVersion()); + break; + default: + display_usage(progname); + exit(option_error); + } +} |