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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <cerrno> |
10 | #include <cstdint> |
11 | #include <cstdio> |
12 | #include <cstdlib> |
13 | #include <cstring> |
14 | |
15 | #ifndef _WIN32 |
16 | #include <csignal> |
17 | #include <unistd.h> |
18 | #endif |
19 | |
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 | #endif |
49 | |
50 | #ifndef LLGS_PROGRAM_NAME |
51 | #define LLGS_PROGRAM_NAME "lldb-server" |
52 | #endif |
53 | |
54 | #ifndef LLGS_VERSION_STR |
55 | #define LLGS_VERSION_STR "local_build" |
56 | #endif |
57 | |
58 | using namespace llvm; |
59 | using namespace lldb; |
60 | using namespace lldb_private; |
61 | using namespace lldb_private::lldb_server; |
62 | using namespace lldb_private::process_gdb_remote; |
63 | |
64 | namespace { |
65 | #if defined(__linux__) |
66 | typedef process_linux::NativeProcessLinux::Manager NativeProcessManager; |
67 | #elif defined(__FreeBSD__) |
68 | typedef process_freebsd::NativeProcessFreeBSD::Manager NativeProcessManager; |
69 | #elif defined(__NetBSD__) |
70 | typedef process_netbsd::NativeProcessNetBSD::Manager NativeProcessManager; |
71 | #elif defined(_WIN32) |
72 | typedef NativeProcessWindows::Manager NativeProcessManager; |
73 | #else |
74 | // Dummy implementation to make sure the code compiles |
75 | class NativeProcessManager : public NativeProcessProtocol::Manager { |
76 | public: |
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 | #endif |
92 | } |
93 | |
94 | #ifndef _WIN32 |
95 | // Watch for signals |
96 | static int g_sighup_received_count = 0; |
97 | |
98 | static void sighup_handler(MainLoopBase &mainloop) { |
99 | ++g_sighup_received_count; |
100 | |
101 | Log *log = GetLog(mask: 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 _WIN32 |
109 | |
110 | void 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, format: "error: failed to attach to pid %" PRIu64 ": %s\n" , pid, |
115 | error.AsCString()); |
116 | exit(status: 1); |
117 | } |
118 | } |
119 | |
120 | void handle_attach_to_process_name(GDBRemoteCommunicationServerLLGS &gdb_server, |
121 | const std::string &process_name) { |
122 | // FIXME implement. |
123 | } |
124 | |
125 | void 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 use |
130 | // it as a pid. |
131 | char *end_p = nullptr; |
132 | const long int pid = strtol(nptr: attach_target.c_str(), endptr: &end_p, base: 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, pid: static_cast<lldb::pid_t>(pid)); |
139 | else |
140 | handle_attach_to_process_name(gdb_server, process_name: attach_target); |
141 | } |
142 | |
143 | void 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: Args(Arguments), first_arg_is_executable: true); |
149 | |
150 | llvm::SmallString<64> cwd; |
151 | if (std::error_code ec = llvm::sys::fs::current_path(result&: cwd)) { |
152 | llvm::errs() << "Error getting current directory: " << ec.message() << "\n" ; |
153 | exit(status: 1); |
154 | } |
155 | FileSpec cwd_spec(cwd); |
156 | FileSystem::Instance().Resolve(file_spec&: 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(Fmt: "error: failed to launch '{0}': {1}\n" , |
165 | Vals: Arguments[0], Vals&: error); |
166 | exit(status: 1); |
167 | } |
168 | } |
169 | |
170 | static 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: buf.data(), size: buf.size())) |
177 | buf = buf.drop_front(N: *written); |
178 | else |
179 | return Status::FromError(error: written.takeError()); |
180 | } |
181 | return Status(); |
182 | } |
183 | |
184 | Status 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(name: named_pipe_path, child_process_inherit: false, |
189 | timeout: std::chrono::seconds{10})) |
190 | return Status::FromError(error: std::move(err)); |
191 | |
192 | return writeSocketIdToPipe(port_pipe&: port_name_pipe, socket_id: socket_id.str()); |
193 | } |
194 | |
195 | Status 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: socket_id.str()); |
199 | } |
200 | |
201 | void 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 _WIN32 |
214 | 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::unique_ptr<Connection>(new ConnectionFileDescriptor( |
222 | new TCPSocket(sockfd, /*should_close=*/true))); |
223 | #else |
224 | url = llvm::formatv(Fmt: "fd://{0}" , Vals&: connection_fd).str(); |
225 | |
226 | // Create the connection. |
227 | ::fcntl(fd: connection_fd, F_SETFD, FD_CLOEXEC); |
228 | #endif |
229 | } else if (!host_and_port.empty()) { |
230 | llvm::Expected<std::string> url_exp = |
231 | LLGSArgToURL(url_arg: host_and_port, reverse_connect); |
232 | if (!url_exp) { |
233 | llvm::errs() << llvm::formatv(Fmt: "error: invalid host:port or URL '{0}': " |
234 | "{1}\n" , |
235 | Vals&: host_and_port, |
236 | Vals: llvm::toString(E: url_exp.takeError())); |
237 | exit(status: -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 | socket_id_callback: [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) { |
250 | // If we have a named pipe to write the socket id back to, do that |
251 | // 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 | Fmt: "failed to write to the named pipe '{0}': {1}\n" , |
257 | Vals: named_pipe_path, Vals: error.AsCString()); |
258 | } |
259 | // If we have an unnamed pipe to write the socket id back to, do |
260 | // 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 | Fmt: "failed to write to the unnamed pipe: {0}\n" , Vals&: error); |
266 | } |
267 | }, |
268 | error_ptr: &error); |
269 | |
270 | if (error.Fail()) { |
271 | llvm::errs() << llvm::formatv( |
272 | Fmt: "error: failed to connect to client at '{0}': {1}\n" , Vals&: url, Vals&: error); |
273 | exit(status: -1); |
274 | } |
275 | if (connection_result != eConnectionStatusSuccess) { |
276 | llvm::errs() << llvm::formatv( |
277 | Fmt: "error: failed to connect to client at '{0}' " |
278 | "(connection status: {1})\n" , |
279 | Vals&: url, Vals: static_cast<int>(connection_result)); |
280 | exit(status: -1); |
281 | } |
282 | connection_up = std::move(conn_fd_up); |
283 | } |
284 | error = gdb_server.InitializeConnection(connection: std::move(connection_up)); |
285 | if (error.Fail()) { |
286 | llvm::errs() << llvm::formatv(Fmt: "failed to initialize connection\n" , Vals&: error); |
287 | exit(status: -1); |
288 | } |
289 | llvm::outs() << "Connection established.\n" ; |
290 | } |
291 | |
292 | namespace { |
293 | using namespace llvm::opt; |
294 | |
295 | enum 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 OPTION |
300 | }; |
301 | |
302 | #define OPTTABLE_STR_TABLE_CODE |
303 | #include "LLGSOptions.inc" |
304 | #undef OPTTABLE_STR_TABLE_CODE |
305 | |
306 | #define OPTTABLE_PREFIXES_TABLE_CODE |
307 | #include "LLGSOptions.inc" |
308 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
309 | |
310 | static constexpr opt::OptTable::Info InfoTable[] = { |
311 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
312 | #include "LLGSOptions.inc" |
313 | #undef OPTION |
314 | }; |
315 | |
316 | class LLGSOptTable : public opt::GenericOptTable { |
317 | public: |
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(OS&: llvm::outs(), Usage: Usage.c_str(), Title: "lldb-server" ); |
325 | llvm::outs() << R"( |
326 | DESCRIPTION |
327 | 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 present |
329 | and will denote the address that lldb-server will listen on. [host] defaults |
330 | to "localhost" if empty. Port can be zero, in which case the port number will |
331 | be chosen dynamically and written to destinations given by --named-pipe and |
332 | --pipe arguments. |
333 | |
334 | If no target is selected at startup, lldb-server can be directed by the LLDB |
335 | client to launch or attach to a process. |
336 | |
337 | )" ; |
338 | } |
339 | }; |
340 | } // namespace |
341 | |
342 | int main_gdbserver(int argc, char *argv[]) { |
343 | Status error; |
344 | MainLoop mainloop; |
345 | #ifndef _WIN32 |
346 | // Setup signal handlers first thing. |
347 | signal(SIGPIPE, SIG_IGN); |
348 | MainLoop::SignalHandleUP sighup_handle = |
349 | mainloop.RegisterSignal(SIGHUP, callback: sighup_handler, error); |
350 | #endif |
351 | |
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 | StringRef |
358 | 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: argc - 1, Argv: argv + 1, Unknown: OPT_UNKNOWN, |
371 | Saver, ErrorFn: [&](llvm::StringRef Msg) { |
372 | WithColor::error() << Msg << "\n" ; |
373 | HasError = true; |
374 | }); |
375 | std::string Name = |
376 | (llvm::sys::path::filename(path: 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 _WIN32 |
390 | if (Args.hasArg(OPT_setsid)) { |
391 | // Put llgs into a new session. Terminals group processes |
392 | // into sessions and when a special terminal key sequences |
393 | // (like control+c) are typed they can cause signals to go out to |
394 | // all processes in a session. Using this --setsid (-S) option |
395 | // will cause debugserver to run in its own sessions and be free |
396 | // from such issues. |
397 | // |
398 | // This is useful when llgs is spawned from a command |
399 | // line application that uses llgs to do the debugging, |
400 | // yet that application doesn't want llgs receiving the |
401 | // 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(Fmt: "failed to set new session id for {0} ({1})\n" , |
407 | LLGS_PROGRAM_NAME, Vals: llvm::sys::StrError()); |
408 | } |
409 | } |
410 | } |
411 | #endif |
412 | |
413 | log_file = Args.getLastArgValue(Id: OPT_log_file).str(); |
414 | log_channels = Args.getLastArgValue(Id: OPT_log_channels); |
415 | named_pipe_path = Args.getLastArgValue(Id: OPT_named_pipe).str(); |
416 | reverse_connect = Args.hasArg(Ids: OPT_reverse_connect); |
417 | attach_target = Args.getLastArgValue(Id: OPT_attach).str(); |
418 | if (Args.hasArg(OPT_pipe)) { |
419 | uint64_t Arg; |
420 | if (!llvm::to_integer(Args.getLastArgValue(Id: 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(Id: 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(position: Inputs.begin()); |
460 | } |
461 | |
462 | // Any arguments left over are for the program that we need to launch. If |
463 | // there |
464 | // are no arguments, then the GDB server will start up and wait for an 'A' |
465 | // packet |
466 | // to launch a program, or a vAttach packet to attach to an existing process, |
467 | // unless |
468 | // 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, Arguments: Inputs); |
473 | |
474 | // Print version info. |
475 | printf(format: "%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: named_pipe_path.c_str(), |
479 | unnamed_pipe, connection_fd); |
480 | |
481 | if (!gdb_server.IsConnected()) { |
482 | fprintf(stderr, format: "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, format: "lldb-server terminating due to error: %s\n" , |
489 | ret.AsCString()); |
490 | return 1; |
491 | } |
492 | fprintf(stderr, format: "lldb-server exiting...\n" ); |
493 | |
494 | return 0; |
495 | } |
496 | |