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/Socket.h" |
28 | #include "lldb/Host/common/NativeProcessProtocol.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 | Status writeSocketIdToPipe(Pipe &port_pipe, llvm::StringRef socket_id) { |
171 | size_t bytes_written = 0; |
172 | // Write the port number as a C string with the NULL terminator. |
173 | return port_pipe.Write(buf: socket_id.data(), size: socket_id.size() + 1, bytes_written); |
174 | } |
175 | |
176 | Status writeSocketIdToPipe(const char *const named_pipe_path, |
177 | llvm::StringRef socket_id) { |
178 | Pipe port_name_pipe; |
179 | // Wait for 10 seconds for pipe to be opened. |
180 | auto error = port_name_pipe.OpenAsWriterWithTimeout(name: named_pipe_path, child_process_inherit: false, |
181 | timeout: std::chrono::seconds{10}); |
182 | if (error.Fail()) |
183 | return error; |
184 | return writeSocketIdToPipe(port_pipe&: port_name_pipe, socket_id); |
185 | } |
186 | |
187 | Status writeSocketIdToPipe(lldb::pipe_t unnamed_pipe, |
188 | llvm::StringRef socket_id) { |
189 | Pipe port_pipe{LLDB_INVALID_PIPE, unnamed_pipe}; |
190 | return writeSocketIdToPipe(port_pipe, socket_id); |
191 | } |
192 | |
193 | void ConnectToRemote(MainLoop &mainloop, |
194 | GDBRemoteCommunicationServerLLGS &gdb_server, |
195 | bool reverse_connect, llvm::StringRef host_and_port, |
196 | const char *const progname, const char *const subcommand, |
197 | const char *const named_pipe_path, pipe_t unnamed_pipe, |
198 | int connection_fd) { |
199 | Status error; |
200 | |
201 | std::unique_ptr<Connection> connection_up; |
202 | std::string url; |
203 | |
204 | if (connection_fd != -1) { |
205 | url = llvm::formatv(Fmt: "fd://{0}" , Vals&: connection_fd).str(); |
206 | |
207 | // Create the connection. |
208 | #if LLDB_ENABLE_POSIX && !defined _WIN32 |
209 | ::fcntl(fd: connection_fd, F_SETFD, FD_CLOEXEC); |
210 | #endif |
211 | } else if (!host_and_port.empty()) { |
212 | llvm::Expected<std::string> url_exp = |
213 | LLGSArgToURL(url_arg: host_and_port, reverse_connect); |
214 | if (!url_exp) { |
215 | llvm::errs() << llvm::formatv(Fmt: "error: invalid host:port or URL '{0}': " |
216 | "{1}\n" , |
217 | Vals&: host_and_port, |
218 | Vals: llvm::toString(E: url_exp.takeError())); |
219 | exit(status: -1); |
220 | } |
221 | |
222 | url = std::move(url_exp.get()); |
223 | } |
224 | |
225 | if (!url.empty()) { |
226 | // Create the connection or server. |
227 | std::unique_ptr<ConnectionFileDescriptor> conn_fd_up{ |
228 | new ConnectionFileDescriptor}; |
229 | auto connection_result = conn_fd_up->Connect( |
230 | url, |
231 | socket_id_callback: [named_pipe_path, unnamed_pipe](llvm::StringRef socket_id) { |
232 | // If we have a named pipe to write the socket id back to, do that |
233 | // now. |
234 | if (named_pipe_path && named_pipe_path[0]) { |
235 | Status error = writeSocketIdToPipe(named_pipe_path, socket_id); |
236 | if (error.Fail()) |
237 | llvm::errs() << llvm::formatv( |
238 | Fmt: "failed to write to the named pipe '{0}': {1}\n" , |
239 | Vals: named_pipe_path, Vals: error.AsCString()); |
240 | } |
241 | // If we have an unnamed pipe to write the socket id back to, do |
242 | // that now. |
243 | else if (unnamed_pipe != LLDB_INVALID_PIPE) { |
244 | Status error = writeSocketIdToPipe(unnamed_pipe, socket_id); |
245 | if (error.Fail()) |
246 | llvm::errs() << llvm::formatv( |
247 | Fmt: "failed to write to the unnamed pipe: {0}\n" , Vals&: error); |
248 | } |
249 | }, |
250 | error_ptr: &error); |
251 | |
252 | if (error.Fail()) { |
253 | llvm::errs() << llvm::formatv( |
254 | Fmt: "error: failed to connect to client at '{0}': {1}\n" , Vals&: url, Vals&: error); |
255 | exit(status: -1); |
256 | } |
257 | if (connection_result != eConnectionStatusSuccess) { |
258 | llvm::errs() << llvm::formatv( |
259 | Fmt: "error: failed to connect to client at '{0}' " |
260 | "(connection status: {1})\n" , |
261 | Vals&: url, Vals: static_cast<int>(connection_result)); |
262 | exit(status: -1); |
263 | } |
264 | connection_up = std::move(conn_fd_up); |
265 | } |
266 | error = gdb_server.InitializeConnection(connection: std::move(connection_up)); |
267 | if (error.Fail()) { |
268 | llvm::errs() << llvm::formatv(Fmt: "failed to initialize connection\n" , Vals&: error); |
269 | exit(status: -1); |
270 | } |
271 | llvm::outs() << "Connection established.\n" ; |
272 | } |
273 | |
274 | namespace { |
275 | using namespace llvm::opt; |
276 | |
277 | enum ID { |
278 | OPT_INVALID = 0, // This is not an option ID. |
279 | #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), |
280 | #include "LLGSOptions.inc" |
281 | #undef OPTION |
282 | }; |
283 | |
284 | #define PREFIX(NAME, VALUE) \ |
285 | constexpr llvm::StringLiteral NAME##_init[] = VALUE; \ |
286 | constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \ |
287 | NAME##_init, std::size(NAME##_init) - 1); |
288 | #include "LLGSOptions.inc" |
289 | #undef PREFIX |
290 | |
291 | static constexpr opt::OptTable::Info InfoTable[] = { |
292 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
293 | #include "LLGSOptions.inc" |
294 | #undef OPTION |
295 | }; |
296 | |
297 | class LLGSOptTable : public opt::GenericOptTable { |
298 | public: |
299 | LLGSOptTable() : opt::GenericOptTable(InfoTable) {} |
300 | |
301 | void PrintHelp(llvm::StringRef Name) { |
302 | std::string Usage = |
303 | (Name + " [options] [[host]:port] [[--] program args...]" ).str(); |
304 | OptTable::printHelp(OS&: llvm::outs(), Usage: Usage.c_str(), Title: "lldb-server" ); |
305 | llvm::outs() << R"( |
306 | DESCRIPTION |
307 | lldb-server connects to the LLDB client, which drives the debugging session. |
308 | If no connection options are given, the [host]:port argument must be present |
309 | and will denote the address that lldb-server will listen on. [host] defaults |
310 | to "localhost" if empty. Port can be zero, in which case the port number will |
311 | be chosen dynamically and written to destinations given by --named-pipe and |
312 | --pipe arguments. |
313 | |
314 | If no target is selected at startup, lldb-server can be directed by the LLDB |
315 | client to launch or attach to a process. |
316 | |
317 | )" ; |
318 | } |
319 | }; |
320 | } // namespace |
321 | |
322 | int main_gdbserver(int argc, char *argv[]) { |
323 | Status error; |
324 | MainLoop mainloop; |
325 | #ifndef _WIN32 |
326 | // Setup signal handlers first thing. |
327 | signal(SIGPIPE, SIG_IGN); |
328 | MainLoop::SignalHandleUP sighup_handle = |
329 | mainloop.RegisterSignal(SIGHUP, callback: sighup_handler, error); |
330 | #endif |
331 | |
332 | const char *progname = argv[0]; |
333 | const char *subcommand = argv[1]; |
334 | std::string attach_target; |
335 | std::string named_pipe_path; |
336 | std::string log_file; |
337 | StringRef |
338 | log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" |
339 | lldb::pipe_t unnamed_pipe = LLDB_INVALID_PIPE; |
340 | bool reverse_connect = false; |
341 | int connection_fd = -1; |
342 | |
343 | // ProcessLaunchInfo launch_info; |
344 | ProcessAttachInfo attach_info; |
345 | |
346 | LLGSOptTable Opts; |
347 | llvm::BumpPtrAllocator Alloc; |
348 | llvm::StringSaver Saver(Alloc); |
349 | bool HasError = false; |
350 | opt::InputArgList Args = Opts.parseArgs(Argc: argc - 1, Argv: argv + 1, Unknown: OPT_UNKNOWN, |
351 | Saver, ErrorFn: [&](llvm::StringRef Msg) { |
352 | WithColor::error() << Msg << "\n" ; |
353 | HasError = true; |
354 | }); |
355 | std::string Name = |
356 | (llvm::sys::path::filename(path: argv[0]) + " g[dbserver]" ).str(); |
357 | std::string HelpText = |
358 | "Use '" + Name + " --help' for a complete list of options.\n" ; |
359 | if (HasError) { |
360 | llvm::errs() << HelpText; |
361 | return 1; |
362 | } |
363 | |
364 | if (Args.hasArg(OPT_help)) { |
365 | Opts.PrintHelp(Name); |
366 | return 0; |
367 | } |
368 | |
369 | #ifndef _WIN32 |
370 | if (Args.hasArg(OPT_setsid)) { |
371 | // Put llgs into a new session. Terminals group processes |
372 | // into sessions and when a special terminal key sequences |
373 | // (like control+c) are typed they can cause signals to go out to |
374 | // all processes in a session. Using this --setsid (-S) option |
375 | // will cause debugserver to run in its own sessions and be free |
376 | // from such issues. |
377 | // |
378 | // This is useful when llgs is spawned from a command |
379 | // line application that uses llgs to do the debugging, |
380 | // yet that application doesn't want llgs receiving the |
381 | // signals sent to the session (i.e. dying when anyone hits ^C). |
382 | { |
383 | const ::pid_t new_sid = setsid(); |
384 | if (new_sid == -1) { |
385 | WithColor::warning() |
386 | << llvm::formatv(Fmt: "failed to set new session id for {0} ({1})\n" , |
387 | LLGS_PROGRAM_NAME, Vals: llvm::sys::StrError()); |
388 | } |
389 | } |
390 | } |
391 | #endif |
392 | |
393 | log_file = Args.getLastArgValue(Id: OPT_log_file).str(); |
394 | log_channels = Args.getLastArgValue(Id: OPT_log_channels); |
395 | named_pipe_path = Args.getLastArgValue(Id: OPT_named_pipe).str(); |
396 | reverse_connect = Args.hasArg(Ids: OPT_reverse_connect); |
397 | attach_target = Args.getLastArgValue(Id: OPT_attach).str(); |
398 | if (Args.hasArg(OPT_pipe)) { |
399 | uint64_t Arg; |
400 | if (!llvm::to_integer(Args.getLastArgValue(Id: OPT_pipe), Arg)) { |
401 | WithColor::error() << "invalid '--pipe' argument\n" << HelpText; |
402 | return 1; |
403 | } |
404 | unnamed_pipe = (pipe_t)Arg; |
405 | } |
406 | if (Args.hasArg(OPT_fd)) { |
407 | if (!llvm::to_integer(Args.getLastArgValue(Id: OPT_fd), connection_fd)) { |
408 | WithColor::error() << "invalid '--fd' argument\n" << HelpText; |
409 | return 1; |
410 | } |
411 | } |
412 | |
413 | if (!LLDBServerUtilities::SetupLogging( |
414 | log_file, log_channels, |
415 | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | |
416 | LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION)) |
417 | return -1; |
418 | |
419 | std::vector<llvm::StringRef> Inputs; |
420 | for (opt::Arg *Arg : Args.filtered(OPT_INPUT)) |
421 | Inputs.push_back(Arg->getValue()); |
422 | if (opt::Arg *Arg = Args.getLastArg(OPT_REM)) { |
423 | for (const char *Val : Arg->getValues()) |
424 | Inputs.push_back(Val); |
425 | } |
426 | if (Inputs.empty() && connection_fd == -1) { |
427 | WithColor::error() << "no connection arguments\n" << HelpText; |
428 | return 1; |
429 | } |
430 | |
431 | NativeProcessManager manager(mainloop); |
432 | GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager); |
433 | |
434 | llvm::StringRef host_and_port; |
435 | if (!Inputs.empty()) { |
436 | host_and_port = Inputs.front(); |
437 | Inputs.erase(position: Inputs.begin()); |
438 | } |
439 | |
440 | // Any arguments left over are for the program that we need to launch. If |
441 | // there |
442 | // are no arguments, then the GDB server will start up and wait for an 'A' |
443 | // packet |
444 | // to launch a program, or a vAttach packet to attach to an existing process, |
445 | // unless |
446 | // explicitly asked to attach with the --attach={pid|program_name} form. |
447 | if (!attach_target.empty()) |
448 | handle_attach(gdb_server, attach_target); |
449 | else if (!Inputs.empty()) |
450 | handle_launch(gdb_server, Arguments: Inputs); |
451 | |
452 | // Print version info. |
453 | printf(format: "%s-%s\n" , LLGS_PROGRAM_NAME, LLGS_VERSION_STR); |
454 | |
455 | ConnectToRemote(mainloop, gdb_server, reverse_connect, host_and_port, |
456 | progname, subcommand, named_pipe_path: named_pipe_path.c_str(), |
457 | unnamed_pipe, connection_fd); |
458 | |
459 | if (!gdb_server.IsConnected()) { |
460 | fprintf(stderr, format: "no connection information provided, unable to run\n" ); |
461 | return 1; |
462 | } |
463 | |
464 | Status ret = mainloop.Run(); |
465 | if (ret.Fail()) { |
466 | fprintf(stderr, format: "lldb-server terminating due to error: %s\n" , |
467 | ret.AsCString()); |
468 | return 1; |
469 | } |
470 | fprintf(stderr, format: "lldb-server exiting...\n" ); |
471 | |
472 | return 0; |
473 | } |
474 | |