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