1 //===-- PlatformLinux.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 "PlatformLinux.h"
13 
14 // C Includes
15 #include <stdio.h>
16 #include <sys/utsname.h>
17 
18 // C++ Includes
19 // Other libraries and framework includes
20 // Project includes
21 #include "lldb/Core/Error.h"
22 #include "lldb/Core/Debugger.h"
23 #include "lldb/Core/Module.h"
24 #include "lldb/Core/ModuleList.h"
25 #include "lldb/Core/ModuleSpec.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/Target.h"
31 #include "lldb/Target/Process.h"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 
36 static uint32_t g_initialize_count = 0;
37 
38 Platform *
39 PlatformLinux::CreateInstance (bool force, const ArchSpec *arch)
40 {
41     bool create = force;
42     if (create == false && arch && arch->IsValid())
43     {
44         const llvm::Triple &triple = arch->GetTriple();
45         switch (triple.getVendor())
46         {
47             case llvm::Triple::PC:
48                 create = true;
49                 break;
50 
51 #if defined(__linux__)
52             // Only accept "unknown" for the vendor if the host is linux and
53             // it "unknown" wasn't specified (it was just returned becasue it
54             // was NOT specified_
55             case llvm::Triple::UnknownArch:
56                 create = !arch->TripleVendorWasSpecified();
57                 break;
58 #endif
59             default:
60                 break;
61         }
62 
63         if (create)
64         {
65             switch (triple.getOS())
66             {
67                 case llvm::Triple::Linux:
68                     break;
69 
70 #if defined(__linux__)
71                 // Only accept "unknown" for the OS if the host is linux and
72                 // it "unknown" wasn't specified (it was just returned becasue it
73                 // was NOT specified)
74                 case llvm::Triple::UnknownOS:
75                     create = !arch->TripleOSWasSpecified();
76                     break;
77 #endif
78                 default:
79                     create = false;
80                     break;
81             }
82         }
83     }
84     if (create)
85         return new PlatformLinux(true);
86     return NULL;
87 }
88 
89 const char *
90 PlatformLinux::GetPluginNameStatic()
91 {
92     return "plugin.platform.linux";
93 }
94 
95 const char *
96 PlatformLinux::GetShortPluginNameStatic (bool is_host)
97 {
98     if (is_host)
99         return Platform::GetHostPlatformName ();
100     else
101         return "remote-linux";
102 }
103 
104 const char *
105 PlatformLinux::GetPluginDescriptionStatic (bool is_host)
106 {
107     if (is_host)
108         return "Local Linux user platform plug-in.";
109     else
110         return "Remote Linux user platform plug-in.";
111 }
112 
113 void
114 PlatformLinux::Initialize ()
115 {
116     if (g_initialize_count++ == 0)
117     {
118 #if defined(__linux__)
119         PlatformSP default_platform_sp (new PlatformLinux(true));
120         default_platform_sp->SetSystemArchitecture (Host::GetArchitecture());
121         Platform::SetDefaultPlatform (default_platform_sp);
122 #endif
123         PluginManager::RegisterPlugin(PlatformLinux::GetShortPluginNameStatic(false),
124                                       PlatformLinux::GetPluginDescriptionStatic(false),
125                                       PlatformLinux::CreateInstance);
126     }
127 }
128 
129 void
130 PlatformLinux::Terminate ()
131 {
132     if (g_initialize_count > 0)
133     {
134         if (--g_initialize_count == 0)
135         {
136             PluginManager::UnregisterPlugin (PlatformLinux::CreateInstance);
137         }
138     }
139 }
140 
141 Error
142 PlatformLinux::ResolveExecutable (const FileSpec &exe_file,
143                                   const ArchSpec &exe_arch,
144                                   lldb::ModuleSP &exe_module_sp,
145                                   const FileSpecList *module_search_paths_ptr)
146 {
147     Error error;
148     // Nothing special to do here, just use the actual file and architecture
149 
150     char exe_path[PATH_MAX];
151     FileSpec resolved_exe_file (exe_file);
152 
153     if (IsHost())
154     {
155         // If we have "ls" as the exe_file, resolve the executable location based on
156         // the current path variables
157         if (!resolved_exe_file.Exists())
158         {
159             exe_file.GetPath(exe_path, sizeof(exe_path));
160             resolved_exe_file.SetFile(exe_path, true);
161         }
162 
163         if (!resolved_exe_file.Exists())
164             resolved_exe_file.ResolveExecutableLocation ();
165 
166         if (resolved_exe_file.Exists())
167             error.Clear();
168         else
169         {
170             exe_file.GetPath(exe_path, sizeof(exe_path));
171             error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path);
172         }
173     }
174     else
175     {
176         if (m_remote_platform_sp)
177         {
178             error = m_remote_platform_sp->ResolveExecutable (exe_file,
179                                                              exe_arch,
180                                                              exe_module_sp,
181                                                              NULL);
182         }
183         else
184         {
185             // We may connect to a process and use the provided executable (Don't use local $PATH).
186 
187             if (resolved_exe_file.Exists())
188                 error.Clear();
189             else
190                 error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path);
191         }
192     }
193 
194     if (error.Success())
195     {
196         ModuleSpec module_spec (resolved_exe_file, exe_arch);
197         if (exe_arch.IsValid())
198         {
199             error = ModuleList::GetSharedModule (module_spec,
200                                                  exe_module_sp,
201                                                  NULL,
202                                                  NULL,
203                                                  NULL);
204 
205             // TODO find out why exe_module_sp might be NULL
206             if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL)
207             {
208                 exe_module_sp.reset();
209                 error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s",
210                                                 exe_file.GetPath().c_str(),
211                                                 exe_arch.GetArchitectureName());
212             }
213         }
214         else
215         {
216             // No valid architecture was specified, ask the platform for
217             // the architectures that we should be using (in the correct order)
218             // and see if we can find a match that way
219             StreamString arch_names;
220             for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, module_spec.GetArchitecture()); ++idx)
221             {
222                 error = ModuleList::GetSharedModule (module_spec,
223                                                      exe_module_sp,
224                                                      NULL,
225                                                      NULL,
226                                                      NULL);
227                 // Did we find an executable using one of the
228                 if (error.Success())
229                 {
230                     if (exe_module_sp && exe_module_sp->GetObjectFile())
231                         break;
232                     else
233                         error.SetErrorToGenericError();
234                 }
235 
236                 if (idx > 0)
237                     arch_names.PutCString (", ");
238                 arch_names.PutCString (module_spec.GetArchitecture().GetArchitectureName());
239             }
240 
241             if (error.Fail() || !exe_module_sp)
242             {
243                 error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s",
244                                                 exe_file.GetPath().c_str(),
245                                                 GetShortPluginName(),
246                                                 arch_names.GetString().c_str());
247             }
248         }
249     }
250 
251     return error;
252 }
253 
254 Error
255 PlatformLinux::GetFile (const FileSpec &platform_file,
256                         const UUID *uuid_ptr, FileSpec &local_file)
257 {
258     if (IsRemote())
259     {
260         if (m_remote_platform_sp)
261             return m_remote_platform_sp->GetFile (platform_file, uuid_ptr, local_file);
262     }
263 
264     // Default to the local case
265     local_file = platform_file;
266     return Error();
267 }
268 
269 
270 //------------------------------------------------------------------
271 /// Default Constructor
272 //------------------------------------------------------------------
273 PlatformLinux::PlatformLinux (bool is_host) :
274     Platform(is_host),  // This is the local host platform
275     m_remote_platform_sp ()
276 {
277 }
278 
279 //------------------------------------------------------------------
280 /// Destructor.
281 ///
282 /// The destructor is virtual since this class is designed to be
283 /// inherited from by the plug-in instance.
284 //------------------------------------------------------------------
285 PlatformLinux::~PlatformLinux()
286 {
287 }
288 
289 bool
290 PlatformLinux::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
291 {
292     bool success = false;
293     if (IsHost())
294     {
295         success = Platform::GetProcessInfo (pid, process_info);
296     }
297     else
298     {
299         if (m_remote_platform_sp)
300             success = m_remote_platform_sp->GetProcessInfo (pid, process_info);
301     }
302     return success;
303 }
304 
305 bool
306 PlatformLinux::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
307 {
308     if (idx == 0)
309     {
310         arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture);
311         return arch.IsValid();
312     }
313     else if (idx == 1)
314     {
315         // If the default host architecture is 64-bit, look for a 32-bit variant
316         ArchSpec hostArch
317                       = Host::GetArchitecture(Host::eSystemDefaultArchitecture);
318         if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit())
319         {
320             arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture32);
321             return arch.IsValid();
322         }
323     }
324     return false;
325 }
326 
327 void
328 PlatformLinux::GetStatus (Stream &strm)
329 {
330     struct utsname un;
331 
332     if (uname(&un)) {
333         strm << "Linux";
334         return;
335     }
336 
337     strm << un.sysname << ' ' << un.release << ' ' << un.version << '\n';
338 }
339 
340 size_t
341 PlatformLinux::GetSoftwareBreakpointTrapOpcode (Target &target,
342                                                 BreakpointSite *bp_site)
343 {
344     ArchSpec arch = target.GetArchitecture();
345     const uint8_t *trap_opcode = NULL;
346     size_t trap_opcode_size = 0;
347 
348     switch (arch.GetCore())
349     {
350     default:
351         assert(false && "CPU type not supported!");
352         break;
353 
354     case ArchSpec::eCore_x86_32_i386:
355     case ArchSpec::eCore_x86_64_x86_64:
356         {
357             static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC };
358             trap_opcode = g_i386_breakpoint_opcode;
359             trap_opcode_size = sizeof(g_i386_breakpoint_opcode);
360         }
361         break;
362     }
363 
364     if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
365         return trap_opcode_size;
366     return 0;
367 }
368 
369 Error
370 PlatformLinux::LaunchProcess (ProcessLaunchInfo &launch_info)
371 {
372     Error error;
373 
374     if (IsHost())
375     {
376         if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
377         {
378             const bool is_localhost = true;
379             const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
380             const bool first_arg_is_full_shell_command = false;
381             if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
382                                                                   is_localhost,
383                                                                   will_debug,
384                                                                   first_arg_is_full_shell_command))
385                 return error;
386         }
387         error = Platform::LaunchProcess (launch_info);
388     }
389     else
390     {
391         error.SetErrorString ("the platform is not currently connected");
392     }
393     return error;
394 }
395 
396 lldb::ProcessSP
397 PlatformLinux::Attach(ProcessAttachInfo &attach_info,
398                       Debugger &debugger,
399                       Target *target,
400                       Listener &listener,
401                       Error &error)
402 {
403     lldb::ProcessSP process_sp;
404     if (IsHost())
405     {
406         if (target == NULL)
407         {
408             TargetSP new_target_sp;
409             ArchSpec emptyArchSpec;
410 
411             error = debugger.GetTargetList().CreateTarget (debugger,
412                                                            NULL,
413                                                            emptyArchSpec,
414                                                            false,
415                                                            m_remote_platform_sp,
416                                                            new_target_sp);
417             target = new_target_sp.get();
418         }
419         else
420             error.Clear();
421 
422         if (target && error.Success())
423         {
424             debugger.GetTargetList().SetSelectedTarget(target);
425 
426             process_sp = target->CreateProcess (listener,
427                                                 attach_info.GetProcessPluginName(),
428                                                 NULL);
429 
430             if (process_sp)
431                 error = process_sp->Attach (attach_info);
432         }
433     }
434     else
435     {
436         if (m_remote_platform_sp)
437             process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
438         else
439             error.SetErrorString ("the platform is not currently connected");
440     }
441     return process_sp;
442 }
443