brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.4 KiB · e1dc15d Raw
496 lines · cpp
1//===-- lldb-gdbserver.cpp --------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <cerrno>10#include <cstdint>11#include <cstdio>12#include <cstdlib>13#include <cstring>14 15#ifndef _WIN3216#include <csignal>17#include <unistd.h>18#endif19 20#include "LLDBServerUtilities.h"21#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h"22#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"23#include "lldb/Host/Config.h"24#include "lldb/Host/ConnectionFileDescriptor.h"25#include "lldb/Host/FileSystem.h"26#include "lldb/Host/Pipe.h"27#include "lldb/Host/common/NativeProcessProtocol.h"28#include "lldb/Host/common/TCPSocket.h"29#include "lldb/Target/Process.h"30#include "lldb/Utility/LLDBLog.h"31#include "lldb/Utility/Status.h"32#include "llvm/ADT/StringRef.h"33#include "llvm/Option/ArgList.h"34#include "llvm/Option/OptTable.h"35#include "llvm/Option/Option.h"36#include "llvm/Support/Errno.h"37#include "llvm/Support/Error.h"38#include "llvm/Support/WithColor.h"39 40#if defined(__linux__)41#include "Plugins/Process/Linux/NativeProcessLinux.h"42#elif defined(__FreeBSD__)43#include "Plugins/Process/FreeBSD/NativeProcessFreeBSD.h"44#elif defined(__NetBSD__)45#include "Plugins/Process/NetBSD/NativeProcessNetBSD.h"46#elif defined(_WIN32)47#include "Plugins/Process/Windows/Common/NativeProcessWindows.h"48#endif49 50#ifndef LLGS_PROGRAM_NAME51#define LLGS_PROGRAM_NAME "lldb-server"52#endif53 54#ifndef LLGS_VERSION_STR55#define LLGS_VERSION_STR "local_build"56#endif57 58using namespace llvm;59using namespace lldb;60using namespace lldb_private;61using namespace lldb_private::lldb_server;62using namespace lldb_private::process_gdb_remote;63 64namespace {65#if defined(__linux__)66typedef process_linux::NativeProcessLinux::Manager NativeProcessManager;67#elif defined(__FreeBSD__)68typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager;69#elif defined(__NetBSD__)70typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager;71#elif defined(_WIN32)72typedef NativeProcessWindows::Manager NativeProcessManager;73#else74// Dummy implementation to make sure the code compiles75class NativeProcessManager : public NativeProcessProtocol::Manager {76public:77  NativeProcessManager(MainLoop &mainloop)78      : NativeProcessProtocol::Manager(mainloop) {}79 80  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>81  Launch(ProcessLaunchInfo &launch_info,82         NativeProcessProtocol::NativeDelegate &native_delegate) override {83    llvm_unreachable("Not implemented");84  }85  llvm::Expected<std::unique_ptr<NativeProcessProtocol>>86  Attach(lldb::pid_t pid,87         NativeProcessProtocol::NativeDelegate &native_delegate) override {88    llvm_unreachable("Not implemented");89  }90};91#endif92}93 94#ifndef _WIN3295// Watch for signals96static int g_sighup_received_count = 0;97 98static void sighup_handler(MainLoopBase &mainloop) {99  ++g_sighup_received_count;100 101  Log *log = GetLog(LLDBLog::Process);102  LLDB_LOGF(log, "lldb-server:%s swallowing SIGHUP (receive count=%d)",103            __FUNCTION__, g_sighup_received_count);104 105  if (g_sighup_received_count >= 2)106    mainloop.RequestTermination();107}108#endif // #ifndef _WIN32109 110void handle_attach_to_pid(GDBRemoteCommunicationServerLLGS &gdb_server,111                          lldb::pid_t pid) {112  Status error = gdb_server.AttachToProcess(pid);113  if (error.Fail()) {114    fprintf(stderr, "error: failed to attach to pid %" PRIu64 ": %s\n", pid,115            error.AsCString());116    exit(1);117  }118}119 120void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server,121                                   const std::string &process_name) {122  // FIXME implement.123}124 125void handle_attach(GDBRemoteCommunicationServerLLGS &gdb_server,126                   const std::string &attach_target) {127  assert(!attach_target.empty() && "attach_target cannot be empty");128 129  // First check if the attach_target is convertible to a long. If so, we'll use130  // it as a pid.131  char *end_p = nullptr;132  const long int pid = strtol(attach_target.c_str(), &end_p, 10);133 134  // We'll call it a match if the entire argument is consumed.135  if (end_p &&136      static_cast<size_t>(end_p - attach_target.c_str()) ==137          attach_target.size())138    handle_attach_to_pid(gdb_server, static_cast<lldb::pid_t>(pid));139  else140    handle_attach_to_process_name(gdb_server, attach_target);141}142 143void handle_launch(GDBRemoteCommunicationServerLLGS &gdb_server,144                   llvm::ArrayRef<llvm::StringRef> Arguments) {145  ProcessLaunchInfo info;146  info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug |147                      eLaunchFlagDisableASLR);148  info.SetArguments(Args(Arguments), true);149 150  llvm::SmallString<64> cwd;151  if (std::error_code ec = llvm::sys::fs::current_path(cwd)) {152    llvm::errs() << "Error getting current directory: " << ec.message() << "\n";153    exit(1);154  }155  FileSpec cwd_spec(cwd);156  FileSystem::Instance().Resolve(cwd_spec);157  info.SetWorkingDirectory(cwd_spec);158  info.GetEnvironment() = Host::GetEnvironment();159 160  gdb_server.SetLaunchInfo(info);161 162  Status error = gdb_server.LaunchProcess();163  if (error.Fail()) {164    llvm::errs() << llvm::formatv("error: failed to launch '{0}': {1}\n",165                                  Arguments[0], error);166    exit(1);167  }168}169 170static Status writeSocketIdToPipe(Pipe &port_pipe,171                                  const std::string &socket_id) {172  // NB: Include the nul character at the end.173  llvm::StringRef buf(socket_id.data(), socket_id.size() + 1);174  while (!buf.empty()) {175    if (llvm::Expected<size_t> written =176            port_pipe.Write(buf.data(), buf.size()))177      buf = buf.drop_front(*written);178    else179      return Status::FromError(written.takeError());180  }181  return Status();182}183 184Status writeSocketIdToPipe(const char *const named_pipe_path,185                           llvm::StringRef socket_id) {186  Pipe port_name_pipe;187  // Wait for 10 seconds for pipe to be opened.188  if (llvm::Error err = port_name_pipe.OpenAsWriter(named_pipe_path,189                                                    std::chrono::seconds{10}))190    return Status::FromError(std::move(err));191 192  return writeSocketIdToPipe(port_name_pipe, socket_id.str());193}194 195Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe,196                           llvm::StringRef socket_id) {197  Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe};198  return writeSocketIdToPipe(port_pipe, socket_id.str());199}200 201void ConnectToRemote(MainLoop &mainloop,202                     GDBRemoteCommunicationServerLLGS &gdb_server,203                     bool reverse_connect, llvm::StringRef host_and_port,204                     const char *const progname, const char *const subcommand,205                     const char *const named_pipe_path, pipe_t unnamed_pipe,206                     shared_fd_t connection_fd) {207  Status error;208 209  std::unique_ptr<Connection> connection_up;210  std::string url;211 212  if (connection_fd != SharedSocket::kInvalidFD) {213#ifdef _WIN32214    NativeSocket sockfd;215    error = SharedSocket::GetNativeSocket(connection_fd, sockfd);216    if (error.Fail()) {217      llvm::errs() << llvm::formatv("error: GetNativeSocket failed: {0}\n",218                                    error.AsCString());219      exit(-1);220    }221    connection_up = std::make_unique<ConnectionFileDescriptor>(222        std::make_unique<TCPSocket>(sockfd, /*should_close=*/true));223#else224    url = llvm::formatv("fd://{0}", connection_fd).str();225 226    // Create the connection.227    ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC);228#endif229  } else if (!host_and_port.empty()) {230    llvm::Expected<std::string> url_exp =231        LLGSArgToURL(host_and_port, reverse_connect);232    if (!url_exp) {233      llvm::errs() << llvm::formatv("error: invalid host:port or URL '{0}': "234                                    "{1}\n",235                                    host_and_port,236                                    llvm::toString(url_exp.takeError()));237      exit(-1);238    }239 240    url = std::move(url_exp.get());241  }242 243  if (!url.empty()) {244    // Create the connection or server.245    std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{246        new ConnectionFileDescriptor};247    auto connection_result = conn_fd_up->Connect(248        url,249        [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) {250          // If we have a named pipe to write the socket id back to, do that251          // now.252          if (named_pipe_path && named_pipe_path[0]) {253            Status error = writeSocketIdToPipe(named_pipe_path, socket_id);254            if (error.Fail())255              llvm::errs() << llvm::formatv(256                  "failed to write to the named pipe '{0}': {1}\n",257                  named_pipe_path, error.AsCString());258          }259          // If we have an unnamed pipe to write the socket id back to, do260          // that now.261          else if (unnamed_pipe != LLDB_INVALID_PIPE) {262            Status error = writeSocketIdToPipe(unnamed_pipe, socket_id);263            if (error.Fail())264              llvm::errs() << llvm::formatv(265                  "failed to write to the unnamed pipe: {0}\n", error);266          }267        },268        &error);269 270    if (error.Fail()) {271      llvm::errs() << llvm::formatv(272          "error: failed to connect to client at '{0}': {1}\n", url, error);273      exit(-1);274    }275    if (connection_result != eConnectionStatusSuccess) {276      llvm::errs() << llvm::formatv(277          "error: failed to connect to client at '{0}' "278          "(connection status: {1})\n",279          url, static_cast<int>(connection_result));280      exit(-1);281    }282    connection_up = std::move(conn_fd_up);283  }284  error = gdb_server.InitializeConnection(std::move(connection_up));285  if (error.Fail()) {286    llvm::errs() << llvm::formatv("failed to initialize connection\n", error);287    exit(-1);288  }289  llvm::outs() << "Connection established.\n";290}291 292namespace {293using namespace llvm::opt;294 295enum ID {296  OPT_INVALID = 0, // This is not an option ID.297#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),298#include "LLGSOptions.inc"299#undef OPTION300};301 302#define OPTTABLE_STR_TABLE_CODE303#include "LLGSOptions.inc"304#undef OPTTABLE_STR_TABLE_CODE305 306#define OPTTABLE_PREFIXES_TABLE_CODE307#include "LLGSOptions.inc"308#undef OPTTABLE_PREFIXES_TABLE_CODE309 310static constexpr opt::OptTable::Info InfoTable[] = {311#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),312#include "LLGSOptions.inc"313#undef OPTION314};315 316class LLGSOptTable : public opt::GenericOptTable {317public:318  LLGSOptTable()319      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}320 321  void PrintHelp(llvm::StringRef Name) {322    std::string Usage =323        (Name + " [options] [[host]:port] [[--] program args...]").str();324    OptTable::printHelp(llvm::outs(), Usage.c_str(), "lldb-server");325    llvm::outs() << R"(326DESCRIPTION327  lldb-server connects to the LLDB client, which drives the debugging session.328  If no connection options are given, the [host]:port argument must be present329  and will denote the address that lldb-server will listen on. [host] defaults330  to "localhost" if empty. Port can be zero, in which case the port number will331  be chosen dynamically and written to destinations given by --named-pipe and332  --pipe arguments.333 334  If no target is selected at startup, lldb-server can be directed by the LLDB335  client to launch or attach to a process.336 337)";338  }339};340} // namespace341 342int main_gdbserver(int argc, char *argv[]) {343  Status error;344  MainLoop mainloop;345#ifndef _WIN32346  // Setup signal handlers first thing.347  signal(SIGPIPE, SIG_IGN);348  MainLoop::SignalHandleUP sighup_handle =349      mainloop.RegisterSignal(SIGHUP, sighup_handler, error);350#endif351 352  const char *progname = argv[0];353  const char *subcommand = argv[1];354  std::string attach_target;355  std::string named_pipe_path;356  std::string log_file;357  StringRef358      log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"359  lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE;360  bool reverse_connect = false;361  shared_fd_t connection_fd = SharedSocket::kInvalidFD;362 363  // ProcessLaunchInfo launch_info;364  ProcessAttachInfo attach_info;365 366  LLGSOptTable Opts;367  llvm::BumpPtrAllocator Alloc;368  llvm::StringSaver Saver(Alloc);369  bool HasError = false;370  opt::InputArgList Args = Opts.parseArgs(argc - 1, argv + 1, OPT_UNKNOWN,371                                          Saver, [&](llvm::StringRef Msg) {372                                            WithColor::error() << Msg << "\n";373                                            HasError = true;374                                          });375  std::string Name =376      (llvm::sys::path::filename(argv[0]) + " g[dbserver]").str();377  std::string HelpText =378      "Use '" + Name + " --help' for a complete list of options.\n";379  if (HasError) {380    llvm::errs() << HelpText;381    return 1;382  }383 384  if (Args.hasArg(OPT_help)) {385    Opts.PrintHelp(Name);386    return 0;387  }388 389#ifndef _WIN32390  if (Args.hasArg(OPT_setsid)) {391    // Put llgs into a new session. Terminals group processes392    // into sessions and when a special terminal key sequences393    // (like control+c) are typed they can cause signals to go out to394    // all processes in a session. Using this --setsid (-S) option395    // will cause debugserver to run in its own sessions and be free396    // from such issues.397    //398    // This is useful when llgs is spawned from a command399    // line application that uses llgs to do the debugging,400    // yet that application doesn't want llgs receiving the401    // signals sent to the session (i.e. dying when anyone hits ^C).402    {403      const ::pid_t new_sid = setsid();404      if (new_sid == -1) {405        WithColor::warning()406            << llvm::formatv("failed to set new session id for {0} ({1})\n",407                             LLGS_PROGRAM_NAME, llvm::sys::StrError());408      }409    }410  }411#endif412 413  log_file = Args.getLastArgValue(OPT_log_file).str();414  log_channels = Args.getLastArgValue(OPT_log_channels);415  named_pipe_path = Args.getLastArgValue(OPT_named_pipe).str();416  reverse_connect = Args.hasArg(OPT_reverse_connect);417  attach_target = Args.getLastArgValue(OPT_attach).str();418  if (Args.hasArg(OPT_pipe)) {419    uint64_t Arg;420    if (!llvm::to_integer(Args.getLastArgValue(OPT_pipe), Arg)) {421      WithColor::error() << "invalid '--pipe' argument\n" << HelpText;422      return 1;423    }424    unnamed_pipe = (pipe_t)Arg;425  }426  if (Args.hasArg(OPT_fd)) {427    int64_t fd;428    if (!llvm::to_integer(Args.getLastArgValue(OPT_fd), fd)) {429      WithColor::error() << "invalid '--fd' argument\n" << HelpText;430      return 1;431    }432    connection_fd = (shared_fd_t)fd;433  }434 435  if (!LLDBServerUtilities::SetupLogging(436          log_file, log_channels,437          LLDB_LOG_OPTION_PREPEND_TIMESTAMP |438              LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION))439    return -1;440 441  std::vector<llvm::StringRef> Inputs;442  for (opt::Arg *Arg : Args.filtered(OPT_INPUT))443    Inputs.push_back(Arg->getValue());444  if (opt::Arg *Arg = Args.getLastArg(OPT_REM)) {445    for (const char *Val : Arg->getValues())446      Inputs.push_back(Val);447  }448  if (Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {449    WithColor::error() << "no connection arguments\n" << HelpText;450    return 1;451  }452 453  NativeProcessManager manager(mainloop);454  GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager);455 456  llvm::StringRef host_and_port;457  if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) {458    host_and_port = Inputs.front();459    Inputs.erase(Inputs.begin());460  }461 462  // Any arguments left over are for the program that we need to launch. If463  // there464  // are no arguments, then the GDB server will start up and wait for an 'A'465  // packet466  // to launch a program, or a vAttach packet to attach to an existing process,467  // unless468  // explicitly asked to attach with the --attach={pid|program_name} form.469  if (!attach_target.empty())470    handle_attach(gdb_server, attach_target);471  else if (!Inputs.empty())472    handle_launch(gdb_server, Inputs);473 474  // Print version info.475  printf("%s-%s\n", LLGS_PROGRAM_NAME, LLGS_VERSION_STR);476 477  ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port,478                  progname, subcommand, named_pipe_path.c_str(),479                  unnamed_pipe, connection_fd);480 481  if (!gdb_server.IsConnected()) {482    fprintf(stderr, "no connection information provided, unable to run\n");483    return 1;484  }485 486  Status ret = mainloop.Run();487  if (ret.Fail()) {488    fprintf(stderr, "lldb-server terminating due to error: %s\n",489            ret.AsCString());490    return 1;491  }492  fprintf(stderr, "lldb-server exiting...\n");493 494  return 0;495}496