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