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