1 //===-- lldb-platform.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 "lldb/lldb-python.h"
11 
12 // C Includes
13 #include <errno.h>
14 #if defined(__APPLE__)
15 #include <netinet/in.h>
16 #endif
17 #include <signal.h>
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 // C++ Includes
24 
25 // Other libraries and framework includes
26 #include "lldb/lldb-private-log.h"
27 #include "lldb/Core/Error.h"
28 #include "lldb/Core/ConnectionMachPort.h"
29 #include "lldb/Core/Debugger.h"
30 #include "lldb/Core/StreamFile.h"
31 #include "lldb/Host/ConnectionFileDescriptor.h"
32 #include "lldb/Host/HostGetOpt.h"
33 #include "lldb/Host/OptionParser.h"
34 #include "lldb/Interpreter/CommandInterpreter.h"
35 #include "lldb/Interpreter/CommandReturnObject.h"
36 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h"
37 #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h"
38 
39 using namespace lldb;
40 using namespace lldb_private;
41 
42 //----------------------------------------------------------------------
43 // option descriptors for getopt_long_only()
44 //----------------------------------------------------------------------
45 
46 static int g_debug = 0;
47 static int g_verbose = 0;
48 static int g_stay_alive = 0;
49 
50 static struct option g_long_options[] =
51 {
52     { "debug",              no_argument,        &g_debug,           1   },
53     { "verbose",            no_argument,        &g_verbose,         1   },
54     { "stay-alive",         no_argument,        &g_stay_alive,      1   },
55     { "listen",             required_argument,  NULL,               'L' },
56     { "port-offset",        required_argument,  NULL,               'p' },
57     { "gdbserver-port",     required_argument,  NULL,               'P' },
58     { "min-gdbserver-port", required_argument,  NULL,               'm' },
59     { "max-gdbserver-port", required_argument,  NULL,               'M' },
60     { "lldb-command",       required_argument,  NULL,               'c' },
61     { NULL,                 0,                  NULL,               0   }
62 };
63 
64 #if defined (__APPLE__)
65 #define LOW_PORT    (IPPORT_RESERVED)
66 #define HIGH_PORT   (IPPORT_HIFIRSTAUTO)
67 #else
68 #define LOW_PORT    (1024u)
69 #define HIGH_PORT   (49151u)
70 #endif
71 
72 
73 //----------------------------------------------------------------------
74 // Watch for signals
75 //----------------------------------------------------------------------
76 static void
77 signal_handler(int signo)
78 {
79     switch (signo)
80     {
81     case SIGHUP:
82         // Use SIGINT first, if that does not work, use SIGHUP as a last resort.
83         // And we should not call exit() here because it results in the global destructors
84         // to be invoked and wreaking havoc on the threads still running.
85         Host::SystemLog(Host::eSystemLogWarning, "SIGHUP received, exiting lldb-platform...\n");
86         abort();
87         break;
88     }
89 }
90 
91 static void
92 display_usage (const char *progname, const char *subcommand)
93 {
94     fprintf(stderr, "Usage:\n  %s %s [--log-file log-file-path] [--log-flags flags] --listen port\n", progname, subcommand);
95     exit(0);
96 }
97 
98 //----------------------------------------------------------------------
99 // main
100 //----------------------------------------------------------------------
101 int
102 main_platform (int argc, char *argv[])
103 {
104     const char *progname = argv[0];
105     const char *subcommand = argv[1];
106     argc--;
107     argv++;
108     signal (SIGPIPE, SIG_IGN);
109     signal (SIGHUP, signal_handler);
110     int long_option_index = 0;
111     Error error;
112     std::string listen_host_port;
113     int ch;
114     Debugger::Initialize(NULL);
115 
116     lldb::DebuggerSP debugger_sp = Debugger::CreateInstance ();
117 
118     debugger_sp->SetInputFileHandle(stdin, false);
119     debugger_sp->SetOutputFileHandle(stdout, false);
120     debugger_sp->SetErrorFileHandle(stderr, false);
121 
122     GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
123     int min_gdbserver_port = 0;
124     int max_gdbserver_port = 0;
125     uint16_t port_offset = 0;
126 
127     std::vector<std::string> lldb_commands;
128     bool show_usage = false;
129     int option_error = 0;
130 
131     std::string short_options(OptionParser::GetShortOptionString(g_long_options));
132 
133 #if __GLIBC__
134     optind = 0;
135 #else
136     optreset = 1;
137     optind = 1;
138 #endif
139 
140     while ((ch = getopt_long_only(argc, argv, short_options.c_str(), g_long_options, &long_option_index)) != -1)
141     {
142         switch (ch)
143         {
144         case 0:   // Any optional that auto set themselves will return 0
145             break;
146 
147         case 'L':
148             listen_host_port.append (optarg);
149             break;
150 
151         case 'p':
152             {
153                 char *end = NULL;
154                 long tmp_port_offset = strtoul(optarg, &end, 0);
155                 if (end && *end == '\0')
156                 {
157                     if (LOW_PORT <= tmp_port_offset && tmp_port_offset <= HIGH_PORT)
158                     {
159                         port_offset = (uint16_t)tmp_port_offset;
160                     }
161                     else
162                     {
163                         fprintf (stderr, "error: port offset %li is not in the valid user port range of %u - %u\n", tmp_port_offset, LOW_PORT, HIGH_PORT);
164                         option_error = 5;
165                     }
166                 }
167                 else
168                 {
169                     fprintf (stderr, "error: invalid port offset string %s\n", optarg);
170                     option_error = 4;
171                 }
172             }
173             break;
174 
175         case 'P':
176         case 'm':
177         case 'M':
178             {
179                 char *end = NULL;
180                 long portnum = strtoul(optarg, &end, 0);
181                 if (end && *end == '\0')
182                 {
183                     if (LOW_PORT <= portnum && portnum <= HIGH_PORT)
184                     {
185                         if (ch  == 'P')
186                             gdbserver_portmap[(uint16_t)portnum] = LLDB_INVALID_PROCESS_ID;
187                         else if (ch == 'm')
188                             min_gdbserver_port = portnum;
189                         else
190                             max_gdbserver_port = portnum;
191                     }
192                     else
193                     {
194                         fprintf (stderr, "error: port number %li is not in the valid user port range of %u - %u\n", portnum, LOW_PORT, HIGH_PORT);
195                         option_error = 1;
196                     }
197                 }
198                 else
199                 {
200                     fprintf (stderr, "error: invalid port number string %s\n", optarg);
201                     option_error = 2;
202                 }
203             }
204             break;
205 
206         case 'c':
207             lldb_commands.push_back(optarg);
208             break;
209 
210         case 'h':   /* fall-through is intentional */
211         case '?':
212             show_usage = true;
213             break;
214         }
215     }
216 
217     // Make a port map for a port range that was specified.
218     if (min_gdbserver_port < max_gdbserver_port)
219     {
220         for (uint16_t port = min_gdbserver_port; port < max_gdbserver_port; ++port)
221             gdbserver_portmap[port] = LLDB_INVALID_PROCESS_ID;
222     }
223     else if (min_gdbserver_port != max_gdbserver_port)
224     {
225         fprintf (stderr, "error: --min-gdbserver-port (%u) is greater than --max-gdbserver-port (%u)\n", min_gdbserver_port, max_gdbserver_port);
226         option_error = 3;
227 
228     }
229 
230     // Print usage and exit if no listening port is specified.
231     if (listen_host_port.empty())
232         show_usage = true;
233 
234     if (show_usage || option_error)
235     {
236         display_usage(progname, subcommand);
237         exit(option_error);
238     }
239 
240     // Execute any LLDB commands that we were asked to evaluate.
241     for (const auto &lldb_command : lldb_commands)
242     {
243         lldb_private::CommandReturnObject result;
244         printf("(lldb) %s\n", lldb_command.c_str());
245         debugger_sp->GetCommandInterpreter().HandleCommand(lldb_command.c_str(), eLazyBoolNo, result);
246         const char *output = result.GetOutputData();
247         if (output && output[0])
248             puts(output);
249     }
250 
251 
252     do {
253         GDBRemoteCommunicationServerPlatform platform;
254 
255         if (port_offset > 0)
256             platform.SetPortOffset(port_offset);
257 
258         if (!gdbserver_portmap.empty())
259         {
260             platform.SetPortMap(std::move(gdbserver_portmap));
261         }
262 
263         if (!listen_host_port.empty())
264         {
265             std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor());
266             if (conn_ap.get())
267             {
268                 std::string connect_url ("listen://");
269                 connect_url.append(listen_host_port.c_str());
270 
271                 printf ("Listening for a connection from %s...\n", listen_host_port.c_str());
272                 if (conn_ap->Connect(connect_url.c_str(), &error) == eConnectionStatusSuccess)
273                 {
274                     printf ("Connection established.\n");
275                     platform.SetConnection (conn_ap.release());
276                 }
277                 else
278                 {
279                     printf ("error: %s\n", error.AsCString());
280                 }
281             }
282 
283             if (platform.IsConnected())
284             {
285                 // After we connected, we need to get an initial ack from...
286                 if (platform.HandshakeWithClient(&error))
287                 {
288                     bool interrupt = false;
289                     bool done = false;
290                     while (!interrupt && !done)
291                     {
292                         if (platform.GetPacketAndSendResponse (UINT32_MAX, error, interrupt, done) != GDBRemoteCommunication::PacketResult::Success)
293                             break;
294                     }
295 
296                     if (error.Fail())
297                     {
298                         fprintf(stderr, "error: %s\n", error.AsCString());
299                     }
300                 }
301                 else
302                 {
303                     fprintf(stderr, "error: handshake with client failed\n");
304                 }
305             }
306         }
307     } while (g_stay_alive);
308 
309     Debugger::Terminate();
310 
311     fprintf(stderr, "lldb-platform exiting...\n");
312 
313     return 0;
314 }
315