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