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