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