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