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