debugserver.cpp revision a2f7423310044cf76d8d52aa9153b7fd8b226b49
1//===-- debugserver.cpp -----------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include <sys/socket.h> 11#include <sys/types.h> 12#include <errno.h> 13#include <getopt.h> 14#include <netinet/in.h> 15#include <sys/select.h> 16#include <sys/sysctl.h> 17#include <string> 18#include <vector> 19#include <asl.h> 20 21#include "CFString.h" 22#include "DNB.h" 23#include "DNBLog.h" 24#include "DNBTimer.h" 25#include "PseudoTerminal.h" 26#include "RNBContext.h" 27#include "RNBServices.h" 28#include "RNBSocket.h" 29#include "RNBRemote.h" 30#include "SysSignal.h" 31 32// Global PID in case we get a signal and need to stop the process... 33nub_process_t g_pid = INVALID_NUB_PROCESS; 34 35//---------------------------------------------------------------------- 36// Run loop modes which determine which run loop function will be called 37//---------------------------------------------------------------------- 38typedef enum 39{ 40 eRNBRunLoopModeInvalid = 0, 41 eRNBRunLoopModeGetStartModeFromRemoteProtocol, 42 eRNBRunLoopModeInferiorAttaching, 43 eRNBRunLoopModeInferiorLaunching, 44 eRNBRunLoopModeInferiorExecuting, 45 eRNBRunLoopModeExit 46} RNBRunLoopMode; 47 48 49//---------------------------------------------------------------------- 50// Global Variables 51//---------------------------------------------------------------------- 52RNBRemoteSP g_remoteSP; 53static int g_lockdown_opt = 0; 54static int g_applist_opt = 0; 55static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault; 56int g_disable_aslr = 0; 57 58int g_isatty = 0; 59 60#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) 61#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0) 62 63//---------------------------------------------------------------------- 64// Get our program path and arguments from the remote connection. 65// We will need to start up the remote connection without a PID, get the 66// arguments, wait for the new process to finish launching and hit its 67// entry point, and then return the run loop mode that should come next. 68//---------------------------------------------------------------------- 69RNBRunLoopMode 70RNBRunLoopGetStartModeFromRemote (RNBRemote* remote) 71{ 72 std::string packet; 73 74 if (remote) 75 { 76 RNBContext& ctx = remote->Context(); 77 uint32_t event_mask = RNBContext::event_read_packet_available | 78 RNBContext::event_read_thread_exiting; 79 80 // Spin waiting to get the A packet. 81 while (1) 82 { 83 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask); 84 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 85 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events); 86 87 if (set_events & RNBContext::event_read_thread_exiting) 88 { 89 RNBLogSTDERR ("error: packet read thread exited."); 90 return eRNBRunLoopModeExit; 91 } 92 93 if (set_events & RNBContext::event_read_packet_available) 94 { 95 rnb_err_t err = rnb_err; 96 RNBRemote::PacketEnum type; 97 98 err = remote->HandleReceivedPacket (&type); 99 100 // check if we tried to attach to a process 101 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) 102 { 103 if (err == rnb_success) 104 return eRNBRunLoopModeInferiorExecuting; 105 else 106 { 107 RNBLogSTDERR ("error: attach failed."); 108 return eRNBRunLoopModeExit; 109 } 110 } 111 112 if (err == rnb_success) 113 { 114 // If we got our arguments we are ready to launch using the arguments 115 // and any environment variables we received. 116 if (type == RNBRemote::set_argv) 117 { 118 return eRNBRunLoopModeInferiorLaunching; 119 } 120 } 121 else if (err == rnb_not_connected) 122 { 123 RNBLogSTDERR ("error: connection lost."); 124 return eRNBRunLoopModeExit; 125 } 126 else 127 { 128 // a catch all for any other gdb remote packets that failed 129 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__); 130 continue; 131 } 132 133 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); 134 } 135 else 136 { 137 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__); 138 return eRNBRunLoopModeExit; 139 } 140 } 141 } 142 return eRNBRunLoopModeExit; 143} 144 145 146//---------------------------------------------------------------------- 147// This run loop mode will wait for the process to launch and hit its 148// entry point. It will currently ignore all events except for the 149// process state changed event, where it watches for the process stopped 150// or crash process state. 151//---------------------------------------------------------------------- 152RNBRunLoopMode 153RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio) 154{ 155 RNBContext& ctx = remote->Context(); 156 157 // The Process stuff takes a c array, the RNBContext has a vector... 158 // So make up a c array. 159 160 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0)); 161 162 size_t inferior_argc = ctx.ArgumentCount(); 163 // Initialize inferior_argv with inferior_argc + 1 NULLs 164 std::vector<const char *> inferior_argv(inferior_argc + 1, NULL); 165 166 size_t i; 167 for (i = 0; i < inferior_argc; i++) 168 inferior_argv[i] = ctx.ArgumentAtIndex(i); 169 170 // Pass the environment array the same way: 171 172 size_t inferior_envc = ctx.EnvironmentCount(); 173 // Initialize inferior_argv with inferior_argc + 1 NULLs 174 std::vector<const char *> inferior_envp(inferior_envc + 1, NULL); 175 176 for (i = 0; i < inferior_envc; i++) 177 inferior_envp[i] = ctx.EnvironmentAtIndex(i); 178 179 // Our launch type hasn't been set to anything concrete, so we need to 180 // figure our how we are going to launch automatically. 181 182 nub_launch_flavor_t launch_flavor = g_launch_flavor; 183 if (launch_flavor == eLaunchFlavorDefault) 184 { 185 // Our default launch method is posix spawn 186 launch_flavor = eLaunchFlavorPosixSpawn; 187 188#if defined (__arm__) 189 // Check if we have an app bundle, if so launch using SpringBoard. 190 if (strstr(inferior_argv[0], ".app")) 191 { 192 launch_flavor = eLaunchFlavorSpringBoard; 193 } 194#endif 195 } 196 197 ctx.SetLaunchFlavor(launch_flavor); 198 char resolved_path[PATH_MAX]; 199 200 // If we fail to resolve the path to our executable, then just use what we 201 // were given and hope for the best 202 if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) ) 203 ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path)); 204 205 char launch_err_str[PATH_MAX]; 206 launch_err_str[0] = '\0'; 207 nub_process_t pid = DNBProcessLaunch (resolved_path, 208 &inferior_argv[0], 209 &inferior_envp[0], 210 ctx.GetWorkingDirectory(), 211 stdin_path, 212 stdout_path, 213 stderr_path, 214 no_stdio, 215 launch_flavor, 216 g_disable_aslr, 217 launch_err_str, 218 sizeof(launch_err_str)); 219 220 g_pid = pid; 221 222 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0) 223 { 224 DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str); 225 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 226 ctx.LaunchStatus().SetErrorString(launch_err_str); 227 } 228 else 229 { 230 ctx.LaunchStatus().Clear(); 231 } 232 233 if (remote->Comm().IsConnected()) 234 { 235 // It we are connected already, the next thing gdb will do is ask 236 // whether the launch succeeded, and if not, whether there is an 237 // error code. So we need to fetch one packet from gdb before we wait 238 // on the stop from the target. 239 240 uint32_t event_mask = RNBContext::event_read_packet_available; 241 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 242 243 if (set_events & RNBContext::event_read_packet_available) 244 { 245 rnb_err_t err = rnb_err; 246 RNBRemote::PacketEnum type; 247 248 err = remote->HandleReceivedPacket (&type); 249 250 if (err != rnb_success) 251 { 252 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__); 253 return eRNBRunLoopModeExit; 254 } 255 if (type != RNBRemote::query_launch_success) 256 { 257 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__); 258 } 259 } 260 } 261 262 while (pid != INVALID_NUB_PROCESS) 263 { 264 // Wait for process to start up and hit entry point 265 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid); 266 nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL); 267 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events); 268 269 if (set_events == 0) 270 { 271 pid = INVALID_NUB_PROCESS; 272 g_pid = pid; 273 } 274 else 275 { 276 if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged)) 277 { 278 nub_state_t pid_state = DNBProcessGetState (pid); 279 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state)); 280 281 switch (pid_state) 282 { 283 default: 284 case eStateInvalid: 285 case eStateUnloaded: 286 case eStateAttaching: 287 case eStateLaunching: 288 case eStateSuspended: 289 break; // Ignore 290 291 case eStateRunning: 292 case eStateStepping: 293 // Still waiting to stop at entry point... 294 break; 295 296 case eStateStopped: 297 case eStateCrashed: 298 ctx.SetProcessID(pid); 299 return eRNBRunLoopModeInferiorExecuting; 300 301 case eStateDetached: 302 case eStateExited: 303 pid = INVALID_NUB_PROCESS; 304 g_pid = pid; 305 return eRNBRunLoopModeExit; 306 } 307 } 308 309 DNBProcessResetEvents(pid, set_events); 310 } 311 } 312 313 return eRNBRunLoopModeExit; 314} 315 316 317//---------------------------------------------------------------------- 318// This run loop mode will wait for the process to launch and hit its 319// entry point. It will currently ignore all events except for the 320// process state changed event, where it watches for the process stopped 321// or crash process state. 322//---------------------------------------------------------------------- 323RNBRunLoopMode 324RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid) 325{ 326 RNBContext& ctx = remote->Context(); 327 328 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid); 329 char err_str[1024]; 330 pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str)); 331 g_pid = pid; 332 333 if (pid == INVALID_NUB_PROCESS) 334 { 335 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 336 if (err_str[0]) 337 ctx.LaunchStatus().SetErrorString(err_str); 338 return eRNBRunLoopModeExit; 339 } 340 else 341 { 342 ctx.SetProcessID(pid); 343 return eRNBRunLoopModeInferiorExecuting; 344 } 345} 346 347//---------------------------------------------------------------------- 348// Watch for signals: 349// SIGINT: so we can halt our inferior. (disabled for now) 350// SIGPIPE: in case our child process dies 351//---------------------------------------------------------------------- 352int g_sigint_received = 0; 353int g_sigpipe_received = 0; 354void 355signal_handler(int signo) 356{ 357 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo)); 358 359 switch (signo) 360 { 361 case SIGINT: 362 g_sigint_received++; 363 if (g_pid != INVALID_NUB_PROCESS) 364 { 365 // Only send a SIGINT once... 366 if (g_sigint_received == 1) 367 { 368 switch (DNBProcessGetState (g_pid)) 369 { 370 case eStateRunning: 371 case eStateStepping: 372 DNBProcessSignal (g_pid, SIGSTOP); 373 return; 374 } 375 } 376 } 377 exit (SIGINT); 378 break; 379 380 case SIGPIPE: 381 g_sigpipe_received = 1; 382 break; 383 } 384} 385 386// Return the new run loop mode based off of the current process state 387RNBRunLoopMode 388HandleProcessStateChange (RNBRemote *remote, bool initialize) 389{ 390 RNBContext& ctx = remote->Context(); 391 nub_process_t pid = ctx.ProcessID(); 392 393 if (pid == INVALID_NUB_PROCESS) 394 { 395 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__); 396 return eRNBRunLoopModeExit; 397 } 398 nub_state_t pid_state = DNBProcessGetState (pid); 399 400 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state)); 401 402 switch (pid_state) 403 { 404 case eStateInvalid: 405 case eStateUnloaded: 406 // Something bad happened 407 return eRNBRunLoopModeExit; 408 break; 409 410 case eStateAttaching: 411 case eStateLaunching: 412 return eRNBRunLoopModeInferiorExecuting; 413 414 case eStateSuspended: 415 case eStateCrashed: 416 case eStateStopped: 417 // If we stop due to a signal, so clear the fact that we got a SIGINT 418 // so we can stop ourselves again (but only while our inferior 419 // process is running..) 420 g_sigint_received = 0; 421 if (initialize == false) 422 { 423 // Compare the last stop count to our current notion of a stop count 424 // to make sure we don't notify more than once for a given stop. 425 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount(); 426 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid)); 427 if (pid_stop_count_changed) 428 { 429 remote->FlushSTDIO(); 430 431 if (ctx.GetProcessStopCount() == 1) 432 { 433 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 434 } 435 else 436 { 437 438 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 439 remote->NotifyThatProcessStopped (); 440 } 441 } 442 else 443 { 444 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count); 445 } 446 } 447 return eRNBRunLoopModeInferiorExecuting; 448 449 case eStateStepping: 450 case eStateRunning: 451 return eRNBRunLoopModeInferiorExecuting; 452 453 case eStateExited: 454 remote->HandlePacket_last_signal(NULL); 455 return eRNBRunLoopModeExit; 456 457 } 458 459 // Catch all... 460 return eRNBRunLoopModeExit; 461} 462// This function handles the case where our inferior program is stopped and 463// we are waiting for gdb remote protocol packets. When a packet occurs that 464// makes the inferior run, we need to leave this function with a new state 465// as the return code. 466RNBRunLoopMode 467RNBRunLoopInferiorExecuting (RNBRemote *remote) 468{ 469 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__); 470 RNBContext& ctx = remote->Context(); 471 472 // Init our mode and set 'is_running' based on the current process state 473 RNBRunLoopMode mode = HandleProcessStateChange (remote, true); 474 475 while (ctx.ProcessID() != INVALID_NUB_PROCESS) 476 { 477 478 std::string set_events_str; 479 uint32_t event_mask = ctx.NormalEventBits(); 480 481 if (!ctx.ProcessStateRunning()) 482 { 483 // Clear the stdio bits if we are not running so we don't send any async packets 484 event_mask &= ~RNBContext::event_proc_stdio_available; 485 } 486 487 // We want to make sure we consume all process state changes and have 488 // whomever is notifying us to wait for us to reset the event bit before 489 // continuing. 490 //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed); 491 492 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask); 493 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask); 494 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str)); 495 496 if (set_events) 497 { 498 if ((set_events & RNBContext::event_proc_thread_exiting) || 499 (set_events & RNBContext::event_proc_stdio_available)) 500 { 501 remote->FlushSTDIO(); 502 } 503 504 if (set_events & RNBContext::event_read_packet_available) 505 { 506 // handleReceivedPacket will take care of resetting the 507 // event_read_packet_available events when there are no more... 508 set_events ^= RNBContext::event_read_packet_available; 509 510 if (ctx.ProcessStateRunning()) 511 { 512 if (remote->HandleAsyncPacket() == rnb_not_connected) 513 { 514 // TODO: connect again? Exit? 515 } 516 } 517 else 518 { 519 if (remote->HandleReceivedPacket() == rnb_not_connected) 520 { 521 // TODO: connect again? Exit? 522 } 523 } 524 } 525 526 if (set_events & RNBContext::event_proc_state_changed) 527 { 528 mode = HandleProcessStateChange (remote, false); 529 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed); 530 set_events ^= RNBContext::event_proc_state_changed; 531 } 532 533 if (set_events & RNBContext::event_proc_thread_exiting) 534 { 535 mode = eRNBRunLoopModeExit; 536 } 537 538 if (set_events & RNBContext::event_read_thread_exiting) 539 { 540 // Out remote packet receiving thread exited, exit for now. 541 if (ctx.HasValidProcessID()) 542 { 543 // TODO: We should add code that will leave the current process 544 // in its current state and listen for another connection... 545 if (ctx.ProcessStateRunning()) 546 { 547 DNBProcessKill (ctx.ProcessID()); 548 } 549 } 550 mode = eRNBRunLoopModeExit; 551 } 552 } 553 554 // Reset all event bits that weren't reset for now... 555 if (set_events != 0) 556 ctx.Events().ResetEvents(set_events); 557 558 if (mode != eRNBRunLoopModeInferiorExecuting) 559 break; 560 } 561 562 return mode; 563} 564 565 566//---------------------------------------------------------------------- 567// Convenience function to set up the remote listening port 568// Returns 1 for success 0 for failure. 569//---------------------------------------------------------------------- 570 571static int 572StartListening (RNBRemote *remote, int listen_port) 573{ 574 if (!remote->Comm().IsConnected()) 575 { 576 RNBLogSTDOUT ("Listening to port %i...\n", listen_port); 577 if (remote->Comm().Listen(listen_port) != rnb_success) 578 { 579 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); 580 return 0; 581 } 582 else 583 { 584 remote->StartReadRemoteDataThread(); 585 } 586 } 587 return 1; 588} 589 590//---------------------------------------------------------------------- 591// ASL Logging callback that can be registered with DNBLogSetLogCallback 592//---------------------------------------------------------------------- 593void 594ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args) 595{ 596 if (format == NULL) 597 return; 598 static aslmsg g_aslmsg = NULL; 599 if (g_aslmsg == NULL) 600 { 601 g_aslmsg = ::asl_new (ASL_TYPE_MSG); 602 char asl_key_sender[PATH_MAX]; 603 snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM); 604 ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); 605 } 606 607 int asl_level; 608 if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT; 609 else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR; 610 else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING; 611 else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO; 612 else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG; 613 614 ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); 615} 616 617//---------------------------------------------------------------------- 618// FILE based Logging callback that can be registered with 619// DNBLogSetLogCallback 620//---------------------------------------------------------------------- 621void 622FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args) 623{ 624 if (baton == NULL || format == NULL) 625 return; 626 627 ::vfprintf ((FILE *)baton, format, args); 628 ::fprintf ((FILE *)baton, "\n"); 629} 630 631 632void 633show_usage_and_exit (int exit_code) 634{ 635 RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); 636 RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME); 637 RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME); 638 RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME); 639 RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME); 640 RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME); 641 exit (exit_code); 642} 643 644 645//---------------------------------------------------------------------- 646// option descriptors for getopt_long() 647//---------------------------------------------------------------------- 648static struct option g_long_options[] = 649{ 650 { "attach", required_argument, NULL, 'a' }, 651 { "arch", required_argument, NULL, 'A' }, 652 { "debug", no_argument, NULL, 'g' }, 653 { "verbose", no_argument, NULL, 'v' }, 654 { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k" 655 { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t" 656 { "log-file", required_argument, NULL, 'l' }, 657 { "log-flags", required_argument, NULL, 'f' }, 658 { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only) 659 { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG 660 { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name 661 { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name 662 { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture. 663 { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) (only if debugserver launches the process) 664 { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process) 665 { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process) 666 { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process) 667 { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process) 668 { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session 669 { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization 670 { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process) 671 { NULL, 0, NULL, 0 } 672}; 673 674 675//---------------------------------------------------------------------- 676// main 677//---------------------------------------------------------------------- 678int 679main (int argc, char *argv[]) 680{ 681 g_isatty = ::isatty (STDIN_FILENO); 682 683 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n", 684 // getuid(), 685 // geteuid(), 686 // getgid(), 687 // getegid()); 688 689 690 // signal (SIGINT, signal_handler); 691 signal (SIGPIPE, signal_handler); 692 signal (SIGHUP, signal_handler); 693 694 g_remoteSP.reset (new RNBRemote ()); 695 696 697 RNBRemote *remote = g_remoteSP.get(); 698 if (remote == NULL) 699 { 700 RNBLogSTDERR ("error: failed to create a remote connection class\n"); 701 return -1; 702 } 703 704 RNBContext& ctx = remote->Context(); 705 706 int i; 707 int attach_pid = INVALID_NUB_PROCESS; 708 709 FILE* log_file = NULL; 710 uint32_t log_flags = 0; 711 // Parse our options 712 int ch; 713 int long_option_index = 0; 714 int debug = 0; 715 std::string compile_options; 716 std::string waitfor_pid_name; // Wait for a process that starts with this name 717 std::string attach_pid_name; 718 std::string arch_name; 719 std::string working_dir; // The new working directory to use for the inferior 720 useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec. 721 useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever. 722 bool no_stdio = false; 723 724#if !defined (DNBLOG_ENABLED) 725 compile_options += "(no-logging) "; 726#endif 727 728 RNBRunLoopMode start_mode = eRNBRunLoopModeExit; 729 730 while ((ch = getopt_long(argc, argv, "a:A:d:gi:vktl:f:w:x:rs:n", g_long_options, &long_option_index)) != -1) 731 { 732 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", 733 ch, (uint8_t)ch, 734 g_long_options[long_option_index].name, 735 g_long_options[long_option_index].has_arg ? '=' : ' ', 736 optarg ? optarg : ""); 737 switch (ch) 738 { 739 case 0: // Any optional that auto set themselves will return 0 740 break; 741 742 case 'A': 743 if (optarg && optarg[0]) 744 arch_name.assign(optarg); 745 break; 746 747 case 'a': 748 if (optarg && optarg[0]) 749 { 750 if (isdigit(optarg[0])) 751 { 752 char *end = NULL; 753 attach_pid = strtoul(optarg, &end, 0); 754 if (end == NULL || *end != '\0') 755 { 756 RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg); 757 exit (4); 758 } 759 } 760 else 761 { 762 attach_pid_name = optarg; 763 } 764 start_mode = eRNBRunLoopModeInferiorAttaching; 765 } 766 break; 767 768 // --waitfor=NAME 769 case 'w': 770 if (optarg && optarg[0]) 771 { 772 waitfor_pid_name = optarg; 773 start_mode = eRNBRunLoopModeInferiorAttaching; 774 } 775 break; 776 777 // --waitfor-interval=USEC 778 case 'i': 779 if (optarg && optarg[0]) 780 { 781 char *end = NULL; 782 waitfor_interval = strtoul(optarg, &end, 0); 783 if (end == NULL || *end != '\0') 784 { 785 RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg); 786 exit (6); 787 } 788 } 789 break; 790 791 // --waitfor-duration=SEC 792 case 'd': 793 if (optarg && optarg[0]) 794 { 795 char *end = NULL; 796 waitfor_duration = strtoul(optarg, &end, 0); 797 if (end == NULL || *end != '\0') 798 { 799 RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg); 800 exit (7); 801 } 802 } 803 break; 804 805 case 'W': 806 if (optarg && optarg[0]) 807 working_dir.assign(optarg); 808 break; 809 810 case 'x': 811 if (optarg && optarg[0]) 812 { 813 if (strcasecmp(optarg, "auto") == 0) 814 g_launch_flavor = eLaunchFlavorDefault; 815 else if (strcasestr(optarg, "posix") == optarg) 816 g_launch_flavor = eLaunchFlavorPosixSpawn; 817 else if (strcasestr(optarg, "fork") == optarg) 818 g_launch_flavor = eLaunchFlavorForkExec; 819#if defined (__arm__) 820 else if (strcasestr(optarg, "spring") == optarg) 821 g_launch_flavor = eLaunchFlavorSpringBoard; 822#endif 823 else 824 { 825 RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg); 826 RNBLogSTDERR ("Valid values TYPE are:\n"); 827 RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n"); 828 RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n"); 829 RNBLogSTDERR (" fork Launch the executable using fork and exec.\n"); 830#if defined (__arm__) 831 RNBLogSTDERR (" spring Launch the executable through Springboard.\n"); 832#endif 833 exit (5); 834 } 835 } 836 break; 837 838 case 'l': // Set Log File 839 if (optarg && optarg[0]) 840 { 841 if (strcasecmp(optarg, "stdout") == 0) 842 log_file = stdout; 843 else if (strcasecmp(optarg, "stderr") == 0) 844 log_file = stderr; 845 else 846 { 847 log_file = fopen(optarg, "w"); 848 if (log_file != NULL) 849 setlinebuf(log_file); 850 } 851 852 if (log_file == NULL) 853 { 854 const char *errno_str = strerror(errno); 855 RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error"); 856 } 857 } 858 break; 859 860 case 'f': // Log Flags 861 if (optarg && optarg[0]) 862 log_flags = strtoul(optarg, NULL, 0); 863 break; 864 865 case 'g': 866 debug = 1; 867 DNBLogSetDebug(1); 868 break; 869 870 case 't': 871 g_applist_opt = 1; 872 break; 873 874 case 'k': 875 g_lockdown_opt = 1; 876 break; 877 878 case 'r': 879 remote->SetUseNativeRegisters (true); 880 break; 881 882 case 'v': 883 DNBLogSetVerbose(1); 884 break; 885 886 case 's': 887 ctx.GetSTDIN().assign(optarg); 888 ctx.GetSTDOUT().assign(optarg); 889 ctx.GetSTDERR().assign(optarg); 890 break; 891 892 case 'I': 893 ctx.GetSTDIN().assign(optarg); 894 break; 895 896 case 'O': 897 ctx.GetSTDOUT().assign(optarg); 898 break; 899 900 case 'E': 901 ctx.GetSTDERR().assign(optarg); 902 break; 903 904 case 'n': 905 no_stdio = true; 906 break; 907 908 case 'S': 909 // Put debugserver into a new session. Terminals group processes 910 // into sessions and when a special terminal key sequences 911 // (like control+c) are typed they can cause signals to go out to 912 // all processes in a session. Using this --setsid (-S) option 913 // will cause debugserver to run in its own sessions and be free 914 // from such issues. 915 // 916 // This is useful when debugserver is spawned from a command 917 // line application that uses debugserver to do the debugging, 918 // yet that application doesn't want debugserver receiving the 919 // signals sent to the session (i.e. dying when anyone hits ^C). 920 setsid(); 921 break; 922 case 'D': 923 g_disable_aslr = 1; 924 break; 925 } 926 } 927 928 if (arch_name.empty()) 929 { 930#if defined (__arm__) 931 arch_name.assign ("arm"); 932#endif 933 } 934 else 935 { 936 DNBSetArchitecture (arch_name.c_str()); 937 } 938 939// if (arch_name.empty()) 940// { 941// fprintf(stderr, "error: no architecture was specified\n"); 942// exit (8); 943// } 944 // Skip any options we consumed with getopt_long 945 argc -= optind; 946 argv += optind; 947 948 949 if (!working_dir.empty()) 950 { 951 if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false) 952 { 953 RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str()); 954 exit (8); 955 } 956 } 957 958 remote->Initialize(); 959 960 // It is ok for us to set NULL as the logfile (this will disable any logging) 961 962 if (log_file != NULL) 963 { 964 DNBLogSetLogCallback(FileLogCallback, log_file); 965 // If our log file was set, yet we have no log flags, log everything! 966 if (log_flags == 0) 967 log_flags = LOG_ALL | LOG_RNB_ALL; 968 969 DNBLogSetLogMask (log_flags); 970 } 971 else 972 { 973 // Enable DNB logging 974 DNBLogSetLogCallback(ASLLogCallback, NULL); 975 DNBLogSetLogMask (log_flags); 976 977 } 978 979 if (DNBLogEnabled()) 980 { 981 for (i=0; i<argc; i++) 982 DNBLogDebug("argv[%i] = %s", i, argv[i]); 983 } 984 985 // as long as we're dropping remotenub in as a replacement for gdbserver, 986 // explicitly note that this is not gdbserver. 987 988 RNBLogSTDOUT ("%s-%g %sfor %s.\n", 989 DEBUGSERVER_PROGRAM_NAME, 990 DEBUGSERVER_VERSION_NUM, 991 compile_options.c_str(), 992 RNB_ARCH); 993 994 int listen_port = INT32_MAX; 995 char str[PATH_MAX]; 996 997 if (g_lockdown_opt == 0 && g_applist_opt == 0) 998 { 999 // Make sure we at least have port 1000 if (argc < 1) 1001 { 1002 show_usage_and_exit (1); 1003 } 1004 // accept 'localhost:' prefix on port number 1005 1006 int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port); 1007 if (items_scanned == 2) 1008 { 1009 DNBLogDebug("host = '%s' port = %i", str, listen_port); 1010 } 1011 else if (argv[0][0] == '/') 1012 { 1013 listen_port = INT32_MAX; 1014 strncpy(str, argv[0], sizeof(str)); 1015 } 1016 else 1017 { 1018 show_usage_and_exit (2); 1019 } 1020 1021 // We just used the 'host:port' or the '/path/file' arg... 1022 argc--; 1023 argv++; 1024 1025 } 1026 1027 // If we know we're waiting to attach, we don't need any of this other info. 1028 if (start_mode != eRNBRunLoopModeInferiorAttaching) 1029 { 1030 if (argc == 0 || g_lockdown_opt) 1031 { 1032 if (g_lockdown_opt != 0) 1033 { 1034 // Work around for SIGPIPE crashes due to posix_spawn issue. 1035 // We have to close STDOUT and STDERR, else the first time we 1036 // try and do any, we get SIGPIPE and die as posix_spawn is 1037 // doing bad things with our file descriptors at the moment. 1038 int null = open("/dev/null", O_RDWR); 1039 dup2(null, STDOUT_FILENO); 1040 dup2(null, STDERR_FILENO); 1041 } 1042 else if (g_applist_opt != 0) 1043 { 1044 // List all applications we are able to see 1045 std::string applist_plist; 1046 int err = ListApplications(applist_plist, false, false); 1047 if (err == 0) 1048 { 1049 fputs (applist_plist.c_str(), stdout); 1050 } 1051 else 1052 { 1053 RNBLogSTDERR ("error: ListApplications returned error %i\n", err); 1054 } 1055 // Exit with appropriate error if we were asked to list the applications 1056 // with no other args were given (and we weren't trying to do this over 1057 // lockdown) 1058 return err; 1059 } 1060 1061 DNBLogDebug("Get args from remote protocol..."); 1062 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol; 1063 } 1064 else 1065 { 1066 start_mode = eRNBRunLoopModeInferiorLaunching; 1067 // Fill in the argv array in the context from the rest of our args. 1068 // Skip the name of this executable and the port number 1069 for (int i = 0; i < argc; i++) 1070 { 1071 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]); 1072 ctx.PushArgument (argv[i]); 1073 } 1074 } 1075 } 1076 1077 if (start_mode == eRNBRunLoopModeExit) 1078 return -1; 1079 1080 RNBRunLoopMode mode = start_mode; 1081 char err_str[1024] = {'\0'}; 1082 1083 while (mode != eRNBRunLoopModeExit) 1084 { 1085 switch (mode) 1086 { 1087 case eRNBRunLoopModeGetStartModeFromRemoteProtocol: 1088#if defined (__arm__) 1089 if (g_lockdown_opt) 1090 { 1091 if (!remote->Comm().IsConnected()) 1092 { 1093 if (remote->Comm().ConnectToService () != rnb_success) 1094 { 1095 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n"); 1096 mode = eRNBRunLoopModeExit; 1097 } 1098 else if (g_applist_opt != 0) 1099 { 1100 // List all applications we are able to see 1101 std::string applist_plist; 1102 if (ListApplications(applist_plist, false, false) == 0) 1103 { 1104 DNBLogDebug("Task list: %s", applist_plist.c_str()); 1105 1106 remote->Comm().Write(applist_plist.c_str(), applist_plist.size()); 1107 // Issue a read that will never yield any data until the other side 1108 // closes the socket so this process doesn't just exit and cause the 1109 // socket to close prematurely on the other end and cause data loss. 1110 std::string buf; 1111 remote->Comm().Read(buf); 1112 } 1113 remote->Comm().Disconnect(false); 1114 mode = eRNBRunLoopModeExit; 1115 break; 1116 } 1117 else 1118 { 1119 // Start watching for remote packets 1120 remote->StartReadRemoteDataThread(); 1121 } 1122 } 1123 } 1124 else 1125#endif 1126 if (listen_port != INT32_MAX) 1127 { 1128 if (!StartListening (remote, listen_port)) 1129 mode = eRNBRunLoopModeExit; 1130 } 1131 else if (str[0] == '/') 1132 { 1133 if (remote->Comm().OpenFile (str)) 1134 mode = eRNBRunLoopModeExit; 1135 } 1136 if (mode != eRNBRunLoopModeExit) 1137 { 1138 RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n"); 1139 1140 mode = RNBRunLoopGetStartModeFromRemote (remote); 1141 } 1142 break; 1143 1144 case eRNBRunLoopModeInferiorAttaching: 1145 if (!waitfor_pid_name.empty()) 1146 { 1147 // Set our end wait time if we are using a waitfor-duration 1148 // option that may have been specified 1149 struct timespec attach_timeout_abstime, *timeout_ptr = NULL; 1150 if (waitfor_duration != 0) 1151 { 1152 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); 1153 timeout_ptr = &attach_timeout_abstime; 1154 } 1155 nub_launch_flavor_t launch_flavor = g_launch_flavor; 1156 if (launch_flavor == eLaunchFlavorDefault) 1157 { 1158 // Our default launch method is posix spawn 1159 launch_flavor = eLaunchFlavorPosixSpawn; 1160 1161#if defined (__arm__) 1162 // Check if we have an app bundle, if so launch using SpringBoard. 1163 if (waitfor_pid_name.find (".app") != std::string::npos) 1164 { 1165 launch_flavor = eLaunchFlavorSpringBoard; 1166 } 1167#endif 1168 } 1169 1170 ctx.SetLaunchFlavor(launch_flavor); 1171 1172 nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str)); 1173 g_pid = pid; 1174 1175 if (pid == INVALID_NUB_PROCESS) 1176 { 1177 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 1178 if (err_str[0]) 1179 ctx.LaunchStatus().SetErrorString(err_str); 1180 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); 1181 mode = eRNBRunLoopModeExit; 1182 } 1183 else 1184 { 1185 ctx.SetProcessID(pid); 1186 mode = eRNBRunLoopModeInferiorExecuting; 1187 } 1188 } 1189 else if (attach_pid != INVALID_NUB_PROCESS) 1190 { 1191 1192 RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid); 1193 nub_process_t attached_pid; 1194 mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid); 1195 if (mode != eRNBRunLoopModeInferiorExecuting) 1196 { 1197 const char *error_str = remote->Context().LaunchStatus().AsString(); 1198 RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error."); 1199 mode = eRNBRunLoopModeExit; 1200 } 1201 } 1202 else if (!attach_pid_name.empty ()) 1203 { 1204 struct timespec attach_timeout_abstime, *timeout_ptr = NULL; 1205 if (waitfor_duration != 0) 1206 { 1207 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0); 1208 timeout_ptr = &attach_timeout_abstime; 1209 } 1210 1211 nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str)); 1212 g_pid = pid; 1213 if (pid == INVALID_NUB_PROCESS) 1214 { 1215 ctx.LaunchStatus().SetError(-1, DNBError::Generic); 1216 if (err_str[0]) 1217 ctx.LaunchStatus().SetErrorString(err_str); 1218 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str); 1219 mode = eRNBRunLoopModeExit; 1220 } 1221 else 1222 { 1223 ctx.SetProcessID(pid); 1224 mode = eRNBRunLoopModeInferiorExecuting; 1225 } 1226 1227 } 1228 else 1229 { 1230 RNBLogSTDERR ("error: asked to attach with empty name and invalid PID."); 1231 mode = eRNBRunLoopModeExit; 1232 } 1233 1234 if (mode != eRNBRunLoopModeExit) 1235 { 1236 if (listen_port != INT32_MAX) 1237 { 1238 if (!StartListening (remote, listen_port)) 1239 mode = eRNBRunLoopModeExit; 1240 } 1241 else if (str[0] == '/') 1242 { 1243 if (remote->Comm().OpenFile (str)) 1244 mode = eRNBRunLoopModeExit; 1245 } 1246 if (mode != eRNBRunLoopModeExit) 1247 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid); 1248 } 1249 break; 1250 1251 case eRNBRunLoopModeInferiorLaunching: 1252 { 1253 mode = RNBRunLoopLaunchInferior (remote, 1254 ctx.GetSTDINPath(), 1255 ctx.GetSTDOUTPath(), 1256 ctx.GetSTDERRPath(), 1257 no_stdio); 1258 1259 if (mode == eRNBRunLoopModeInferiorExecuting) 1260 { 1261 if (listen_port != INT32_MAX) 1262 { 1263 if (!StartListening (remote, listen_port)) 1264 mode = eRNBRunLoopModeExit; 1265 } 1266 else if (str[0] == '/') 1267 { 1268 if (remote->Comm().OpenFile (str)) 1269 mode = eRNBRunLoopModeExit; 1270 } 1271 1272 if (mode != eRNBRunLoopModeExit) 1273 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n"); 1274 } 1275 else 1276 { 1277 const char *error_str = remote->Context().LaunchStatus().AsString(); 1278 RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error."); 1279 } 1280 } 1281 break; 1282 1283 case eRNBRunLoopModeInferiorExecuting: 1284 mode = RNBRunLoopInferiorExecuting(remote); 1285 break; 1286 1287 default: 1288 mode = eRNBRunLoopModeExit; 1289 case eRNBRunLoopModeExit: 1290 break; 1291 } 1292 } 1293 1294 remote->StopReadRemoteDataThread (); 1295 remote->Context().SetProcessID(INVALID_NUB_PROCESS); 1296 1297 return 0; 1298} 1299