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