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 "Acceptor.h" 30 #include "LLDBServerUtilities.h" 31 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h" 32 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" 33 #include "lldb/Host/ConnectionFileDescriptor.h" 34 #include "lldb/Host/HostGetOpt.h" 35 #include "lldb/Host/OptionParser.h" 36 #include "lldb/Host/Socket.h" 37 #include "lldb/Host/common/TCPSocket.h" 38 #include "lldb/Utility/FileSpec.h" 39 #include "lldb/Utility/LLDBLog.h" 40 #include "lldb/Utility/Status.h" 41 42 using namespace lldb; 43 using namespace lldb_private; 44 using namespace lldb_private::lldb_server; 45 using namespace lldb_private::process_gdb_remote; 46 using namespace llvm; 47 48 // option descriptors for getopt_long_only() 49 50 static int g_debug = 0; 51 static int g_verbose = 0; 52 static int g_server = 0; 53 54 static struct option g_long_options[] = { 55 {"debug", no_argument, &g_debug, 1}, 56 {"verbose", no_argument, &g_verbose, 1}, 57 {"log-file", required_argument, nullptr, 'l'}, 58 {"log-channels", required_argument, nullptr, 'c'}, 59 {"listen", required_argument, nullptr, 'L'}, 60 {"port-offset", required_argument, nullptr, 'p'}, 61 {"gdbserver-port", required_argument, nullptr, 'P'}, 62 {"min-gdbserver-port", required_argument, nullptr, 'm'}, 63 {"max-gdbserver-port", required_argument, nullptr, 'M'}, 64 {"socket-file", required_argument, nullptr, 'f'}, 65 {"server", no_argument, &g_server, 1}, 66 {"child-platform-fd", required_argument, nullptr, 2}, 67 {nullptr, 0, nullptr, 0}}; 68 69 #if defined(__APPLE__) 70 #define LOW_PORT (IPPORT_RESERVED) 71 #define HIGH_PORT (IPPORT_HIFIRSTAUTO) 72 #else 73 #define LOW_PORT (1024u) 74 #define HIGH_PORT (49151u) 75 #endif 76 77 #if !defined(_WIN32) 78 // Watch for signals 79 static void signal_handler(int signo) { 80 switch (signo) { 81 case SIGHUP: 82 // Use SIGINT first, if that does not work, use SIGHUP as a last resort. 83 // And we should not call exit() here because it results in the global 84 // destructors to be invoked and wreaking havoc on the threads still 85 // running. 86 llvm::errs() << "SIGHUP received, exiting lldb-server...\n"; 87 abort(); 88 break; 89 } 90 } 91 #endif 92 93 static void display_usage(const char *progname, const char *subcommand) { 94 fprintf(stderr, "Usage:\n %s %s [--log-file log-file-name] [--log-channels " 95 "log-channel-list] [--port-file port-file-path] --server " 96 "--listen port\n", 97 progname, subcommand); 98 exit(0); 99 } 100 101 static Status save_socket_id_to_file(const std::string &socket_id, 102 const FileSpec &file_spec) { 103 FileSpec temp_file_spec(file_spec.GetDirectory().GetStringRef()); 104 Status error(llvm::sys::fs::create_directory(temp_file_spec.GetPath())); 105 if (error.Fail()) 106 return Status::FromErrorStringWithFormat( 107 "Failed to create directory %s: %s", temp_file_spec.GetPath().c_str(), 108 error.AsCString()); 109 110 Status status; 111 if (auto Err = llvm::writeToOutput(file_spec.GetPath(), 112 [&socket_id](llvm::raw_ostream &OS) { 113 OS << socket_id; 114 return llvm::Error::success(); 115 })) 116 return Status::FromErrorStringWithFormat( 117 "Failed to atomically write file %s: %s", file_spec.GetPath().c_str(), 118 llvm::toString(std::move(Err)).c_str()); 119 return status; 120 } 121 122 static void client_handle(GDBRemoteCommunicationServerPlatform &platform, 123 const lldb_private::Args &args) { 124 if (!platform.IsConnected()) 125 return; 126 127 if (args.GetArgumentCount() > 0) { 128 lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; 129 std::optional<uint16_t> port; 130 std::string socket_name; 131 Status error = platform.LaunchGDBServer(args, 132 "", // hostname 133 pid, port, socket_name); 134 if (error.Success()) 135 platform.SetPendingGdbServer(pid, *port, socket_name); 136 else 137 fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); 138 } 139 140 bool interrupt = false; 141 bool done = false; 142 Status error; 143 while (!interrupt && !done) { 144 if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt, 145 done) != 146 GDBRemoteCommunication::PacketResult::Success) 147 break; 148 } 149 150 printf("Disconnected.\n"); 151 } 152 153 static GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap; 154 static std::mutex gdbserver_portmap_mutex; 155 156 static void spawn_process_reaped(lldb::pid_t pid, int signal, int status) { 157 std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); 158 gdbserver_portmap.FreePortForProcess(pid); 159 } 160 161 static Status spawn_process(const char *progname, const Socket *conn_socket, 162 uint16_t gdb_port, uint16_t port_offset, 163 const lldb_private::Args &args, 164 const std::string &log_file, 165 const StringRef log_channels) { 166 Status error; 167 SharedSocket shared_socket(conn_socket, error); 168 if (error.Fail()) 169 return error; 170 171 ProcessLaunchInfo launch_info; 172 173 FileSpec self_spec(progname, FileSpec::Style::native); 174 launch_info.SetExecutableFile(self_spec, true); 175 Args &self_args = launch_info.GetArguments(); 176 self_args.AppendArgument(llvm::StringRef("platform")); 177 self_args.AppendArgument(llvm::StringRef("--child-platform-fd")); 178 self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD())); 179 if (gdb_port) { 180 self_args.AppendArgument(llvm::StringRef("--gdbserver-port")); 181 self_args.AppendArgument(llvm::to_string(gdb_port)); 182 } 183 if (port_offset > 0) { 184 self_args.AppendArgument(llvm::StringRef("--port-offset")); 185 self_args.AppendArgument(llvm::to_string(port_offset)); 186 } 187 if (!log_file.empty()) { 188 self_args.AppendArgument(llvm::StringRef("--log-file")); 189 self_args.AppendArgument(log_file); 190 } 191 if (!log_channels.empty()) { 192 self_args.AppendArgument(llvm::StringRef("--log-channels")); 193 self_args.AppendArgument(log_channels); 194 } 195 if (args.GetArgumentCount() > 0) { 196 self_args.AppendArgument("--"); 197 self_args.AppendArguments(args); 198 } 199 200 launch_info.SetLaunchInSeparateProcessGroup(false); 201 launch_info.SetMonitorProcessCallback(&spawn_process_reaped); 202 203 // Copy the current environment. 204 launch_info.GetEnvironment() = Host::GetEnvironment(); 205 206 launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO); 207 208 // Close STDIN, STDOUT and STDERR. 209 launch_info.AppendCloseFileAction(STDIN_FILENO); 210 launch_info.AppendCloseFileAction(STDOUT_FILENO); 211 launch_info.AppendCloseFileAction(STDERR_FILENO); 212 213 // Redirect STDIN, STDOUT and STDERR to "/dev/null". 214 launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false); 215 launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true); 216 launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true); 217 218 std::string cmd; 219 self_args.GetCommandString(cmd); 220 221 error = Host::LaunchProcess(launch_info); 222 if (error.Fail()) 223 return error; 224 225 lldb::pid_t child_pid = launch_info.GetProcessID(); 226 if (child_pid == LLDB_INVALID_PROCESS_ID) 227 return Status::FromErrorString("invalid pid"); 228 229 LLDB_LOG(GetLog(LLDBLog::Platform), "lldb-platform launched '{0}', pid={1}", 230 cmd, child_pid); 231 232 { 233 std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); 234 gdbserver_portmap.AssociatePortWithProcess(gdb_port, child_pid); 235 } 236 237 error = shared_socket.CompleteSending(child_pid); 238 if (error.Fail()) { 239 Host::Kill(child_pid, SIGTERM); 240 return error; 241 } 242 243 return Status(); 244 } 245 246 // main 247 int main_platform(int argc, char *argv[]) { 248 const char *progname = argv[0]; 249 const char *subcommand = argv[1]; 250 argc--; 251 argv++; 252 #if !defined(_WIN32) 253 signal(SIGPIPE, SIG_IGN); 254 signal(SIGHUP, signal_handler); 255 #endif 256 int long_option_index = 0; 257 Status error; 258 std::string listen_host_port; 259 int ch; 260 261 std::string log_file; 262 StringRef 263 log_channels; // e.g. "lldb process threads:gdb-remote default:linux all" 264 265 shared_fd_t fd = SharedSocket::kInvalidFD; 266 267 int min_gdbserver_port = 0; 268 int max_gdbserver_port = 0; 269 uint16_t port_offset = 0; 270 271 FileSpec socket_file; 272 bool show_usage = false; 273 int option_error = 0; 274 int socket_error = -1; 275 276 std::string short_options(OptionParser::GetShortOptionString(g_long_options)); 277 278 #if __GLIBC__ 279 optind = 0; 280 #else 281 optreset = 1; 282 optind = 1; 283 #endif 284 285 while ((ch = getopt_long_only(argc, argv, short_options.c_str(), 286 g_long_options, &long_option_index)) != -1) { 287 switch (ch) { 288 case 0: // Any optional that auto set themselves will return 0 289 break; 290 291 case 'L': 292 listen_host_port.append(optarg); 293 break; 294 295 case 'l': // Set Log File 296 if (optarg && optarg[0]) 297 log_file.assign(optarg); 298 break; 299 300 case 'c': // Log Channels 301 if (optarg && optarg[0]) 302 log_channels = StringRef(optarg); 303 break; 304 305 case 'f': // Socket file 306 if (optarg && optarg[0]) 307 socket_file.SetFile(optarg, FileSpec::Style::native); 308 break; 309 310 case 'p': { 311 if (!llvm::to_integer(optarg, port_offset)) { 312 WithColor::error() << "invalid port offset string " << optarg << "\n"; 313 option_error = 4; 314 break; 315 } 316 if (port_offset < LOW_PORT || port_offset > HIGH_PORT) { 317 WithColor::error() << llvm::formatv( 318 "port offset {0} is not in the " 319 "valid user port range of {1} - {2}\n", 320 port_offset, LOW_PORT, HIGH_PORT); 321 option_error = 5; 322 } 323 } break; 324 325 case 'P': 326 case 'm': 327 case 'M': { 328 uint16_t portnum; 329 if (!llvm::to_integer(optarg, portnum)) { 330 WithColor::error() << "invalid port number string " << optarg << "\n"; 331 option_error = 2; 332 break; 333 } 334 if (portnum < LOW_PORT || portnum > HIGH_PORT) { 335 WithColor::error() << llvm::formatv( 336 "port number {0} is not in the " 337 "valid user port range of {1} - {2}\n", 338 portnum, LOW_PORT, HIGH_PORT); 339 option_error = 1; 340 break; 341 } 342 if (ch == 'P') 343 gdbserver_portmap.AllowPort(portnum); 344 else if (ch == 'm') 345 min_gdbserver_port = portnum; 346 else 347 max_gdbserver_port = portnum; 348 } break; 349 350 case 2: { 351 uint64_t _fd; 352 if (!llvm::to_integer(optarg, _fd)) { 353 WithColor::error() << "invalid fd " << optarg << "\n"; 354 option_error = 6; 355 } else 356 fd = (shared_fd_t)_fd; 357 } break; 358 359 case 'h': /* fall-through is intentional */ 360 case '?': 361 show_usage = true; 362 break; 363 } 364 } 365 366 if (!LLDBServerUtilities::SetupLogging(log_file, log_channels, 0)) 367 return -1; 368 369 // Make a port map for a port range that was specified. 370 if (min_gdbserver_port && min_gdbserver_port < max_gdbserver_port) { 371 gdbserver_portmap = GDBRemoteCommunicationServerPlatform::PortMap( 372 min_gdbserver_port, max_gdbserver_port); 373 } else if (min_gdbserver_port || max_gdbserver_port) { 374 WithColor::error() << llvm::formatv( 375 "--min-gdbserver-port ({0}) is not lower than " 376 "--max-gdbserver-port ({1})\n", 377 min_gdbserver_port, max_gdbserver_port); 378 option_error = 3; 379 } 380 381 // Print usage and exit if no listening port is specified. 382 if (listen_host_port.empty() && fd == SharedSocket::kInvalidFD) 383 show_usage = true; 384 385 if (show_usage || option_error) { 386 display_usage(progname, subcommand); 387 exit(option_error); 388 } 389 390 // Skip any options we consumed with getopt_long_only. 391 argc -= optind; 392 argv += optind; 393 lldb_private::Args inferior_arguments; 394 inferior_arguments.SetArguments(argc, const_cast<const char **>(argv)); 395 396 if (fd != SharedSocket::kInvalidFD) { 397 // Child process will handle the connection and exit. 398 Log *log = GetLog(LLDBLog::Platform); 399 if (!listen_host_port.empty()) { 400 LLDB_LOGF(log, "lldb-platform child: " 401 "ambiguous parameters --listen and --child-platform-fd"); 402 return socket_error; 403 } 404 405 NativeSocket socket; 406 error = SharedSocket::GetNativeSocket(fd, socket); 407 if (error.Fail()) { 408 LLDB_LOGF(log, "lldb-platform child: %s", error.AsCString()); 409 return socket_error; 410 } 411 412 GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp, "tcp"); 413 if (port_offset > 0) 414 platform.SetPortOffset(port_offset); 415 platform.SetPortMap(std::move(gdbserver_portmap)); 416 platform.SetConnection( 417 std::unique_ptr<Connection>(new ConnectionFileDescriptor( 418 new TCPSocket(socket, /*should_close=*/true, 419 /*child_processes_inherit=*/false)))); 420 client_handle(platform, inferior_arguments); 421 return 0; 422 } 423 424 const bool children_inherit_listen_socket = false; 425 // the test suite makes many connections in parallel, let's not miss any. 426 // The highest this should get reasonably is a function of the number 427 // of target CPUs. For now, let's just use 100. 428 const int backlog = 100; 429 430 std::unique_ptr<Acceptor> acceptor_up(Acceptor::Create( 431 listen_host_port, children_inherit_listen_socket, error)); 432 if (error.Fail()) { 433 fprintf(stderr, "failed to create acceptor: %s", error.AsCString()); 434 exit(socket_error); 435 } 436 437 error = acceptor_up->Listen(backlog); 438 if (error.Fail()) { 439 printf("failed to listen: %s\n", error.AsCString()); 440 exit(socket_error); 441 } 442 if (socket_file) { 443 error = 444 save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); 445 if (error.Fail()) { 446 fprintf(stderr, "failed to write socket id to %s: %s\n", 447 socket_file.GetPath().c_str(), error.AsCString()); 448 return 1; 449 } 450 } 451 452 GDBRemoteCommunicationServerPlatform platform( 453 acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); 454 if (port_offset > 0) 455 platform.SetPortOffset(port_offset); 456 457 do { 458 const bool children_inherit_accept_socket = true; 459 Connection *conn = nullptr; 460 error = acceptor_up->Accept(children_inherit_accept_socket, conn); 461 if (error.Fail()) { 462 WithColor::error() << error.AsCString() << '\n'; 463 exit(socket_error); 464 } 465 printf("Connection established.\n"); 466 467 if (g_server) { 468 std::optional<uint16_t> available_port; 469 { 470 std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); 471 auto port = gdbserver_portmap.GetNextAvailablePort(); 472 if (port) 473 available_port = *port; 474 else 475 llvm::consumeError(port.takeError()); 476 } 477 if (!available_port) { 478 fprintf(stderr, 479 "no available gdbserver port for connection - dropping...\n"); 480 } else { 481 const Socket *conn_socket = 482 static_cast<const Socket *>(conn->GetReadObject().get()); 483 error = 484 spawn_process(progname, conn_socket, *available_port, port_offset, 485 inferior_arguments, log_file, log_channels); 486 if (error.Fail()) { 487 { 488 489 std::lock_guard<std::mutex> guard(gdbserver_portmap_mutex); 490 gdbserver_portmap.FreePort(*available_port); 491 } 492 LLDB_LOGF(GetLog(LLDBLog::Platform), "spawn_process failed: %s", 493 error.AsCString()); 494 WithColor::error() 495 << "spawn_process failed: " << error.AsCString() << "\n"; 496 } 497 } 498 // Parent doesn't need a connection to the lldb client 499 delete conn; 500 501 // Parent will continue to listen for new connections. 502 continue; 503 } else { 504 // If not running as a server, this process will not accept 505 // connections while a connection is active. 506 acceptor_up.reset(); 507 508 // When not running in server mode, use all available ports 509 platform.SetPortMap(std::move(gdbserver_portmap)); 510 } 511 512 platform.SetConnection(std::unique_ptr<Connection>(conn)); 513 client_handle(platform, inferior_arguments); 514 } while (g_server); 515 516 fprintf(stderr, "lldb-server exiting...\n"); 517 518 return 0; 519 } 520