1 //===-- PlatformNetBSD.cpp -------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "PlatformNetBSD.h"
10 #include "lldb/Host/Config.h"
11 
12 #include <stdio.h>
13 #ifndef LLDB_DISABLE_POSIX
14 #include <sys/utsname.h>
15 #endif
16 
17 #include "lldb/Core/Debugger.h"
18 #include "lldb/Core/PluginManager.h"
19 #include "lldb/Host/HostInfo.h"
20 #include "lldb/Target/Process.h"
21 #include "lldb/Target/Target.h"
22 #include "lldb/Utility/FileSpec.h"
23 #include "lldb/Utility/Log.h"
24 #include "lldb/Utility/State.h"
25 #include "lldb/Utility/Status.h"
26 #include "lldb/Utility/StreamString.h"
27 
28 // Define these constants from NetBSD mman.h for use when targeting remote
29 // netbsd systems even when host has different values.
30 #define MAP_PRIVATE 0x0002
31 #define MAP_ANON 0x1000
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 using namespace lldb_private::platform_netbsd;
36 
37 static uint32_t g_initialize_count = 0;
38 
39 //------------------------------------------------------------------
40 
41 PlatformSP PlatformNetBSD::CreateInstance(bool force, const ArchSpec *arch) {
42   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
43   LLDB_LOG(log, "force = {0}, arch=({1}, {2})", force,
44            arch ? arch->GetArchitectureName() : "<null>",
45            arch ? arch->GetTriple().getTriple() : "<null>");
46 
47   bool create = force;
48   if (!create && arch && arch->IsValid()) {
49     const llvm::Triple &triple = arch->GetTriple();
50     switch (triple.getOS()) {
51     case llvm::Triple::NetBSD:
52       create = true;
53       break;
54 
55     default:
56       break;
57     }
58   }
59 
60   LLDB_LOG(log, "create = {0}", create);
61   if (create) {
62     return PlatformSP(new PlatformNetBSD(false));
63   }
64   return PlatformSP();
65 }
66 
67 ConstString PlatformNetBSD::GetPluginNameStatic(bool is_host) {
68   if (is_host) {
69     static ConstString g_host_name(Platform::GetHostPlatformName());
70     return g_host_name;
71   } else {
72     static ConstString g_remote_name("remote-netbsd");
73     return g_remote_name;
74   }
75 }
76 
77 const char *PlatformNetBSD::GetPluginDescriptionStatic(bool is_host) {
78   if (is_host)
79     return "Local NetBSD user platform plug-in.";
80   else
81     return "Remote NetBSD user platform plug-in.";
82 }
83 
84 ConstString PlatformNetBSD::GetPluginName() {
85   return GetPluginNameStatic(IsHost());
86 }
87 
88 void PlatformNetBSD::Initialize() {
89   PlatformPOSIX::Initialize();
90 
91   if (g_initialize_count++ == 0) {
92 #if defined(__NetBSD__)
93     PlatformSP default_platform_sp(new PlatformNetBSD(true));
94     default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
95     Platform::SetHostPlatform(default_platform_sp);
96 #endif
97     PluginManager::RegisterPlugin(
98         PlatformNetBSD::GetPluginNameStatic(false),
99         PlatformNetBSD::GetPluginDescriptionStatic(false),
100         PlatformNetBSD::CreateInstance, nullptr);
101   }
102 }
103 
104 void PlatformNetBSD::Terminate() {
105   if (g_initialize_count > 0) {
106     if (--g_initialize_count == 0) {
107       PluginManager::UnregisterPlugin(PlatformNetBSD::CreateInstance);
108     }
109   }
110 
111   PlatformPOSIX::Terminate();
112 }
113 
114 //------------------------------------------------------------------
115 /// Default Constructor
116 //------------------------------------------------------------------
117 PlatformNetBSD::PlatformNetBSD(bool is_host)
118     : PlatformPOSIX(is_host) // This is the local host platform
119 {}
120 
121 PlatformNetBSD::~PlatformNetBSD() = default;
122 
123 bool PlatformNetBSD::GetSupportedArchitectureAtIndex(uint32_t idx,
124                                                      ArchSpec &arch) {
125   if (IsHost()) {
126     ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
127     if (hostArch.GetTriple().isOSNetBSD()) {
128       if (idx == 0) {
129         arch = hostArch;
130         return arch.IsValid();
131       } else if (idx == 1) {
132         // If the default host architecture is 64-bit, look for a 32-bit
133         // variant
134         if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) {
135           arch = HostInfo::GetArchitecture(HostInfo::eArchKind32);
136           return arch.IsValid();
137         }
138       }
139     }
140   } else {
141     if (m_remote_platform_sp)
142       return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch);
143 
144     llvm::Triple triple;
145     // Set the OS to NetBSD
146     triple.setOS(llvm::Triple::NetBSD);
147     // Set the architecture
148     switch (idx) {
149     case 0:
150       triple.setArchName("x86_64");
151       break;
152     case 1:
153       triple.setArchName("i386");
154       break;
155     default:
156       return false;
157     }
158     // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the
159     // vendor by calling triple.SetVendorName("unknown") so that it is a
160     // "unspecified unknown". This means when someone calls
161     // triple.GetVendorName() it will return an empty string which indicates
162     // that the vendor can be set when two architectures are merged
163 
164     // Now set the triple into "arch" and return true
165     arch.SetTriple(triple);
166     return true;
167   }
168   return false;
169 }
170 
171 void PlatformNetBSD::GetStatus(Stream &strm) {
172   Platform::GetStatus(strm);
173 
174 #ifndef LLDB_DISABLE_POSIX
175   // Display local kernel information only when we are running in host mode.
176   // Otherwise, we would end up printing non-NetBSD information (when running
177   // on Mac OS for example).
178   if (IsHost()) {
179     struct utsname un;
180 
181     if (uname(&un))
182       return;
183 
184     strm.Printf("    Kernel: %s\n", un.sysname);
185     strm.Printf("   Release: %s\n", un.release);
186     strm.Printf("   Version: %s\n", un.version);
187   }
188 #endif
189 }
190 
191 int32_t
192 PlatformNetBSD::GetResumeCountForLaunchInfo(ProcessLaunchInfo &launch_info) {
193   int32_t resume_count = 0;
194 
195   // Always resume past the initial stop when we use eLaunchFlagDebug
196   if (launch_info.GetFlags().Test(eLaunchFlagDebug)) {
197     // Resume past the stop for the final exec into the true inferior.
198     ++resume_count;
199   }
200 
201   // If we're not launching a shell, we're done.
202   const FileSpec &shell = launch_info.GetShell();
203   if (!shell)
204     return resume_count;
205 
206   std::string shell_string = shell.GetPath();
207   // We're in a shell, so for sure we have to resume past the shell exec.
208   ++resume_count;
209 
210   // Figure out what shell we're planning on using.
211   const char *shell_name = strrchr(shell_string.c_str(), '/');
212   if (shell_name == NULL)
213     shell_name = shell_string.c_str();
214   else
215     shell_name++;
216 
217   if (strcmp(shell_name, "csh") == 0 || strcmp(shell_name, "tcsh") == 0 ||
218       strcmp(shell_name, "zsh") == 0 || strcmp(shell_name, "sh") == 0) {
219     // These shells seem to re-exec themselves.  Add another resume.
220     ++resume_count;
221   }
222 
223   return resume_count;
224 }
225 
226 bool PlatformNetBSD::CanDebugProcess() {
227   if (IsHost()) {
228     return true;
229   } else {
230     // If we're connected, we can debug.
231     return IsConnected();
232   }
233 }
234 
235 // For local debugging, NetBSD will override the debug logic to use llgs-launch
236 // rather than lldb-launch, llgs-attach.  This differs from current lldb-
237 // launch, debugserver-attach approach on MacOSX.
238 lldb::ProcessSP
239 PlatformNetBSD::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger,
240                              Target *target, // Can be NULL, if NULL create a new
241                                              // target, else use existing one
242                              Status &error) {
243   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
244   LLDB_LOG(log, "target {0}", target);
245 
246   // If we're a remote host, use standard behavior from parent class.
247   if (!IsHost())
248     return PlatformPOSIX::DebugProcess(launch_info, debugger, target, error);
249 
250   //
251   // For local debugging, we'll insist on having ProcessGDBRemote create the
252   // process.
253   //
254 
255   ProcessSP process_sp;
256 
257   // Make sure we stop at the entry point
258   launch_info.GetFlags().Set(eLaunchFlagDebug);
259 
260   // We always launch the process we are going to debug in a separate process
261   // group, since then we can handle ^C interrupts ourselves w/o having to
262   // worry about the target getting them as well.
263   launch_info.SetLaunchInSeparateProcessGroup(true);
264 
265   // Ensure we have a target.
266   if (target == nullptr) {
267     LLDB_LOG(log, "creating new target");
268     TargetSP new_target_sp;
269     error = debugger.GetTargetList().CreateTarget(
270         debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
271     if (error.Fail()) {
272       LLDB_LOG(log, "failed to create new target: {0}", error);
273       return process_sp;
274     }
275 
276     target = new_target_sp.get();
277     if (!target) {
278       error.SetErrorString("CreateTarget() returned nullptr");
279       LLDB_LOG(log, "error: {0}", error);
280       return process_sp;
281     }
282   }
283 
284   // Mark target as currently selected target.
285   debugger.GetTargetList().SetSelectedTarget(target);
286 
287   // Now create the gdb-remote process.
288   LLDB_LOG(log, "having target create process with gdb-remote plugin");
289   process_sp =
290       target->CreateProcess(launch_info.GetListener(), "gdb-remote", nullptr);
291 
292   if (!process_sp) {
293     error.SetErrorString("CreateProcess() failed for gdb-remote process");
294     LLDB_LOG(log, "error: {0}", error);
295     return process_sp;
296   }
297 
298   LLDB_LOG(log, "successfully created process");
299   // Adjust launch for a hijacker.
300   ListenerSP listener_sp;
301   if (!launch_info.GetHijackListener()) {
302     LLDB_LOG(log, "setting up hijacker");
303     listener_sp =
304         Listener::MakeListener("lldb.PlatformNetBSD.DebugProcess.hijack");
305     launch_info.SetHijackListener(listener_sp);
306     process_sp->HijackProcessEvents(listener_sp);
307   }
308 
309   // Log file actions.
310   if (log) {
311     LLDB_LOG(log, "launching process with the following file actions:");
312     StreamString stream;
313     size_t i = 0;
314     const FileAction *file_action;
315     while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
316       file_action->Dump(stream);
317       LLDB_LOG(log, "{0}", stream.GetData());
318       stream.Clear();
319     }
320   }
321 
322   // Do the launch.
323   error = process_sp->Launch(launch_info);
324   if (error.Success()) {
325     // Handle the hijacking of process events.
326     if (listener_sp) {
327       const StateType state = process_sp->WaitForProcessToStop(
328           llvm::None, NULL, false, listener_sp);
329 
330       LLDB_LOG(log, "pid {0} state {0}", process_sp->GetID(), state);
331     }
332 
333     // Hook up process PTY if we have one (which we should for local debugging
334     // with llgs).
335     int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
336     if (pty_fd != PseudoTerminal::invalid_fd) {
337       process_sp->SetSTDIOFileDescriptor(pty_fd);
338       LLDB_LOG(log, "hooked up STDIO pty to process");
339     } else
340       LLDB_LOG(log, "not using process STDIO pty");
341   } else {
342     LLDB_LOG(log, "process launch failed: {0}", error);
343     // FIXME figure out appropriate cleanup here.  Do we delete the target? Do
344     // we delete the process?  Does our caller do that?
345   }
346 
347   return process_sp;
348 }
349 
350 void PlatformNetBSD::CalculateTrapHandlerSymbolNames() {
351   m_trap_handlers.push_back(ConstString("_sigtramp"));
352 }
353 
354 MmapArgList PlatformNetBSD::GetMmapArgumentList(const ArchSpec &arch,
355                                                 addr_t addr, addr_t length,
356                                                 unsigned prot, unsigned flags,
357                                                 addr_t fd, addr_t offset) {
358   uint64_t flags_platform = 0;
359 
360   if (flags & eMmapFlagsPrivate)
361     flags_platform |= MAP_PRIVATE;
362   if (flags & eMmapFlagsAnon)
363     flags_platform |= MAP_ANON;
364 
365   MmapArgList args({addr, length, prot, flags_platform, fd, offset});
366   return args;
367 }
368