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
30enum RNBRunLoopMode {
31 eRNBRunLoopModeInvalid = 0,
32 eRNBRunLoopModeGetStartModeFromRemoteProtocol,
33 eRNBRunLoopModeInferiorExecuting,
34 eRNBRunLoopModeExit
35};
36
37// Global Variables
38RNBRemoteSP 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 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.
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(mask: 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: &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
125nub_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 state
143RNBRunLoopMode 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.
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 state
231 RNBRunLoopMode mode = HandleProcessStateChange(remote, initialize: 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(mask: 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, initialize: false);
282 ctx.Events().ResetEvents(mask: 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(pid: 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(mask: set_events);
306
307 if (mode != eRNBRunLoopModeInferiorExecuting)
308 break;
309 }
310
311 return mode;
312}
313
314void ASLLogCallback(void *baton, uint32_t flags, const char *format,
315 va_list args) {
316#if 0
317 vprintf(format, args);
318#endif
319}
320
321extern "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, handler: 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(remoteSP&: g_remoteSP);
354 } else {
355 mode = eRNBRunLoopModeExit;
356 }
357 break;
358
359 case eRNBRunLoopModeInferiorExecuting:
360 mode = RNBRunLoopInferiorExecuting(remote&: 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

source code of lldb/tools/debugserver/source/libdebugserver.cpp