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