brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.5 KiB · 17a5c13 Raw
369 lines · cpp
1//===-- libdebugserver.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 <cerrno>10#include <getopt.h>11#include <netinet/in.h>12#include <sys/select.h>13#include <sys/socket.h>14#include <sys/sysctl.h>15#include <sys/types.h>16 17#include <memory>18 19#include "DNB.h"20#include "DNBLog.h"21#include "DNBTimer.h"22#include "PseudoTerminal.h"23#include "RNBContext.h"24#include "RNBRemote.h"25#include "RNBServices.h"26#include "RNBSocket.h"27#include "SysSignal.h"28 29// Run loop modes which determine which run loop function will be called30enum RNBRunLoopMode {31  eRNBRunLoopModeInvalid = 0,32  eRNBRunLoopModeGetStartModeFromRemoteProtocol,33  eRNBRunLoopModeInferiorExecuting,34  eRNBRunLoopModeExit35};36 37// Global Variables38RNBRemoteSP g_remoteSP;39int g_disable_aslr = 0;40int g_isatty = 0;41 42#define RNBLogSTDOUT(fmt, ...)                                                 \43  do {                                                                         \44    if (g_isatty) {                                                            \45      fprintf(stdout, fmt, ##__VA_ARGS__);                                     \46    } else {                                                                   \47      _DNBLog(0, fmt, ##__VA_ARGS__);                                          \48    }                                                                          \49  } while (0)50#define RNBLogSTDERR(fmt, ...)                                                 \51  do {                                                                         \52    if (g_isatty) {                                                            \53      fprintf(stderr, fmt, ##__VA_ARGS__);                                     \54    } else {                                                                   \55      _DNBLog(0, fmt, ##__VA_ARGS__);                                          \56    }                                                                          \57  } while (0)58 59// Get our program path and arguments from the remote connection.60// We will need to start up the remote connection without a PID, get the61// arguments, wait for the new process to finish launching and hit its62// entry point,  and then return the run loop mode that should come next.63RNBRunLoopMode RNBRunLoopGetStartModeFromRemote(RNBRemoteSP &remoteSP) {64  std::string packet;65 66  if (remoteSP.get() != NULL) {67    RNBRemote *remote = remoteSP.get();68    RNBContext &ctx = remote->Context();69    uint32_t event_mask = RNBContext::event_read_packet_available;70 71    // Spin waiting to get the A packet.72    while (true) {73      DNBLogThreadedIf(LOG_RNB_MAX,74                       "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",75                       __FUNCTION__, event_mask);76      nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);77      DNBLogThreadedIf(LOG_RNB_MAX,78                       "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x",79                       __FUNCTION__, event_mask, set_events);80 81      if (set_events & RNBContext::event_read_packet_available) {82        rnb_err_t err = rnb_err;83        RNBRemote::PacketEnum type;84 85        err = remote->HandleReceivedPacket(&type);86 87        // check if we tried to attach to a process88        if (type == RNBRemote::vattach || type == RNBRemote::vattachwait) {89          if (err == rnb_success)90            return eRNBRunLoopModeInferiorExecuting;91          else {92            RNBLogSTDERR("error: attach failed.");93            return eRNBRunLoopModeExit;94          }95        }96 97        if (err == rnb_success) {98          DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Got success...", __FUNCTION__);99          continue;100        } else if (err == rnb_not_connected) {101          RNBLogSTDERR("error: connection lost.");102          return eRNBRunLoopModeExit;103        } else {104          // a catch all for any other gdb remote packets that failed105          DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s Error getting packet.",106                           __FUNCTION__);107          continue;108        }109 110        DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);111      } else {112        DNBLogThreadedIf(LOG_RNB_MINIMAL,113                         "%s Connection closed before getting \"A\" packet.",114                         __FUNCTION__);115        return eRNBRunLoopModeExit;116      }117    }118  }119  return eRNBRunLoopModeExit;120}121 122// Watch for signals:123// SIGINT: so we can halt our inferior. (disabled for now)124// SIGPIPE: in case our child process dies125nub_process_t g_pid;126int g_sigpipe_received = 0;127void signal_handler(int signo) {128  DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__,129                   SysSignal::Name(signo));130 131  switch (signo) {132  //  case SIGINT:133  //      DNBProcessKill (g_pid, signo);134  //      break;135 136  case SIGPIPE:137    g_sigpipe_received = 1;138    break;139  }140}141 142// Return the new run loop mode based off of the current process state143RNBRunLoopMode HandleProcessStateChange(RNBRemoteSP &remote, bool initialize) {144  RNBContext &ctx = remote->Context();145  nub_process_t pid = ctx.ProcessID();146 147  if (pid == INVALID_NUB_PROCESS) {148    DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...",149                     __FUNCTION__);150    return eRNBRunLoopModeExit;151  }152  nub_state_t pid_state = DNBProcessGetState(pid);153 154  DNBLogThreadedIf(LOG_RNB_MINIMAL,155                   "%s (&remote, initialize=%i)  pid_state = %s", __FUNCTION__,156                   (int)initialize, DNBStateAsString(pid_state));157 158  switch (pid_state) {159  case eStateInvalid:160  case eStateUnloaded:161    // Something bad happened162    return eRNBRunLoopModeExit;163    break;164 165  case eStateAttaching:166  case eStateLaunching:167    return eRNBRunLoopModeInferiorExecuting;168 169  case eStateSuspended:170  case eStateCrashed:171  case eStateStopped:172    if (!initialize) {173      // Compare the last stop count to our current notion of a stop count174      // to make sure we don't notify more than once for a given stop.175      nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();176      bool pid_stop_count_changed =177          ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));178      if (pid_stop_count_changed) {179        remote->FlushSTDIO();180 181        if (ctx.GetProcessStopCount() == 1) {182          DNBLogThreadedIf(183              LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s "184                               "pid_stop_count %zu (old %zu)) Notify??? no, "185                               "first stop...",186              __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),187              ctx.GetProcessStopCount(), prev_pid_stop_count);188        } else {189 190          DNBLogThreadedIf(191              LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  pid_state = %s "192                               "pid_stop_count %zu (old %zu)) Notify??? YES!!!",193              __FUNCTION__, (int)initialize, DNBStateAsString(pid_state),194              ctx.GetProcessStopCount(), prev_pid_stop_count);195          remote->NotifyThatProcessStopped();196        }197      } else {198        DNBLogThreadedIf(LOG_RNB_MINIMAL, "%s (&remote, initialize=%i)  "199                                          "pid_state = %s pid_stop_count %zu "200                                          "(old %zu)) Notify??? skipping...",201                         __FUNCTION__, (int)initialize,202                         DNBStateAsString(pid_state), ctx.GetProcessStopCount(),203                         prev_pid_stop_count);204      }205    }206    return eRNBRunLoopModeInferiorExecuting;207 208  case eStateStepping:209  case eStateRunning:210    return eRNBRunLoopModeInferiorExecuting;211 212  case eStateExited:213    remote->HandlePacket_last_signal(NULL);214    return eRNBRunLoopModeExit;215  case eStateDetached:216    return eRNBRunLoopModeExit;217  }218 219  // Catch all...220  return eRNBRunLoopModeExit;221}222// This function handles the case where our inferior program is stopped and223// we are waiting for gdb remote protocol packets. When a packet occurs that224// makes the inferior run, we need to leave this function with a new state225// as the return code.226RNBRunLoopMode RNBRunLoopInferiorExecuting(RNBRemoteSP &remote) {227  DNBLogThreadedIf(LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);228  RNBContext &ctx = remote->Context();229 230  // Init our mode and set 'is_running' based on the current process state231  RNBRunLoopMode mode = HandleProcessStateChange(remote, true);232 233  while (ctx.ProcessID() != INVALID_NUB_PROCESS) {234 235    std::string set_events_str;236    uint32_t event_mask = ctx.NormalEventBits();237 238    if (!ctx.ProcessStateRunning()) {239      // Clear the stdio bits if we are not running so we don't send any async240      // packets241      event_mask &= ~RNBContext::event_proc_stdio_available;242    }243 244    // We want to make sure we consume all process state changes and have245    // whomever is notifying us to wait for us to reset the event bit before246    // continuing.247    // ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);248 249    DNBLogThreadedIf(LOG_RNB_EVENTS,250                     "%s ctx.Events().WaitForSetEvents(0x%08x) ...",251                     __FUNCTION__, event_mask);252    nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);253    DNBLogThreadedIf(LOG_RNB_EVENTS,254                     "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",255                     __FUNCTION__, event_mask, set_events,256                     ctx.EventsAsString(set_events, set_events_str));257 258    if (set_events) {259      if ((set_events & RNBContext::event_proc_thread_exiting) ||260          (set_events & RNBContext::event_proc_stdio_available)) {261        remote->FlushSTDIO();262      }263 264      if (set_events & RNBContext::event_read_packet_available) {265        // handleReceivedPacket will take care of resetting the266        // event_read_packet_available events when there are no more...267        set_events ^= RNBContext::event_read_packet_available;268 269        if (ctx.ProcessStateRunning()) {270          if (remote->HandleAsyncPacket() == rnb_not_connected) {271            // TODO: connect again? Exit?272          }273        } else {274          if (remote->HandleReceivedPacket() == rnb_not_connected) {275            // TODO: connect again? Exit?276          }277        }278      }279 280      if (set_events & RNBContext::event_proc_state_changed) {281        mode = HandleProcessStateChange(remote, false);282        ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);283        set_events ^= RNBContext::event_proc_state_changed;284      }285 286      if (set_events & RNBContext::event_proc_thread_exiting) {287        mode = eRNBRunLoopModeExit;288      }289 290      if (set_events & RNBContext::event_read_thread_exiting) {291        // Out remote packet receiving thread exited, exit for now.292        if (ctx.HasValidProcessID()) {293          // TODO: We should add code that will leave the current process294          // in its current state and listen for another connection...295          if (ctx.ProcessStateRunning()) {296            DNBProcessKill(ctx.ProcessID());297          }298        }299        mode = eRNBRunLoopModeExit;300      }301    }302 303    // Reset all event bits that weren't reset for now...304    if (set_events != 0)305      ctx.Events().ResetEvents(set_events);306 307    if (mode != eRNBRunLoopModeInferiorExecuting)308      break;309  }310 311  return mode;312}313 314extern "C" int debug_server_main(int fd) {315#if 1316  g_isatty = 0;317#else318  g_isatty = ::isatty(STDIN_FILENO);319 320  DNBLogSetDebug(1);321  DNBLogSetVerbose(1);322  DNBLogSetLogMask(-1);323#endif324 325  signal(SIGPIPE, signal_handler);326 327  g_remoteSP = std::make_shared<RNBRemote>();328 329  RNBRemote *remote = g_remoteSP.get();330  if (remote == NULL) {331    RNBLogSTDERR("error: failed to create a remote connection class\n");332    return -1;333  }334 335  RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;336 337  while (mode != eRNBRunLoopModeExit) {338    switch (mode) {339    case eRNBRunLoopModeGetStartModeFromRemoteProtocol:340      if (g_remoteSP->Comm().useFD(fd) == rnb_success) {341        RNBLogSTDOUT("Starting remote data thread.\n");342        g_remoteSP->StartReadRemoteDataThread();343 344        RNBLogSTDOUT("Waiting for start mode from remote.\n");345        mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);346      } else {347        mode = eRNBRunLoopModeExit;348      }349      break;350 351    case eRNBRunLoopModeInferiorExecuting:352      mode = RNBRunLoopInferiorExecuting(g_remoteSP);353      break;354 355    default:356      mode = eRNBRunLoopModeExit;357      break;358 359    case eRNBRunLoopModeExit:360      break;361    }362  }363 364  g_remoteSP->StopReadRemoteDataThread();365  g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);366 367  return 0;368}369