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