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