PlatformFreeBSD.cpp revision 263363
1//===-- PlatformFreeBSD.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 "PlatformFreeBSD.h"
13#include "lldb/Host/Config.h"
14
15// C Includes
16#include <stdio.h>
17#ifndef LLDB_DISABLE_POSIX
18#include <sys/utsname.h>
19#endif
20
21// C++ Includes
22// Other libraries and framework includes
23// Project includes
24#include "lldb/Core/Error.h"
25#include "lldb/Core/Debugger.h"
26#include "lldb/Core/Module.h"
27#include "lldb/Core/ModuleSpec.h"
28#include "lldb/Core/PluginManager.h"
29#include "lldb/Host/Host.h"
30
31using namespace lldb;
32using namespace lldb_private;
33
34Platform *
35PlatformFreeBSD::CreateInstance (bool force, const lldb_private::ArchSpec *arch)
36{
37    // The only time we create an instance is when we are creating a remote
38    // freebsd platform
39    const bool is_host = false;
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(__FreeBSD__) || defined(__OpenBSD__)
52            // Only accept "unknown" for the vendor if the host is BSD 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::FreeBSD:
68                case llvm::Triple::KFreeBSD:
69                    break;
70
71#if defined(__FreeBSD__) || defined(__OpenBSD__)
72                // Only accept "unknown" for the OS if the host is BSD and
73                // it "unknown" wasn't specified (it was just returned becasue it
74                // was NOT specified)
75                case llvm::Triple::UnknownOS:
76                    create = arch->TripleOSWasSpecified();
77                    break;
78#endif
79                default:
80                    create = false;
81                    break;
82            }
83        }
84    }
85    if (create)
86        return new PlatformFreeBSD (is_host);
87    return NULL;
88
89}
90
91lldb_private::ConstString
92PlatformFreeBSD::GetPluginNameStatic (bool is_host)
93{
94    if (is_host)
95    {
96        static ConstString g_host_name(Platform::GetHostPlatformName ());
97        return g_host_name;
98    }
99    else
100    {
101        static ConstString g_remote_name("remote-freebsd");
102        return g_remote_name;
103    }
104}
105
106const char *
107PlatformFreeBSD::GetDescriptionStatic (bool is_host)
108{
109    if (is_host)
110        return "Local FreeBSD user platform plug-in.";
111    else
112        return "Remote FreeBSD user platform plug-in.";
113}
114
115static uint32_t g_initialize_count = 0;
116
117void
118PlatformFreeBSD::Initialize ()
119{
120    if (g_initialize_count++ == 0)
121    {
122#if defined (__FreeBSD__)
123    	// Force a host flag to true for the default platform object.
124        PlatformSP default_platform_sp (new PlatformFreeBSD(true));
125        default_platform_sp->SetSystemArchitecture (Host::GetArchitecture());
126        Platform::SetDefaultPlatform (default_platform_sp);
127#endif
128        PluginManager::RegisterPlugin(PlatformFreeBSD::GetPluginNameStatic(false),
129                                      PlatformFreeBSD::GetDescriptionStatic(false),
130                                      PlatformFreeBSD::CreateInstance);
131    }
132}
133
134void
135PlatformFreeBSD::Terminate ()
136{
137    if (g_initialize_count > 0 && --g_initialize_count == 0)
138    	PluginManager::UnregisterPlugin (PlatformFreeBSD::CreateInstance);
139}
140
141//------------------------------------------------------------------
142/// Default Constructor
143//------------------------------------------------------------------
144PlatformFreeBSD::PlatformFreeBSD (bool is_host) :
145Platform(is_host)
146{
147}
148
149//------------------------------------------------------------------
150/// Destructor.
151///
152/// The destructor is virtual since this class is designed to be
153/// inherited from by the plug-in instance.
154//------------------------------------------------------------------
155PlatformFreeBSD::~PlatformFreeBSD()
156{
157}
158
159//TODO:VK: inherit PlatformPOSIX
160lldb_private::Error
161PlatformFreeBSD::RunShellCommand (const char *command,
162                                  const char *working_dir,
163                                  int *status_ptr,
164                                  int *signo_ptr,
165                                  std::string *command_output,
166                                  uint32_t timeout_sec)
167{
168    if (IsHost())
169        return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
170    else
171    {
172        if (m_remote_platform_sp)
173            return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
174        else
175            return Error("unable to run a remote command without a platform");
176    }
177}
178
179
180Error
181PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file,
182                                    const ArchSpec &exe_arch,
183                                    lldb::ModuleSP &exe_module_sp,
184                                    const FileSpecList *module_search_paths_ptr)
185{
186    Error error;
187    // Nothing special to do here, just use the actual file and architecture
188
189    char exe_path[PATH_MAX];
190    FileSpec resolved_exe_file (exe_file);
191
192    if (IsHost())
193    {
194        // If we have "ls" as the exe_file, resolve the executable location based on
195        // the current path variables
196        if (!resolved_exe_file.Exists())
197        {
198            exe_file.GetPath(exe_path, sizeof(exe_path));
199            resolved_exe_file.SetFile(exe_path, true);
200        }
201
202        if (!resolved_exe_file.Exists())
203            resolved_exe_file.ResolveExecutableLocation ();
204
205        if (resolved_exe_file.Exists())
206            error.Clear();
207        else
208        {
209            exe_file.GetPath(exe_path, sizeof(exe_path));
210            error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path);
211        }
212    }
213    else
214    {
215        if (m_remote_platform_sp)
216        {
217            error = m_remote_platform_sp->ResolveExecutable (exe_file,
218                                                             exe_arch,
219                                                             exe_module_sp,
220                                                             module_search_paths_ptr);
221        }
222        else
223        {
224            // We may connect to a process and use the provided executable (Don't use local $PATH).
225
226            // Resolve any executable within a bundle on MacOSX
227            Host::ResolveExecutableInBundle (resolved_exe_file);
228
229            if (resolved_exe_file.Exists()) {
230                error.Clear();
231            }
232            else
233            {
234                exe_file.GetPath(exe_path, sizeof(exe_path));
235                error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path);
236            }
237        }
238    }
239
240    if (error.Success())
241    {
242        ModuleSpec module_spec (resolved_exe_file, exe_arch);
243        if (module_spec.GetArchitecture().IsValid())
244        {
245            error = ModuleList::GetSharedModule (module_spec,
246                                                 exe_module_sp,
247                                                 module_search_paths_ptr,
248                                                 NULL,
249                                                 NULL);
250
251            if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL)
252            {
253                exe_module_sp.reset();
254                error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s",
255                                                exe_file.GetPath().c_str(),
256                                                exe_arch.GetArchitectureName());
257            }
258        }
259        else
260        {
261            // No valid architecture was specified, ask the platform for
262            // the architectures that we should be using (in the correct order)
263            // and see if we can find a match that way
264            StreamString arch_names;
265            ArchSpec platform_arch;
266            for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, platform_arch); ++idx)
267            {
268                error = ModuleList::GetSharedModule (module_spec,
269                                                     exe_module_sp,
270                                                     module_search_paths_ptr,
271                                                     NULL,
272                                                     NULL);
273                // Did we find an executable using one of the
274                if (error.Success())
275                {
276                    if (exe_module_sp && exe_module_sp->GetObjectFile())
277                        break;
278                    else
279                        error.SetErrorToGenericError();
280                }
281
282                if (idx > 0)
283                    arch_names.PutCString (", ");
284                arch_names.PutCString (platform_arch.GetArchitectureName());
285            }
286
287            if (error.Fail() || !exe_module_sp)
288            {
289                error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s",
290                                                exe_file.GetPath().c_str(),
291                                                GetPluginName().GetCString(),
292                                                arch_names.GetString().c_str());
293            }
294        }
295    }
296
297    return error;
298}
299
300size_t
301PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site)
302{
303    ArchSpec arch = target.GetArchitecture();
304    const uint8_t *trap_opcode = NULL;
305    size_t trap_opcode_size = 0;
306
307    switch (arch.GetCore())
308    {
309    default:
310        assert(false && "Unhandled architecture in PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode()");
311        break;
312
313    case ArchSpec::eCore_x86_32_i386:
314    case ArchSpec::eCore_x86_64_x86_64:
315        {
316            static const uint8_t g_i386_opcode[] = { 0xCC };
317            trap_opcode = g_i386_opcode;
318            trap_opcode_size = sizeof(g_i386_opcode);
319        }
320        break;
321    }
322
323    if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
324        return trap_opcode_size;
325
326    return 0;
327}
328
329bool
330PlatformFreeBSD::GetRemoteOSVersion ()
331{
332    if (m_remote_platform_sp)
333        return m_remote_platform_sp->GetOSVersion (m_major_os_version,
334                                                   m_minor_os_version,
335                                                   m_update_os_version);
336    return false;
337}
338
339bool
340PlatformFreeBSD::GetRemoteOSBuildString (std::string &s)
341{
342    if (m_remote_platform_sp)
343        return m_remote_platform_sp->GetRemoteOSBuildString (s);
344    s.clear();
345    return false;
346}
347
348bool
349PlatformFreeBSD::GetRemoteOSKernelDescription (std::string &s)
350{
351    if (m_remote_platform_sp)
352        return m_remote_platform_sp->GetRemoteOSKernelDescription (s);
353    s.clear();
354    return false;
355}
356
357// Remote Platform subclasses need to override this function
358ArchSpec
359PlatformFreeBSD::GetRemoteSystemArchitecture ()
360{
361    if (m_remote_platform_sp)
362        return m_remote_platform_sp->GetRemoteSystemArchitecture ();
363    return ArchSpec();
364}
365
366
367const char *
368PlatformFreeBSD::GetHostname ()
369{
370    if (IsHost())
371        return Platform::GetHostname();
372
373    if (m_remote_platform_sp)
374        return m_remote_platform_sp->GetHostname ();
375    return NULL;
376}
377
378bool
379PlatformFreeBSD::IsConnected () const
380{
381    if (IsHost())
382        return true;
383    else if (m_remote_platform_sp)
384        return m_remote_platform_sp->IsConnected();
385    return false;
386}
387
388Error
389PlatformFreeBSD::ConnectRemote (Args& args)
390{
391    Error error;
392    if (IsHost())
393    {
394        error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
395    }
396    else
397    {
398        if (!m_remote_platform_sp)
399            m_remote_platform_sp = Platform::Create ("remote-gdb-server", error);
400
401        if (m_remote_platform_sp)
402        {
403            if (error.Success())
404            {
405                if (m_remote_platform_sp)
406                {
407                    error = m_remote_platform_sp->ConnectRemote (args);
408                }
409                else
410                {
411                    error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>");
412                }
413            }
414        }
415        else
416            error.SetErrorString ("failed to create a 'remote-gdb-server' platform");
417
418        if (error.Fail())
419            m_remote_platform_sp.reset();
420    }
421
422    return error;
423}
424
425Error
426PlatformFreeBSD::DisconnectRemote ()
427{
428    Error error;
429
430    if (IsHost())
431    {
432        error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().GetCString());
433    }
434    else
435    {
436        if (m_remote_platform_sp)
437            error = m_remote_platform_sp->DisconnectRemote ();
438        else
439            error.SetErrorString ("the platform is not currently connected");
440    }
441    return error;
442}
443
444bool
445PlatformFreeBSD::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
446{
447    bool success = false;
448    if (IsHost())
449    {
450        success = Platform::GetProcessInfo (pid, process_info);
451    }
452    else if (m_remote_platform_sp)
453    {
454        success = m_remote_platform_sp->GetProcessInfo (pid, process_info);
455    }
456    return success;
457}
458
459
460
461uint32_t
462PlatformFreeBSD::FindProcesses (const ProcessInstanceInfoMatch &match_info,
463                               ProcessInstanceInfoList &process_infos)
464{
465    uint32_t match_count = 0;
466    if (IsHost())
467    {
468        // Let the base class figure out the host details
469        match_count = Platform::FindProcesses (match_info, process_infos);
470    }
471    else
472    {
473        // If we are remote, we can only return results if we are connected
474        if (m_remote_platform_sp)
475            match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos);
476    }
477    return match_count;
478}
479
480Error
481PlatformFreeBSD::LaunchProcess (ProcessLaunchInfo &launch_info)
482{
483    Error error;
484    if (IsHost())
485    {
486        error = Platform::LaunchProcess (launch_info);
487    }
488    else
489    {
490        if (m_remote_platform_sp)
491            error = m_remote_platform_sp->LaunchProcess (launch_info);
492        else
493            error.SetErrorString ("the platform is not currently connected");
494    }
495    return error;
496}
497
498lldb::ProcessSP
499PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
500                        Debugger &debugger,
501                        Target *target,
502                        Listener &listener,
503                        Error &error)
504{
505    lldb::ProcessSP process_sp;
506    if (IsHost())
507    {
508        if (target == NULL)
509        {
510            TargetSP new_target_sp;
511            ArchSpec emptyArchSpec;
512
513            error = debugger.GetTargetList().CreateTarget (debugger,
514                                                           NULL,
515                                                           emptyArchSpec,
516                                                           false,
517                                                           m_remote_platform_sp,
518                                                           new_target_sp);
519            target = new_target_sp.get();
520        }
521        else
522            error.Clear();
523
524        if (target && error.Success())
525        {
526            debugger.GetTargetList().SetSelectedTarget(target);
527            // The freebsd always currently uses the GDB remote debugger plug-in
528            // so even when debugging locally we are debugging remotely!
529            // Just like the darwin plugin.
530            process_sp = target->CreateProcess (listener, "gdb-remote", NULL);
531
532            if (process_sp)
533                error = process_sp->Attach (attach_info);
534        }
535    }
536    else
537    {
538        if (m_remote_platform_sp)
539            process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
540        else
541            error.SetErrorString ("the platform is not currently connected");
542    }
543    return process_sp;
544}
545
546const char *
547PlatformFreeBSD::GetUserName (uint32_t uid)
548{
549    // Check the cache in Platform in case we have already looked this uid up
550    const char *user_name = Platform::GetUserName(uid);
551    if (user_name)
552        return user_name;
553
554    if (IsRemote() && m_remote_platform_sp)
555        return m_remote_platform_sp->GetUserName(uid);
556    return NULL;
557}
558
559const char *
560PlatformFreeBSD::GetGroupName (uint32_t gid)
561{
562    const char *group_name = Platform::GetGroupName(gid);
563    if (group_name)
564        return group_name;
565
566    if (IsRemote() && m_remote_platform_sp)
567        return m_remote_platform_sp->GetGroupName(gid);
568    return NULL;
569}
570
571
572// From PlatformMacOSX only
573Error
574PlatformFreeBSD::GetFile (const FileSpec &platform_file,
575                          const UUID *uuid_ptr,
576                          FileSpec &local_file)
577{
578    if (IsRemote())
579    {
580        if (m_remote_platform_sp)
581            return m_remote_platform_sp->GetFile (platform_file, uuid_ptr, local_file);
582    }
583
584    // Default to the local case
585    local_file = platform_file;
586    return Error();
587}
588
589Error
590PlatformFreeBSD::GetSharedModule (const ModuleSpec &module_spec,
591                                  ModuleSP &module_sp,
592                                  const FileSpecList *module_search_paths_ptr,
593                                  ModuleSP *old_module_sp_ptr,
594                                  bool *did_create_ptr)
595{
596    Error error;
597    module_sp.reset();
598
599    if (IsRemote())
600    {
601        // If we have a remote platform always, let it try and locate
602        // the shared module first.
603        if (m_remote_platform_sp)
604        {
605            error = m_remote_platform_sp->GetSharedModule (module_spec,
606                                                           module_sp,
607                                                           module_search_paths_ptr,
608                                                           old_module_sp_ptr,
609                                                           did_create_ptr);
610        }
611    }
612
613    if (!module_sp)
614    {
615        // Fall back to the local platform and find the file locally
616        error = Platform::GetSharedModule (module_spec,
617                                           module_sp,
618                                           module_search_paths_ptr,
619                                           old_module_sp_ptr,
620                                           did_create_ptr);
621    }
622    if (module_sp)
623        module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
624    return error;
625}
626
627
628bool
629PlatformFreeBSD::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
630{
631    // From macosx;s plugin code. For FreeBSD we may want to support more archs.
632    if (idx == 0)
633    {
634        arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture);
635        return arch.IsValid();
636    }
637    else if (idx == 1)
638    {
639        ArchSpec platform_arch (Host::GetArchitecture (Host::eSystemDefaultArchitecture));
640        ArchSpec platform_arch64 (Host::GetArchitecture (Host::eSystemDefaultArchitecture64));
641        if (platform_arch.IsExactMatch(platform_arch64))
642        {
643            // This freebsd platform supports both 32 and 64 bit. Since we already
644            // returned the 64 bit arch for idx == 0, return the 32 bit arch
645            // for idx == 1
646            arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture32);
647            return arch.IsValid();
648        }
649    }
650    return false;
651}
652
653void
654PlatformFreeBSD::GetStatus (Stream &strm)
655{
656#ifndef LLDB_DISABLE_POSIX
657    struct utsname un;
658
659    strm << "      Host: ";
660
661    ::memset(&un, 0, sizeof(utsname));
662    if (uname(&un) == -1)
663        strm << "FreeBSD" << '\n';
664
665    strm << un.sysname << ' ' << un.release;
666    if (un.nodename[0] != '\0')
667        strm << " (" << un.nodename << ')';
668    strm << '\n';
669
670    // Dump a common information about the platform status.
671    strm << "Host: " << un.sysname << ' ' << un.release << ' ' << un.version << '\n';
672#endif
673
674    Platform::GetStatus(strm);
675}
676