Path: blob/main/contrib/llvm-project/lldb/tools/lldb-server/lldb-gdbserver.cpp
34879 views
//===-- lldb-gdbserver.cpp --------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include <cerrno>9#include <cstdint>10#include <cstdio>11#include <cstdlib>12#include <cstring>1314#ifndef _WIN3215#include <csignal>16#include <unistd.h>17#endif1819#include "LLDBServerUtilities.h"20#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"21#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"22#include "lldb/Host/Config.h"23#include "lldb/Host/ConnectionFileDescriptor.h"24#include "lldb/Host/FileSystem.h"25#include "lldb/Host/Pipe.h"26#include "lldb/Host/Socket.h"27#include "lldb/Host/common/NativeProcessProtocol.h"28#include "lldb/Target/Process.h"29#include "lldb/Utility/LLDBLog.h"30#include "lldb/Utility/Status.h"31#include "llvm/ADT/StringRef.h"32#include "llvm/Option/ArgList.h"33#include "llvm/Option/OptTable.h"34#include "llvm/Option/Option.h"35#include "llvm/Support/Errno.h"36#include "llvm/Support/Error.h"37#include "llvm/Support/WithColor.h"3839#if defined(__linux__)40#include "Plugins/Process/Linux/NativeProcessLinux.h"41#elif defined(__FreeBSD__)42#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"43#elif defined(__NetBSD__)44#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"45#elif defined(_WIN32)46#include "Plugins/Process/Windows/Common/NativeProcessWindows.h"47#endif4849#ifndef LLGS_PROGRAM_NAME50#define LLGS_PROGRAM_NAME "lldb-server"51#endif5253#ifndef LLGS_VERSION_STR54#define LLGS_VERSION_STR "local_build"55#endif5657using namespace llvm;58using namespace lldb;59using namespace lldb_private;60using namespace lldb_private::lldb_server;61using namespace lldb_private::process_gdb_remote;6263namespace {64#if defined(__linux__)65typedef process_linux::NativeProcessLinux::Manager NativeProcessManager;66#elif defined(__FreeBSD__)67typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager;68#elif defined(__NetBSD__)69typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager;70#elif defined(_WIN32)71typedef NativeProcessWindows::Manager NativeProcessManager;72#else73// Dummy implementation to make sure the code compiles74class NativeProcessManager : public NativeProcessProtocol::Manager {75public:76NativeProcessManager(MainLoop &mainloop)77: NativeProcessProtocol::Manager(mainloop) {}7879llvm::Expected<std::unique_ptr<NativeProcessProtocol>>80Launch(ProcessLaunchInfo &launch_info,81NativeProcessProtocol::NativeDelegate &native_delegate) override {82llvm_unreachable("Not implemented");83}84llvm::Expected<std::unique_ptr<NativeProcessProtocol>>85Attach(lldb::pid_t pid,86NativeProcessProtocol::NativeDelegate &native_delegate) override {87llvm_unreachable("Not implemented");88}89};90#endif91}9293#ifndef _WIN3294// Watch for signals95static int g_sighup_received_count = 0;9697static void sighup_handler(MainLoopBase &mainloop) {98++g_sighup_received_count;99100Log *log = GetLog(LLDBLog::Process);101LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)",102__FUNCTION__, g_sighup_received_count);103104if (g_sighup_received_count >= 2)105mainloop.RequestTermination();106}107#endif // #ifndef _WIN32108109void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,110lldb::pid_t pid) {111Status error = gdb_server.AttachToProcess(pid);112if (error.Fail()) {113fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid,114error.AsCString());115exit(1);116}117}118119void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,120const std::string &process_name) {121// FIXME implement.122}123124void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,125const std::string &attach_target) {126assert(!attach_target.empty() && "attach_target cannot be empty");127128// First check if the attach_target is convertible to a long. If so, we'll use129// it as a pid.130char *end_p = nullptr;131const long int pid = strtol(attach_target.c_str(), &end_p, 10);132133// We'll call it a match if the entire argument is consumed.134if (end_p &&135static_cast<size_t>(end_p - attach_target.c_str()) ==136attach_target.size())137handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));138else139handle_attach_to_process_name(gdb_server, attach_target);140}141142void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,143llvm::ArrayRef<llvm::StringRef> Arguments) {144ProcessLaunchInfo info;145info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |146eLaunchFlagDisableASLR);147info.SetArguments(Args(Arguments), true);148149llvm::SmallString<64> cwd;150if (std::error_code ec = llvm::sys::fs::current_path(cwd)) {151llvm::errs() << "Error getting current directory: " << ec.message() << "\n";152exit(1);153}154FileSpec cwd_spec(cwd);155FileSystem::Instance().Resolve(cwd_spec);156info.SetWorkingDirectory(cwd_spec);157info.GetEnvironment() = Host::GetEnvironment();158159gdb_server.SetLaunchInfo(info);160161Status error = gdb_server.LaunchProcess();162if (error.Fail()) {163llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n",164Arguments[0], error);165exit(1);166}167}168169Status writeSocketIdToPipe(Pipe &port_pipe, llvm::StringRef socket_id) {170size_t bytes_written = 0;171// Write the port number as a C string with the NULL terminator.172return port_pipe.Write(socket_id.data(), socket_id.size() + 1, bytes_written);173}174175Status writeSocketIdToPipe(const char *const named_pipe_path,176llvm::StringRef socket_id) {177Pipe port_name_pipe;178// Wait for 10 seconds for pipe to be opened.179auto error = port_name_pipe.OpenAsWriterWithTimeout(named_pipe_path, false,180std::chrono::seconds{10});181if (error.Fail())182return error;183return writeSocketIdToPipe(port_name_pipe, socket_id);184}185186Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,187llvm::StringRef socket_id) {188Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};189return writeSocketIdToPipe(port_pipe, socket_id);190}191192void ConnectToRemote(MainLoop &mainloop,193GDBRemoteCommunicationServerLLGS &gdb_server,194bool reverse_connect, llvm::StringRef host_and_port,195const char *const progname, const char *const subcommand,196const char *const named_pipe_path, pipe_t unnamed_pipe,197int connection_fd) {198Status error;199200std::unique_ptr<Connection> connection_up;201std::string url;202203if (connection_fd != -1) {204url = llvm::formatv("fd://{0}", connection_fd).str();205206// Create the connection.207#if LLDB_ENABLE_POSIX && !defined _WIN32208::fcntl(connection_fd, F_SETFD, FD_CLOEXEC);209#endif210} else if (!host_and_port.empty()) {211llvm::Expected<std::string> url_exp =212LLGSArgToURL(host_and_port, reverse_connect);213if (!url_exp) {214llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': "215"{1}\n",216host_and_port,217llvm::toString(url_exp.takeError()));218exit(-1);219}220221url = std::move(url_exp.get());222}223224if (!url.empty()) {225// Create the connection or server.226std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{227new ConnectionFileDescriptor};228auto connection_result = conn_fd_up->Connect(229url,230[named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) {231// If we have a named pipe to write the socket id back to, do that232// now.233if (named_pipe_path && named_pipe_path[0]) {234Status error = writeSocketIdToPipe(named_pipe_path, socket_id);235if (error.Fail())236llvm::errs() << llvm::formatv(237"failed to write to the named pipe '{0}': {1}\n",238named_pipe_path, error.AsCString());239}240// If we have an unnamed pipe to write the socket id back to, do241// that now.242else if (unnamed_pipe != LLDB_INVALID_PIPE) {243Status error = writeSocketIdToPipe(unnamed_pipe, socket_id);244if (error.Fail())245llvm::errs() << llvm::formatv(246"failed to write to the unnamed pipe: {0}\n", error);247}248},249&error);250251if (error.Fail()) {252llvm::errs() << llvm::formatv(253"error: failed to connect to client at '{0}': {1}\n", url, error);254exit(-1);255}256if (connection_result != eConnectionStatusSuccess) {257llvm::errs() << llvm::formatv(258"error: failed to connect to client at '{0}' "259"(connection status: {1})\n",260url, static_cast<int>(connection_result));261exit(-1);262}263connection_up = std::move(conn_fd_up);264}265error = gdb_server.InitializeConnection(std::move(connection_up));266if (error.Fail()) {267llvm::errs() << llvm::formatv("failed to initialize connection\n", error);268exit(-1);269}270llvm::outs() << "Connection established.\n";271}272273namespace {274using namespace llvm::opt;275276enum ID {277OPT_INVALID = 0, // This is not an option ID.278#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),279#include "LLGSOptions.inc"280#undef OPTION281};282283#define PREFIX(NAME, VALUE) \284constexpr llvm::StringLiteral NAME##_init[] = VALUE; \285constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \286NAME##_init, std::size(NAME##_init) - 1);287#include "LLGSOptions.inc"288#undef PREFIX289290static constexpr opt::OptTable::Info InfoTable[] = {291#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),292#include "LLGSOptions.inc"293#undef OPTION294};295296class LLGSOptTable : public opt::GenericOptTable {297public:298LLGSOptTable() : opt::GenericOptTable(InfoTable) {}299300void PrintHelp(llvm::StringRef Name) {301std::string Usage =302(Name + " [options] [[host]:port] [[--] program args...]").str();303OptTable::printHelp(llvm::outs(), Usage.c_str(), "lldb-server");304llvm::outs() << R"(305DESCRIPTION306lldb-server connects to the LLDB client, which drives the debugging session.307If no connection options are given, the [host]:port argument must be present308and will denote the address that lldb-server will listen on. [host] defaults309to "localhost" if empty. Port can be zero, in which case the port number will310be chosen dynamically and written to destinations given by --named-pipe and311--pipe arguments.312313If no target is selected at startup, lldb-server can be directed by the LLDB314client to launch or attach to a process.315316)";317}318};319} // namespace320321int main_gdbserver(int argc, char *argv[]) {322Status error;323MainLoop mainloop;324#ifndef _WIN32325// Setup signal handlers first thing.326signal(SIGPIPE, SIG_IGN);327MainLoop::SignalHandleUP sighup_handle =328mainloop.RegisterSignal(SIGHUP, sighup_handler, error);329#endif330331const char *progname = argv[0];332const char *subcommand = argv[1];333std::string attach_target;334std::string named_pipe_path;335std::string log_file;336StringRef337log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"338lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE;339bool reverse_connect = false;340int connection_fd = -1;341342// ProcessLaunchInfo launch_info;343ProcessAttachInfo attach_info;344345LLGSOptTable Opts;346llvm::BumpPtrAllocator Alloc;347llvm::StringSaver Saver(Alloc);348bool HasError = false;349opt::InputArgList Args = Opts.parseArgs(argc - 1, argv + 1, OPT_UNKNOWN,350Saver, [&](llvm::StringRef Msg) {351WithColor::error() << Msg << "\n";352HasError = true;353});354std::string Name =355(llvm::sys::path::filename(argv[0]) + " g[dbserver]").str();356std::string HelpText =357"Use '" + Name + " --help' for a complete list of options.\n";358if (HasError) {359llvm::errs() << HelpText;360return 1;361}362363if (Args.hasArg(OPT_help)) {364Opts.PrintHelp(Name);365return 0;366}367368#ifndef _WIN32369if (Args.hasArg(OPT_setsid)) {370// Put llgs into a new session. Terminals group processes371// into sessions and when a special terminal key sequences372// (like control+c) are typed they can cause signals to go out to373// all processes in a session. Using this --setsid (-S) option374// will cause debugserver to run in its own sessions and be free375// from such issues.376//377// This is useful when llgs is spawned from a command378// line application that uses llgs to do the debugging,379// yet that application doesn't want llgs receiving the380// signals sent to the session (i.e. dying when anyone hits ^C).381{382const ::pid_t new_sid = setsid();383if (new_sid == -1) {384WithColor::warning()385<< llvm::formatv("failed to set new session id for {0} ({1})\n",386LLGS_PROGRAM_NAME, llvm::sys::StrError());387}388}389}390#endif391392log_file = Args.getLastArgValue(OPT_log_file).str();393log_channels = Args.getLastArgValue(OPT_log_channels);394named_pipe_path = Args.getLastArgValue(OPT_named_pipe).str();395reverse_connect = Args.hasArg(OPT_reverse_connect);396attach_target = Args.getLastArgValue(OPT_attach).str();397if (Args.hasArg(OPT_pipe)) {398uint64_t Arg;399if (!llvm::to_integer(Args.getLastArgValue(OPT_pipe), Arg)) {400WithColor::error() << "invalid '--pipe' argument\n" << HelpText;401return 1;402}403unnamed_pipe = (pipe_t)Arg;404}405if (Args.hasArg(OPT_fd)) {406if (!llvm::to_integer(Args.getLastArgValue(OPT_fd), connection_fd)) {407WithColor::error() << "invalid '--fd' argument\n" << HelpText;408return 1;409}410}411412if (!LLDBServerUtilities::SetupLogging(413log_file, log_channels,414LLDB_LOG_OPTION_PREPEND_TIMESTAMP |415LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION))416return -1;417418std::vector<llvm::StringRef> Inputs;419for (opt::Arg *Arg : Args.filtered(OPT_INPUT))420Inputs.push_back(Arg->getValue());421if (opt::Arg *Arg = Args.getLastArg(OPT_REM)) {422for (const char *Val : Arg->getValues())423Inputs.push_back(Val);424}425if (Inputs.empty() && connection_fd == -1) {426WithColor::error() << "no connection arguments\n" << HelpText;427return 1;428}429430NativeProcessManager manager(mainloop);431GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);432433llvm::StringRef host_and_port;434if (!Inputs.empty()) {435host_and_port = Inputs.front();436Inputs.erase(Inputs.begin());437}438439// Any arguments left over are for the program that we need to launch. If440// there441// are no arguments, then the GDB server will start up and wait for an 'A'442// packet443// to launch a program, or a vAttach packet to attach to an existing process,444// unless445// explicitly asked to attach with the --attach={pid|program_name} form.446if (!attach_target.empty())447handle_attach(gdb_server, attach_target);448else if (!Inputs.empty())449handle_launch(gdb_server, Inputs);450451// Print version info.452printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);453454ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,455progname, subcommand, named_pipe_path.c_str(),456unnamed_pipe, connection_fd);457458if (!gdb_server.IsConnected()) {459fprintf(stderr, "no connection information provided, unable to run\n");460return 1;461}462463Status ret = mainloop.Run();464if (ret.Fail()) {465fprintf(stderr, "lldb-server terminating due to error: %s\n",466ret.AsCString());467return 1;468}469fprintf(stderr, "lldb-server exiting...\n");470471return 0;472}473474475