blob: 6e08d5dd8dc1a81052dfced786554f50c8c30031 [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- debugserver.cpp -----------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include <sys/socket.h>
11#include <sys/types.h>
12#include <errno.h>
13#include <getopt.h>
14#include <netinet/in.h>
15#include <sys/select.h>
16#include <sys/sysctl.h>
17#include <string>
18#include <vector>
19#include <asl.h>
20
21#include "CFString.h"
22#include "DNB.h"
23#include "DNBLog.h"
24#include "DNBTimer.h"
25#include "PseudoTerminal.h"
26#include "RNBContext.h"
27#include "RNBServices.h"
28#include "RNBSocket.h"
29#include "RNBRemote.h"
30#include "SysSignal.h"
31
32// Global PID in case we get a signal and need to stop the process...
33nub_process_t g_pid = INVALID_NUB_PROCESS;
34
35//----------------------------------------------------------------------
36// Run loop modes which determine which run loop function will be called
37//----------------------------------------------------------------------
38typedef enum
39{
40 eRNBRunLoopModeInvalid = 0,
41 eRNBRunLoopModeGetStartModeFromRemoteProtocol,
42 eRNBRunLoopModeInferiorAttaching,
43 eRNBRunLoopModeInferiorLaunching,
44 eRNBRunLoopModeInferiorExecuting,
Greg Clayton7a5388b2011-03-20 04:57:14 +000045 eRNBRunLoopModePlatformMode,
Chris Lattner30fdc8d2010-06-08 16:52:24 +000046 eRNBRunLoopModeExit
47} RNBRunLoopMode;
48
49
50//----------------------------------------------------------------------
51// Global Variables
52//----------------------------------------------------------------------
53RNBRemoteSP g_remoteSP;
54static int g_lockdown_opt = 0;
55static int g_applist_opt = 0;
56static nub_launch_flavor_t g_launch_flavor = eLaunchFlavorDefault;
Greg Clayton6f35f5c2010-09-09 06:32:46 +000057int g_disable_aslr = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000058
59int g_isatty = 0;
60
61#define RNBLogSTDOUT(fmt, ...) do { if (g_isatty) { fprintf(stdout, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
62#define RNBLogSTDERR(fmt, ...) do { if (g_isatty) { fprintf(stderr, fmt, ## __VA_ARGS__); } else { _DNBLog(0, fmt, ## __VA_ARGS__); } } while (0)
63
64//----------------------------------------------------------------------
Chris Lattner30fdc8d2010-06-08 16:52:24 +000065// Get our program path and arguments from the remote connection.
66// We will need to start up the remote connection without a PID, get the
67// arguments, wait for the new process to finish launching and hit its
68// entry point, and then return the run loop mode that should come next.
69//----------------------------------------------------------------------
70RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +000071RNBRunLoopGetStartModeFromRemote (RNBRemote* remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000072{
73 std::string packet;
74
Greg Clayton6779606a2011-01-22 23:43:18 +000075 if (remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +000076 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +000077 RNBContext& ctx = remote->Context();
Greg Clayton71337622011-02-24 22:24:29 +000078 uint32_t event_mask = RNBContext::event_read_packet_available |
79 RNBContext::event_read_thread_exiting;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000080
81 // Spin waiting to get the A packet.
82 while (1)
83 {
84 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) ...",__FUNCTION__, event_mask);
85 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
86 DNBLogThreadedIf (LOG_RNB_MAX, "%s ctx.Events().WaitForSetEvents( 0x%08x ) => 0x%08x", __FUNCTION__, event_mask, set_events);
87
Greg Clayton71337622011-02-24 22:24:29 +000088 if (set_events & RNBContext::event_read_thread_exiting)
89 {
90 RNBLogSTDERR ("error: packet read thread exited.");
91 return eRNBRunLoopModeExit;
92 }
93
Chris Lattner30fdc8d2010-06-08 16:52:24 +000094 if (set_events & RNBContext::event_read_packet_available)
95 {
96 rnb_err_t err = rnb_err;
97 RNBRemote::PacketEnum type;
98
99 err = remote->HandleReceivedPacket (&type);
100
101 // check if we tried to attach to a process
102 if (type == RNBRemote::vattach || type == RNBRemote::vattachwait)
103 {
104 if (err == rnb_success)
105 return eRNBRunLoopModeInferiorExecuting;
106 else
107 {
108 RNBLogSTDERR ("error: attach failed.");
109 return eRNBRunLoopModeExit;
110 }
111 }
112
113 if (err == rnb_success)
114 {
115 // If we got our arguments we are ready to launch using the arguments
116 // and any environment variables we received.
117 if (type == RNBRemote::set_argv)
118 {
119 return eRNBRunLoopModeInferiorLaunching;
120 }
121 }
122 else if (err == rnb_not_connected)
123 {
124 RNBLogSTDERR ("error: connection lost.");
125 return eRNBRunLoopModeExit;
126 }
127 else
128 {
129 // a catch all for any other gdb remote packets that failed
130 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.",__FUNCTION__);
131 continue;
132 }
133
134 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
135 }
136 else
137 {
138 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Connection closed before getting \"A\" packet.", __FUNCTION__);
139 return eRNBRunLoopModeExit;
140 }
141 }
142 }
143 return eRNBRunLoopModeExit;
144}
145
146
147//----------------------------------------------------------------------
148// This run loop mode will wait for the process to launch and hit its
149// entry point. It will currently ignore all events except for the
150// process state changed event, where it watches for the process stopped
151// or crash process state.
152//----------------------------------------------------------------------
153RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000154RNBRunLoopLaunchInferior (RNBRemote *remote, const char *stdin_path, const char *stdout_path, const char *stderr_path, bool no_stdio)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000155{
156 RNBContext& ctx = remote->Context();
157
158 // The Process stuff takes a c array, the RNBContext has a vector...
159 // So make up a c array.
160
161 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Launching '%s'...", __FUNCTION__, ctx.ArgumentAtIndex(0));
162
163 size_t inferior_argc = ctx.ArgumentCount();
164 // Initialize inferior_argv with inferior_argc + 1 NULLs
165 std::vector<const char *> inferior_argv(inferior_argc + 1, NULL);
166
167 size_t i;
168 for (i = 0; i < inferior_argc; i++)
169 inferior_argv[i] = ctx.ArgumentAtIndex(i);
170
171 // Pass the environment array the same way:
172
173 size_t inferior_envc = ctx.EnvironmentCount();
174 // Initialize inferior_argv with inferior_argc + 1 NULLs
175 std::vector<const char *> inferior_envp(inferior_envc + 1, NULL);
176
177 for (i = 0; i < inferior_envc; i++)
178 inferior_envp[i] = ctx.EnvironmentAtIndex(i);
179
180 // Our launch type hasn't been set to anything concrete, so we need to
181 // figure our how we are going to launch automatically.
182
183 nub_launch_flavor_t launch_flavor = g_launch_flavor;
184 if (launch_flavor == eLaunchFlavorDefault)
185 {
186 // Our default launch method is posix spawn
187 launch_flavor = eLaunchFlavorPosixSpawn;
188
189#if defined (__arm__)
190 // Check if we have an app bundle, if so launch using SpringBoard.
191 if (strstr(inferior_argv[0], ".app"))
192 {
193 launch_flavor = eLaunchFlavorSpringBoard;
194 }
195#endif
196 }
197
198 ctx.SetLaunchFlavor(launch_flavor);
199 char resolved_path[PATH_MAX];
200
201 // If we fail to resolve the path to our executable, then just use what we
202 // were given and hope for the best
203 if ( !DNBResolveExecutablePath (inferior_argv[0], resolved_path, sizeof(resolved_path)) )
204 ::strncpy(resolved_path, inferior_argv[0], sizeof(resolved_path));
205
206 char launch_err_str[PATH_MAX];
207 launch_err_str[0] = '\0';
Johnny Chen725269a2011-02-26 01:36:13 +0000208 const char * cwd = (ctx.GetWorkingDirPath() != NULL ? ctx.GetWorkingDirPath()
209 : ctx.GetWorkingDirectory());
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000210 nub_process_t pid = DNBProcessLaunch (resolved_path,
211 &inferior_argv[0],
212 &inferior_envp[0],
Johnny Chen725269a2011-02-26 01:36:13 +0000213 cwd,
Greg Clayton6779606a2011-01-22 23:43:18 +0000214 stdin_path,
215 stdout_path,
216 stderr_path,
Caroline Ticef8da8632010-12-03 18:46:09 +0000217 no_stdio,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000218 launch_flavor,
Greg Claytonf681b942010-08-31 18:35:14 +0000219 g_disable_aslr,
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000220 launch_err_str,
221 sizeof(launch_err_str));
222
223 g_pid = pid;
224
225 if (pid == INVALID_NUB_PROCESS && strlen(launch_err_str) > 0)
226 {
Greg Clayton3382c2c2010-07-30 23:14:42 +0000227 DNBLogThreaded ("%s DNBProcessLaunch() returned error: '%s'", __FUNCTION__, launch_err_str);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000228 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
229 ctx.LaunchStatus().SetErrorString(launch_err_str);
230 }
231 else
Greg Clayton71337622011-02-24 22:24:29 +0000232 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000233 ctx.LaunchStatus().Clear();
Greg Clayton71337622011-02-24 22:24:29 +0000234 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000235
236 if (remote->Comm().IsConnected())
237 {
238 // It we are connected already, the next thing gdb will do is ask
239 // whether the launch succeeded, and if not, whether there is an
240 // error code. So we need to fetch one packet from gdb before we wait
241 // on the stop from the target.
242
243 uint32_t event_mask = RNBContext::event_read_packet_available;
244 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
245
246 if (set_events & RNBContext::event_read_packet_available)
247 {
248 rnb_err_t err = rnb_err;
249 RNBRemote::PacketEnum type;
250
251 err = remote->HandleReceivedPacket (&type);
252
253 if (err != rnb_success)
254 {
255 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Error getting packet.", __FUNCTION__);
256 return eRNBRunLoopModeExit;
257 }
258 if (type != RNBRemote::query_launch_success)
259 {
260 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Didn't get the expected qLaunchSuccess packet.", __FUNCTION__);
261 }
262 }
263 }
264
265 while (pid != INVALID_NUB_PROCESS)
266 {
267 // Wait for process to start up and hit entry point
268 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE)...", __FUNCTION__, pid);
269 nub_event_t set_events = DNBProcessWaitForEvents (pid, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, NULL);
270 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s DNBProcessWaitForEvent (%4.4x, eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged, true, INFINITE) => 0x%8.8x", __FUNCTION__, pid, set_events);
271
272 if (set_events == 0)
273 {
274 pid = INVALID_NUB_PROCESS;
275 g_pid = pid;
276 }
277 else
278 {
279 if (set_events & (eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged))
280 {
281 nub_state_t pid_state = DNBProcessGetState (pid);
282 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s process %4.4x state changed (eEventProcessStateChanged): %s", __FUNCTION__, pid, DNBStateAsString(pid_state));
283
284 switch (pid_state)
285 {
286 default:
287 case eStateInvalid:
288 case eStateUnloaded:
289 case eStateAttaching:
290 case eStateLaunching:
291 case eStateSuspended:
292 break; // Ignore
293
294 case eStateRunning:
295 case eStateStepping:
296 // Still waiting to stop at entry point...
297 break;
298
299 case eStateStopped:
300 case eStateCrashed:
301 ctx.SetProcessID(pid);
302 return eRNBRunLoopModeInferiorExecuting;
303
304 case eStateDetached:
305 case eStateExited:
306 pid = INVALID_NUB_PROCESS;
307 g_pid = pid;
308 return eRNBRunLoopModeExit;
309 }
310 }
311
312 DNBProcessResetEvents(pid, set_events);
313 }
314 }
315
316 return eRNBRunLoopModeExit;
317}
318
319
320//----------------------------------------------------------------------
321// This run loop mode will wait for the process to launch and hit its
322// entry point. It will currently ignore all events except for the
323// process state changed event, where it watches for the process stopped
324// or crash process state.
325//----------------------------------------------------------------------
326RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000327RNBRunLoopLaunchAttaching (RNBRemote *remote, nub_process_t attach_pid, nub_process_t& pid)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000328{
329 RNBContext& ctx = remote->Context();
330
331 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s Attaching to pid %i...", __FUNCTION__, attach_pid);
332 char err_str[1024];
333 pid = DNBProcessAttach (attach_pid, NULL, err_str, sizeof(err_str));
334 g_pid = pid;
335
336 if (pid == INVALID_NUB_PROCESS)
337 {
338 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
339 if (err_str[0])
340 ctx.LaunchStatus().SetErrorString(err_str);
341 return eRNBRunLoopModeExit;
342 }
343 else
344 {
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000345 ctx.SetProcessID(pid);
346 return eRNBRunLoopModeInferiorExecuting;
347 }
348}
349
350//----------------------------------------------------------------------
351// Watch for signals:
352// SIGINT: so we can halt our inferior. (disabled for now)
353// SIGPIPE: in case our child process dies
354//----------------------------------------------------------------------
355int g_sigint_received = 0;
356int g_sigpipe_received = 0;
357void
358signal_handler(int signo)
359{
360 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (%s)", __FUNCTION__, SysSignal::Name(signo));
361
362 switch (signo)
363 {
364 case SIGINT:
365 g_sigint_received++;
366 if (g_pid != INVALID_NUB_PROCESS)
367 {
368 // Only send a SIGINT once...
369 if (g_sigint_received == 1)
370 {
371 switch (DNBProcessGetState (g_pid))
372 {
373 case eStateRunning:
374 case eStateStepping:
375 DNBProcessSignal (g_pid, SIGSTOP);
376 return;
Greg Clayton95bf0fd2011-04-01 00:29:43 +0000377 default:
378 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000379 }
380 }
381 }
382 exit (SIGINT);
383 break;
384
385 case SIGPIPE:
386 g_sigpipe_received = 1;
387 break;
388 }
389}
390
391// Return the new run loop mode based off of the current process state
392RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000393HandleProcessStateChange (RNBRemote *remote, bool initialize)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000394{
395 RNBContext& ctx = remote->Context();
396 nub_process_t pid = ctx.ProcessID();
397
398 if (pid == INVALID_NUB_PROCESS)
399 {
400 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s error: pid invalid, exiting...", __FUNCTION__);
401 return eRNBRunLoopModeExit;
402 }
403 nub_state_t pid_state = DNBProcessGetState (pid);
404
405 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state));
406
407 switch (pid_state)
408 {
409 case eStateInvalid:
410 case eStateUnloaded:
411 // Something bad happened
412 return eRNBRunLoopModeExit;
413 break;
414
415 case eStateAttaching:
416 case eStateLaunching:
417 return eRNBRunLoopModeInferiorExecuting;
418
419 case eStateSuspended:
420 case eStateCrashed:
421 case eStateStopped:
422 // If we stop due to a signal, so clear the fact that we got a SIGINT
423 // so we can stop ourselves again (but only while our inferior
424 // process is running..)
425 g_sigint_received = 0;
426 if (initialize == false)
427 {
428 // Compare the last stop count to our current notion of a stop count
429 // to make sure we don't notify more than once for a given stop.
430 nub_size_t prev_pid_stop_count = ctx.GetProcessStopCount();
431 bool pid_stop_count_changed = ctx.SetProcessStopCount(DNBProcessGetStopCount(pid));
432 if (pid_stop_count_changed)
433 {
434 remote->FlushSTDIO();
435
436 if (ctx.GetProcessStopCount() == 1)
437 {
438 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? no, first stop...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
439 }
440 else
441 {
442
443 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? YES!!!", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
444 remote->NotifyThatProcessStopped ();
445 }
446 }
447 else
448 {
449 DNBLogThreadedIf (LOG_RNB_MINIMAL, "%s (&remote, initialize=%i) pid_state = %s pid_stop_count %u (old %u)) Notify??? skipping...", __FUNCTION__, (int)initialize, DNBStateAsString (pid_state), ctx.GetProcessStopCount(), prev_pid_stop_count);
450 }
451 }
452 return eRNBRunLoopModeInferiorExecuting;
453
454 case eStateStepping:
455 case eStateRunning:
456 return eRNBRunLoopModeInferiorExecuting;
457
458 case eStateExited:
459 remote->HandlePacket_last_signal(NULL);
Greg Clayton95bf0fd2011-04-01 00:29:43 +0000460 case eStateDetached:
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000461 return eRNBRunLoopModeExit;
462
463 }
464
465 // Catch all...
466 return eRNBRunLoopModeExit;
467}
468// This function handles the case where our inferior program is stopped and
469// we are waiting for gdb remote protocol packets. When a packet occurs that
470// makes the inferior run, we need to leave this function with a new state
471// as the return code.
472RNBRunLoopMode
Greg Clayton6779606a2011-01-22 23:43:18 +0000473RNBRunLoopInferiorExecuting (RNBRemote *remote)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000474{
475 DNBLogThreadedIf (LOG_RNB_MINIMAL, "#### %s", __FUNCTION__);
476 RNBContext& ctx = remote->Context();
477
478 // Init our mode and set 'is_running' based on the current process state
479 RNBRunLoopMode mode = HandleProcessStateChange (remote, true);
480
481 while (ctx.ProcessID() != INVALID_NUB_PROCESS)
482 {
483
484 std::string set_events_str;
485 uint32_t event_mask = ctx.NormalEventBits();
486
487 if (!ctx.ProcessStateRunning())
488 {
489 // Clear the stdio bits if we are not running so we don't send any async packets
490 event_mask &= ~RNBContext::event_proc_stdio_available;
491 }
492
493 // We want to make sure we consume all process state changes and have
494 // whomever is notifying us to wait for us to reset the event bit before
495 // continuing.
496 //ctx.Events().SetResetAckMask (RNBContext::event_proc_state_changed);
497
498 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
499 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
500 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
501
502 if (set_events)
503 {
504 if ((set_events & RNBContext::event_proc_thread_exiting) ||
505 (set_events & RNBContext::event_proc_stdio_available))
506 {
507 remote->FlushSTDIO();
508 }
509
510 if (set_events & RNBContext::event_read_packet_available)
511 {
512 // handleReceivedPacket will take care of resetting the
513 // event_read_packet_available events when there are no more...
514 set_events ^= RNBContext::event_read_packet_available;
515
516 if (ctx.ProcessStateRunning())
517 {
518 if (remote->HandleAsyncPacket() == rnb_not_connected)
519 {
520 // TODO: connect again? Exit?
521 }
522 }
523 else
524 {
525 if (remote->HandleReceivedPacket() == rnb_not_connected)
526 {
527 // TODO: connect again? Exit?
528 }
529 }
530 }
531
532 if (set_events & RNBContext::event_proc_state_changed)
533 {
534 mode = HandleProcessStateChange (remote, false);
535 ctx.Events().ResetEvents(RNBContext::event_proc_state_changed);
536 set_events ^= RNBContext::event_proc_state_changed;
537 }
538
539 if (set_events & RNBContext::event_proc_thread_exiting)
540 {
541 mode = eRNBRunLoopModeExit;
542 }
543
544 if (set_events & RNBContext::event_read_thread_exiting)
545 {
546 // Out remote packet receiving thread exited, exit for now.
547 if (ctx.HasValidProcessID())
548 {
549 // TODO: We should add code that will leave the current process
550 // in its current state and listen for another connection...
551 if (ctx.ProcessStateRunning())
552 {
553 DNBProcessKill (ctx.ProcessID());
554 }
555 }
556 mode = eRNBRunLoopModeExit;
557 }
558 }
559
560 // Reset all event bits that weren't reset for now...
561 if (set_events != 0)
562 ctx.Events().ResetEvents(set_events);
563
564 if (mode != eRNBRunLoopModeInferiorExecuting)
565 break;
566 }
567
568 return mode;
569}
570
571
Greg Clayton7a5388b2011-03-20 04:57:14 +0000572RNBRunLoopMode
573RNBRunLoopPlatform (RNBRemote *remote)
574{
575 RNBRunLoopMode mode = eRNBRunLoopModePlatformMode;
576 RNBContext& ctx = remote->Context();
577
578 while (mode == eRNBRunLoopModePlatformMode)
579 {
580 std::string set_events_str;
581 const uint32_t event_mask = RNBContext::event_read_packet_available |
582 RNBContext::event_read_thread_exiting;
583
584 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) ...",__FUNCTION__, event_mask);
585 nub_event_t set_events = ctx.Events().WaitForSetEvents(event_mask);
586 DNBLogThreadedIf (LOG_RNB_EVENTS, "%s ctx.Events().WaitForSetEvents(0x%08x) => 0x%08x (%s)",__FUNCTION__, event_mask, set_events, ctx.EventsAsString(set_events, set_events_str));
587
588 if (set_events)
589 {
590 if (set_events & RNBContext::event_read_packet_available)
591 {
592 if (remote->HandleReceivedPacket() == rnb_not_connected)
593 mode = eRNBRunLoopModeExit;
594 }
595
596 if (set_events & RNBContext::event_read_thread_exiting)
597 {
598 mode = eRNBRunLoopModeExit;
599 }
600 ctx.Events().ResetEvents(set_events);
601 }
602 }
603 return eRNBRunLoopModeExit;
604}
605
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000606//----------------------------------------------------------------------
607// Convenience function to set up the remote listening port
608// Returns 1 for success 0 for failure.
609//----------------------------------------------------------------------
610
611static int
Greg Clayton6779606a2011-01-22 23:43:18 +0000612StartListening (RNBRemote *remote, int listen_port)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000613{
Greg Clayton6779606a2011-01-22 23:43:18 +0000614 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000615 {
616 RNBLogSTDOUT ("Listening to port %i...\n", listen_port);
Greg Clayton6779606a2011-01-22 23:43:18 +0000617 if (remote->Comm().Listen(listen_port) != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000618 {
619 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
620 return 0;
621 }
622 else
623 {
Greg Clayton6779606a2011-01-22 23:43:18 +0000624 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000625 }
626 }
627 return 1;
628}
629
630//----------------------------------------------------------------------
631// ASL Logging callback that can be registered with DNBLogSetLogCallback
632//----------------------------------------------------------------------
633void
634ASLLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
635{
636 if (format == NULL)
637 return;
638 static aslmsg g_aslmsg = NULL;
639 if (g_aslmsg == NULL)
640 {
641 g_aslmsg = ::asl_new (ASL_TYPE_MSG);
642 char asl_key_sender[PATH_MAX];
643 snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.%s-%g", DEBUGSERVER_PROGRAM_NAME, DEBUGSERVER_VERSION_NUM);
644 ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender);
645 }
646
647 int asl_level;
648 if (flags & DNBLOG_FLAG_FATAL) asl_level = ASL_LEVEL_CRIT;
649 else if (flags & DNBLOG_FLAG_ERROR) asl_level = ASL_LEVEL_ERR;
650 else if (flags & DNBLOG_FLAG_WARNING) asl_level = ASL_LEVEL_WARNING;
651 else if (flags & DNBLOG_FLAG_VERBOSE) asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_INFO;
652 else asl_level = ASL_LEVEL_WARNING; //ASL_LEVEL_DEBUG;
653
654 ::asl_vlog (NULL, g_aslmsg, asl_level, format, args);
655}
656
657//----------------------------------------------------------------------
658// FILE based Logging callback that can be registered with
659// DNBLogSetLogCallback
660//----------------------------------------------------------------------
661void
662FileLogCallback(void *baton, uint32_t flags, const char *format, va_list args)
663{
664 if (baton == NULL || format == NULL)
665 return;
666
667 ::vfprintf ((FILE *)baton, format, args);
668 ::fprintf ((FILE *)baton, "\n");
669}
670
671
672void
673show_usage_and_exit (int exit_code)
674{
675 RNBLogSTDERR ("Usage:\n %s host:port [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
676 RNBLogSTDERR (" %s /path/file [program-name program-arg1 program-arg2 ...]\n", DEBUGSERVER_PROGRAM_NAME);
677 RNBLogSTDERR (" %s host:port --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
678 RNBLogSTDERR (" %s /path/file --attach=<pid>\n", DEBUGSERVER_PROGRAM_NAME);
679 RNBLogSTDERR (" %s host:port --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
680 RNBLogSTDERR (" %s /path/file --attach=<process_name>\n", DEBUGSERVER_PROGRAM_NAME);
681 exit (exit_code);
682}
683
684
685//----------------------------------------------------------------------
686// option descriptors for getopt_long()
687//----------------------------------------------------------------------
688static struct option g_long_options[] =
689{
690 { "attach", required_argument, NULL, 'a' },
Greg Clayton3af9ea52010-11-18 05:57:03 +0000691 { "arch", required_argument, NULL, 'A' },
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000692 { "debug", no_argument, NULL, 'g' },
693 { "verbose", no_argument, NULL, 'v' },
694 { "lockdown", no_argument, &g_lockdown_opt, 1 }, // short option "-k"
695 { "applist", no_argument, &g_applist_opt, 1 }, // short option "-t"
696 { "log-file", required_argument, NULL, 'l' },
697 { "log-flags", required_argument, NULL, 'f' },
698 { "launch", required_argument, NULL, 'x' }, // Valid values are "auto", "posix-spawn", "fork-exec", "springboard" (arm only)
699 { "waitfor", required_argument, NULL, 'w' }, // Wait for a process whose name starts with ARG
700 { "waitfor-interval", required_argument, NULL, 'i' }, // Time in usecs to wait between sampling the pid list when waiting for a process by name
701 { "waitfor-duration", required_argument, NULL, 'd' }, // The time in seconds to wait for a process to show up by name
702 { "native-regs", no_argument, NULL, 'r' }, // Specify to use the native registers instead of the gdb defaults for the architecture.
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000703 { "stdio-path", required_argument, NULL, 's' }, // Set the STDIO path to be used when launching applications (STDIN, STDOUT and STDERR) (only if debugserver launches the process)
704 { "stdin-path", required_argument, NULL, 'I' }, // Set the STDIN path to be used when launching applications (only if debugserver launches the process)
Johnny Chena3435452011-01-25 16:56:01 +0000705 { "stdout-path", required_argument, NULL, 'O' }, // Set the STDOUT path to be used when launching applications (only if debugserver launches the process)
706 { "stderr-path", required_argument, NULL, 'E' }, // Set the STDERR path to be used when launching applications (only if debugserver launches the process)
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000707 { "no-stdio", no_argument, NULL, 'n' }, // Do not set up any stdio (perhaps the program is a GUI program) (only if debugserver launches the process)
708 { "setsid", no_argument, NULL, 'S' }, // call setsid() to make debugserver run in its own session
Greg Claytonf681b942010-08-31 18:35:14 +0000709 { "disable-aslr", no_argument, NULL, 'D' }, // Use _POSIX_SPAWN_DISABLE_ASLR to avoid shared library randomization
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000710 { "working-dir", required_argument, NULL, 'W' }, // The working directory that the inferior process should have (only if debugserver launches the process)
Greg Clayton7a5388b2011-03-20 04:57:14 +0000711 { "platform", required_argument, NULL, 'p' }, // Put this executable into a remote platform mode
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000712 { NULL, 0, NULL, 0 }
713};
714
715
716//----------------------------------------------------------------------
717// main
718//----------------------------------------------------------------------
719int
720main (int argc, char *argv[])
721{
722 g_isatty = ::isatty (STDIN_FILENO);
723
724 // ::printf ("uid=%u euid=%u gid=%u egid=%u\n",
725 // getuid(),
726 // geteuid(),
727 // getgid(),
728 // getegid());
729
730
731 // signal (SIGINT, signal_handler);
732 signal (SIGPIPE, signal_handler);
Greg Clayton3382c2c2010-07-30 23:14:42 +0000733 signal (SIGHUP, signal_handler);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000734
Greg Clayton71337622011-02-24 22:24:29 +0000735 g_remoteSP.reset (new RNBRemote ());
736
737
738 RNBRemote *remote = g_remoteSP.get();
739 if (remote == NULL)
740 {
741 RNBLogSTDERR ("error: failed to create a remote connection class\n");
742 return -1;
743 }
744
745 RNBContext& ctx = remote->Context();
746
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000747 int i;
748 int attach_pid = INVALID_NUB_PROCESS;
749
750 FILE* log_file = NULL;
751 uint32_t log_flags = 0;
752 // Parse our options
753 int ch;
754 int long_option_index = 0;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000755 int debug = 0;
756 std::string compile_options;
757 std::string waitfor_pid_name; // Wait for a process that starts with this name
758 std::string attach_pid_name;
Greg Clayton3af9ea52010-11-18 05:57:03 +0000759 std::string arch_name;
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000760 std::string working_dir; // The new working directory to use for the inferior
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000761 useconds_t waitfor_interval = 1000; // Time in usecs between process lists polls when waiting for a process by name, default 1 msec.
762 useconds_t waitfor_duration = 0; // Time in seconds to wait for a process by name, 0 means wait forever.
Caroline Ticef8da8632010-12-03 18:46:09 +0000763 bool no_stdio = false;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000764
765#if !defined (DNBLOG_ENABLED)
766 compile_options += "(no-logging) ";
767#endif
768
769 RNBRunLoopMode start_mode = eRNBRunLoopModeExit;
770
Caroline Ticef8da8632010-12-03 18:46:09 +0000771 while ((ch = getopt_long(argc, argv, "a:A:d:gi:vktl:f:w:x:rs:n", g_long_options, &long_option_index)) != -1)
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000772 {
773 DNBLogDebug("option: ch == %c (0x%2.2x) --%s%c%s\n",
774 ch, (uint8_t)ch,
775 g_long_options[long_option_index].name,
776 g_long_options[long_option_index].has_arg ? '=' : ' ',
777 optarg ? optarg : "");
778 switch (ch)
779 {
780 case 0: // Any optional that auto set themselves will return 0
781 break;
782
Greg Clayton3af9ea52010-11-18 05:57:03 +0000783 case 'A':
784 if (optarg && optarg[0])
785 arch_name.assign(optarg);
786 break;
787
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000788 case 'a':
789 if (optarg && optarg[0])
790 {
791 if (isdigit(optarg[0]))
792 {
793 char *end = NULL;
794 attach_pid = strtoul(optarg, &end, 0);
795 if (end == NULL || *end != '\0')
796 {
797 RNBLogSTDERR ("error: invalid pid option '%s'\n", optarg);
798 exit (4);
799 }
800 }
801 else
802 {
803 attach_pid_name = optarg;
804 }
805 start_mode = eRNBRunLoopModeInferiorAttaching;
806 }
807 break;
808
809 // --waitfor=NAME
810 case 'w':
811 if (optarg && optarg[0])
812 {
813 waitfor_pid_name = optarg;
814 start_mode = eRNBRunLoopModeInferiorAttaching;
815 }
816 break;
817
818 // --waitfor-interval=USEC
819 case 'i':
820 if (optarg && optarg[0])
821 {
822 char *end = NULL;
823 waitfor_interval = strtoul(optarg, &end, 0);
824 if (end == NULL || *end != '\0')
825 {
826 RNBLogSTDERR ("error: invalid waitfor-interval option value '%s'.\n", optarg);
827 exit (6);
828 }
829 }
830 break;
831
832 // --waitfor-duration=SEC
833 case 'd':
834 if (optarg && optarg[0])
835 {
836 char *end = NULL;
837 waitfor_duration = strtoul(optarg, &end, 0);
838 if (end == NULL || *end != '\0')
839 {
840 RNBLogSTDERR ("error: invalid waitfor-duration option value '%s'.\n", optarg);
841 exit (7);
842 }
843 }
844 break;
845
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000846 case 'W':
Greg Clayton6779606a2011-01-22 23:43:18 +0000847 if (optarg && optarg[0])
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000848 working_dir.assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000849 break;
850
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000851 case 'x':
852 if (optarg && optarg[0])
853 {
854 if (strcasecmp(optarg, "auto") == 0)
855 g_launch_flavor = eLaunchFlavorDefault;
856 else if (strcasestr(optarg, "posix") == optarg)
857 g_launch_flavor = eLaunchFlavorPosixSpawn;
858 else if (strcasestr(optarg, "fork") == optarg)
859 g_launch_flavor = eLaunchFlavorForkExec;
860#if defined (__arm__)
861 else if (strcasestr(optarg, "spring") == optarg)
862 g_launch_flavor = eLaunchFlavorSpringBoard;
863#endif
864 else
865 {
866 RNBLogSTDERR ("error: invalid TYPE for the --launch=TYPE (-x TYPE) option: '%s'\n", optarg);
867 RNBLogSTDERR ("Valid values TYPE are:\n");
868 RNBLogSTDERR (" auto Auto-detect the best launch method to use.\n");
869 RNBLogSTDERR (" posix Launch the executable using posix_spawn.\n");
870 RNBLogSTDERR (" fork Launch the executable using fork and exec.\n");
871#if defined (__arm__)
872 RNBLogSTDERR (" spring Launch the executable through Springboard.\n");
873#endif
874 exit (5);
875 }
876 }
877 break;
878
879 case 'l': // Set Log File
880 if (optarg && optarg[0])
881 {
882 if (strcasecmp(optarg, "stdout") == 0)
883 log_file = stdout;
884 else if (strcasecmp(optarg, "stderr") == 0)
885 log_file = stderr;
886 else
Jim Inghamc530be62011-01-24 03:46:59 +0000887 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000888 log_file = fopen(optarg, "w");
Jim Inghamc530be62011-01-24 03:46:59 +0000889 if (log_file != NULL)
890 setlinebuf(log_file);
891 }
892
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000893 if (log_file == NULL)
894 {
895 const char *errno_str = strerror(errno);
896 RNBLogSTDERR ("Failed to open log file '%s' for writing: errno = %i (%s)", optarg, errno, errno_str ? errno_str : "unknown error");
897 }
898 }
899 break;
900
901 case 'f': // Log Flags
902 if (optarg && optarg[0])
903 log_flags = strtoul(optarg, NULL, 0);
904 break;
905
906 case 'g':
907 debug = 1;
908 DNBLogSetDebug(1);
909 break;
910
911 case 't':
912 g_applist_opt = 1;
913 break;
914
915 case 'k':
916 g_lockdown_opt = 1;
917 break;
918
919 case 'r':
Greg Clayton71337622011-02-24 22:24:29 +0000920 remote->SetUseNativeRegisters (true);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000921 break;
922
923 case 'v':
924 DNBLogSetVerbose(1);
925 break;
926
927 case 's':
Greg Clayton71337622011-02-24 22:24:29 +0000928 ctx.GetSTDIN().assign(optarg);
929 ctx.GetSTDOUT().assign(optarg);
930 ctx.GetSTDERR().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000931 break;
932
933 case 'I':
Greg Clayton71337622011-02-24 22:24:29 +0000934 ctx.GetSTDIN().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000935 break;
936
937 case 'O':
Greg Clayton71337622011-02-24 22:24:29 +0000938 ctx.GetSTDOUT().assign(optarg);
Greg Clayton6779606a2011-01-22 23:43:18 +0000939 break;
940
941 case 'E':
Greg Clayton71337622011-02-24 22:24:29 +0000942 ctx.GetSTDERR().assign(optarg);
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000943 break;
944
Caroline Ticef8da8632010-12-03 18:46:09 +0000945 case 'n':
946 no_stdio = true;
947 break;
948
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000949 case 'S':
950 // Put debugserver into a new session. Terminals group processes
951 // into sessions and when a special terminal key sequences
952 // (like control+c) are typed they can cause signals to go out to
953 // all processes in a session. Using this --setsid (-S) option
954 // will cause debugserver to run in its own sessions and be free
955 // from such issues.
956 //
957 // This is useful when debugserver is spawned from a command
958 // line application that uses debugserver to do the debugging,
959 // yet that application doesn't want debugserver receiving the
960 // signals sent to the session (i.e. dying when anyone hits ^C).
961 setsid();
962 break;
Greg Claytonf681b942010-08-31 18:35:14 +0000963 case 'D':
964 g_disable_aslr = 1;
965 break;
Greg Clayton7a5388b2011-03-20 04:57:14 +0000966
967 case 'p':
968 start_mode = eRNBRunLoopModePlatformMode;
969 break;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000970 }
971 }
Greg Clayton3af9ea52010-11-18 05:57:03 +0000972
973 if (arch_name.empty())
974 {
Greg Clayton71337622011-02-24 22:24:29 +0000975#if defined (__arm__)
Greg Clayton3af9ea52010-11-18 05:57:03 +0000976 arch_name.assign ("arm");
977#endif
978 }
Greg Clayton3c144382010-12-01 22:45:40 +0000979 else
980 {
981 DNBSetArchitecture (arch_name.c_str());
982 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000983
Greg Clayton71337622011-02-24 22:24:29 +0000984// if (arch_name.empty())
985// {
986// fprintf(stderr, "error: no architecture was specified\n");
987// exit (8);
988// }
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000989 // Skip any options we consumed with getopt_long
990 argc -= optind;
991 argv += optind;
992
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000993
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000994 if (!working_dir.empty())
Greg Clayton6779606a2011-01-22 23:43:18 +0000995 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000996 if (remote->Context().SetWorkingDirectory (working_dir.c_str()) == false)
Greg Clayton6779606a2011-01-22 23:43:18 +0000997 {
Greg Claytonbd82a5d2011-01-23 05:56:20 +0000998 RNBLogSTDERR ("error: working directory doesn't exist '%s'.\n", working_dir.c_str());
Greg Clayton6779606a2011-01-22 23:43:18 +0000999 exit (8);
1000 }
1001 }
1002
1003 remote->Initialize();
Greg Clayton3af9ea52010-11-18 05:57:03 +00001004
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001005 // It is ok for us to set NULL as the logfile (this will disable any logging)
1006
1007 if (log_file != NULL)
1008 {
1009 DNBLogSetLogCallback(FileLogCallback, log_file);
1010 // If our log file was set, yet we have no log flags, log everything!
1011 if (log_flags == 0)
1012 log_flags = LOG_ALL | LOG_RNB_ALL;
1013
1014 DNBLogSetLogMask (log_flags);
1015 }
1016 else
1017 {
1018 // Enable DNB logging
1019 DNBLogSetLogCallback(ASLLogCallback, NULL);
1020 DNBLogSetLogMask (log_flags);
1021
1022 }
1023
1024 if (DNBLogEnabled())
1025 {
1026 for (i=0; i<argc; i++)
1027 DNBLogDebug("argv[%i] = %s", i, argv[i]);
1028 }
1029
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001030 // as long as we're dropping remotenub in as a replacement for gdbserver,
1031 // explicitly note that this is not gdbserver.
1032
1033 RNBLogSTDOUT ("%s-%g %sfor %s.\n",
1034 DEBUGSERVER_PROGRAM_NAME,
1035 DEBUGSERVER_VERSION_NUM,
1036 compile_options.c_str(),
1037 RNB_ARCH);
1038
1039 int listen_port = INT32_MAX;
1040 char str[PATH_MAX];
1041
1042 if (g_lockdown_opt == 0 && g_applist_opt == 0)
1043 {
1044 // Make sure we at least have port
1045 if (argc < 1)
1046 {
1047 show_usage_and_exit (1);
1048 }
1049 // accept 'localhost:' prefix on port number
1050
1051 int items_scanned = ::sscanf (argv[0], "%[^:]:%i", str, &listen_port);
1052 if (items_scanned == 2)
1053 {
1054 DNBLogDebug("host = '%s' port = %i", str, listen_port);
1055 }
1056 else if (argv[0][0] == '/')
1057 {
1058 listen_port = INT32_MAX;
1059 strncpy(str, argv[0], sizeof(str));
1060 }
1061 else
1062 {
1063 show_usage_and_exit (2);
1064 }
1065
1066 // We just used the 'host:port' or the '/path/file' arg...
1067 argc--;
1068 argv++;
1069
1070 }
1071
1072 // If we know we're waiting to attach, we don't need any of this other info.
Greg Clayton7a5388b2011-03-20 04:57:14 +00001073 if (start_mode != eRNBRunLoopModeInferiorAttaching &&
1074 start_mode != eRNBRunLoopModePlatformMode)
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001075 {
1076 if (argc == 0 || g_lockdown_opt)
1077 {
1078 if (g_lockdown_opt != 0)
1079 {
1080 // Work around for SIGPIPE crashes due to posix_spawn issue.
1081 // We have to close STDOUT and STDERR, else the first time we
1082 // try and do any, we get SIGPIPE and die as posix_spawn is
1083 // doing bad things with our file descriptors at the moment.
1084 int null = open("/dev/null", O_RDWR);
1085 dup2(null, STDOUT_FILENO);
1086 dup2(null, STDERR_FILENO);
1087 }
1088 else if (g_applist_opt != 0)
1089 {
1090 // List all applications we are able to see
1091 std::string applist_plist;
1092 int err = ListApplications(applist_plist, false, false);
1093 if (err == 0)
1094 {
1095 fputs (applist_plist.c_str(), stdout);
1096 }
1097 else
1098 {
1099 RNBLogSTDERR ("error: ListApplications returned error %i\n", err);
1100 }
1101 // Exit with appropriate error if we were asked to list the applications
1102 // with no other args were given (and we weren't trying to do this over
1103 // lockdown)
1104 return err;
1105 }
1106
1107 DNBLogDebug("Get args from remote protocol...");
1108 start_mode = eRNBRunLoopModeGetStartModeFromRemoteProtocol;
1109 }
1110 else
1111 {
1112 start_mode = eRNBRunLoopModeInferiorLaunching;
1113 // Fill in the argv array in the context from the rest of our args.
1114 // Skip the name of this executable and the port number
1115 for (int i = 0; i < argc; i++)
1116 {
1117 DNBLogDebug("inferior_argv[%i] = '%s'", i, argv[i]);
1118 ctx.PushArgument (argv[i]);
1119 }
1120 }
1121 }
1122
1123 if (start_mode == eRNBRunLoopModeExit)
1124 return -1;
1125
1126 RNBRunLoopMode mode = start_mode;
1127 char err_str[1024] = {'\0'};
1128
1129 while (mode != eRNBRunLoopModeExit)
1130 {
1131 switch (mode)
1132 {
1133 case eRNBRunLoopModeGetStartModeFromRemoteProtocol:
1134#if defined (__arm__)
1135 if (g_lockdown_opt)
1136 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001137 if (!remote->Comm().IsConnected())
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001138 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001139 if (remote->Comm().ConnectToService () != rnb_success)
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001140 {
1141 RNBLogSTDERR ("Failed to get connection from a remote gdb process.\n");
1142 mode = eRNBRunLoopModeExit;
1143 }
1144 else if (g_applist_opt != 0)
1145 {
1146 // List all applications we are able to see
1147 std::string applist_plist;
1148 if (ListApplications(applist_plist, false, false) == 0)
1149 {
1150 DNBLogDebug("Task list: %s", applist_plist.c_str());
1151
Greg Clayton6779606a2011-01-22 23:43:18 +00001152 remote->Comm().Write(applist_plist.c_str(), applist_plist.size());
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001153 // Issue a read that will never yield any data until the other side
1154 // closes the socket so this process doesn't just exit and cause the
1155 // socket to close prematurely on the other end and cause data loss.
1156 std::string buf;
Greg Clayton6779606a2011-01-22 23:43:18 +00001157 remote->Comm().Read(buf);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001158 }
Greg Clayton6779606a2011-01-22 23:43:18 +00001159 remote->Comm().Disconnect(false);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001160 mode = eRNBRunLoopModeExit;
1161 break;
1162 }
1163 else
1164 {
1165 // Start watching for remote packets
Greg Clayton6779606a2011-01-22 23:43:18 +00001166 remote->StartReadRemoteDataThread();
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001167 }
1168 }
1169 }
1170 else
1171#endif
Greg Clayton7a5388b2011-03-20 04:57:14 +00001172 if (listen_port != INT32_MAX)
1173 {
1174 if (!StartListening (remote, listen_port))
1175 mode = eRNBRunLoopModeExit;
1176 }
1177 else if (str[0] == '/')
1178 {
1179 if (remote->Comm().OpenFile (str))
1180 mode = eRNBRunLoopModeExit;
1181 }
1182
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001183 if (mode != eRNBRunLoopModeExit)
1184 {
1185 RNBLogSTDOUT ("Got a connection, waiting for process information for launching or attaching.\n");
1186
Greg Clayton6779606a2011-01-22 23:43:18 +00001187 mode = RNBRunLoopGetStartModeFromRemote (remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001188 }
1189 break;
1190
1191 case eRNBRunLoopModeInferiorAttaching:
1192 if (!waitfor_pid_name.empty())
1193 {
1194 // Set our end wait time if we are using a waitfor-duration
1195 // option that may have been specified
1196 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1197 if (waitfor_duration != 0)
1198 {
1199 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1200 timeout_ptr = &attach_timeout_abstime;
1201 }
1202 nub_launch_flavor_t launch_flavor = g_launch_flavor;
1203 if (launch_flavor == eLaunchFlavorDefault)
1204 {
1205 // Our default launch method is posix spawn
1206 launch_flavor = eLaunchFlavorPosixSpawn;
1207
1208#if defined (__arm__)
1209 // Check if we have an app bundle, if so launch using SpringBoard.
1210 if (waitfor_pid_name.find (".app") != std::string::npos)
1211 {
1212 launch_flavor = eLaunchFlavorSpringBoard;
1213 }
1214#endif
1215 }
1216
1217 ctx.SetLaunchFlavor(launch_flavor);
1218
1219 nub_process_t pid = DNBProcessAttachWait (waitfor_pid_name.c_str(), launch_flavor, timeout_ptr, waitfor_interval, err_str, sizeof(err_str));
1220 g_pid = pid;
1221
1222 if (pid == INVALID_NUB_PROCESS)
1223 {
1224 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1225 if (err_str[0])
1226 ctx.LaunchStatus().SetErrorString(err_str);
1227 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1228 mode = eRNBRunLoopModeExit;
1229 }
1230 else
1231 {
1232 ctx.SetProcessID(pid);
1233 mode = eRNBRunLoopModeInferiorExecuting;
1234 }
1235 }
1236 else if (attach_pid != INVALID_NUB_PROCESS)
1237 {
1238
1239 RNBLogSTDOUT ("Attaching to process %i...\n", attach_pid);
1240 nub_process_t attached_pid;
Greg Clayton6779606a2011-01-22 23:43:18 +00001241 mode = RNBRunLoopLaunchAttaching (remote, attach_pid, attached_pid);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001242 if (mode != eRNBRunLoopModeInferiorExecuting)
1243 {
1244 const char *error_str = remote->Context().LaunchStatus().AsString();
1245 RNBLogSTDERR ("error: failed to attach process %i: %s\n", attach_pid, error_str ? error_str : "unknown error.");
1246 mode = eRNBRunLoopModeExit;
1247 }
1248 }
1249 else if (!attach_pid_name.empty ())
1250 {
1251 struct timespec attach_timeout_abstime, *timeout_ptr = NULL;
1252 if (waitfor_duration != 0)
1253 {
1254 DNBTimer::OffsetTimeOfDay(&attach_timeout_abstime, waitfor_duration, 0);
1255 timeout_ptr = &attach_timeout_abstime;
1256 }
1257
1258 nub_process_t pid = DNBProcessAttachByName (attach_pid_name.c_str(), timeout_ptr, err_str, sizeof(err_str));
1259 g_pid = pid;
1260 if (pid == INVALID_NUB_PROCESS)
1261 {
1262 ctx.LaunchStatus().SetError(-1, DNBError::Generic);
1263 if (err_str[0])
1264 ctx.LaunchStatus().SetErrorString(err_str);
1265 RNBLogSTDERR ("error: failed to attach to process named: \"%s\" %s", waitfor_pid_name.c_str(), err_str);
1266 mode = eRNBRunLoopModeExit;
1267 }
1268 else
1269 {
1270 ctx.SetProcessID(pid);
1271 mode = eRNBRunLoopModeInferiorExecuting;
1272 }
1273
1274 }
1275 else
1276 {
1277 RNBLogSTDERR ("error: asked to attach with empty name and invalid PID.");
1278 mode = eRNBRunLoopModeExit;
1279 }
1280
1281 if (mode != eRNBRunLoopModeExit)
1282 {
1283 if (listen_port != INT32_MAX)
1284 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001285 if (!StartListening (remote, listen_port))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001286 mode = eRNBRunLoopModeExit;
1287 }
1288 else if (str[0] == '/')
1289 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001290 if (remote->Comm().OpenFile (str))
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001291 mode = eRNBRunLoopModeExit;
1292 }
1293 if (mode != eRNBRunLoopModeExit)
1294 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions for process %d.\n", attach_pid);
1295 }
1296 break;
1297
1298 case eRNBRunLoopModeInferiorLaunching:
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001299 {
Greg Clayton6779606a2011-01-22 23:43:18 +00001300 mode = RNBRunLoopLaunchInferior (remote,
Greg Clayton71337622011-02-24 22:24:29 +00001301 ctx.GetSTDINPath(),
1302 ctx.GetSTDOUTPath(),
1303 ctx.GetSTDERRPath(),
Greg Clayton6779606a2011-01-22 23:43:18 +00001304 no_stdio);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001305
Greg Clayton6779606a2011-01-22 23:43:18 +00001306 if (mode == eRNBRunLoopModeInferiorExecuting)
1307 {
1308 if (listen_port != INT32_MAX)
1309 {
1310 if (!StartListening (remote, listen_port))
1311 mode = eRNBRunLoopModeExit;
1312 }
1313 else if (str[0] == '/')
1314 {
1315 if (remote->Comm().OpenFile (str))
1316 mode = eRNBRunLoopModeExit;
1317 }
1318
1319 if (mode != eRNBRunLoopModeExit)
1320 RNBLogSTDOUT ("Got a connection, waiting for debugger instructions.\n");
1321 }
1322 else
1323 {
1324 const char *error_str = remote->Context().LaunchStatus().AsString();
1325 RNBLogSTDERR ("error: failed to launch process %s: %s\n", argv[0], error_str ? error_str : "unknown error.");
1326 }
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001327 }
1328 break;
1329
1330 case eRNBRunLoopModeInferiorExecuting:
Greg Clayton6779606a2011-01-22 23:43:18 +00001331 mode = RNBRunLoopInferiorExecuting(remote);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001332 break;
1333
Greg Clayton7a5388b2011-03-20 04:57:14 +00001334 case eRNBRunLoopModePlatformMode:
1335 if (listen_port != INT32_MAX)
1336 {
1337 if (!StartListening (remote, listen_port))
1338 mode = eRNBRunLoopModeExit;
1339 }
1340 else if (str[0] == '/')
1341 {
1342 if (remote->Comm().OpenFile (str))
1343 mode = eRNBRunLoopModeExit;
1344 }
1345
1346 if (mode != eRNBRunLoopModeExit)
1347 mode = RNBRunLoopPlatform (remote);
1348 break;
1349
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001350 default:
1351 mode = eRNBRunLoopModeExit;
1352 case eRNBRunLoopModeExit:
1353 break;
1354 }
1355 }
1356
Greg Clayton6779606a2011-01-22 23:43:18 +00001357 remote->StopReadRemoteDataThread ();
1358 remote->Context().SetProcessID(INVALID_NUB_PROCESS);
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001359
1360 return 0;
1361}