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 <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 called
30 enum RNBRunLoopMode {
31   eRNBRunLoopModeInvalid = 0,
32   eRNBRunLoopModeGetStartModeFromRemoteProtocol,
33   eRNBRunLoopModeInferiorExecuting,
34   eRNBRunLoopModeExit
35 };
36 
37 // Global Variables
38 RNBRemoteSP g_remoteSP;
39 int g_disable_aslr = 0;
40 int 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 the
61 // arguments, wait for the new process to finish launching and hit its
62 // entry point,  and then return the run loop mode that should come next.
RNBRunLoopGetStartModeFromRemote(RNBRemoteSP & remoteSP)63 RNBRunLoopMode 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 process
88         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 failed
105           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 dies
125 nub_process_t g_pid;
126 int g_sigpipe_received = 0;
signal_handler(int signo)127 void 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 state
HandleProcessStateChange(RNBRemoteSP & remote,bool initialize)143 RNBRunLoopMode 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 happened
162     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 count
174       // 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 and
223 // we are waiting for gdb remote protocol packets. When a packet occurs that
224 // makes the inferior run, we need to leave this function with a new state
225 // as the return code.
RNBRunLoopInferiorExecuting(RNBRemoteSP & remote)226 RNBRunLoopMode 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 state
231   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 async
240       // packets
241       event_mask &= ~RNBContext::event_proc_stdio_available;
242     }
243 
244     // We want to make sure we consume all process state changes and have
245     // whomever is notifying us to wait for us to reset the event bit before
246     // 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 the
266         // 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 process
294           // 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 
ASLLogCallback(void * baton,uint32_t flags,const char * format,va_list args)314 void ASLLogCallback(void *baton, uint32_t flags, const char *format,
315                     va_list args) {
316 #if 0
317 	vprintf(format, args);
318 #endif
319 }
320 
debug_server_main(int fd)321 extern "C" int debug_server_main(int fd) {
322 #if 1
323   g_isatty = 0;
324 #else
325   g_isatty = ::isatty(STDIN_FILENO);
326 
327   DNBLogSetDebug(1);
328   DNBLogSetVerbose(1);
329   DNBLogSetLogMask(-1);
330   DNBLogSetLogCallback(ASLLogCallback, NULL);
331 #endif
332 
333   signal(SIGPIPE, signal_handler);
334 
335   g_remoteSP = std::make_shared<RNBRemote>();
336 
337   RNBRemote *remote = g_remoteSP.get();
338   if (remote == NULL) {
339     RNBLogSTDERR("error: failed to create a remote connection class\n");
340     return -1;
341   }
342 
343   RNBRunLoopMode mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
344 
345   while (mode != eRNBRunLoopModeExit) {
346     switch (mode) {
347     case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
348       if (g_remoteSP->Comm().useFD(fd) == rnb_success) {
349         RNBLogSTDOUT("Starting remote data thread.\n");
350         g_remoteSP->StartReadRemoteDataThread();
351 
352         RNBLogSTDOUT("Waiting for start mode from remote.\n");
353         mode = RNBRunLoopGetStartModeFromRemote(g_remoteSP);
354       } else {
355         mode = eRNBRunLoopModeExit;
356       }
357       break;
358 
359     case eRNBRunLoopModeInferiorExecuting:
360       mode = RNBRunLoopInferiorExecuting(g_remoteSP);
361       break;
362 
363     default:
364       mode = eRNBRunLoopModeExit;
365       break;
366 
367     case eRNBRunLoopModeExit:
368       break;
369     }
370   }
371 
372   g_remoteSP->StopReadRemoteDataThread();
373   g_remoteSP->Context().SetProcessID(INVALID_NUB_PROCESS);
374 
375   return 0;
376 }
377