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