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