brintos

brintos / llvm-project-archived public Read only

0
0
Text · 60.2 KiB · 2c32fe9 Raw
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