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