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 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 Platform *
35 PlatformFreeBSD::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 
91 lldb_private::ConstString
92 PlatformFreeBSD::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 
106 const char *
107 PlatformFreeBSD::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 
115 static uint32_t g_initialize_count = 0;
116 
117 void
118 PlatformFreeBSD::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 
134 void
135 PlatformFreeBSD::Terminate ()
136 {
137     if (g_initialize_count > 0 && --g_initialize_count == 0)
138     	PluginManager::UnregisterPlugin (PlatformFreeBSD::CreateInstance);
139 }
140 
141 //------------------------------------------------------------------
142 /// Default Constructor
143 //------------------------------------------------------------------
144 PlatformFreeBSD::PlatformFreeBSD (bool is_host) :
145 Platform(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 //------------------------------------------------------------------
155 PlatformFreeBSD::~PlatformFreeBSD()
156 {
157 }
158 
159 //TODO:VK: inherit PlatformPOSIX
160 lldb_private::Error
161 PlatformFreeBSD::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 
180 Error
181 PlatformFreeBSD::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 
300 size_t
301 PlatformFreeBSD::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     case ArchSpec::eCore_x86_64_x86_64h:
316         {
317             static const uint8_t g_i386_opcode[] = { 0xCC };
318             trap_opcode = g_i386_opcode;
319             trap_opcode_size = sizeof(g_i386_opcode);
320         }
321         break;
322     }
323 
324     if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
325         return trap_opcode_size;
326 
327     return 0;
328 }
329 
330 bool
331 PlatformFreeBSD::GetRemoteOSVersion ()
332 {
333     if (m_remote_platform_sp)
334         return m_remote_platform_sp->GetOSVersion (m_major_os_version,
335                                                    m_minor_os_version,
336                                                    m_update_os_version);
337     return false;
338 }
339 
340 bool
341 PlatformFreeBSD::GetRemoteOSBuildString (std::string &s)
342 {
343     if (m_remote_platform_sp)
344         return m_remote_platform_sp->GetRemoteOSBuildString (s);
345     s.clear();
346     return false;
347 }
348 
349 bool
350 PlatformFreeBSD::GetRemoteOSKernelDescription (std::string &s)
351 {
352     if (m_remote_platform_sp)
353         return m_remote_platform_sp->GetRemoteOSKernelDescription (s);
354     s.clear();
355     return false;
356 }
357 
358 // Remote Platform subclasses need to override this function
359 ArchSpec
360 PlatformFreeBSD::GetRemoteSystemArchitecture ()
361 {
362     if (m_remote_platform_sp)
363         return m_remote_platform_sp->GetRemoteSystemArchitecture ();
364     return ArchSpec();
365 }
366 
367 
368 const char *
369 PlatformFreeBSD::GetHostname ()
370 {
371     if (IsHost())
372         return Platform::GetHostname();
373 
374     if (m_remote_platform_sp)
375         return m_remote_platform_sp->GetHostname ();
376     return NULL;
377 }
378 
379 bool
380 PlatformFreeBSD::IsConnected () const
381 {
382     if (IsHost())
383         return true;
384     else if (m_remote_platform_sp)
385         return m_remote_platform_sp->IsConnected();
386     return false;
387 }
388 
389 Error
390 PlatformFreeBSD::ConnectRemote (Args& args)
391 {
392     Error error;
393     if (IsHost())
394     {
395         error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
396     }
397     else
398     {
399         if (!m_remote_platform_sp)
400             m_remote_platform_sp = Platform::Create ("remote-gdb-server", error);
401 
402         if (m_remote_platform_sp)
403         {
404             if (error.Success())
405             {
406                 if (m_remote_platform_sp)
407                 {
408                     error = m_remote_platform_sp->ConnectRemote (args);
409                 }
410                 else
411                 {
412                     error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>");
413                 }
414             }
415         }
416         else
417             error.SetErrorString ("failed to create a 'remote-gdb-server' platform");
418 
419         if (error.Fail())
420             m_remote_platform_sp.reset();
421     }
422 
423     return error;
424 }
425 
426 Error
427 PlatformFreeBSD::DisconnectRemote ()
428 {
429     Error error;
430 
431     if (IsHost())
432     {
433         error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().GetCString());
434     }
435     else
436     {
437         if (m_remote_platform_sp)
438             error = m_remote_platform_sp->DisconnectRemote ();
439         else
440             error.SetErrorString ("the platform is not currently connected");
441     }
442     return error;
443 }
444 
445 bool
446 PlatformFreeBSD::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
447 {
448     bool success = false;
449     if (IsHost())
450     {
451         success = Platform::GetProcessInfo (pid, process_info);
452     }
453     else if (m_remote_platform_sp)
454     {
455         success = m_remote_platform_sp->GetProcessInfo (pid, process_info);
456     }
457     return success;
458 }
459 
460 
461 
462 uint32_t
463 PlatformFreeBSD::FindProcesses (const ProcessInstanceInfoMatch &match_info,
464                                ProcessInstanceInfoList &process_infos)
465 {
466     uint32_t match_count = 0;
467     if (IsHost())
468     {
469         // Let the base class figure out the host details
470         match_count = Platform::FindProcesses (match_info, process_infos);
471     }
472     else
473     {
474         // If we are remote, we can only return results if we are connected
475         if (m_remote_platform_sp)
476             match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos);
477     }
478     return match_count;
479 }
480 
481 Error
482 PlatformFreeBSD::LaunchProcess (ProcessLaunchInfo &launch_info)
483 {
484     Error error;
485     if (IsHost())
486     {
487         error = Platform::LaunchProcess (launch_info);
488     }
489     else
490     {
491         if (m_remote_platform_sp)
492             error = m_remote_platform_sp->LaunchProcess (launch_info);
493         else
494             error.SetErrorString ("the platform is not currently connected");
495     }
496     return error;
497 }
498 
499 lldb::ProcessSP
500 PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
501                         Debugger &debugger,
502                         Target *target,
503                         Listener &listener,
504                         Error &error)
505 {
506     lldb::ProcessSP process_sp;
507     if (IsHost())
508     {
509         if (target == NULL)
510         {
511             TargetSP new_target_sp;
512             ArchSpec emptyArchSpec;
513 
514             error = debugger.GetTargetList().CreateTarget (debugger,
515                                                            NULL,
516                                                            emptyArchSpec,
517                                                            false,
518                                                            m_remote_platform_sp,
519                                                            new_target_sp);
520             target = new_target_sp.get();
521         }
522         else
523             error.Clear();
524 
525         if (target && error.Success())
526         {
527             debugger.GetTargetList().SetSelectedTarget(target);
528             // The freebsd always currently uses the GDB remote debugger plug-in
529             // so even when debugging locally we are debugging remotely!
530             // Just like the darwin plugin.
531             process_sp = target->CreateProcess (listener, "gdb-remote", NULL);
532 
533             if (process_sp)
534                 error = process_sp->Attach (attach_info);
535         }
536     }
537     else
538     {
539         if (m_remote_platform_sp)
540             process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error);
541         else
542             error.SetErrorString ("the platform is not currently connected");
543     }
544     return process_sp;
545 }
546 
547 const char *
548 PlatformFreeBSD::GetUserName (uint32_t uid)
549 {
550     // Check the cache in Platform in case we have already looked this uid up
551     const char *user_name = Platform::GetUserName(uid);
552     if (user_name)
553         return user_name;
554 
555     if (IsRemote() && m_remote_platform_sp)
556         return m_remote_platform_sp->GetUserName(uid);
557     return NULL;
558 }
559 
560 const char *
561 PlatformFreeBSD::GetGroupName (uint32_t gid)
562 {
563     const char *group_name = Platform::GetGroupName(gid);
564     if (group_name)
565         return group_name;
566 
567     if (IsRemote() && m_remote_platform_sp)
568         return m_remote_platform_sp->GetGroupName(gid);
569     return NULL;
570 }
571 
572 
573 // From PlatformMacOSX only
574 Error
575 PlatformFreeBSD::GetFileWithUUID (const FileSpec &platform_file,
576                                   const UUID *uuid_ptr,
577                                   FileSpec &local_file)
578 {
579     if (IsRemote())
580     {
581         if (m_remote_platform_sp)
582             return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file);
583     }
584 
585     // Default to the local case
586     local_file = platform_file;
587     return Error();
588 }
589 
590 Error
591 PlatformFreeBSD::GetSharedModule (const ModuleSpec &module_spec,
592                                   ModuleSP &module_sp,
593                                   const FileSpecList *module_search_paths_ptr,
594                                   ModuleSP *old_module_sp_ptr,
595                                   bool *did_create_ptr)
596 {
597     Error error;
598     module_sp.reset();
599 
600     if (IsRemote())
601     {
602         // If we have a remote platform always, let it try and locate
603         // the shared module first.
604         if (m_remote_platform_sp)
605         {
606             error = m_remote_platform_sp->GetSharedModule (module_spec,
607                                                            module_sp,
608                                                            module_search_paths_ptr,
609                                                            old_module_sp_ptr,
610                                                            did_create_ptr);
611         }
612     }
613 
614     if (!module_sp)
615     {
616         // Fall back to the local platform and find the file locally
617         error = Platform::GetSharedModule (module_spec,
618                                            module_sp,
619                                            module_search_paths_ptr,
620                                            old_module_sp_ptr,
621                                            did_create_ptr);
622     }
623     if (module_sp)
624         module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
625     return error;
626 }
627 
628 
629 bool
630 PlatformFreeBSD::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
631 {
632     // From macosx;s plugin code. For FreeBSD we may want to support more archs.
633     if (idx == 0)
634     {
635         arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture);
636         return arch.IsValid();
637     }
638     else if (idx == 1)
639     {
640         ArchSpec platform_arch (Host::GetArchitecture (Host::eSystemDefaultArchitecture));
641         ArchSpec platform_arch64 (Host::GetArchitecture (Host::eSystemDefaultArchitecture64));
642         if (platform_arch.IsExactMatch(platform_arch64))
643         {
644             // This freebsd platform supports both 32 and 64 bit. Since we already
645             // returned the 64 bit arch for idx == 0, return the 32 bit arch
646             // for idx == 1
647             arch = Host::GetArchitecture (Host::eSystemDefaultArchitecture32);
648             return arch.IsValid();
649         }
650     }
651     return false;
652 }
653 
654 void
655 PlatformFreeBSD::GetStatus (Stream &strm)
656 {
657 #ifndef LLDB_DISABLE_POSIX
658     struct utsname un;
659 
660     strm << "      Host: ";
661 
662     ::memset(&un, 0, sizeof(utsname));
663     if (uname(&un) == -1)
664         strm << "FreeBSD" << '\n';
665 
666     strm << un.sysname << ' ' << un.release;
667     if (un.nodename[0] != '\0')
668         strm << " (" << un.nodename << ')';
669     strm << '\n';
670 
671     // Dump a common information about the platform status.
672     strm << "Host: " << un.sysname << ' ' << un.release << ' ' << un.version << '\n';
673 #endif
674 
675     Platform::GetStatus(strm);
676 }
677