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