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