1698 lines · cpp
1//===-- debugserver.cpp -----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <arpa/inet.h>10#include <asl.h>11#include <cerrno>12#include <crt_externs.h>13#include <getopt.h>14#include <netdb.h>15#include <netinet/in.h>16#include <netinet/tcp.h>17#include <string>18#include <sys/select.h>19#include <sys/socket.h>20#include <sys/sysctl.h>21#include <sys/types.h>22#include <sys/un.h>23 24#include <memory>25#include <vector>26 27#if defined(__APPLE__)28#include <sched.h>29extern "C" int proc_set_wakemon_params(pid_t, int,30 int); // <libproc_internal.h> SPI31#endif32 33#include "CFString.h"34#include "DNB.h"35#include "DNBLog.h"36#include "DNBTimer.h"37#include "OsLogger.h"38#include "PseudoTerminal.h"39#include "RNBContext.h"40#include "RNBRemote.h"41#include "RNBServices.h"42#include "RNBSocket.h"43#include "SysSignal.h"44 45// Global PID in case we get a signal and need to stop the process...46nub_process_t g_pid = INVALID_NUB_PROCESS;47 48// Run loop modes which determine which run loop function will be called49enum RNBRunLoopMode {50 eRNBRunLoopModeInvalid = 0,51 eRNBRunLoopModeGetStartModeFromRemoteProtocol,52 eRNBRunLoopModeInferiorAttaching,53 eRNBRunLoopModeInferiorLaunching,54 eRNBRunLoopModeInferiorExecuting,55 eRNBRunLoopModePlatformMode,56 eRNBRunLoopModeExit57};58 59// Global Variables60RNBRemoteSP g_remoteSP;61static int g_lockdown_opt = 0;62static int g_applist_opt = 0;63static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;64int g_disable_aslr = 0;65 66int g_isatty = 0;67bool g_detach_on_error = true;68 69#define RNBLogSTDOUT(fmt, ...) \70 do { \71 if (g_isatty) { \72 fprintf(stdout, fmt, ##__VA_ARGS__); \73 } else { \74 _DNBLog(0, fmt, ##__VA_ARGS__); \75 } \76 } while (0)77#define RNBLogSTDERR(fmt, ...) \78 do { \79 if (g_isatty) { \80 fprintf(stderr, fmt, ##__VA_ARGS__); \81 } else { \82 _DNBLog(0, fmt, ##__VA_ARGS__); \83 } \84 } while (0)85 86// Get our program path and arguments from the remote connection.87// We will need to start up the remote connection without a PID, get the88// arguments, wait for the new process to finish launching and hit its89// entry point, and then return the run loop mode that should come next.90RNBRunLoopMode RNBRunLoopGetStartModeFromRemote(RNBRemote *remote) {91 std::string packet;92 93 if (remote) {94 RNBContext &ctx = remote->Context();95 uint32_t event_mask = RNBContext::event_read_packet_available |96 RNBContext::event_read_thread_exiting;97 98 // Spin waiting to get the A packet.99 while (true) {100 DNBLogThreadedIf(LOG_RNB_MAX,101 "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",102 __FUNCTION__, event_mask);103 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);104 DNBLogThreadedIf(LOG_RNB_MAX,105 "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x",106 __FUNCTION__, event_mask, set_events);107 108 if (set_events & RNBContext::event_read_thread_exiting) {109 RNBLogSTDERR("error: packet read thread exited.\n");110 return eRNBRunLoopModeExit;111 }112 113 if (set_events & RNBContext::event_read_packet_available) {114 rnb_err_t err = rnb_err;115 RNBRemote::PacketEnum type;116 117 err = remote->HandleReceivedPacket(&type);118 119 // check if we tried to attach to a process120 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait ||121 type == RNBRemote::vattachorwait) {122 if (err == rnb_success) {123 RNBLogSTDOUT("Attach succeeded, ready to debug.\n");124 return eRNBRunLoopModeInferiorExecuting;125 } else {126 RNBLogSTDERR("error: attach failed.\n");127 return eRNBRunLoopModeExit;128 }129 }130 131 if (err == rnb_success) {132 // If we got our arguments we are ready to launch using the arguments133 // and any environment variables we received.134 if (type == RNBRemote::set_argv) {135 return eRNBRunLoopModeInferiorLaunching;136 }137 } else if (err == rnb_not_connected) {138 RNBLogSTDERR("error: connection lost.\n");139 return eRNBRunLoopModeExit;140 } else {141 // a catch all for any other gdb remote packets that failed142 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",143 __FUNCTION__);144 continue;145 }146 147 DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);148 } else {149 DNBLogThreadedIf(LOG_RNB_MINIMAL,150 "%s Connection closed before getting \"A\" packet.",151 __FUNCTION__);152 return eRNBRunLoopModeExit;153 }154 }155 }156 return eRNBRunLoopModeExit;157}158 159static nub_launch_flavor_t default_launch_flavor(const char *app_name) {160#if defined(WITH_FBS) || defined(WITH_BKS) || defined(WITH_SPRINGBOARD)161 // Check the name to see if it ends with .app162 auto is_dot_app = [](const char *app_name) {163 size_t len = strlen(app_name);164 if (len < 4)165 return false;166 167 if (app_name[len - 4] == '.' && app_name[len - 3] == 'a' &&168 app_name[len - 2] == 'p' && app_name[len - 1] == 'p')169 return true;170 return false;171 };172 173 if (is_dot_app(app_name)) {174#if defined WITH_FBS175 // Check if we have an app bundle, if so launch using FrontBoard Services.176 return eLaunchFlavorFBS;177#elif defined WITH_BKS178 // Check if we have an app bundle, if so launch using BackBoard Services.179 return eLaunchFlavorBKS;180#elif defined WITH_SPRINGBOARD181 // Check if we have an app bundle, if so launch using SpringBoard.182 return eLaunchFlavorSpringBoard;183#endif184 }185#endif186 187 // Our default launch method is posix spawn188 return eLaunchFlavorPosixSpawn;189}190 191// This run loop mode will wait for the process to launch and hit its192// entry point. It will currently ignore all events except for the193// process state changed event, where it watches for the process stopped194// or crash process state.195RNBRunLoopMode RNBRunLoopLaunchInferior(RNBRemote *remote,196 const char *stdin_path,197 const char *stdout_path,198 const char *stderr_path,199 bool no_stdio) {200 RNBContext &ctx = remote->Context();201 202 // The Process stuff takes a c array, the RNBContext has a vector...203 // So make up a c array.204 205 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__,206 ctx.ArgumentAtIndex(0));207 208 size_t inferior_argc = ctx.ArgumentCount();209 // Initialize inferior_argv with inferior_argc + 1 NULLs210 std::vector<const char *> inferior_argv(inferior_argc + 1, NULL);211 212 size_t i;213 for (i = 0; i < inferior_argc; i++)214 inferior_argv[i] = ctx.ArgumentAtIndex(i);215 216 // Pass the environment array the same way:217 218 size_t inferior_envc = ctx.EnvironmentCount();219 // Initialize inferior_argv with inferior_argc + 1 NULLs220 std::vector<const char *> inferior_envp(inferior_envc + 1, NULL);221 222 for (i = 0; i < inferior_envc; i++)223 inferior_envp[i] = ctx.EnvironmentAtIndex(i);224 225 // Our launch type hasn't been set to anything concrete, so we need to226 // figure our how we are going to launch automatically.227 228 nub_launch_flavor_t launch_flavor = g_launch_flavor;229 if (launch_flavor == eLaunchFlavorDefault)230 launch_flavor = default_launch_flavor(inferior_argv[0]);231 232 ctx.SetLaunchFlavor(launch_flavor);233 char resolved_path[PATH_MAX];234 235 // If we fail to resolve the path to our executable, then just use what we236 // were given and hope for the best237 if (!DNBResolveExecutablePath(inferior_argv[0], resolved_path,238 sizeof(resolved_path)))239 ::strlcpy(resolved_path, inferior_argv[0], sizeof(resolved_path));240 241 char launch_err_str[PATH_MAX];242 launch_err_str[0] = '\0';243 const char *cwd =244 (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()245 : ctx.GetWorkingDirectory());246 const char *process_event = ctx.GetProcessEvent();247 nub_process_t pid = DNBProcessLaunch(248 &ctx, resolved_path, &inferior_argv[0], &inferior_envp[0], cwd,249 stdin_path, stdout_path, stderr_path, no_stdio, g_disable_aslr,250 process_event, launch_err_str, sizeof(launch_err_str));251 252 g_pid = pid;253 254 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0) {255 DNBLogThreaded("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__,256 launch_err_str);257 ctx.LaunchStatus().SetError(-1, DNBError::Generic);258 ctx.LaunchStatus().SetErrorString(launch_err_str);259 } else if (pid == INVALID_NUB_PROCESS) {260 DNBLogThreaded(261 "%s DNBProcessLaunch() failed to launch process, unknown failure",262 __FUNCTION__);263 ctx.LaunchStatus().SetError(-1, DNBError::Generic);264 ctx.LaunchStatus().SetErrorString("<unknown failure>");265 } else {266 ctx.LaunchStatus().Clear();267 }268 269 if (remote->Comm().IsConnected()) {270 // It we are connected already, the next thing gdb will do is ask271 // whether the launch succeeded, and if not, whether there is an272 // error code. So we need to fetch one packet from gdb before we wait273 // on the stop from the target.274 275 uint32_t event_mask = RNBContext::event_read_packet_available;276 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);277 278 if (set_events & RNBContext::event_read_packet_available) {279 rnb_err_t err = rnb_err;280 RNBRemote::PacketEnum type;281 282 err = remote->HandleReceivedPacket(&type);283 284 if (err != rnb_success) {285 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",286 __FUNCTION__);287 return eRNBRunLoopModeExit;288 }289 if (type != RNBRemote::query_launch_success) {290 DNBLogThreadedIf(LOG_RNB_MINIMAL,291 "%s Didn't get the expected qLaunchSuccess packet.",292 __FUNCTION__);293 }294 }295 }296 297 while (pid != INVALID_NUB_PROCESS) {298 // Wait for process to start up and hit entry point299 DNBLogThreadedIf(LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, "300 "eEventProcessRunningStateChanged | "301 "eEventProcessStoppedStateChanged, true, "302 "INFINITE)...",303 __FUNCTION__, pid);304 nub_event_t set_events =305 DNBProcessWaitForEvents(pid, eEventProcessRunningStateChanged |306 eEventProcessStoppedStateChanged,307 true, NULL);308 DNBLogThreadedIf(LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, "309 "eEventProcessRunningStateChanged | "310 "eEventProcessStoppedStateChanged, true, "311 "INFINITE) => 0x%8.8x",312 __FUNCTION__, pid, set_events);313 314 if (set_events == 0) {315 pid = INVALID_NUB_PROCESS;316 g_pid = pid;317 } else {318 if (set_events & (eEventProcessRunningStateChanged |319 eEventProcessStoppedStateChanged)) {320 nub_state_t pid_state = DNBProcessGetState(pid);321 DNBLogThreadedIf(322 LOG_RNB_EVENTS,323 "%s process %4.4x state changed (eEventProcessStateChanged): %s",324 __FUNCTION__, pid, DNBStateAsString(pid_state));325 326 switch (pid_state) {327 case eStateInvalid:328 case eStateUnloaded:329 case eStateAttaching:330 case eStateLaunching:331 case eStateSuspended:332 break; // Ignore333 334 case eStateRunning:335 case eStateStepping:336 // Still waiting to stop at entry point...337 break;338 339 case eStateStopped:340 case eStateCrashed:341 ctx.SetProcessID(pid);342 return eRNBRunLoopModeInferiorExecuting;343 344 case eStateDetached:345 case eStateExited:346 pid = INVALID_NUB_PROCESS;347 g_pid = pid;348 return eRNBRunLoopModeExit;349 }350 }351 352 DNBProcessResetEvents(pid, set_events);353 }354 }355 356 return eRNBRunLoopModeExit;357}358 359// This run loop mode will wait for the process to launch and hit its360// entry point. It will currently ignore all events except for the361// process state changed event, where it watches for the process stopped362// or crash process state.363RNBRunLoopMode RNBRunLoopLaunchAttaching(RNBRemote *remote,364 nub_process_t attach_pid,365 nub_process_t &pid) {366 RNBContext &ctx = remote->Context();367 368 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__,369 attach_pid);370 char err_str[1024];371 pid = DNBProcessAttach(attach_pid, NULL, ctx.GetIgnoredExceptions(), err_str,372 sizeof(err_str));373 g_pid = pid;374 375 if (pid == INVALID_NUB_PROCESS) {376 ctx.LaunchStatus().SetError(-1, DNBError::Generic);377 if (err_str[0])378 ctx.LaunchStatus().SetErrorString(err_str);379 return eRNBRunLoopModeExit;380 } else {381 ctx.SetProcessID(pid);382 return eRNBRunLoopModeInferiorExecuting;383 }384}385 386// Watch for signals:387// SIGINT: so we can halt our inferior. (disabled for now)388// SIGPIPE: in case our child process dies389int g_sigint_received = 0;390int g_sigpipe_received = 0;391void signal_handler(int signo) {392 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__,393 SysSignal::Name(signo));394 395 switch (signo) {396 case SIGINT:397 g_sigint_received++;398 if (g_pid != INVALID_NUB_PROCESS) {399 // Only send a SIGINT once...400 if (g_sigint_received == 1) {401 switch (DNBProcessGetState(g_pid)) {402 case eStateRunning:403 case eStateStepping:404 DNBProcessSignal(g_pid, SIGSTOP);405 return;406 default:407 break;408 }409 }410 }411 exit(SIGINT);412 break;413 414 case SIGPIPE:415 g_sigpipe_received = 1;416 break;417 }418}419 420// Return the new run loop mode based off of the current process state421RNBRunLoopMode HandleProcessStateChange(RNBRemote *remote, bool initialize) {422 RNBContext &ctx = remote->Context();423 nub_process_t pid = ctx.ProcessID();424 425 if (pid == INVALID_NUB_PROCESS) {426 DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...",427 __FUNCTION__);428 return eRNBRunLoopModeExit;429 }430 nub_state_t pid_state = DNBProcessGetState(pid);431 432 DNBLogThreadedIf(LOG_RNB_MINIMAL,433 "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__,434 (int)initialize, DNBStateAsString(pid_state));435 436 switch (pid_state) {437 case eStateInvalid:438 case eStateUnloaded:439 // Something bad happened440 return eRNBRunLoopModeExit;441 break;442 443 case eStateAttaching:444 case eStateLaunching:445 return eRNBRunLoopModeInferiorExecuting;446 447 case eStateSuspended:448 case eStateCrashed:449 case eStateStopped:450 // If we stop due to a signal, so clear the fact that we got a SIGINT451 // so we can stop ourselves again (but only while our inferior452 // process is running..)453 g_sigint_received = 0;454 if (initialize == false) {455 // Compare the last stop count to our current notion of a stop count456 // to make sure we don't notify more than once for a given stop.457 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();458 bool pid_stop_count_changed =459 ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));460 if (pid_stop_count_changed) {461 remote->FlushSTDIO();462 463 if (ctx.GetProcessStopCount() == 1) {464 DNBLogThreadedIf(465 LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "466 "pid_stop_count %llu (old %llu)) Notify??? no, "467 "first stop...",468 __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),469 (uint64_t)ctx.GetProcessStopCount(),470 (uint64_t)prev_pid_stop_count);471 } else {472 473 DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) "474 "pid_state = %s pid_stop_count "475 "%llu (old %llu)) Notify??? YES!!!",476 __FUNCTION__, (int)initialize,477 DNBStateAsString(pid_state),478 (uint64_t)ctx.GetProcessStopCount(),479 (uint64_t)prev_pid_stop_count);480 remote->NotifyThatProcessStopped();481 }482 } else {483 DNBLogThreadedIf(484 LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s "485 "pid_stop_count %llu (old %llu)) Notify??? "486 "skipping...",487 __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),488 (uint64_t)ctx.GetProcessStopCount(), (uint64_t)prev_pid_stop_count);489 }490 }491 return eRNBRunLoopModeInferiorExecuting;492 493 case eStateStepping:494 case eStateRunning:495 return eRNBRunLoopModeInferiorExecuting;496 497 case eStateExited:498 remote->HandlePacket_last_signal(NULL);499 return eRNBRunLoopModeExit;500 case eStateDetached:501 return eRNBRunLoopModeExit;502 }503 504 // Catch all...505 return eRNBRunLoopModeExit;506}507 508// This function handles the case where our inferior program is stopped and509// we are waiting for gdb remote protocol packets. When a packet occurs that510// makes the inferior run, we need to leave this function with a new state511// as the return code.512RNBRunLoopMode RNBRunLoopInferiorExecuting(RNBRemote *remote) {513 DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);514 RNBContext &ctx = remote->Context();515 516 // Init our mode and set 'is_running' based on the current process state517 RNBRunLoopMode mode = HandleProcessStateChange(remote, true);518 519 while (ctx.ProcessID() != INVALID_NUB_PROCESS) {520 521 std::string set_events_str;522 uint32_t event_mask = ctx.NormalEventBits();523 524 if (!ctx.ProcessStateRunning()) {525 // Clear some bits if we are not running so we don't send any async526 // packets527 event_mask &= ~RNBContext::event_proc_stdio_available;528 event_mask &= ~RNBContext::event_proc_profile_data;529 }530 531 // We want to make sure we consume all process state changes and have532 // whomever is notifying us to wait for us to reset the event bit before533 // continuing.534 // ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);535 536 DNBLogThreadedIf(LOG_RNB_EVENTS,537 "%s ctx.Events().WaitForSetEvents(0x%08x) ...",538 __FUNCTION__, event_mask);539 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);540 DNBLogThreadedIf(LOG_RNB_EVENTS,541 "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",542 __FUNCTION__, event_mask, set_events,543 ctx.EventsAsString(set_events, set_events_str));544 545 if (set_events) {546 if ((set_events & RNBContext::event_proc_thread_exiting) ||547 (set_events & RNBContext::event_proc_stdio_available)) {548 remote->FlushSTDIO();549 }550 551 if (set_events & RNBContext::event_proc_profile_data) {552 remote->SendAsyncProfileData();553 }554 555 if (set_events & RNBContext::event_read_packet_available) {556 // handleReceivedPacket will take care of resetting the557 // event_read_packet_available events when there are no more...558 set_events ^= RNBContext::event_read_packet_available;559 560 if (ctx.ProcessStateRunning()) {561 if (remote->HandleAsyncPacket() == rnb_not_connected) {562 // TODO: connect again? Exit?563 }564 } else {565 if (remote->HandleReceivedPacket() == rnb_not_connected) {566 // TODO: connect again? Exit?567 }568 }569 }570 571 if (set_events & RNBContext::event_proc_state_changed) {572 mode = HandleProcessStateChange(remote, false);573 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);574 set_events ^= RNBContext::event_proc_state_changed;575 }576 577 if (set_events & RNBContext::event_proc_thread_exiting) {578 DNBLog("debugserver's process monitoring thread has exited.");579 mode = eRNBRunLoopModeExit;580 }581 582 if (set_events & RNBContext::event_read_thread_exiting) {583 // Out remote packet receiving thread exited, exit for now.584 DNBLog(585 "debugserver's packet communication to lldb has been shut down.");586 if (ctx.HasValidProcessID()) {587 nub_process_t pid = ctx.ProcessID();588 // TODO: We should add code that will leave the current process589 // in its current state and listen for another connection...590 if (ctx.ProcessStateRunning()) {591 if (ctx.GetDetachOnError()) {592 DNBLog("debugserver has a valid PID %d, it is still running. "593 "detaching from the inferior process.",594 pid);595 DNBProcessDetach(pid);596 } else {597 DNBLog("debugserver killing the inferior process, pid %d.", pid);598 DNBProcessKill(pid);599 }600 } else {601 if (ctx.GetDetachOnError()) {602 DNBLog("debugserver has a valid PID %d but it may no longer "603 "be running, detaching from the inferior process.",604 pid);605 DNBProcessDetach(pid);606 }607 }608 }609 mode = eRNBRunLoopModeExit;610 }611 }612 613 // Reset all event bits that weren't reset for now...614 if (set_events != 0)615 ctx.Events().ResetEvents(set_events);616 617 if (mode != eRNBRunLoopModeInferiorExecuting)618 break;619 }620 621 return mode;622}623 624RNBRunLoopMode RNBRunLoopPlatform(RNBRemote *remote) {625 RNBRunLoopMode mode = eRNBRunLoopModePlatformMode;626 RNBContext &ctx = remote->Context();627 628 while (mode == eRNBRunLoopModePlatformMode) {629 std::string set_events_str;630 const uint32_t event_mask = RNBContext::event_read_packet_available |631 RNBContext::event_read_thread_exiting;632 633 DNBLogThreadedIf(LOG_RNB_EVENTS,634 "%s ctx.Events().WaitForSetEvents(0x%08x) ...",635 __FUNCTION__, event_mask);636 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);637 DNBLogThreadedIf(LOG_RNB_EVENTS,638 "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",639 __FUNCTION__, event_mask, set_events,640 ctx.EventsAsString(set_events, set_events_str));641 642 if (set_events) {643 if (set_events & RNBContext::event_read_packet_available) {644 if (remote->HandleReceivedPacket() == rnb_not_connected)645 mode = eRNBRunLoopModeExit;646 }647 648 if (set_events & RNBContext::event_read_thread_exiting) {649 mode = eRNBRunLoopModeExit;650 }651 ctx.Events().ResetEvents(set_events);652 }653 }654 return eRNBRunLoopModeExit;655}656 657// Convenience function to set up the remote listening port658// Returns 1 for success 0 for failure.659 660static void PortWasBoundCallbackUnixSocket(const void *baton, in_port_t port) {661 //::printf ("PortWasBoundCallbackUnixSocket (baton = %p, port = %u)\n", baton,662 //port);663 664 const char *unix_socket_name = (const char *)baton;665 666 if (unix_socket_name && unix_socket_name[0]) {667 // We were given a unix socket name to use to communicate the port668 // that we ended up binding to back to our parent process669 struct sockaddr_un saddr_un;670 int s = ::socket(AF_UNIX, SOCK_STREAM, 0);671 if (s < 0) {672 perror("error: socket (AF_UNIX, SOCK_STREAM, 0)");673 exit(1);674 }675 676 saddr_un.sun_family = AF_UNIX;677 ::strlcpy(saddr_un.sun_path, unix_socket_name,678 sizeof(saddr_un.sun_path) - 1);679 saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';680 saddr_un.sun_len = SUN_LEN(&saddr_un);681 682 if (::connect(s, (struct sockaddr *)&saddr_un,683 static_cast<socklen_t>(SUN_LEN(&saddr_un))) < 0) {684 perror("error: connect (socket, &saddr_un, saddr_un_len)");685 exit(1);686 }687 688 //::printf ("connect () sucess!!\n");689 690 // We were able to connect to the socket, now write our PID so whomever691 // launched us will know this process's ID692 RNBLogSTDOUT("Listening to port %i...\n", port);693 694 char pid_str[64];695 const int pid_str_len = ::snprintf(pid_str, sizeof(pid_str), "%u", port);696 const ssize_t bytes_sent = ::send(s, pid_str, pid_str_len, 0);697 698 if (pid_str_len != bytes_sent) {699 perror("error: send (s, pid_str, pid_str_len, 0)");700 exit(1);701 }702 703 //::printf ("send () sucess!!\n");704 705 // We are done with the socket706 close(s);707 }708}709 710static void PortWasBoundCallbackNamedPipe(const void *baton, uint16_t port) {711 const char *named_pipe = (const char *)baton;712 if (named_pipe && named_pipe[0]) {713 int fd = ::open(named_pipe, O_WRONLY);714 if (fd > -1) {715 char port_str[64];716 const ssize_t port_str_len =717 ::snprintf(port_str, sizeof(port_str), "%u", port);718 // Write the port number as a C string with the NULL terminator719 ::write(fd, port_str, port_str_len + 1);720 close(fd);721 }722 }723}724 725static int ConnectRemote(RNBRemote *remote, const char *host, int port,726 bool reverse_connect, const char *named_pipe_path,727 const char *unix_socket_name) {728 if (!remote->Comm().IsConnected()) {729 if (reverse_connect) {730 if (port == 0) {731 DNBLogThreaded(732 "error: invalid port supplied for reverse connection: %i.\n", port);733 return 0;734 }735 if (remote->Comm().Connect(host, port) != rnb_success) {736 DNBLogThreaded("Failed to reverse connect to %s:%i.\n", host, port);737 return 0;738 }739 } else {740 if (port != 0)741 RNBLogSTDOUT("Listening to port %i for a connection from %s...\n", port,742 host ? host : "127.0.0.1");743 if (unix_socket_name && unix_socket_name[0]) {744 if (remote->Comm().Listen(host, port, PortWasBoundCallbackUnixSocket,745 unix_socket_name) != rnb_success) {746 RNBLogSTDERR("Failed to get connection from a remote gdb process.\n");747 return 0;748 }749 } else {750 if (remote->Comm().Listen(host, port, PortWasBoundCallbackNamedPipe,751 named_pipe_path) != rnb_success) {752 RNBLogSTDERR("Failed to get connection from a remote gdb process.\n");753 return 0;754 }755 }756 }757 remote->StartReadRemoteDataThread();758 }759 return 1;760}761 762// FILE based Logging callback that can be registered with763// DNBLogSetLogCallback764void FileLogCallback(void *baton, uint32_t flags, const char *format,765 va_list args) {766 if (baton == NULL || format == NULL)767 return;768 769 ::vfprintf((FILE *)baton, format, args);770 ::fprintf((FILE *)baton, "\n");771 ::fflush((FILE *)baton);772}773 774void show_version_and_exit(int exit_code) {775 const char *in_translation = "";776 if (DNBDebugserverIsTranslated())777 in_translation = " (running under translation)";778 printf("%s-%s for %s%s.\n", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_STR,779 RNB_ARCH, in_translation);780 exit(exit_code);781}782 783void show_usage_and_exit(int exit_code) {784 RNBLogSTDERR(785 "Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n",786 DEBUGSERVER_PROGRAM_NAME);787 RNBLogSTDERR(" %s /path/file [program-name program-arg1 program-arg2 ...]\n",788 DEBUGSERVER_PROGRAM_NAME);789 RNBLogSTDERR(" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);790 RNBLogSTDERR(" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);791 RNBLogSTDERR(" %s host:port --attach=<process_name>\n",792 DEBUGSERVER_PROGRAM_NAME);793 RNBLogSTDERR(" %s /path/file --attach=<process_name>\n",794 DEBUGSERVER_PROGRAM_NAME);795 RNBLogSTDERR("\n");796 RNBLogSTDERR(" -a | --attach <pid>\n");797 RNBLogSTDERR(" -w | --waitfor <name>\n");798 RNBLogSTDERR(" -A | --arch <arch>\n");799 RNBLogSTDERR(" -g | --debug\n");800 RNBLogSTDERR(" -K | --kill-on-error\n");801 RNBLogSTDERR(" -v | --verbose\n");802 RNBLogSTDERR(" -V | --version\n");803 RNBLogSTDERR(" -k | --lockdown\n");804 RNBLogSTDERR(" -t | --applist\n");805 RNBLogSTDERR(" -l | --log-file\n");806 RNBLogSTDERR(" -f | --log-flags\n");807 RNBLogSTDERR(" -x | --launch <auto|posix-spawn|fork-exec|springboard>\n");808 RNBLogSTDERR(" -d | --waitfor-duration <seconds>\n");809 RNBLogSTDERR(" -i | --waitfor-interval <usecs>\n");810 RNBLogSTDERR(" -r | --native-regs\n");811 RNBLogSTDERR(" -s | --studio-path <path>\n");812 RNBLogSTDERR(" -I | --stdin-path <path>\n");813 RNBLogSTDERR(" -O | --stdout-path <path>\n");814 RNBLogSTDERR(" -E | --stderr-path <path>\n");815 RNBLogSTDERR(" -n | --no-stdio\n");816 RNBLogSTDERR(" -S | --setsid\n");817 RNBLogSTDERR(" -D | --disable-aslr\n");818 RNBLogSTDERR(" -W | --working-dir <dir>\n");819 RNBLogSTDERR(" -p | --platform <arg?>\n");820 RNBLogSTDERR(" -u | --unix-socket <unix socket name>\n");821 RNBLogSTDERR(" -2 | --fd <file descriptor number>\n");822 RNBLogSTDERR(" -P | --named-pipe <pipe>\n");823 RNBLogSTDERR(" -R | --reverse-connect\n");824 RNBLogSTDERR(" -e | --env <env>\n");825 RNBLogSTDERR(" -F | --forward-env <env>\n");826 RNBLogSTDERR(" -U | --unmask-signals\n");827 828 exit(exit_code);829}830 831// option descriptors for getopt_long_only()832static struct option g_long_options[] = {833 {"attach", required_argument, NULL, 'a'},834 {"arch", required_argument, NULL, 'A'},835 {"debug", no_argument, NULL, 'g'},836 {"kill-on-error", no_argument, NULL, 'K'},837 {"verbose", no_argument, NULL, 'v'},838 {"version", no_argument, NULL, 'V'},839 {"lockdown", no_argument, &g_lockdown_opt, 1}, // short option "-k"840 {"applist", no_argument, &g_applist_opt, 1}, // short option "-t"841 {"log-file", required_argument, NULL, 'l'},842 {"log-flags", required_argument, NULL, 'f'},843 {"launch", required_argument, NULL, 'x'}, // Valid values are "auto",844 // "posix-spawn", "fork-exec",845 // "springboard" (arm only)846 {"waitfor", required_argument, NULL,847 'w'}, // Wait for a process whose name starts with ARG848 {"waitfor-interval", required_argument, NULL,849 'i'}, // Time in usecs to wait between sampling the pid list when waiting850 // for a process by name851 {"waitfor-duration", required_argument, NULL,852 'd'}, // The time in seconds to wait for a process to show up by name853 {"native-regs", no_argument, NULL, 'r'}, // Specify to use the native854 // registers instead of the gdb855 // defaults for the architecture.856 {"stdio-path", required_argument, NULL,857 's'}, // Set the STDIO path to be used when launching applications (STDIN,858 // STDOUT and STDERR) (only if debugserver launches the process)859 {"stdin-path", required_argument, NULL,860 'I'}, // Set the STDIN path to be used when launching applications (only if861 // debugserver launches the process)862 {"stdout-path", required_argument, NULL,863 'O'}, // Set the STDOUT path to be used when launching applications (only864 // if debugserver launches the process)865 {"stderr-path", required_argument, NULL,866 'E'}, // Set the STDERR path to be used when launching applications (only867 // if debugserver launches the process)868 {"no-stdio", no_argument, NULL,869 'n'}, // Do not set up any stdio (perhaps the program is a GUI program)870 // (only if debugserver launches the process)871 {"setsid", no_argument, NULL,872 'S'}, // call setsid() to make debugserver run in its own session873 {"disable-aslr", no_argument, NULL, 'D'}, // Use _POSIX_SPAWN_DISABLE_ASLR874 // to avoid shared library875 // randomization876 {"working-dir", required_argument, NULL,877 'W'}, // The working directory that the inferior process should have (only878 // if debugserver launches the process)879 {"platform", required_argument, NULL,880 'p'}, // Put this executable into a remote platform mode881 {"unix-socket", required_argument, NULL,882 'u'}, // If we need to handshake with our parent process, an option will be883 // passed down that specifies a unix socket name to use884 {"fd", required_argument, NULL,885 '2'}, // A file descriptor was passed to this process when spawned that886 // is already open and ready for communication887 {"named-pipe", required_argument, NULL, 'P'},888 {"reverse-connect", no_argument, NULL, 'R'},889 {"env", required_argument, NULL,890 'e'}, // When debugserver launches the process, set a single environment891 // entry as specified by the option value ("./debugserver -e FOO=1 -e892 // BAR=2 localhost:1234 -- /bin/ls")893 {"forward-env", no_argument, NULL,894 'F'}, // When debugserver launches the process, forward debugserver's895 // current environment variables to the child process ("./debugserver896 // -F localhost:1234 -- /bin/ls"897 {"unmask-signals", no_argument, NULL,898 'U'}, // debugserver will ignore EXC_MASK_BAD_ACCESS,899 // EXC_MASK_BAD_INSTRUCTION and EXC_MASK_ARITHMETIC, which results in900 // SIGSEGV, SIGILL and SIGFPE being propagated to the target process.901 {NULL, 0, NULL, 0}};902 903int communication_fd = -1;904 905// main906int main(int argc, char *argv[]) {907 // If debugserver is launched with DYLD_INSERT_LIBRARIES, unset it so we908 // don't spawn child processes with this enabled.909 unsetenv("DYLD_INSERT_LIBRARIES");910 911 const char *argv_sub_zero =912 argv[0]; // save a copy of argv[0] for error reporting post-launch913 914#if defined(__APPLE__)915 pthread_setname_np("main thread");916#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)917 struct sched_param thread_param;918 int thread_sched_policy;919 if (pthread_getschedparam(pthread_self(), &thread_sched_policy,920 &thread_param) == 0) {921 thread_param.sched_priority = 47;922 pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param);923 }924 925 ::proc_set_wakemon_params(926 getpid(), 500,927 0); // Allow up to 500 wakeups/sec to avoid EXC_RESOURCE for normal use.928#endif929#endif930 931 g_isatty = ::isatty(STDIN_FILENO);932 933 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",934 // getuid(),935 // geteuid(),936 // getgid(),937 // getegid());938 939 // signal (SIGINT, signal_handler);940 signal(SIGPIPE, signal_handler);941 signal(SIGHUP, signal_handler);942 943 // We're always sitting in waitpid or kevent waiting on our target process'944 // death,945 // we don't need no stinking SIGCHLD's...946 947 sigset_t sigset;948 sigemptyset(&sigset);949 sigaddset(&sigset, SIGCHLD);950 sigprocmask(SIG_BLOCK, &sigset, NULL);951 952 // Set up DNB logging by default. If the user passes different log flags or a953 // log file, these settings will be modified after processing the command line954 // arguments.955 if (auto log_callback = OsLogger::GetLogFunction())956 DNBLogSetLogCallback(log_callback, nullptr);957 DNBLogSetLogMask(/*log_flags*/ 0);958 959 g_remoteSP = std::make_shared<RNBRemote>();960 961 RNBRemote *remote = g_remoteSP.get();962 if (remote == NULL) {963 RNBLogSTDERR("error: failed to create a remote connection class\n");964 return -1;965 }966 967 RNBContext &ctx = remote->Context();968 969 int i;970 int attach_pid = INVALID_NUB_PROCESS;971 972 FILE *log_file = NULL;973 uint32_t log_flags = 0;974 // Parse our options975 int ch;976 int long_option_index = 0;977 int debug = 0;978 std::string compile_options;979 std::string waitfor_pid_name; // Wait for a process that starts with this name980 std::string attach_pid_name;981 std::string arch_name;982 std::string working_dir; // The new working directory to use for the inferior983 std::string unix_socket_name; // If we need to handshake with our parent984 // process, an option will be passed down that985 // specifies a unix socket name to use986 std::string named_pipe_path; // If we need to handshake with our parent987 // process, an option will be passed down that988 // specifies a named pipe to use989 useconds_t waitfor_interval = 1000; // Time in usecs between process lists990 // polls when waiting for a process by991 // name, default 1 msec.992 useconds_t waitfor_duration =993 0; // Time in seconds to wait for a process by name, 0 means wait forever.994 bool no_stdio = false;995 bool reverse_connect = false; // Set to true by an option to indicate we996 // should reverse connect to the host:port997 // supplied as the first debugserver argument998 999#if !defined(DNBLOG_ENABLED)1000 compile_options += "(no-logging) ";1001#endif1002 1003 RNBRunLoopMode start_mode = eRNBRunLoopModeExit;1004 1005 char short_options[512];1006 uint32_t short_options_idx = 0;1007 1008 // Handle the two case that don't have short options in g_long_options1009 short_options[short_options_idx++] = 'k';1010 short_options[short_options_idx++] = 't';1011 1012 for (i = 0; g_long_options[i].name != NULL; ++i) {1013 if (isalpha(g_long_options[i].val)) {1014 short_options[short_options_idx++] = g_long_options[i].val;1015 switch (g_long_options[i].has_arg) {1016 default:1017 case no_argument:1018 break;1019 1020 case optional_argument:1021 short_options[short_options_idx++] = ':';1022 short_options[short_options_idx++] = ':';1023 break;1024 case required_argument:1025 short_options[short_options_idx++] = ':';1026 break;1027 }1028 }1029 }1030 // NULL terminate the short option string.1031 short_options[short_options_idx++] = '\0';1032 1033#if __GLIBC__1034 optind = 0;1035#else1036 optreset = 1;1037 optind = 1;1038#endif1039 1040 bool forward_env = false;1041 while ((ch = getopt_long_only(argc, argv, short_options, g_long_options,1042 &long_option_index)) != -1) {1043 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n", ch, (uint8_t)ch,1044 g_long_options[long_option_index].name,1045 g_long_options[long_option_index].has_arg ? '=' : ' ',1046 optarg ? optarg : "");1047 switch (ch) {1048 case 0: // Any optional that auto set themselves will return 01049 break;1050 1051 case 'A':1052 if (optarg && optarg[0])1053 arch_name.assign(optarg);1054 break;1055 1056 case 'a':1057 if (optarg && optarg[0]) {1058 if (isdigit(optarg[0])) {1059 char *end = NULL;1060 attach_pid = static_cast<int>(strtoul(optarg, &end, 0));1061 if (end == NULL || *end != '\0') {1062 RNBLogSTDERR("error: invalid pid option '%s'\n", optarg);1063 exit(4);1064 }1065 } else {1066 attach_pid_name = optarg;1067 }1068 start_mode = eRNBRunLoopModeInferiorAttaching;1069 }1070 break;1071 1072 // --waitfor=NAME1073 case 'w':1074 if (optarg && optarg[0]) {1075 waitfor_pid_name = optarg;1076 start_mode = eRNBRunLoopModeInferiorAttaching;1077 }1078 break;1079 1080 // --waitfor-interval=USEC1081 case 'i':1082 if (optarg && optarg[0]) {1083 char *end = NULL;1084 waitfor_interval = static_cast<useconds_t>(strtoul(optarg, &end, 0));1085 if (end == NULL || *end != '\0') {1086 RNBLogSTDERR("error: invalid waitfor-interval option value '%s'.\n",1087 optarg);1088 exit(6);1089 }1090 }1091 break;1092 1093 // --waitfor-duration=SEC1094 case 'd':1095 if (optarg && optarg[0]) {1096 char *end = NULL;1097 waitfor_duration = static_cast<useconds_t>(strtoul(optarg, &end, 0));1098 if (end == NULL || *end != '\0') {1099 RNBLogSTDERR("error: invalid waitfor-duration option value '%s'.\n",1100 optarg);1101 exit(7);1102 }1103 }1104 break;1105 1106 case 'K':1107 g_detach_on_error = false;1108 break;1109 case 'W':1110 if (optarg && optarg[0])1111 working_dir.assign(optarg);1112 break;1113 1114 case 'x':1115 if (optarg && optarg[0]) {1116 if (strcasecmp(optarg, "auto") == 0)1117 g_launch_flavor = eLaunchFlavorDefault;1118 else if (strcasestr(optarg, "posix") == optarg) {1119 DNBLog(1120 "[LaunchAttach] launch flavor is posix_spawn via cmdline option");1121 g_launch_flavor = eLaunchFlavorPosixSpawn;1122 } else if (strcasestr(optarg, "fork") == optarg)1123 g_launch_flavor = eLaunchFlavorForkExec;1124#ifdef WITH_SPRINGBOARD1125 else if (strcasestr(optarg, "spring") == optarg) {1126 DNBLog(1127 "[LaunchAttach] launch flavor is SpringBoard via cmdline option");1128 g_launch_flavor = eLaunchFlavorSpringBoard;1129 }1130#endif1131#ifdef WITH_BKS1132 else if (strcasestr(optarg, "backboard") == optarg) {1133 DNBLog("[LaunchAttach] launch flavor is BKS via cmdline option");1134 g_launch_flavor = eLaunchFlavorBKS;1135 }1136#endif1137#ifdef WITH_FBS1138 else if (strcasestr(optarg, "frontboard") == optarg) {1139 DNBLog("[LaunchAttach] launch flavor is FBS via cmdline option");1140 g_launch_flavor = eLaunchFlavorFBS;1141 }1142#endif1143 1144 else {1145 RNBLogSTDERR("error: invalid TYPE for the --launch=TYPE (-x TYPE) "1146 "option: '%s'\n",1147 optarg);1148 RNBLogSTDERR("Valid values TYPE are:\n");1149 RNBLogSTDERR(1150 " auto Auto-detect the best launch method to use.\n");1151 RNBLogSTDERR(1152 " posix Launch the executable using posix_spawn.\n");1153 RNBLogSTDERR(1154 " fork Launch the executable using fork and exec.\n");1155#ifdef WITH_SPRINGBOARD1156 RNBLogSTDERR(1157 " spring Launch the executable through Springboard.\n");1158#endif1159#ifdef WITH_BKS1160 RNBLogSTDERR(" backboard Launch the executable through BackBoard "1161 "Services.\n");1162#endif1163#ifdef WITH_FBS1164 RNBLogSTDERR(" frontboard Launch the executable through FrontBoard "1165 "Services.\n");1166#endif1167 exit(5);1168 }1169 }1170 break;1171 1172 case 'l': // Set Log File1173 if (optarg && optarg[0]) {1174 if (strcasecmp(optarg, "stdout") == 0)1175 log_file = stdout;1176 else if (strcasecmp(optarg, "stderr") == 0)1177 log_file = stderr;1178 else {1179 log_file = fopen(optarg, "w");1180 if (log_file != NULL)1181 setlinebuf(log_file);1182 }1183 1184 if (log_file == NULL) {1185 const char *errno_str = strerror(errno);1186 RNBLogSTDERR(1187 "Failed to open log file '%s' for writing: errno = %i (%s)",1188 optarg, errno, errno_str ? errno_str : "unknown error");1189 }1190 }1191 break;1192 1193 case 'f': // Log Flags1194 if (optarg && optarg[0])1195 log_flags = static_cast<uint32_t>(strtoul(optarg, NULL, 0));1196 break;1197 1198 case 'g':1199 debug = 1;1200 DNBLogSetDebug(debug);1201 break;1202 1203 case 't':1204 g_applist_opt = 1;1205 break;1206 1207 case 'k':1208 g_lockdown_opt = 1;1209 break;1210 1211 case 'r':1212 // Do nothing, native regs is the default these days1213 break;1214 1215 case 'R':1216 reverse_connect = true;1217 break;1218 case 'v':1219 DNBLogSetVerbose(1);1220 break;1221 1222 case 'V':1223 show_version_and_exit(0);1224 break;1225 1226 case 's':1227 ctx.GetSTDIN().assign(optarg);1228 ctx.GetSTDOUT().assign(optarg);1229 ctx.GetSTDERR().assign(optarg);1230 break;1231 1232 case 'I':1233 ctx.GetSTDIN().assign(optarg);1234 break;1235 1236 case 'O':1237 ctx.GetSTDOUT().assign(optarg);1238 break;1239 1240 case 'E':1241 ctx.GetSTDERR().assign(optarg);1242 break;1243 1244 case 'n':1245 no_stdio = true;1246 break;1247 1248 case 'S':1249 // Put debugserver into a new session. Terminals group processes1250 // into sessions and when a special terminal key sequences1251 // (like control+c) are typed they can cause signals to go out to1252 // all processes in a session. Using this --setsid (-S) option1253 // will cause debugserver to run in its own sessions and be free1254 // from such issues.1255 //1256 // This is useful when debugserver is spawned from a command1257 // line application that uses debugserver to do the debugging,1258 // yet that application doesn't want debugserver receiving the1259 // signals sent to the session (i.e. dying when anyone hits ^C).1260 setsid();1261 break;1262 case 'D':1263 g_disable_aslr = 1;1264 break;1265 1266 case 'p':1267 start_mode = eRNBRunLoopModePlatformMode;1268 break;1269 1270 case 'u':1271 unix_socket_name.assign(optarg);1272 break;1273 1274 case 'P':1275 named_pipe_path.assign(optarg);1276 break;1277 1278 case 'e':1279 // Pass a single specified environment variable down to the process that1280 // gets launched1281 remote->Context().PushEnvironment(optarg);1282 break;1283 1284 case 'F':1285 forward_env = true;1286 break;1287 1288 case 'U':1289 ctx.AddDefaultIgnoredExceptions();1290 break;1291 1292 case '2':1293 // File descriptor passed to this process during fork/exec and is already1294 // open and ready for communication.1295 communication_fd = atoi(optarg);1296 break;1297 }1298 }1299 1300 if (arch_name.empty()) {1301#if defined(__arm__)1302 arch_name.assign("arm");1303#endif1304 } else {1305 DNBSetArchitecture(arch_name.c_str());1306 }1307 1308 // if (arch_name.empty())1309 // {1310 // fprintf(stderr, "error: no architecture was specified\n");1311 // exit (8);1312 // }1313 // Skip any options we consumed with getopt_long_only1314 argc -= optind;1315 argv += optind;1316 1317 if (!working_dir.empty()) {1318 if (remote->Context().SetWorkingDirectory(working_dir.c_str()) == false) {1319 RNBLogSTDERR("error: working directory doesn't exist '%s'.\n",1320 working_dir.c_str());1321 exit(8);1322 }1323 }1324 1325 remote->Context().SetDetachOnError(g_detach_on_error);1326 1327 remote->Initialize();1328 1329 // It is ok for us to set NULL as the logfile (this will disable any logging)1330 1331 if (log_file != NULL) {1332 DNBLog("debugserver is switching to logging to a file.");1333 DNBLogSetLogCallback(FileLogCallback, log_file);1334 // If our log file was set, yet we have no log flags, log everything!1335 if (log_flags == 0)1336 log_flags = LOG_ALL | LOG_RNB_ALL;1337 }1338 DNBLogSetLogMask(log_flags);1339 1340 if (DNBLogEnabled()) {1341 for (i = 0; i < argc; i++)1342 DNBLogDebug("argv[%i] = %s", i, argv[i]);1343 }1344 1345 // as long as we're dropping remotenub in as a replacement for gdbserver,1346 // explicitly note that this is not gdbserver.1347 1348 const char *in_translation = "";1349 if (DNBDebugserverIsTranslated())1350 in_translation = " (running under translation)";1351 RNBLogSTDOUT("%s-%s %sfor %s%s.\n", DEBUGSERVER_PROGRAM_NAME,1352 DEBUGSERVER_VERSION_STR, compile_options.c_str(), RNB_ARCH,1353 in_translation);1354 1355 std::string host;1356 int port = INT32_MAX;1357 char str[PATH_MAX];1358 str[0] = '\0';1359 1360 if (g_lockdown_opt == 0 && g_applist_opt == 0 && communication_fd == -1) {1361 // Make sure we at least have port1362 if (argc < 1) {1363 show_usage_and_exit(1);1364 }1365 // accept 'localhost:' prefix on port number1366 std::string host_specifier = argv[0];1367 auto colon_location = host_specifier.rfind(':');1368 if (colon_location != std::string::npos) {1369 host = host_specifier.substr(0, colon_location);1370 std::string port_str =1371 host_specifier.substr(colon_location + 1, std::string::npos);1372 char *end_ptr;1373 port = strtoul(port_str.c_str(), &end_ptr, 0);1374 if (end_ptr < port_str.c_str() + port_str.size())1375 show_usage_and_exit(2);1376 if (host.front() == '[' && host.back() == ']')1377 host = host.substr(1, host.size() - 2);1378 DNBLogDebug("host = '%s' port = %i", host.c_str(), port);1379 } else {1380 // No hostname means "localhost"1381 int items_scanned = ::sscanf(argv[0], "%i", &port);1382 if (items_scanned == 1) {1383 host = "127.0.0.1";1384 DNBLogDebug("host = '%s' port = %i", host.c_str(), port);1385 } else if (argv[0][0] == '/') {1386 port = INT32_MAX;1387 strlcpy(str, argv[0], sizeof(str));1388 } else {1389 show_usage_and_exit(2);1390 }1391 }1392 1393 // We just used the 'host:port' or the '/path/file' arg...1394 argc--;1395 argv++;1396 }1397 1398 // If we know we're waiting to attach, we don't need any of this other info.1399 if (start_mode != eRNBRunLoopModeInferiorAttaching &&1400 start_mode != eRNBRunLoopModePlatformMode) {1401 if (argc == 0 || g_lockdown_opt) {1402 if (g_lockdown_opt != 0) {1403 // Work around for SIGPIPE crashes due to posix_spawn issue.1404 // We have to close STDOUT and STDERR, else the first time we1405 // try and do any, we get SIGPIPE and die as posix_spawn is1406 // doing bad things with our file descriptors at the moment.1407 int null = open("/dev/null", O_RDWR);1408 dup2(null, STDOUT_FILENO);1409 dup2(null, STDERR_FILENO);1410 } else if (g_applist_opt != 0) {1411 DNBLog("debugserver running in --applist mode");1412 // List all applications we are able to see1413 std::string applist_plist;1414 int err = ListApplications(applist_plist, false, false);1415 if (err == 0) {1416 fputs(applist_plist.c_str(), stdout);1417 } else {1418 RNBLogSTDERR("error: ListApplications returned error %i\n", err);1419 }1420 // Exit with appropriate error if we were asked to list the applications1421 // with no other args were given (and we weren't trying to do this over1422 // lockdown)1423 return err;1424 }1425 1426 DNBLogDebug("Get args from remote protocol...");1427 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;1428 } else {1429 start_mode = eRNBRunLoopModeInferiorLaunching;1430 // Fill in the argv array in the context from the rest of our args.1431 // Skip the name of this executable and the port number1432 for (int i = 0; i < argc; i++) {1433 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);1434 ctx.PushArgument(argv[i]);1435 }1436 }1437 }1438 1439 if (start_mode == eRNBRunLoopModeExit)1440 return -1;1441 1442 if (forward_env || start_mode == eRNBRunLoopModeInferiorLaunching) {1443 // Pass the current environment down to the process that gets launched1444 // This happens automatically in the "launching" mode. For the rest, we1445 // only do that if the user explicitly requested this via --forward-env1446 // argument.1447 char **host_env = *_NSGetEnviron();1448 char *env_entry;1449 size_t i;1450 for (i = 0; (env_entry = host_env[i]) != NULL; ++i)1451 remote->Context().PushEnvironmentIfNeeded(env_entry);1452 }1453 1454 RNBRunLoopMode mode = start_mode;1455 char err_str[1024] = {'\0'};1456 1457 while (mode != eRNBRunLoopModeExit) {1458 switch (mode) {1459 case eRNBRunLoopModeGetStartModeFromRemoteProtocol:1460#ifdef WITH_LOCKDOWN1461 if (g_lockdown_opt) {1462 if (!remote->Comm().IsConnected()) {1463 if (remote->Comm().ConnectToService() != rnb_success) {1464 RNBLogSTDERR(1465 "Failed to get connection from a remote gdb process.\n");1466 mode = eRNBRunLoopModeExit;1467 } else if (g_applist_opt != 0) {1468 // List all applications we are able to see1469 DNBLog("debugserver running in applist mode under lockdown");1470 std::string applist_plist;1471 if (ListApplications(applist_plist, false, false) == 0) {1472 DNBLogDebug("Task list: %s", applist_plist.c_str());1473 1474 remote->Comm().Write(applist_plist.c_str(), applist_plist.size());1475 // Issue a read that will never yield any data until the other1476 // side1477 // closes the socket so this process doesn't just exit and cause1478 // the1479 // socket to close prematurely on the other end and cause data1480 // loss.1481 std::string buf;1482 remote->Comm().Read(buf);1483 }1484 remote->Comm().Disconnect(false);1485 mode = eRNBRunLoopModeExit;1486 break;1487 } else {1488 // Start watching for remote packets1489 remote->StartReadRemoteDataThread();1490 }1491 }1492 } else1493#endif1494 if (port != INT32_MAX) {1495 if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,1496 named_pipe_path.c_str(), unix_socket_name.c_str()))1497 mode = eRNBRunLoopModeExit;1498 } else if (str[0] == '/') {1499 if (remote->Comm().OpenFile(str))1500 mode = eRNBRunLoopModeExit;1501 } else if (communication_fd >= 0) {1502 // We were passed a file descriptor to use during fork/exec that is1503 // already open1504 // in our process, so lets just use it!1505 if (remote->Comm().useFD(communication_fd))1506 mode = eRNBRunLoopModeExit;1507 else1508 remote->StartReadRemoteDataThread();1509 }1510 1511 if (mode != eRNBRunLoopModeExit) {1512 RNBLogSTDOUT("Got a connection, waiting for process information for "1513 "launching or attaching.\n");1514 1515 mode = RNBRunLoopGetStartModeFromRemote(remote);1516 }1517 break;1518 1519 case eRNBRunLoopModeInferiorAttaching:1520 if (!waitfor_pid_name.empty()) {1521 // Set our end wait time if we are using a waitfor-duration1522 // option that may have been specified1523 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;1524 if (waitfor_duration != 0) {1525 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration,1526 0);1527 timeout_ptr = &attach_timeout_abstime;1528 }1529 nub_launch_flavor_t launch_flavor = g_launch_flavor;1530 if (launch_flavor == eLaunchFlavorDefault)1531 launch_flavor = default_launch_flavor(waitfor_pid_name.c_str());1532 1533 ctx.SetLaunchFlavor(launch_flavor);1534 bool ignore_existing = false;1535 RNBLogSTDOUT("Waiting to attach to process %s...\n",1536 waitfor_pid_name.c_str());1537 nub_process_t pid = DNBProcessAttachWait(1538 &ctx, waitfor_pid_name.c_str(), ignore_existing, timeout_ptr,1539 waitfor_interval, err_str, sizeof(err_str));1540 g_pid = pid;1541 1542 if (pid == INVALID_NUB_PROCESS) {1543 ctx.LaunchStatus().SetError(-1, DNBError::Generic);1544 if (err_str[0])1545 ctx.LaunchStatus().SetErrorString(err_str);1546 RNBLogSTDERR("error: failed to attach to process named: \"%s\" %s\n",1547 waitfor_pid_name.c_str(), err_str);1548 mode = eRNBRunLoopModeExit;1549 } else {1550 ctx.SetProcessID(pid);1551 mode = eRNBRunLoopModeInferiorExecuting;1552 }1553 } else if (attach_pid != INVALID_NUB_PROCESS) {1554 1555 RNBLogSTDOUT("Attaching to process %i...\n", attach_pid);1556 nub_process_t attached_pid;1557 mode = RNBRunLoopLaunchAttaching(remote, attach_pid, attached_pid);1558 if (mode != eRNBRunLoopModeInferiorExecuting) {1559 const char *error_str = remote->Context().LaunchStatus().AsString();1560 RNBLogSTDERR("error: failed to attach process %i: %s\n", attach_pid,1561 error_str ? error_str : "unknown error.");1562 mode = eRNBRunLoopModeExit;1563 }1564 } else if (!attach_pid_name.empty()) {1565 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;1566 if (waitfor_duration != 0) {1567 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration,1568 0);1569 timeout_ptr = &attach_timeout_abstime;1570 }1571 1572 RNBLogSTDOUT("Attaching to process %s...\n", attach_pid_name.c_str());1573 nub_process_t pid = DNBProcessAttachByName(1574 attach_pid_name.c_str(), timeout_ptr, ctx.GetIgnoredExceptions(),1575 err_str, sizeof(err_str));1576 g_pid = pid;1577 if (pid == INVALID_NUB_PROCESS) {1578 ctx.LaunchStatus().SetError(-1, DNBError::Generic);1579 if (err_str[0])1580 ctx.LaunchStatus().SetErrorString(err_str);1581 RNBLogSTDERR("error: failed to attach to process named: \"%s\" %s\n",1582 waitfor_pid_name.c_str(), err_str);1583 mode = eRNBRunLoopModeExit;1584 } else {1585 ctx.SetProcessID(pid);1586 mode = eRNBRunLoopModeInferiorExecuting;1587 }1588 1589 } else {1590 RNBLogSTDERR(1591 "error: asked to attach with empty name and invalid PID.\n");1592 mode = eRNBRunLoopModeExit;1593 }1594 1595 if (mode != eRNBRunLoopModeExit) {1596 if (port != INT32_MAX) {1597 if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,1598 named_pipe_path.c_str(), unix_socket_name.c_str()))1599 mode = eRNBRunLoopModeExit;1600 } else if (str[0] == '/') {1601 if (remote->Comm().OpenFile(str))1602 mode = eRNBRunLoopModeExit;1603 } else if (communication_fd >= 0) {1604 // We were passed a file descriptor to use during fork/exec that is1605 // already open1606 // in our process, so lets just use it!1607 if (remote->Comm().useFD(communication_fd))1608 mode = eRNBRunLoopModeExit;1609 else1610 remote->StartReadRemoteDataThread();1611 }1612 1613 if (mode != eRNBRunLoopModeExit)1614 RNBLogSTDOUT("Waiting for debugger instructions for process %d.\n",1615 attach_pid);1616 }1617 break;1618 1619 case eRNBRunLoopModeInferiorLaunching: {1620 mode = RNBRunLoopLaunchInferior(remote, ctx.GetSTDINPath(),1621 ctx.GetSTDOUTPath(), ctx.GetSTDERRPath(),1622 no_stdio);1623 1624 if (mode == eRNBRunLoopModeInferiorExecuting) {1625 if (port != INT32_MAX) {1626 if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,1627 named_pipe_path.c_str(), unix_socket_name.c_str()))1628 mode = eRNBRunLoopModeExit;1629 } else if (str[0] == '/') {1630 if (remote->Comm().OpenFile(str))1631 mode = eRNBRunLoopModeExit;1632 } else if (communication_fd >= 0) {1633 // We were passed a file descriptor to use during fork/exec that is1634 // already open1635 // in our process, so lets just use it!1636 if (remote->Comm().useFD(communication_fd))1637 mode = eRNBRunLoopModeExit;1638 else1639 remote->StartReadRemoteDataThread();1640 }1641 1642 if (mode != eRNBRunLoopModeExit) {1643 const char *proc_name = "<unknown>";1644 if (ctx.ArgumentCount() > 0)1645 proc_name = ctx.ArgumentAtIndex(0);1646 DNBLog("[LaunchAttach] Successfully launched %s (pid = %d).\n",1647 proc_name, ctx.ProcessID());1648 RNBLogSTDOUT("Got a connection, launched process %s (pid = %d).\n",1649 proc_name, ctx.ProcessID());1650 }1651 } else {1652 const char *error_str = remote->Context().LaunchStatus().AsString();1653 RNBLogSTDERR("error: failed to launch process %s: %s\n", argv_sub_zero,1654 error_str ? error_str : "unknown error.");1655 }1656 } break;1657 1658 case eRNBRunLoopModeInferiorExecuting:1659 mode = RNBRunLoopInferiorExecuting(remote);1660 break;1661 1662 case eRNBRunLoopModePlatformMode:1663 if (port != INT32_MAX) {1664 if (!ConnectRemote(remote, host.c_str(), port, reverse_connect,1665 named_pipe_path.c_str(), unix_socket_name.c_str()))1666 mode = eRNBRunLoopModeExit;1667 } else if (str[0] == '/') {1668 if (remote->Comm().OpenFile(str))1669 mode = eRNBRunLoopModeExit;1670 } else if (communication_fd >= 0) {1671 // We were passed a file descriptor to use during fork/exec that is1672 // already open1673 // in our process, so lets just use it!1674 if (remote->Comm().useFD(communication_fd))1675 mode = eRNBRunLoopModeExit;1676 else1677 remote->StartReadRemoteDataThread();1678 }1679 1680 if (mode != eRNBRunLoopModeExit)1681 mode = RNBRunLoopPlatform(remote);1682 break;1683 1684 default:1685 mode = eRNBRunLoopModeExit;1686 break;1687 case eRNBRunLoopModeExit:1688 break;1689 }1690 }1691 1692 remote->StopReadRemoteDataThread();1693 remote->Context().SetProcessID(INVALID_NUB_PROCESS);1694 RNBLogSTDOUT("Exiting.\n");1695 1696 return 0;1697}1698