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 #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/Debugger.h"
25 #include "lldb/Core/PluginManager.h"
26 #include "lldb/Host/HostInfo.h"
27 #include "lldb/Target/Process.h"
28 #include "lldb/Target/Target.h"
29 #include "lldb/Utility/FileSpec.h"
30 #include "lldb/Utility/Log.h"
31 #include "lldb/Utility/State.h"
32 #include "lldb/Utility/Status.h"
33 #include "lldb/Utility/StreamString.h"
34 
35 // Define these constants from FreeBSD mman.h for use when targeting remote
36 // FreeBSD systems even when host has different values.
37 #define MAP_PRIVATE 0x0002
38 #define MAP_ANON 0x1000
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 using namespace lldb_private::platform_freebsd;
43 
44 static uint32_t g_initialize_count = 0;
45 
46 //------------------------------------------------------------------
47 
48 PlatformSP PlatformFreeBSD::CreateInstance(bool force, const ArchSpec *arch) {
49   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
50   LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
51            arch ? arch->GetArchitectureName() : "<null>",
52            arch ? arch->GetTriple().getTriple() : "<null>");
53 
54   bool create = force;
55   if (create == false && arch && arch->IsValid()) {
56     const llvm::Triple &triple = arch->GetTriple();
57     switch (triple.getOS()) {
58     case llvm::Triple::FreeBSD:
59       create = true;
60       break;
61 
62 #if defined(__FreeBSD__)
63     // Only accept "unknown" for the OS if the host is BSD and it "unknown"
64     // wasn't specified (it was just returned because it was NOT specified)
65     case llvm::Triple::OSType::UnknownOS:
66       create = !arch->TripleOSWasSpecified();
67       break;
68 #endif
69     default:
70       break;
71     }
72   }
73   LLDB_LOG(log, "create = {0}", create);
74   if (create) {
75     return PlatformSP(new PlatformFreeBSD(false));
76   }
77   return PlatformSP();
78 }
79 
80 ConstString PlatformFreeBSD::GetPluginNameStatic(bool is_host) {
81   if (is_host) {
82     static ConstString g_host_name(Platform::GetHostPlatformName());
83     return g_host_name;
84   } else {
85     static ConstString g_remote_name("remote-freebsd");
86     return g_remote_name;
87   }
88 }
89 
90 const char *PlatformFreeBSD::GetPluginDescriptionStatic(bool is_host) {
91   if (is_host)
92     return "Local FreeBSD user platform plug-in.";
93   else
94     return "Remote FreeBSD user platform plug-in.";
95 }
96 
97 ConstString PlatformFreeBSD::GetPluginName() {
98   return GetPluginNameStatic(IsHost());
99 }
100 
101 void PlatformFreeBSD::Initialize() {
102   Platform::Initialize();
103 
104   if (g_initialize_count++ == 0) {
105 #if defined(__FreeBSD__)
106     PlatformSP default_platform_sp(new PlatformFreeBSD(true));
107     default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
108     Platform::SetHostPlatform(default_platform_sp);
109 #endif
110     PluginManager::RegisterPlugin(
111         PlatformFreeBSD::GetPluginNameStatic(false),
112         PlatformFreeBSD::GetPluginDescriptionStatic(false),
113         PlatformFreeBSD::CreateInstance, nullptr);
114   }
115 }
116 
117 void PlatformFreeBSD::Terminate() {
118   if (g_initialize_count > 0) {
119     if (--g_initialize_count == 0) {
120       PluginManager::UnregisterPlugin(PlatformFreeBSD::CreateInstance);
121     }
122   }
123 
124   PlatformPOSIX::Terminate();
125 }
126 
127 //------------------------------------------------------------------
128 /// Default Constructor
129 //------------------------------------------------------------------
130 PlatformFreeBSD::PlatformFreeBSD(bool is_host)
131     : PlatformPOSIX(is_host) // This is the local host platform
132 {}
133 
134 PlatformFreeBSD::~PlatformFreeBSD() = default;
135 
136 bool PlatformFreeBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
137                                                       ArchSpec &arch) {
138   if (IsHost()) {
139     ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
140     if (hostArch.GetTriple().isOSFreeBSD()) {
141       if (idx == 0) {
142         arch = hostArch;
143         return arch.IsValid();
144       } else if (idx == 1) {
145         // If the default host architecture is 64-bit, look for a 32-bit
146         // variant
147         if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
148           arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
149           return arch.IsValid();
150         }
151       }
152     }
153   } else {
154     if (m_remote_platform_sp)
155       return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
156 
157     llvm::Triple triple;
158     // Set the OS to FreeBSD
159     triple.setOS(llvm::Triple::FreeBSD);
160     // Set the architecture
161     switch (idx) {
162     case 0:
163       triple.setArchName("x86_64");
164       break;
165     case 1:
166       triple.setArchName("i386");
167       break;
168     case 2:
169       triple.setArchName("aarch64");
170       break;
171     case 3:
172       triple.setArchName("arm");
173       break;
174     case 4:
175       triple.setArchName("mips64");
176       break;
177     case 5:
178       triple.setArchName("mips");
179       break;
180     case 6:
181       triple.setArchName("ppc64");
182       break;
183     case 7:
184       triple.setArchName("ppc");
185       break;
186     default:
187       return false;
188     }
189     // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
190     // vendor by calling triple.SetVendorName("unknown") so that it is a
191     // "unspecified unknown". This means when someone calls
192     // triple.GetVendorName() it will return an empty string which indicates
193     // that the vendor can be set when two architectures are merged
194 
195     // Now set the triple into "arch" and return true
196     arch.SetTriple(triple);
197     return true;
198   }
199   return false;
200 }
201 
202 void PlatformFreeBSD::GetStatus(Stream &strm) {
203   Platform::GetStatus(strm);
204 
205 #ifndef LLDB_DISABLE_POSIX
206   // Display local kernel information only when we are running in host mode.
207   // Otherwise, we would end up printing non-FreeBSD information (when running
208   // on Mac OS for example).
209   if (IsHost()) {
210     struct utsname un;
211 
212     if (uname(&un))
213       return;
214 
215     strm.Printf("    Kernel: %s\n", un.sysname);
216     strm.Printf("   Release: %s\n", un.release);
217     strm.Printf("   Version: %s\n", un.version);
218   }
219 #endif
220 }
221 
222 size_t
223 PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target,
224                                                  BreakpointSite *bp_site) {
225   switch (target.GetArchitecture().GetMachine()) {
226   case llvm::Triple::arm: {
227     lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0));
228     AddressClass addr_class = AddressClass::eUnknown;
229 
230     if (bp_loc_sp) {
231       addr_class = bp_loc_sp->GetAddress().GetAddressClass();
232       if (addr_class == AddressClass::eUnknown &&
233           (bp_loc_sp->GetAddress().GetFileAddress() & 1))
234         addr_class = AddressClass::eCodeAlternateISA;
235     }
236 
237     if (addr_class == AddressClass::eCodeAlternateISA) {
238       // TODO: Enable when FreeBSD supports thumb breakpoints.
239       // FreeBSD kernel as of 10.x, does not support thumb breakpoints
240       return 0;
241     }
242 
243     static const uint8_t g_arm_breakpoint_opcode[] = {0xFE, 0xDE, 0xFF, 0xE7};
244     size_t trap_opcode_size = sizeof(g_arm_breakpoint_opcode);
245     assert(bp_site);
246     if (bp_site->SetTrapOpcode(g_arm_breakpoint_opcode, trap_opcode_size))
247       return trap_opcode_size;
248   }
249     LLVM_FALLTHROUGH;
250   default:
251     return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site);
252   }
253 }
254 
255 Status PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) {
256   Status error;
257   if (IsHost()) {
258     error = Platform::LaunchProcess(launch_info);
259   } else {
260     if (m_remote_platform_sp)
261       error = m_remote_platform_sp->LaunchProcess(launch_info);
262     else
263       error.SetErrorString("the platform is not currently connected");
264   }
265   return error;
266 }
267 
268 lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info,
269                                         Debugger &debugger, Target *target,
270                                         Status &error) {
271   lldb::ProcessSP process_sp;
272   if (IsHost()) {
273     if (target == NULL) {
274       TargetSP new_target_sp;
275       ArchSpec emptyArchSpec;
276 
277       error = debugger.GetTargetList().CreateTarget(
278           debugger, "", emptyArchSpec, eLoadDependentsNo, m_remote_platform_sp,
279           new_target_sp);
280       target = new_target_sp.get();
281     } else
282       error.Clear();
283 
284     if (target && error.Success()) {
285       debugger.GetTargetList().SetSelectedTarget(target);
286       // The freebsd always currently uses the GDB remote debugger plug-in so
287       // even when debugging locally we are debugging remotely! Just like the
288       // darwin plugin.
289       process_sp = target->CreateProcess(
290           attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL);
291 
292       if (process_sp)
293         error = process_sp->Attach(attach_info);
294     }
295   } else {
296     if (m_remote_platform_sp)
297       process_sp =
298           m_remote_platform_sp->Attach(attach_info, debugger, target, error);
299     else
300       error.SetErrorString("the platform is not currently connected");
301   }
302   return process_sp;
303 }
304 
305 // FreeBSD processes cannot yet be launched by spawning and attaching.
306 bool PlatformFreeBSD::CanDebugProcess() {
307   return false;
308 }
309 
310 void PlatformFreeBSD::CalculateTrapHandlerSymbolNames() {
311   m_trap_handlers.push_back(ConstString("_sigtramp"));
312 }
313 
314 MmapArgList PlatformFreeBSD::GetMmapArgumentList(const ArchSpec &arch,
315                                                  addr_t addr, addr_t length,
316                                                  unsigned prot, unsigned flags,
317                                                  addr_t fd, addr_t offset) {
318   uint64_t flags_platform = 0;
319 
320   if (flags & eMmapFlagsPrivate)
321     flags_platform |= MAP_PRIVATE;
322   if (flags & eMmapFlagsAnon)
323     flags_platform |= MAP_ANON;
324 
325   MmapArgList args({addr, length, prot, flags_platform, fd, offset});
326   if (arch.GetTriple().getArch() == llvm::Triple::x86)
327     args.push_back(0);
328   return args;
329 }
330