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/Error.h" 26 #include "lldb/Core/Module.h" 27 #include "lldb/Core/ModuleSpec.h" 28 #include "lldb/Core/PluginManager.h" 29 #include "lldb/Host/Host.h" 30 #include "lldb/Host/HostInfo.h" 31 #include "lldb/Target/Process.h" 32 33 using namespace lldb; 34 using namespace lldb_private; 35 using namespace lldb_private::platform_freebsd; 36 37 PlatformSP PlatformFreeBSD::CreateInstance(bool force, const ArchSpec *arch) { 38 // The only time we create an instance is when we are creating a remote 39 // freebsd platform 40 const bool is_host = false; 41 42 bool create = force; 43 if (create == false && arch && arch->IsValid()) { 44 const llvm::Triple &triple = arch->GetTriple(); 45 switch (triple.getOS()) { 46 case llvm::Triple::FreeBSD: 47 create = true; 48 break; 49 50 #if defined(__FreeBSD__) || defined(__OpenBSD__) 51 // Only accept "unknown" for the OS if the host is BSD and 52 // it "unknown" wasn't specified (it was just returned because it 53 // was NOT specified) 54 case llvm::Triple::OSType::UnknownOS: 55 create = !arch->TripleOSWasSpecified(); 56 break; 57 #endif 58 default: 59 break; 60 } 61 } 62 if (create) 63 return PlatformSP(new PlatformFreeBSD(is_host)); 64 return PlatformSP(); 65 } 66 67 ConstString PlatformFreeBSD::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-freebsd"); 73 return g_remote_name; 74 } 75 } 76 77 const char *PlatformFreeBSD::GetDescriptionStatic(bool is_host) { 78 if (is_host) 79 return "Local FreeBSD user platform plug-in."; 80 else 81 return "Remote FreeBSD user platform plug-in."; 82 } 83 84 static uint32_t g_initialize_count = 0; 85 86 void PlatformFreeBSD::Initialize() { 87 Platform::Initialize(); 88 89 if (g_initialize_count++ == 0) { 90 #if defined(__FreeBSD__) 91 // Force a host flag to true for the default platform object. 92 PlatformSP default_platform_sp(new PlatformFreeBSD(true)); 93 default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); 94 Platform::SetHostPlatform(default_platform_sp); 95 #endif 96 PluginManager::RegisterPlugin(PlatformFreeBSD::GetPluginNameStatic(false), 97 PlatformFreeBSD::GetDescriptionStatic(false), 98 PlatformFreeBSD::CreateInstance); 99 } 100 } 101 102 void PlatformFreeBSD::Terminate() { 103 if (g_initialize_count > 0 && --g_initialize_count == 0) 104 PluginManager::UnregisterPlugin(PlatformFreeBSD::CreateInstance); 105 106 Platform::Terminate(); 107 } 108 109 bool PlatformFreeBSD::GetModuleSpec(const FileSpec &module_file_spec, 110 const ArchSpec &arch, 111 ModuleSpec &module_spec) { 112 if (m_remote_platform_sp) 113 return m_remote_platform_sp->GetModuleSpec(module_file_spec, arch, 114 module_spec); 115 116 return Platform::GetModuleSpec(module_file_spec, arch, module_spec); 117 } 118 119 Error PlatformFreeBSD::RunShellCommand(const char *command, 120 const FileSpec &working_dir, 121 int *status_ptr, int *signo_ptr, 122 std::string *command_output, 123 uint32_t timeout_sec) { 124 if (IsHost()) 125 return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, 126 command_output, timeout_sec); 127 else { 128 if (m_remote_platform_sp) 129 return m_remote_platform_sp->RunShellCommand(command, working_dir, 130 status_ptr, signo_ptr, 131 command_output, timeout_sec); 132 else 133 return Error("unable to run a remote command without a platform"); 134 } 135 } 136 137 Error PlatformFreeBSD::ResolveExecutable( 138 const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, 139 const FileSpecList *module_search_paths_ptr) { 140 Error error; 141 // Nothing special to do here, just use the actual file and architecture 142 143 char exe_path[PATH_MAX]; 144 ModuleSpec resolved_module_spec(module_spec); 145 146 if (IsHost()) { 147 // If we have "ls" as the module_spec's file, resolve the executable 148 // location based on 149 // the current path variables 150 if (!resolved_module_spec.GetFileSpec().Exists()) { 151 module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); 152 resolved_module_spec.GetFileSpec().SetFile(exe_path, true); 153 } 154 155 if (!resolved_module_spec.GetFileSpec().Exists()) 156 resolved_module_spec.GetFileSpec().ResolveExecutableLocation(); 157 158 if (resolved_module_spec.GetFileSpec().Exists()) 159 error.Clear(); 160 else { 161 error.SetErrorStringWithFormat( 162 "unable to find executable for '%s'", 163 resolved_module_spec.GetFileSpec().GetPath().c_str()); 164 } 165 } else { 166 if (m_remote_platform_sp) { 167 error = 168 GetCachedExecutable(resolved_module_spec, exe_module_sp, 169 module_search_paths_ptr, *m_remote_platform_sp); 170 } else { 171 // We may connect to a process and use the provided executable (Don't use 172 // local $PATH). 173 174 // Resolve any executable within a bundle on MacOSX 175 Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); 176 177 if (resolved_module_spec.GetFileSpec().Exists()) { 178 error.Clear(); 179 } else { 180 error.SetErrorStringWithFormat( 181 "the platform is not currently connected, and '%s' doesn't exist " 182 "in the system root.", 183 resolved_module_spec.GetFileSpec().GetPath().c_str()); 184 } 185 } 186 } 187 188 if (error.Success()) { 189 if (resolved_module_spec.GetArchitecture().IsValid()) { 190 error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, 191 module_search_paths_ptr, NULL, NULL); 192 193 if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) { 194 exe_module_sp.reset(); 195 error.SetErrorStringWithFormat( 196 "'%s' doesn't contain the architecture %s", 197 resolved_module_spec.GetFileSpec().GetPath().c_str(), 198 resolved_module_spec.GetArchitecture().GetArchitectureName()); 199 } 200 } else { 201 // No valid architecture was specified, ask the platform for 202 // the architectures that we should be using (in the correct order) 203 // and see if we can find a match that way 204 StreamString arch_names; 205 for (uint32_t idx = 0; GetSupportedArchitectureAtIndex( 206 idx, resolved_module_spec.GetArchitecture()); 207 ++idx) { 208 error = 209 ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, 210 module_search_paths_ptr, NULL, NULL); 211 // Did we find an executable using one of the 212 if (error.Success()) { 213 if (exe_module_sp && exe_module_sp->GetObjectFile()) 214 break; 215 else 216 error.SetErrorToGenericError(); 217 } 218 219 if (idx > 0) 220 arch_names.PutCString(", "); 221 arch_names.PutCString( 222 resolved_module_spec.GetArchitecture().GetArchitectureName()); 223 } 224 225 if (error.Fail() || !exe_module_sp) { 226 if (resolved_module_spec.GetFileSpec().Readable()) { 227 error.SetErrorStringWithFormat( 228 "'%s' doesn't contain any '%s' platform architectures: %s", 229 resolved_module_spec.GetFileSpec().GetPath().c_str(), 230 GetPluginName().GetCString(), arch_names.GetData()); 231 } else { 232 error.SetErrorStringWithFormat( 233 "'%s' is not readable", 234 resolved_module_spec.GetFileSpec().GetPath().c_str()); 235 } 236 } 237 } 238 } 239 240 return error; 241 } 242 243 // From PlatformMacOSX only 244 Error PlatformFreeBSD::GetFileWithUUID(const FileSpec &platform_file, 245 const UUID *uuid_ptr, 246 FileSpec &local_file) { 247 if (IsRemote()) { 248 if (m_remote_platform_sp) 249 return m_remote_platform_sp->GetFileWithUUID(platform_file, uuid_ptr, 250 local_file); 251 } 252 253 // Default to the local case 254 local_file = platform_file; 255 return Error(); 256 } 257 258 //------------------------------------------------------------------ 259 /// Default Constructor 260 //------------------------------------------------------------------ 261 PlatformFreeBSD::PlatformFreeBSD(bool is_host) 262 : Platform(is_host), m_remote_platform_sp() {} 263 264 //------------------------------------------------------------------ 265 /// Destructor. 266 /// 267 /// The destructor is virtual since this class is designed to be 268 /// inherited from by the plug-in instance. 269 //------------------------------------------------------------------ 270 PlatformFreeBSD::~PlatformFreeBSD() {} 271 272 // TODO:VK: inherit PlatformPOSIX 273 274 bool PlatformFreeBSD::GetRemoteOSVersion() { 275 if (m_remote_platform_sp) 276 return m_remote_platform_sp->GetOSVersion( 277 m_major_os_version, m_minor_os_version, m_update_os_version); 278 return false; 279 } 280 281 bool PlatformFreeBSD::GetRemoteOSBuildString(std::string &s) { 282 if (m_remote_platform_sp) 283 return m_remote_platform_sp->GetRemoteOSBuildString(s); 284 s.clear(); 285 return false; 286 } 287 288 bool PlatformFreeBSD::GetRemoteOSKernelDescription(std::string &s) { 289 if (m_remote_platform_sp) 290 return m_remote_platform_sp->GetRemoteOSKernelDescription(s); 291 s.clear(); 292 return false; 293 } 294 295 // Remote Platform subclasses need to override this function 296 ArchSpec PlatformFreeBSD::GetRemoteSystemArchitecture() { 297 if (m_remote_platform_sp) 298 return m_remote_platform_sp->GetRemoteSystemArchitecture(); 299 return ArchSpec(); 300 } 301 302 const char *PlatformFreeBSD::GetHostname() { 303 if (IsHost()) 304 return Platform::GetHostname(); 305 306 if (m_remote_platform_sp) 307 return m_remote_platform_sp->GetHostname(); 308 return NULL; 309 } 310 311 bool PlatformFreeBSD::IsConnected() const { 312 if (IsHost()) 313 return true; 314 else if (m_remote_platform_sp) 315 return m_remote_platform_sp->IsConnected(); 316 return false; 317 } 318 319 Error PlatformFreeBSD::ConnectRemote(Args &args) { 320 Error error; 321 if (IsHost()) { 322 error.SetErrorStringWithFormat( 323 "can't connect to the host platform '%s', always connected", 324 GetPluginName().GetCString()); 325 } else { 326 if (!m_remote_platform_sp) 327 m_remote_platform_sp = 328 Platform::Create(ConstString("remote-gdb-server"), error); 329 330 if (m_remote_platform_sp) { 331 if (error.Success()) { 332 if (m_remote_platform_sp) { 333 error = m_remote_platform_sp->ConnectRemote(args); 334 } else { 335 error.SetErrorString( 336 "\"platform connect\" takes a single argument: <connect-url>"); 337 } 338 } 339 } else 340 error.SetErrorString("failed to create a 'remote-gdb-server' platform"); 341 342 if (error.Fail()) 343 m_remote_platform_sp.reset(); 344 } 345 346 return error; 347 } 348 349 Error PlatformFreeBSD::DisconnectRemote() { 350 Error error; 351 352 if (IsHost()) { 353 error.SetErrorStringWithFormat( 354 "can't disconnect from the host platform '%s', always connected", 355 GetPluginName().GetCString()); 356 } else { 357 if (m_remote_platform_sp) 358 error = m_remote_platform_sp->DisconnectRemote(); 359 else 360 error.SetErrorString("the platform is not currently connected"); 361 } 362 return error; 363 } 364 365 bool PlatformFreeBSD::GetProcessInfo(lldb::pid_t pid, 366 ProcessInstanceInfo &process_info) { 367 bool success = false; 368 if (IsHost()) { 369 success = Platform::GetProcessInfo(pid, process_info); 370 } else if (m_remote_platform_sp) { 371 success = m_remote_platform_sp->GetProcessInfo(pid, process_info); 372 } 373 return success; 374 } 375 376 uint32_t 377 PlatformFreeBSD::FindProcesses(const ProcessInstanceInfoMatch &match_info, 378 ProcessInstanceInfoList &process_infos) { 379 uint32_t match_count = 0; 380 if (IsHost()) { 381 // Let the base class figure out the host details 382 match_count = Platform::FindProcesses(match_info, process_infos); 383 } else { 384 // If we are remote, we can only return results if we are connected 385 if (m_remote_platform_sp) 386 match_count = 387 m_remote_platform_sp->FindProcesses(match_info, process_infos); 388 } 389 return match_count; 390 } 391 392 const char *PlatformFreeBSD::GetUserName(uint32_t uid) { 393 // Check the cache in Platform in case we have already looked this uid up 394 const char *user_name = Platform::GetUserName(uid); 395 if (user_name) 396 return user_name; 397 398 if (IsRemote() && m_remote_platform_sp) 399 return m_remote_platform_sp->GetUserName(uid); 400 return NULL; 401 } 402 403 const char *PlatformFreeBSD::GetGroupName(uint32_t gid) { 404 const char *group_name = Platform::GetGroupName(gid); 405 if (group_name) 406 return group_name; 407 408 if (IsRemote() && m_remote_platform_sp) 409 return m_remote_platform_sp->GetGroupName(gid); 410 return NULL; 411 } 412 413 Error PlatformFreeBSD::GetSharedModule( 414 const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, 415 const FileSpecList *module_search_paths_ptr, ModuleSP *old_module_sp_ptr, 416 bool *did_create_ptr) { 417 Error error; 418 module_sp.reset(); 419 420 if (IsRemote()) { 421 // If we have a remote platform always, let it try and locate 422 // the shared module first. 423 if (m_remote_platform_sp) { 424 error = m_remote_platform_sp->GetSharedModule( 425 module_spec, process, module_sp, module_search_paths_ptr, 426 old_module_sp_ptr, did_create_ptr); 427 } 428 } 429 430 if (!module_sp) { 431 // Fall back to the local platform and find the file locally 432 error = Platform::GetSharedModule(module_spec, process, module_sp, 433 module_search_paths_ptr, 434 old_module_sp_ptr, did_create_ptr); 435 } 436 if (module_sp) 437 module_sp->SetPlatformFileSpec(module_spec.GetFileSpec()); 438 return error; 439 } 440 441 bool PlatformFreeBSD::GetSupportedArchitectureAtIndex(uint32_t idx, 442 ArchSpec &arch) { 443 if (IsHost()) { 444 ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); 445 if (hostArch.GetTriple().isOSFreeBSD()) { 446 if (idx == 0) { 447 arch = hostArch; 448 return arch.IsValid(); 449 } else if (idx == 1) { 450 // If the default host architecture is 64-bit, look for a 32-bit variant 451 if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) { 452 arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); 453 return arch.IsValid(); 454 } 455 } 456 } 457 } else { 458 if (m_remote_platform_sp) 459 return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch); 460 461 llvm::Triple triple; 462 // Set the OS to FreeBSD 463 triple.setOS(llvm::Triple::FreeBSD); 464 // Set the architecture 465 switch (idx) { 466 case 0: 467 triple.setArchName("x86_64"); 468 break; 469 case 1: 470 triple.setArchName("i386"); 471 break; 472 case 2: 473 triple.setArchName("aarch64"); 474 break; 475 case 3: 476 triple.setArchName("arm"); 477 break; 478 case 4: 479 triple.setArchName("mips64"); 480 break; 481 case 5: 482 triple.setArchName("mips"); 483 break; 484 case 6: 485 triple.setArchName("ppc64"); 486 break; 487 case 7: 488 triple.setArchName("ppc"); 489 break; 490 default: 491 return false; 492 } 493 // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the 494 // vendor by 495 // calling triple.SetVendorName("unknown") so that it is a "unspecified 496 // unknown". 497 // This means when someone calls triple.GetVendorName() it will return an 498 // empty string 499 // which indicates that the vendor can be set when two architectures are 500 // merged 501 502 // Now set the triple into "arch" and return true 503 arch.SetTriple(triple); 504 return true; 505 } 506 return false; 507 } 508 509 void PlatformFreeBSD::GetStatus(Stream &strm) { 510 #ifndef LLDB_DISABLE_POSIX 511 struct utsname un; 512 513 strm << " Host: "; 514 515 ::memset(&un, 0, sizeof(utsname)); 516 if (uname(&un) == -1) 517 strm << "FreeBSD" << '\n'; 518 519 strm << un.sysname << ' ' << un.release; 520 if (un.nodename[0] != '\0') 521 strm << " (" << un.nodename << ')'; 522 strm << '\n'; 523 524 // Dump a common information about the platform status. 525 strm << "Host: " << un.sysname << ' ' << un.release << ' ' << un.version 526 << '\n'; 527 #endif 528 529 Platform::GetStatus(strm); 530 } 531 532 size_t 533 PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode(Target &target, 534 BreakpointSite *bp_site) { 535 switch (target.GetArchitecture().GetMachine()) { 536 case llvm::Triple::arm: { 537 lldb::BreakpointLocationSP bp_loc_sp(bp_site->GetOwnerAtIndex(0)); 538 AddressClass addr_class = eAddressClassUnknown; 539 540 if (bp_loc_sp) { 541 addr_class = bp_loc_sp->GetAddress().GetAddressClass(); 542 if (addr_class == eAddressClassUnknown && 543 (bp_loc_sp->GetAddress().GetFileAddress() & 1)) 544 addr_class = eAddressClassCodeAlternateISA; 545 } 546 547 if (addr_class == eAddressClassCodeAlternateISA) { 548 // TODO: Enable when FreeBSD supports thumb breakpoints. 549 // FreeBSD kernel as of 10.x, does not support thumb breakpoints 550 return 0; 551 } 552 } 553 LLVM_FALLTHROUGH; 554 default: 555 return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site); 556 } 557 } 558 559 void PlatformFreeBSD::CalculateTrapHandlerSymbolNames() { 560 m_trap_handlers.push_back(ConstString("_sigtramp")); 561 } 562 563 Error PlatformFreeBSD::LaunchProcess(ProcessLaunchInfo &launch_info) { 564 Error error; 565 if (IsHost()) { 566 error = Platform::LaunchProcess(launch_info); 567 } else { 568 if (m_remote_platform_sp) 569 error = m_remote_platform_sp->LaunchProcess(launch_info); 570 else 571 error.SetErrorString("the platform is not currently connected"); 572 } 573 return error; 574 } 575 576 lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, 577 Debugger &debugger, Target *target, 578 Error &error) { 579 lldb::ProcessSP process_sp; 580 if (IsHost()) { 581 if (target == NULL) { 582 TargetSP new_target_sp; 583 ArchSpec emptyArchSpec; 584 585 error = debugger.GetTargetList().CreateTarget(debugger, "", emptyArchSpec, 586 false, m_remote_platform_sp, 587 new_target_sp); 588 target = new_target_sp.get(); 589 } else 590 error.Clear(); 591 592 if (target && error.Success()) { 593 debugger.GetTargetList().SetSelectedTarget(target); 594 // The freebsd always currently uses the GDB remote debugger plug-in 595 // so even when debugging locally we are debugging remotely! 596 // Just like the darwin plugin. 597 process_sp = target->CreateProcess( 598 attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL); 599 600 if (process_sp) 601 error = process_sp->Attach(attach_info); 602 } 603 } else { 604 if (m_remote_platform_sp) 605 process_sp = 606 m_remote_platform_sp->Attach(attach_info, debugger, target, error); 607 else 608 error.SetErrorString("the platform is not currently connected"); 609 } 610 return process_sp; 611 } 612