1//===-- lldb-platform.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#if defined(__APPLE__)
11#include <netinet/in.h>
12#endif
13#include <csignal>
14#include <cstdint>
15#include <cstdio>
16#include <cstdlib>
17#include <cstring>
18#if !defined(_WIN32)
19#include <sys/wait.h>
20#endif
21#include <fstream>
22#include <optional>
23
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/ScopedPrinter.h"
26#include "llvm/Support/WithColor.h"
27#include "llvm/Support/raw_ostream.h"
28
29#include "LLDBServerUtilities.h"
30#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
31#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
32#include "lldb/Host/ConnectionFileDescriptor.h"
33#include "lldb/Host/HostGetOpt.h"
34#include "lldb/Host/HostInfo.h"
35#include "lldb/Host/MainLoop.h"
36#include "lldb/Host/OptionParser.h"
37#include "lldb/Host/Socket.h"
38#include "lldb/Host/common/TCPSocket.h"
39#if LLDB_ENABLE_POSIX
40#include "lldb/Host/posix/DomainSocket.h"
41#endif
42#include "lldb/Utility/FileSpec.h"
43#include "lldb/Utility/LLDBLog.h"
44#include "lldb/Utility/Status.h"
45#include "lldb/Utility/UriParser.h"
46
47using namespace lldb;
48using namespace lldb_private;
49using namespace lldb_private::lldb_server;
50using namespace lldb_private::process_gdb_remote;
51using namespace llvm;
52
53// The test suite makes many connections in parallel, let's not miss any.
54// The highest this should get reasonably is a function of the number
55// of target CPUs. For now, let's just use 100.
56static const int backlog = 100;
57static const int socket_error = -1;
58static int g_debug = 0;
59static int g_verbose = 0;
60static int g_server = 0;
61
62// option descriptors for getopt_long_only()
63static struct option g_long_options[] = {
64 {.name: "debug", no_argument, .flag: &g_debug, .val: 1},
65 {.name: "verbose", no_argument, .flag: &g_verbose, .val: 1},
66 {.name: "log-file", required_argument, .flag: nullptr, .val: 'l'},
67 {.name: "log-channels", required_argument, .flag: nullptr, .val: 'c'},
68 {.name: "listen", required_argument, .flag: nullptr, .val: 'L'},
69 {.name: "gdbserver-port", required_argument, .flag: nullptr, .val: 'P'},
70 {.name: "socket-file", required_argument, .flag: nullptr, .val: 'f'},
71 {.name: "server", no_argument, .flag: &g_server, .val: 1},
72 {.name: "child-platform-fd", required_argument, .flag: nullptr, .val: 2},
73 {.name: nullptr, .has_arg: 0, .flag: nullptr, .val: 0}};
74
75#if defined(__APPLE__)
76#define LOW_PORT (IPPORT_RESERVED)
77#define HIGH_PORT (IPPORT_HIFIRSTAUTO)
78#else
79#define LOW_PORT (1024u)
80#define HIGH_PORT (49151u)
81#endif
82
83#if !defined(_WIN32)
84// Watch for signals
85static void signal_handler(int signo) {
86 switch (signo) {
87 case SIGHUP:
88 // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
89 // And we should not call exit() here because it results in the global
90 // destructors to be invoked and wreaking havoc on the threads still
91 // running.
92 llvm::errs() << "SIGHUP received, exiting lldb-server...\n";
93 abort();
94 break;
95 }
96}
97#endif
98
99static void display_usage(const char *progname, const char *subcommand) {
100 fprintf(stderr, format: "Usage:\n %s %s [--log-file log-file-name] [--log-channels "
101 "log-channel-list] [--port-file port-file-path] --server "
102 "--listen port\n",
103 progname, subcommand);
104 exit(status: 0);
105}
106
107static Status parse_listen_host_port(Socket::SocketProtocol &protocol,
108 const std::string &listen_host_port,
109 std::string &address,
110 uint16_t &platform_port,
111 std::string &gdb_address,
112 const uint16_t gdbserver_port) {
113 std::string hostname;
114 // Try to match socket name as URL - e.g., tcp://localhost:5555
115 if (std::optional<URI> uri = URI::Parse(uri: listen_host_port)) {
116 if (!Socket::FindProtocolByScheme(scheme: uri->scheme.str().c_str(), protocol)) {
117 return Status::FromErrorStringWithFormat(
118 format: "Unknown protocol scheme \"%s\".", uri->scheme.str().c_str());
119 }
120 if (protocol == Socket::ProtocolTcp) {
121 hostname = uri->hostname;
122 if (uri->port) {
123 platform_port = *(uri->port);
124 }
125 } else
126 address = listen_host_port.substr(pos: uri->scheme.size() + strlen(s: "://"));
127 } else {
128 // Try to match socket name as $host:port - e.g., localhost:5555
129 llvm::Expected<Socket::HostAndPort> host_port =
130 Socket::DecodeHostAndPort(host_and_port: listen_host_port);
131 if (!llvm::errorToBool(Err: host_port.takeError())) {
132 protocol = Socket::ProtocolTcp;
133 hostname = host_port->hostname;
134 platform_port = host_port->port;
135 } else
136 address = listen_host_port;
137 }
138
139 if (protocol == Socket::ProtocolTcp) {
140 if (platform_port != 0 && platform_port == gdbserver_port) {
141 return Status::FromErrorStringWithFormat(
142 format: "The same platform and gdb ports %u.", platform_port);
143 }
144 address = llvm::formatv(Fmt: "[{0}]:{1}", Vals&: hostname, Vals&: platform_port).str();
145 gdb_address = llvm::formatv(Fmt: "[{0}]:{1}", Vals&: hostname, Vals: gdbserver_port).str();
146 } else {
147 if (gdbserver_port) {
148 return Status::FromErrorStringWithFormat(
149 format: "--gdbserver-port %u is redundant for non-tcp protocol %s.",
150 gdbserver_port, Socket::FindSchemeByProtocol(protocol));
151 }
152 }
153 return Status();
154}
155
156static Status save_socket_id_to_file(const std::string &socket_id,
157 const FileSpec &file_spec) {
158 FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef());
159 Status error(llvm::sys::fs::create_directory(path: temp_file_spec.GetPath()));
160 if (error.Fail())
161 return Status::FromErrorStringWithFormat(
162 format: "Failed to create directory %s: %s", temp_file_spec.GetPath().c_str(),
163 error.AsCString());
164
165 Status status;
166 if (auto Err = llvm::writeToOutput(OutputFileName: file_spec.GetPath(),
167 Write: [&socket_id](llvm::raw_ostream &OS) {
168 OS << socket_id;
169 return llvm::Error::success();
170 }))
171 return Status::FromErrorStringWithFormat(
172 format: "Failed to atomically write file %s: %s", file_spec.GetPath().c_str(),
173 llvm::toString(E: std::move(Err)).c_str());
174 return status;
175}
176
177static Status ListenGdbConnectionsIfNeeded(
178 const Socket::SocketProtocol protocol, std::unique_ptr<TCPSocket> &gdb_sock,
179 const std::string &gdb_address, uint16_t &gdbserver_port) {
180 if (protocol != Socket::ProtocolTcp)
181 return Status();
182
183 gdb_sock = std::make_unique<TCPSocket>(/*should_close=*/args: true);
184 Status error = gdb_sock->Listen(name: gdb_address, backlog);
185 if (error.Fail())
186 return error;
187
188 if (gdbserver_port == 0)
189 gdbserver_port = gdb_sock->GetLocalPortNumber();
190
191 return Status();
192}
193
194static llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>>
195AcceptGdbConnectionsIfNeeded(const Socket::SocketProtocol protocol,
196 std::unique_ptr<TCPSocket> &gdb_sock,
197 MainLoop &main_loop, const uint16_t gdbserver_port,
198 const lldb_private::Args &args) {
199 if (protocol != Socket::ProtocolTcp)
200 return std::vector<MainLoopBase::ReadHandleUP>();
201
202 return gdb_sock->Accept(loop&: main_loop, sock_cb: [gdbserver_port,
203 &args](std::unique_ptr<Socket> sock_up) {
204 Log *log = GetLog(mask: LLDBLog::Platform);
205 Status error;
206 SharedSocket shared_socket(sock_up.get(), error);
207 if (error.Fail()) {
208 LLDB_LOGF(log, "gdbserver SharedSocket failed: %s", error.AsCString());
209 return;
210 }
211 lldb::pid_t child_pid = LLDB_INVALID_PROCESS_ID;
212 std::string socket_name;
213 GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp,
214 gdbserver_port);
215 error = platform.LaunchGDBServer(args, pid&: child_pid, socket_name,
216 fd: shared_socket.GetSendableFD());
217 if (error.Success() && child_pid != LLDB_INVALID_PROCESS_ID) {
218 error = shared_socket.CompleteSending(child_pid);
219 if (error.Fail()) {
220 Host::Kill(pid: child_pid, SIGTERM);
221 LLDB_LOGF(log, "gdbserver CompleteSending failed: %s",
222 error.AsCString());
223 return;
224 }
225 }
226 });
227}
228
229static void client_handle(GDBRemoteCommunicationServerPlatform &platform,
230 const lldb_private::Args &args) {
231 if (!platform.IsConnected())
232 return;
233
234 if (args.GetArgumentCount() > 0) {
235 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
236 std::string socket_name;
237 Status error = platform.LaunchGDBServer(args, pid, socket_name,
238 fd: SharedSocket::kInvalidFD);
239 if (error.Success())
240 platform.SetPendingGdbServer(socket_name);
241 else
242 fprintf(stderr, format: "failed to start gdbserver: %s\n", error.AsCString());
243 }
244
245 bool interrupt = false;
246 bool done = false;
247 Status error;
248 while (!interrupt && !done) {
249 if (platform.GetPacketAndSendResponse(timeout: std::nullopt, error, interrupt,
250 quit&: done) !=
251 GDBRemoteCommunication::PacketResult::Success)
252 break;
253 }
254
255 printf(format: "Disconnected.\n");
256}
257
258static Status spawn_process(const char *progname, const FileSpec &prog,
259 const Socket *conn_socket, uint16_t gdb_port,
260 const lldb_private::Args &args,
261 const std::string &log_file,
262 const StringRef log_channels, MainLoop &main_loop) {
263 Status error;
264 SharedSocket shared_socket(conn_socket, error);
265 if (error.Fail())
266 return error;
267
268 ProcessLaunchInfo launch_info;
269
270 launch_info.SetExecutableFile(exe_file: prog, add_exe_file_as_first_arg: false);
271 launch_info.SetArg0(progname);
272 Args &self_args = launch_info.GetArguments();
273 self_args.AppendArgument(arg_str: progname);
274 self_args.AppendArgument(arg_str: llvm::StringRef("platform"));
275 self_args.AppendArgument(arg_str: llvm::StringRef("--child-platform-fd"));
276 self_args.AppendArgument(arg_str: llvm::to_string(Value: shared_socket.GetSendableFD()));
277 launch_info.AppendDuplicateFileAction(fd: (int64_t)shared_socket.GetSendableFD(),
278 dup_fd: (int64_t)shared_socket.GetSendableFD());
279 if (gdb_port) {
280 self_args.AppendArgument(arg_str: llvm::StringRef("--gdbserver-port"));
281 self_args.AppendArgument(arg_str: llvm::to_string(Value: gdb_port));
282 }
283 if (!log_file.empty()) {
284 self_args.AppendArgument(arg_str: llvm::StringRef("--log-file"));
285 self_args.AppendArgument(arg_str: log_file);
286 }
287 if (!log_channels.empty()) {
288 self_args.AppendArgument(arg_str: llvm::StringRef("--log-channels"));
289 self_args.AppendArgument(arg_str: log_channels);
290 }
291 if (args.GetArgumentCount() > 0) {
292 self_args.AppendArgument(arg_str: "--");
293 self_args.AppendArguments(rhs: args);
294 }
295
296 launch_info.SetLaunchInSeparateProcessGroup(false);
297
298 if (g_server)
299 launch_info.SetMonitorProcessCallback([](lldb::pid_t, int, int) {});
300 else
301 launch_info.SetMonitorProcessCallback([&main_loop](lldb::pid_t, int, int) {
302 main_loop.AddPendingCallback(
303 callback: [](MainLoopBase &loop) { loop.RequestTermination(); });
304 });
305
306 // Copy the current environment.
307 launch_info.GetEnvironment() = Host::GetEnvironment();
308
309 launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
310
311 // Close STDIN, STDOUT and STDERR.
312 launch_info.AppendCloseFileAction(STDIN_FILENO);
313 launch_info.AppendCloseFileAction(STDOUT_FILENO);
314 launch_info.AppendCloseFileAction(STDERR_FILENO);
315
316 // Redirect STDIN, STDOUT and STDERR to "/dev/null".
317 launch_info.AppendSuppressFileAction(STDIN_FILENO, read: true, write: false);
318 launch_info.AppendSuppressFileAction(STDOUT_FILENO, read: false, write: true);
319 launch_info.AppendSuppressFileAction(STDERR_FILENO, read: false, write: true);
320
321 std::string cmd;
322 self_args.GetCommandString(command&: cmd);
323
324 error = Host::LaunchProcess(launch_info);
325 if (error.Fail())
326 return error;
327
328 lldb::pid_t child_pid = launch_info.GetProcessID();
329 if (child_pid == LLDB_INVALID_PROCESS_ID)
330 return Status::FromErrorString(str: "invalid pid");
331
332 LLDB_LOG(GetLog(LLDBLog::Platform), "lldb-platform launched '{0}', pid={1}",
333 cmd, child_pid);
334
335 error = shared_socket.CompleteSending(child_pid);
336 if (error.Fail()) {
337 Host::Kill(pid: child_pid, SIGTERM);
338 return error;
339 }
340
341 return Status();
342}
343
344// main
345int main_platform(int argc, char *argv[]) {
346 const char *progname = argv[0];
347 const char *subcommand = argv[1];
348 argc--;
349 argv++;
350#if !defined(_WIN32)
351 signal(SIGPIPE, SIG_IGN);
352 signal(SIGHUP, handler: signal_handler);
353#endif
354 int long_option_index = 0;
355 Status error;
356 std::string listen_host_port;
357 int ch;
358
359 std::string log_file;
360 StringRef
361 log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
362
363 shared_fd_t fd = SharedSocket::kInvalidFD;
364
365 uint16_t gdbserver_port = 0;
366
367 FileSpec socket_file;
368 bool show_usage = false;
369 int option_error = 0;
370
371 std::string short_options(OptionParser::GetShortOptionString(long_options: g_long_options));
372
373#if __GLIBC__
374 optind = 0;
375#else
376 optreset = 1;
377 optind = 1;
378#endif
379
380 while ((ch = getopt_long_only(argc: argc, argv: argv, shortopts: short_options.c_str(),
381 longopts: g_long_options, longind: &long_option_index)) != -1) {
382 switch (ch) {
383 case 0: // Any optional that auto set themselves will return 0
384 break;
385
386 case 'L':
387 listen_host_port.append(s: optarg);
388 break;
389
390 case 'l': // Set Log File
391 if (optarg && optarg[0])
392 log_file.assign(s: optarg);
393 break;
394
395 case 'c': // Log Channels
396 if (optarg && optarg[0])
397 log_channels = StringRef(optarg);
398 break;
399
400 case 'f': // Socket file
401 if (optarg && optarg[0])
402 socket_file.SetFile(path: optarg, style: FileSpec::Style::native);
403 break;
404
405 case 'P':
406 case 'm':
407 case 'M': {
408 uint16_t portnum;
409 if (!llvm::to_integer(S: optarg, Num&: portnum)) {
410 WithColor::error() << "invalid port number string " << optarg << "\n";
411 option_error = 2;
412 break;
413 }
414 // Note the condition gdbserver_port > HIGH_PORT is valid in case of using
415 // --child-platform-fd. Check gdbserver_port later.
416 if (ch == 'P')
417 gdbserver_port = portnum;
418 else if (gdbserver_port == 0)
419 gdbserver_port = portnum;
420 } break;
421
422 case 2: {
423 uint64_t _fd;
424 if (!llvm::to_integer(S: optarg, Num&: _fd)) {
425 WithColor::error() << "invalid fd " << optarg << "\n";
426 option_error = 6;
427 } else
428 fd = (shared_fd_t)_fd;
429 } break;
430
431 case 'h': /* fall-through is intentional */
432 case '?':
433 show_usage = true;
434 break;
435 }
436 }
437
438 if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, log_options: 0))
439 return -1;
440
441 // Print usage and exit if no listening port is specified.
442 if (listen_host_port.empty() && fd == SharedSocket::kInvalidFD)
443 show_usage = true;
444
445 if (show_usage || option_error) {
446 display_usage(progname, subcommand);
447 exit(status: option_error);
448 }
449
450 // Skip any options we consumed with getopt_long_only.
451 argc -= optind;
452 argv += optind;
453 lldb_private::Args inferior_arguments;
454 inferior_arguments.SetArguments(argc, argv: const_cast<const char **>(argv));
455
456 Log *log = GetLog(mask: LLDBLog::Platform);
457 if (fd != SharedSocket::kInvalidFD) {
458 // Child process will handle the connection and exit.
459 NativeSocket sockfd;
460 error = SharedSocket::GetNativeSocket(fd, socket&: sockfd);
461 if (error.Fail()) {
462 LLDB_LOGF(log, "lldb-platform child: %s", error.AsCString());
463 return socket_error;
464 }
465
466 std::unique_ptr<Socket> socket;
467 if (gdbserver_port) {
468 socket = std::make_unique<TCPSocket>(args&: sockfd, /*should_close=*/args: true);
469 } else {
470#if LLDB_ENABLE_POSIX
471 llvm::Expected<std::unique_ptr<DomainSocket>> domain_socket =
472 DomainSocket::FromBoundNativeSocket(sockfd, /*should_close=*/true);
473 if (!domain_socket) {
474 LLDB_LOG_ERROR(log, domain_socket.takeError(),
475 "Failed to create socket: {0}");
476 return socket_error;
477 }
478 socket = std::move(domain_socket.get());
479#else
480 WithColor::error() << "lldb-platform child: Unix domain sockets are not "
481 "supported on this platform.";
482 return socket_error;
483#endif
484 }
485
486 GDBRemoteCommunicationServerPlatform platform(socket->GetSocketProtocol(),
487 gdbserver_port);
488 platform.SetConnection(std::unique_ptr<Connection>(
489 new ConnectionFileDescriptor(socket.release())));
490 client_handle(platform, args: inferior_arguments);
491 return 0;
492 }
493
494 if (gdbserver_port != 0 &&
495 (gdbserver_port < LOW_PORT || gdbserver_port > HIGH_PORT)) {
496 WithColor::error() << llvm::formatv(Fmt: "Port number {0} is not in the "
497 "valid user port range of {1} - {2}\n",
498 Vals&: gdbserver_port, LOW_PORT, HIGH_PORT);
499 return 1;
500 }
501
502 Socket::SocketProtocol protocol = Socket::ProtocolUnixDomain;
503 std::string address;
504 std::string gdb_address;
505 uint16_t platform_port = 0;
506 error = parse_listen_host_port(protocol, listen_host_port, address,
507 platform_port, gdb_address, gdbserver_port);
508 if (error.Fail()) {
509 printf(format: "Failed to parse listen address: %s\n", error.AsCString());
510 return socket_error;
511 }
512
513 std::unique_ptr<Socket> platform_sock = Socket::Create(protocol, error);
514 if (error.Fail()) {
515 printf(format: "Failed to create platform socket: %s\n", error.AsCString());
516 return socket_error;
517 }
518 error = platform_sock->Listen(name: address, backlog);
519 if (error.Fail()) {
520 printf(format: "Failed to listen platform: %s\n", error.AsCString());
521 return socket_error;
522 }
523 if (protocol == Socket::ProtocolTcp && platform_port == 0)
524 platform_port =
525 static_cast<TCPSocket *>(platform_sock.get())->GetLocalPortNumber();
526
527 if (socket_file) {
528 error = save_socket_id_to_file(
529 socket_id: protocol == Socket::ProtocolTcp
530 ? (platform_port ? llvm::to_string(Value: platform_port) : "")
531 : address,
532 file_spec: socket_file);
533 if (error.Fail()) {
534 fprintf(stderr, format: "failed to write socket id to %s: %s\n",
535 socket_file.GetPath().c_str(), error.AsCString());
536 return 1;
537 }
538 }
539
540 std::unique_ptr<TCPSocket> gdb_sock;
541 // Update gdbserver_port if it is still 0 and protocol is tcp.
542 error = ListenGdbConnectionsIfNeeded(protocol, gdb_sock, gdb_address,
543 gdbserver_port);
544 if (error.Fail()) {
545 printf(format: "Failed to listen gdb: %s\n", error.AsCString());
546 return socket_error;
547 }
548
549 MainLoop main_loop;
550 {
551 llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> platform_handles =
552 platform_sock->Accept(
553 loop&: main_loop, sock_cb: [progname, gdbserver_port, &inferior_arguments, log_file,
554 log_channels, &main_loop,
555 &platform_handles](std::unique_ptr<Socket> sock_up) {
556 printf(format: "Connection established.\n");
557 Status error = spawn_process(
558 progname, prog: HostInfo::GetProgramFileSpec(), conn_socket: sock_up.get(),
559 gdb_port: gdbserver_port, args: inferior_arguments, log_file, log_channels,
560 main_loop);
561 if (error.Fail()) {
562 Log *log = GetLog(mask: LLDBLog::Platform);
563 LLDB_LOGF(log, "spawn_process failed: %s", error.AsCString());
564 WithColor::error()
565 << "spawn_process failed: " << error.AsCString() << "\n";
566 if (!g_server)
567 main_loop.RequestTermination();
568 }
569 if (!g_server)
570 platform_handles->clear();
571 });
572 if (!platform_handles) {
573 printf(format: "Failed to accept platform: %s\n",
574 llvm::toString(E: platform_handles.takeError()).c_str());
575 return socket_error;
576 }
577
578 llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> gdb_handles =
579 AcceptGdbConnectionsIfNeeded(protocol, gdb_sock, main_loop,
580 gdbserver_port, args: inferior_arguments);
581 if (!gdb_handles) {
582 printf(format: "Failed to accept gdb: %s\n",
583 llvm::toString(E: gdb_handles.takeError()).c_str());
584 return socket_error;
585 }
586
587 main_loop.Run();
588 }
589
590 fprintf(stderr, format: "lldb-server exiting...\n");
591
592 return 0;
593}
594

source code of lldb/tools/lldb-server/lldb-platform.cpp