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