|  | //===-- lldb-gdbserver.cpp --------------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | // C Includes | 
|  | #include <errno.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #ifndef _WIN32 | 
|  | #include <signal.h> | 
|  | #include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | // C++ Includes | 
|  |  | 
|  |  | 
|  | #include "Acceptor.h" | 
|  | #include "LLDBServerUtilities.h" | 
|  | #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" | 
|  | #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" | 
|  | #include "lldb/Core/PluginManager.h" | 
|  | #include "lldb/Host/ConnectionFileDescriptor.h" | 
|  | #include "lldb/Host/HostGetOpt.h" | 
|  | #include "lldb/Host/OptionParser.h" | 
|  | #include "lldb/Host/Pipe.h" | 
|  | #include "lldb/Host/Socket.h" | 
|  | #include "lldb/Host/StringConvert.h" | 
|  | #include "lldb/Host/common/NativeProcessProtocol.h" | 
|  | #include "lldb/Utility/Status.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "llvm/Support/Errno.h" | 
|  |  | 
|  | #if defined(__linux__) | 
|  | #include "Plugins/Process/Linux/NativeProcessLinux.h" | 
|  | #elif defined(__NetBSD__) | 
|  | #include "Plugins/Process/NetBSD/NativeProcessNetBSD.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::Factory NativeProcessFactory; | 
|  | #elif defined(__NetBSD__) | 
|  | typedef process_netbsd::NativeProcessNetBSD::Factory NativeProcessFactory; | 
|  | #else | 
|  | // Dummy implementation to make sure the code compiles | 
|  | class NativeProcessFactory : public NativeProcessProtocol::Factory { | 
|  | public: | 
|  | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> | 
|  | Launch(ProcessLaunchInfo &launch_info, | 
|  | NativeProcessProtocol::NativeDelegate &delegate, | 
|  | MainLoop &mainloop) const override { | 
|  | llvm_unreachable("Not implemented"); | 
|  | } | 
|  | llvm::Expected<std::unique_ptr<NativeProcessProtocol>> | 
|  | Attach(lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &delegate, | 
|  | MainLoop &mainloop) const override { | 
|  | llvm_unreachable("Not implemented"); | 
|  | } | 
|  | }; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // option descriptors for getopt_long_only() | 
|  | //---------------------------------------------------------------------- | 
|  |  | 
|  | static int g_debug = 0; | 
|  | static int g_verbose = 0; | 
|  |  | 
|  | static struct option g_long_options[] = { | 
|  | {"debug", no_argument, &g_debug, 1}, | 
|  | {"verbose", no_argument, &g_verbose, 1}, | 
|  | {"log-file", required_argument, NULL, 'l'}, | 
|  | {"log-channels", required_argument, NULL, 'c'}, | 
|  | {"attach", required_argument, NULL, 'a'}, | 
|  | {"named-pipe", required_argument, NULL, 'N'}, | 
|  | {"pipe", required_argument, NULL, 'U'}, | 
|  | {"native-regs", no_argument, NULL, | 
|  | 'r'}, // Specify to use the native registers instead of the gdb defaults | 
|  | // for the architecture.  NOTE: this is a do-nothing arg as it's | 
|  | // behavior is default now.  FIXME remove call from lldb-platform. | 
|  | {"reverse-connect", no_argument, NULL, | 
|  | 'R'}, // Specifies that llgs attaches to the client address:port rather | 
|  | // than llgs listening for a connection from address on port. | 
|  | {"setsid", no_argument, NULL, | 
|  | 'S'}, // Call setsid() to make llgs run in its own session. | 
|  | {"fd", required_argument, NULL, 'F'}, | 
|  | {NULL, 0, NULL, 0}}; | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Watch for signals | 
|  | //---------------------------------------------------------------------- | 
|  | static int g_sighup_received_count = 0; | 
|  |  | 
|  | #ifndef _WIN32 | 
|  | static void sighup_handler(MainLoopBase &mainloop) { | 
|  | ++g_sighup_received_count; | 
|  |  | 
|  | Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); | 
|  | if (log) | 
|  | log->Printf("lldb-server:%s swallowing SIGHUP (receive count=%d)", | 
|  | __FUNCTION__, g_sighup_received_count); | 
|  |  | 
|  | if (g_sighup_received_count >= 2) | 
|  | mainloop.RequestTermination(); | 
|  | } | 
|  | #endif // #ifndef _WIN32 | 
|  |  | 
|  | 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] " | 
|  | "[--setsid] " | 
|  | "[--fd file-descriptor]" | 
|  | "[--named-pipe named-pipe-path] " | 
|  | "[--native-regs] " | 
|  | "[--attach pid] " | 
|  | "[[HOST]:PORT] " | 
|  | "[-- PROGRAM ARG1 ARG2 ...]\n", | 
|  | progname, subcommand); | 
|  | } | 
|  |  | 
|  | 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, int argc, | 
|  | const char *const argv[]) { | 
|  | ProcessLaunchInfo info; | 
|  | info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | | 
|  | eLaunchFlagDisableASLR); | 
|  | info.SetArguments(const_cast<const char **>(argv), 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); | 
|  | } | 
|  | info.SetWorkingDirectory(FileSpec(cwd, true)); | 
|  | 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", | 
|  | argv[0], error); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | Status writeSocketIdToPipe(Pipe &port_pipe, const std::string &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.c_str(), socket_id.size() + 1, | 
|  | bytes_written); | 
|  | } | 
|  |  | 
|  | Status writeSocketIdToPipe(const char *const named_pipe_path, | 
|  | const std::string &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(int unnamed_pipe_fd, const std::string &socket_id) { | 
|  | #if defined(_WIN32) | 
|  | return Status("Unnamed pipes are not supported on Windows."); | 
|  | #else | 
|  | Pipe port_pipe{Pipe::kInvalidDescriptor, unnamed_pipe_fd}; | 
|  | return writeSocketIdToPipe(port_pipe, socket_id); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void ConnectToRemote(MainLoop &mainloop, | 
|  | GDBRemoteCommunicationServerLLGS &gdb_server, | 
|  | bool reverse_connect, const char *const host_and_port, | 
|  | const char *const progname, const char *const subcommand, | 
|  | const char *const named_pipe_path, int unnamed_pipe_fd, | 
|  | int connection_fd) { | 
|  | Status error; | 
|  |  | 
|  | std::unique_ptr<Connection> connection_up; | 
|  | if (connection_fd != -1) { | 
|  | // Build the connection string. | 
|  | char connection_url[512]; | 
|  | snprintf(connection_url, sizeof(connection_url), "fd://%d", connection_fd); | 
|  |  | 
|  | // Create the connection. | 
|  | #if !defined LLDB_DISABLE_POSIX && !defined _WIN32 | 
|  | ::fcntl(connection_fd, F_SETFD, FD_CLOEXEC); | 
|  | #endif | 
|  | connection_up.reset(new ConnectionFileDescriptor); | 
|  | auto connection_result = connection_up->Connect(connection_url, &error); | 
|  | if (connection_result != eConnectionStatusSuccess) { | 
|  | fprintf(stderr, "error: failed to connect to client at '%s' " | 
|  | "(connection status: %d)\n", | 
|  | connection_url, static_cast<int>(connection_result)); | 
|  | exit(-1); | 
|  | } | 
|  | if (error.Fail()) { | 
|  | fprintf(stderr, "error: failed to connect to client at '%s': %s\n", | 
|  | connection_url, error.AsCString()); | 
|  | exit(-1); | 
|  | } | 
|  | } else if (host_and_port && host_and_port[0]) { | 
|  | // Parse out host and port. | 
|  | std::string final_host_and_port; | 
|  | std::string connection_host; | 
|  | std::string connection_port; | 
|  | uint32_t connection_portno = 0; | 
|  |  | 
|  | // If host_and_port starts with ':', default the host to be "localhost" and | 
|  | // expect the remainder to be the port. | 
|  | if (host_and_port[0] == ':') | 
|  | final_host_and_port.append("localhost"); | 
|  | final_host_and_port.append(host_and_port); | 
|  |  | 
|  | const std::string::size_type colon_pos = final_host_and_port.find(':'); | 
|  | if (colon_pos != std::string::npos) { | 
|  | connection_host = final_host_and_port.substr(0, colon_pos); | 
|  | connection_port = final_host_and_port.substr(colon_pos + 1); | 
|  | connection_portno = StringConvert::ToUInt32(connection_port.c_str(), 0); | 
|  | } | 
|  |  | 
|  |  | 
|  | if (reverse_connect) { | 
|  | // llgs will connect to the gdb-remote client. | 
|  |  | 
|  | // Ensure we have a port number for the connection. | 
|  | if (connection_portno == 0) { | 
|  | fprintf(stderr, "error: port number must be specified on when using " | 
|  | "reverse connect\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | // Build the connection string. | 
|  | char connection_url[512]; | 
|  | snprintf(connection_url, sizeof(connection_url), "connect://%s", | 
|  | final_host_and_port.c_str()); | 
|  |  | 
|  | // Create the connection. | 
|  | connection_up.reset(new ConnectionFileDescriptor); | 
|  | auto connection_result = connection_up->Connect(connection_url, &error); | 
|  | if (connection_result != eConnectionStatusSuccess) { | 
|  | fprintf(stderr, "error: failed to connect to client at '%s' " | 
|  | "(connection status: %d)\n", | 
|  | connection_url, static_cast<int>(connection_result)); | 
|  | exit(-1); | 
|  | } | 
|  | if (error.Fail()) { | 
|  | fprintf(stderr, "error: failed to connect to client at '%s': %s\n", | 
|  | connection_url, error.AsCString()); | 
|  | exit(-1); | 
|  | } | 
|  | } else { | 
|  | std::unique_ptr<Acceptor> acceptor_up( | 
|  | Acceptor::Create(final_host_and_port, false, error)); | 
|  | if (error.Fail()) { | 
|  | fprintf(stderr, "failed to create acceptor: %s\n", error.AsCString()); | 
|  | exit(1); | 
|  | } | 
|  | error = acceptor_up->Listen(1); | 
|  | if (error.Fail()) { | 
|  | fprintf(stderr, "failed to listen: %s\n", error.AsCString()); | 
|  | exit(1); | 
|  | } | 
|  | const std::string socket_id = acceptor_up->GetLocalSocketId(); | 
|  | if (!socket_id.empty()) { | 
|  | // If we have a named pipe to write the socket id back to, do that now. | 
|  | if (named_pipe_path && named_pipe_path[0]) { | 
|  | error = writeSocketIdToPipe(named_pipe_path, socket_id); | 
|  | if (error.Fail()) | 
|  | fprintf(stderr, "failed to write to the named pipe \'%s\': %s\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_fd >= 0) { | 
|  | error = writeSocketIdToPipe(unnamed_pipe_fd, socket_id); | 
|  | if (error.Fail()) | 
|  | fprintf(stderr, "failed to write to the unnamed pipe: %s\n", | 
|  | error.AsCString()); | 
|  | } | 
|  | } else { | 
|  | fprintf(stderr, | 
|  | "unable to get the socket id for the listening connection\n"); | 
|  | } | 
|  |  | 
|  | Connection *conn = nullptr; | 
|  | error = acceptor_up->Accept(false, conn); | 
|  | if (error.Fail()) { | 
|  | printf("failed to accept new connection: %s\n", error.AsCString()); | 
|  | exit(1); | 
|  | } | 
|  | connection_up.reset(conn); | 
|  | } | 
|  | } | 
|  | error = gdb_server.InitializeConnection(std::move(connection_up)); | 
|  | if (error.Fail()) { | 
|  | fprintf(stderr, "Failed to initialize connection: %s\n", | 
|  | error.AsCString()); | 
|  | exit(-1); | 
|  | } | 
|  | printf("Connection established.\n"); | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // main | 
|  | //---------------------------------------------------------------------- | 
|  | 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]; | 
|  | argc--; | 
|  | argv++; | 
|  | int long_option_index = 0; | 
|  | int ch; | 
|  | 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" | 
|  | int unnamed_pipe_fd = -1; | 
|  | bool reverse_connect = false; | 
|  | int connection_fd = -1; | 
|  |  | 
|  | // ProcessLaunchInfo launch_info; | 
|  | ProcessAttachInfo attach_info; | 
|  |  | 
|  | bool show_usage = false; | 
|  | int option_error = 0; | 
|  | #if __GLIBC__ | 
|  | optind = 0; | 
|  | #else | 
|  | optreset = 1; | 
|  | optind = 1; | 
|  | #endif | 
|  |  | 
|  | std::string short_options(OptionParser::GetShortOptionString(g_long_options)); | 
|  |  | 
|  | 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': // 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 'N': // named pipe | 
|  | if (optarg && optarg[0]) | 
|  | named_pipe_path = optarg; | 
|  | break; | 
|  |  | 
|  | case 'U': // unnamed pipe | 
|  | if (optarg && optarg[0]) | 
|  | unnamed_pipe_fd = StringConvert::ToUInt32(optarg, -1); | 
|  | break; | 
|  |  | 
|  | case 'r': | 
|  | // Do nothing, native regs is the default these days | 
|  | break; | 
|  |  | 
|  | case 'R': | 
|  | reverse_connect = true; | 
|  | break; | 
|  |  | 
|  | case 'F': | 
|  | connection_fd = StringConvert::ToUInt32(optarg, -1); | 
|  | break; | 
|  |  | 
|  | #ifndef _WIN32 | 
|  | case 'S': | 
|  | // 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) { | 
|  | llvm::errs() << llvm::formatv( | 
|  | "failed to set new session id for {0} ({1})\n", LLGS_PROGRAM_NAME, | 
|  | llvm::sys::StrError()); | 
|  | } | 
|  | } | 
|  | break; | 
|  | #endif | 
|  |  | 
|  | case 'a': // attach {pid|process_name} | 
|  | if (optarg && optarg[0]) | 
|  | attach_target = optarg; | 
|  | break; | 
|  |  | 
|  | case 'h': /* fall-through is intentional */ | 
|  | case '?': | 
|  | show_usage = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (show_usage || option_error) { | 
|  | display_usage(progname, subcommand); | 
|  | exit(option_error); | 
|  | } | 
|  |  | 
|  | if (!LLDBServerUtilities::SetupLogging( | 
|  | log_file, log_channels, | 
|  | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | | 
|  | LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) | 
|  | return -1; | 
|  |  | 
|  | Log *log(lldb_private::GetLogIfAnyCategoriesSet(GDBR_LOG_PROCESS)); | 
|  | if (log) { | 
|  | log->Printf("lldb-server launch"); | 
|  | for (int i = 0; i < argc; i++) { | 
|  | log->Printf("argv[%i] = '%s'", i, argv[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Skip any options we consumed with getopt_long_only. | 
|  | argc -= optind; | 
|  | argv += optind; | 
|  |  | 
|  | if (argc == 0 && connection_fd == -1) { | 
|  | fputs("No arguments\n", stderr); | 
|  | display_usage(progname, subcommand); | 
|  | exit(255); | 
|  | } | 
|  |  | 
|  | NativeProcessFactory factory; | 
|  | GDBRemoteCommunicationServerLLGS gdb_server(mainloop, factory); | 
|  |  | 
|  | const char *const host_and_port = argv[0]; | 
|  | argc -= 1; | 
|  | argv += 1; | 
|  |  | 
|  | // 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 (argc > 0) | 
|  | handle_launch(gdb_server, argc, argv); | 
|  |  | 
|  | // Print version info. | 
|  | printf("%s-%s", LLGS_PROGRAM_NAME, LLGS_VERSION_STR); | 
|  |  | 
|  | ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, | 
|  | progname, subcommand, named_pipe_path.c_str(), | 
|  | unnamed_pipe_fd, connection_fd); | 
|  |  | 
|  | if (!gdb_server.IsConnected()) { | 
|  | fprintf(stderr, "no connection information provided, unable to run\n"); | 
|  | display_usage(progname, subcommand); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | mainloop.Run(); | 
|  | fprintf(stderr, "lldb-server exiting...\n"); | 
|  |  | 
|  | return 0; | 
|  | } |