1 //===-- PlatformRemoteGDBServer.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 #include "PlatformRemoteGDBServer.h"
13 
14 // C Includes
15 #include <sys/sysctl.h>
16 
17 // C++ Includes
18 // Other libraries and framework includes
19 // Project includes
20 #include "lldb/Breakpoint/BreakpointLocation.h"
21 #include "lldb/Core/ConnectionFileDescriptor.h"
22 #include "lldb/Core/Debugger.h"
23 #include "lldb/Core/Error.h"
24 #include "lldb/Core/Module.h"
25 #include "lldb/Core/ModuleList.h"
26 #include "lldb/Core/PluginManager.h"
27 #include "lldb/Core/StreamString.h"
28 #include "lldb/Host/FileSpec.h"
29 #include "lldb/Host/Host.h"
30 #include "lldb/Target/Process.h"
31 #include "lldb/Target/Target.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 
36 static bool g_initialized = false;
37 
38 void
39 PlatformRemoteGDBServer::Initialize ()
40 {
41     if (g_initialized == false)
42     {
43         g_initialized = true;
44         PluginManager::RegisterPlugin (PlatformRemoteGDBServer::GetShortPluginNameStatic(),
45                                        PlatformRemoteGDBServer::GetDescriptionStatic(),
46                                        PlatformRemoteGDBServer::CreateInstance);
47     }
48 }
49 
50 void
51 PlatformRemoteGDBServer::Terminate ()
52 {
53     if (g_initialized)
54     {
55         g_initialized = false;
56         PluginManager::UnregisterPlugin (PlatformRemoteGDBServer::CreateInstance);
57     }
58 }
59 
60 Platform*
61 PlatformRemoteGDBServer::CreateInstance (bool force, const lldb_private::ArchSpec *arch)
62 {
63     bool create = force;
64     if (!create)
65     {
66         create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified();
67     }
68     if (create)
69         return new PlatformRemoteGDBServer ();
70     return NULL;
71 }
72 
73 
74 const char *
75 PlatformRemoteGDBServer::GetShortPluginNameStatic()
76 {
77     return "remote-gdb-server";
78 }
79 
80 const char *
81 PlatformRemoteGDBServer::GetDescriptionStatic()
82 {
83     return "A platform that uses the GDB remote protocol as the communication transport.";
84 }
85 
86 const char *
87 PlatformRemoteGDBServer::GetDescription ()
88 {
89     if (m_platform_description.empty())
90     {
91         if (IsConnected())
92         {
93             // Send the get description packet
94         }
95     }
96 
97     if (!m_platform_description.empty())
98         return m_platform_description.c_str();
99     return GetDescriptionStatic();
100 }
101 
102 Error
103 PlatformRemoteGDBServer::ResolveExecutable (const FileSpec &exe_file,
104                                             const ArchSpec &exe_arch,
105                                             lldb::ModuleSP &exe_module_sp,
106                                             const FileSpecList *module_search_paths_ptr)
107 {
108     Error error;
109     error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented");
110     return error;
111 }
112 
113 Error
114 PlatformRemoteGDBServer::GetFile (const FileSpec &platform_file,
115                                   const UUID *uuid_ptr,
116                                   FileSpec &local_file)
117 {
118     // Default to the local case
119     local_file = platform_file;
120     return Error();
121 }
122 
123 //------------------------------------------------------------------
124 /// Default Constructor
125 //------------------------------------------------------------------
126 PlatformRemoteGDBServer::PlatformRemoteGDBServer () :
127     Platform(false), // This is a remote platform
128     m_gdb_client(true)
129 {
130 }
131 
132 //------------------------------------------------------------------
133 /// Destructor.
134 ///
135 /// The destructor is virtual since this class is designed to be
136 /// inherited from by the plug-in instance.
137 //------------------------------------------------------------------
138 PlatformRemoteGDBServer::~PlatformRemoteGDBServer()
139 {
140 }
141 
142 bool
143 PlatformRemoteGDBServer::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
144 {
145     return false;
146 }
147 
148 size_t
149 PlatformRemoteGDBServer::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site)
150 {
151     // This isn't needed if the z/Z packets are supported in the GDB remote
152     // server. But we might need a packet to detect this.
153     return 0;
154 }
155 
156 bool
157 PlatformRemoteGDBServer::GetRemoteOSVersion ()
158 {
159     uint32_t major, minor, update;
160     if (m_gdb_client.GetOSVersion (major, minor, update))
161     {
162         m_major_os_version = major;
163         m_minor_os_version = minor;
164         m_update_os_version = update;
165         return true;
166     }
167     return false;
168 }
169 
170 bool
171 PlatformRemoteGDBServer::GetRemoteOSBuildString (std::string &s)
172 {
173     return m_gdb_client.GetOSBuildString (s);
174 }
175 
176 bool
177 PlatformRemoteGDBServer::GetRemoteOSKernelDescription (std::string &s)
178 {
179     return m_gdb_client.GetOSKernelDescription (s);
180 }
181 
182 // Remote Platform subclasses need to override this function
183 ArchSpec
184 PlatformRemoteGDBServer::GetRemoteSystemArchitecture ()
185 {
186     return m_gdb_client.GetSystemArchitecture();
187 }
188 
189 bool
190 PlatformRemoteGDBServer::IsConnected () const
191 {
192     return m_gdb_client.IsConnected();
193 }
194 
195 Error
196 PlatformRemoteGDBServer::ConnectRemote (Args& args)
197 {
198     Error error;
199     if (IsConnected())
200     {
201         error.SetErrorStringWithFormat ("the platform is already connected to '%s', execute 'platform disconnect' to close the current connection",
202                                         GetHostname());
203     }
204     else
205     {
206         if (args.GetArgumentCount() == 1)
207         {
208             const char *url = args.GetArgumentAtIndex(0);
209             m_gdb_client.SetConnection (new ConnectionFileDescriptor());
210             const ConnectionStatus status = m_gdb_client.Connect(url, &error);
211             if (status == eConnectionStatusSuccess)
212             {
213                 if (m_gdb_client.HandshakeWithServer(&error))
214                 {
215                     m_gdb_client.QueryNoAckModeSupported();
216                     m_gdb_client.GetHostInfo();
217 #if 0
218                     m_gdb_client.TestPacketSpeed(10000);
219 #endif
220                 }
221                 else
222                 {
223                     m_gdb_client.Disconnect();
224                 }
225             }
226         }
227         else
228         {
229             error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>");
230         }
231     }
232 
233     return error;
234 }
235 
236 Error
237 PlatformRemoteGDBServer::DisconnectRemote ()
238 {
239     Error error;
240     m_gdb_client.Disconnect(&error);
241     return error;
242 }
243 
244 const char *
245 PlatformRemoteGDBServer::GetHostname ()
246 {
247     m_gdb_client.GetHostname (m_name);
248     if (m_name.empty())
249         return NULL;
250     return m_name.c_str();
251 }
252 
253 const char *
254 PlatformRemoteGDBServer::GetUserName (uint32_t uid)
255 {
256     // Try and get a cache user name first
257     const char *cached_user_name = Platform::GetUserName(uid);
258     if (cached_user_name)
259         return cached_user_name;
260     std::string name;
261     if (m_gdb_client.GetUserName(uid, name))
262         return SetCachedUserName(uid, name.c_str(), name.size());
263 
264     SetUserNameNotFound(uid); // Negative cache so we don't keep sending packets
265     return NULL;
266 }
267 
268 const char *
269 PlatformRemoteGDBServer::GetGroupName (uint32_t gid)
270 {
271     const char *cached_group_name = Platform::GetGroupName(gid);
272     if (cached_group_name)
273         return cached_group_name;
274     std::string name;
275     if (m_gdb_client.GetGroupName(gid, name))
276         return SetCachedGroupName(gid, name.c_str(), name.size());
277 
278     SetGroupNameNotFound(gid); // Negative cache so we don't keep sending packets
279     return NULL;
280 }
281 
282 uint32_t
283 PlatformRemoteGDBServer::FindProcesses (const ProcessInstanceInfoMatch &match_info,
284                                         ProcessInstanceInfoList &process_infos)
285 {
286     return m_gdb_client.FindProcesses (match_info, process_infos);
287 }
288 
289 bool
290 PlatformRemoteGDBServer::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
291 {
292     return m_gdb_client.GetProcessInfo (pid, process_info);
293 }
294 
295 
296 Error
297 PlatformRemoteGDBServer::LaunchProcess (ProcessLaunchInfo &launch_info)
298 {
299     Error error;
300     lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
301 
302     m_gdb_client.SetSTDIN ("/dev/null");
303     m_gdb_client.SetSTDOUT ("/dev/null");
304     m_gdb_client.SetSTDERR ("/dev/null");
305     m_gdb_client.SetDisableASLR (launch_info.GetFlags().Test (eLaunchFlagDisableASLR));
306 
307     const char *working_dir = launch_info.GetWorkingDirectory();
308     if (working_dir && working_dir[0])
309     {
310         m_gdb_client.SetWorkingDir (working_dir);
311     }
312 
313     // Send the environment and the program + arguments after we connect
314     const char **argv = launch_info.GetArguments().GetConstArgumentVector();
315     const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector();
316 
317     if (envp)
318     {
319         const char *env_entry;
320         for (int i=0; (env_entry = envp[i]); ++i)
321         {
322             if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0)
323                 break;
324         }
325     }
326     const uint32_t old_packet_timeout = m_gdb_client.SetPacketTimeout (5);
327     int arg_packet_err = m_gdb_client.SendArgumentsPacket (argv);
328     m_gdb_client.SetPacketTimeout (old_packet_timeout);
329     if (arg_packet_err == 0)
330     {
331         std::string error_str;
332         if (m_gdb_client.GetLaunchSuccess (error_str))
333         {
334             pid = m_gdb_client.GetCurrentProcessID ();
335             if (pid != LLDB_INVALID_PROCESS_ID)
336                 launch_info.SetProcessID (pid);
337         }
338         else
339         {
340             error.SetErrorString (error_str.c_str());
341         }
342     }
343     else
344     {
345         error.SetErrorStringWithFormat("'A' packet returned an error: %i", arg_packet_err);
346     }
347     return error;
348 }
349 
350 lldb::ProcessSP
351 PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info,
352                                  Debugger &debugger,
353                                  Target *target,       // Can be NULL, if NULL create a new target, else use existing one
354                                  Listener &listener,
355                                  Error &error)
356 {
357     lldb::ProcessSP process_sp;
358     if (IsRemote())
359     {
360         if (IsConnected())
361         {
362             uint16_t port = m_gdb_client.LaunchGDBserverAndGetPort();
363 
364             if (port == 0)
365             {
366                 error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ());
367             }
368             else
369             {
370                 if (target == NULL)
371                 {
372                     TargetSP new_target_sp;
373 
374                     error = debugger.GetTargetList().CreateTarget (debugger,
375                                                                    NULL,
376                                                                    NULL,
377                                                                    false,
378                                                                    NULL,
379                                                                    new_target_sp);
380                     target = new_target_sp.get();
381                 }
382                 else
383                     error.Clear();
384 
385                 if (target && error.Success())
386                 {
387                     debugger.GetTargetList().SetSelectedTarget(target);
388 
389                     // The darwin always currently uses the GDB remote debugger plug-in
390                     // so even when debugging locally we are debugging remotely!
391                     process_sp = target->CreateProcess (listener, "gdb-remote", NULL);
392 
393                     if (process_sp)
394                     {
395                         char connect_url[256];
396                         const int connect_url_len = ::snprintf (connect_url,
397                                                                 sizeof(connect_url),
398                                                                 "connect://%s:%u",
399                                                                 GetHostname (),
400                                                                 port);
401                         assert (connect_url_len < sizeof(connect_url));
402                         error = process_sp->ConnectRemote (NULL, connect_url);
403                         if (error.Success())
404                             error = process_sp->Attach(attach_info);
405                     }
406                 }
407             }
408         }
409         else
410         {
411             error.SetErrorString("not connected to remote gdb server");
412         }
413     }
414     return process_sp;
415 }
416 
417 
418