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