//===-- PlatformLinux.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PlatformLinux.h" #include "lldb/Host/Config.h" // C Includes #include #ifndef LLDB_DISABLE_POSIX #include #endif // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" #include "lldb/Target/Target.h" #include "lldb/Target/Process.h" #if defined(__linux__) #include "../../Process/Linux/NativeProcessLinux.h" #endif // Define these constants from Linux mman.h for use when targeting // remote linux systems even when host has different values. #define MAP_PRIVATE 2 #define MAP_ANON 0x20 using namespace lldb; using namespace lldb_private; using namespace lldb_private::platform_linux; static uint32_t g_initialize_count = 0; //------------------------------------------------------------------ /// Code to handle the PlatformLinux settings //------------------------------------------------------------------ namespace { class PlatformLinuxProperties : public Properties { public: static ConstString& GetSettingName (); PlatformLinuxProperties(); virtual ~PlatformLinuxProperties() = default; private: static const PropertyDefinition* GetStaticPropertyDefinitions(); }; typedef std::shared_ptr PlatformLinuxPropertiesSP; } // anonymous namespace PlatformLinuxProperties::PlatformLinuxProperties() : Properties () { m_collection_sp.reset (new OptionValueProperties(GetSettingName ())); m_collection_sp->Initialize (GetStaticPropertyDefinitions ()); } ConstString& PlatformLinuxProperties::GetSettingName () { static ConstString g_setting_name("linux"); return g_setting_name; } const PropertyDefinition* PlatformLinuxProperties::GetStaticPropertyDefinitions() { static PropertyDefinition g_properties[] = { { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL } }; return g_properties; } static const PlatformLinuxPropertiesSP & GetGlobalProperties() { static PlatformLinuxPropertiesSP g_settings_sp; if (!g_settings_sp) g_settings_sp.reset (new PlatformLinuxProperties ()); return g_settings_sp; } void PlatformLinux::DebuggerInitialize (Debugger &debugger) { if (!PluginManager::GetSettingForPlatformPlugin (debugger, PlatformLinuxProperties::GetSettingName())) { const bool is_global_setting = true; PluginManager::CreateSettingForPlatformPlugin (debugger, GetGlobalProperties()->GetValueProperties(), ConstString ("Properties for the PlatformLinux plug-in."), is_global_setting); } } //------------------------------------------------------------------ PlatformSP PlatformLinux::CreateInstance (bool force, const ArchSpec *arch) { Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); if (log) { const char *arch_name; if (arch && arch->GetArchitectureName ()) arch_name = arch->GetArchitectureName (); else arch_name = ""; const char *triple_cstr = arch ? arch->GetTriple ().getTriple ().c_str() : ""; log->Printf ("PlatformLinux::%s(force=%s, arch={%s,%s})", __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); } bool create = force; if (create == false && arch && arch->IsValid()) { const llvm::Triple &triple = arch->GetTriple(); switch (triple.getOS()) { case llvm::Triple::Linux: create = true; break; #if defined(__linux__) // Only accept "unknown" for the OS if the host is linux and // it "unknown" wasn't specified (it was just returned because it // was NOT specified) case llvm::Triple::OSType::UnknownOS: create = !arch->TripleOSWasSpecified(); break; #endif default: break; } } if (create) { if (log) log->Printf ("PlatformLinux::%s() creating remote-linux platform", __FUNCTION__); return PlatformSP(new PlatformLinux(false)); } if (log) log->Printf ("PlatformLinux::%s() aborting creation of remote-linux platform", __FUNCTION__); return PlatformSP(); } ConstString PlatformLinux::GetPluginNameStatic (bool is_host) { if (is_host) { static ConstString g_host_name(Platform::GetHostPlatformName ()); return g_host_name; } else { static ConstString g_remote_name("remote-linux"); return g_remote_name; } } const char * PlatformLinux::GetPluginDescriptionStatic (bool is_host) { if (is_host) return "Local Linux user platform plug-in."; else return "Remote Linux user platform plug-in."; } ConstString PlatformLinux::GetPluginName() { return GetPluginNameStatic(IsHost()); } void PlatformLinux::Initialize () { PlatformPOSIX::Initialize (); if (g_initialize_count++ == 0) { #if defined(__linux__) && !defined(__ANDROID__) PlatformSP default_platform_sp (new PlatformLinux(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); Platform::SetHostPlatform (default_platform_sp); #endif PluginManager::RegisterPlugin(PlatformLinux::GetPluginNameStatic(false), PlatformLinux::GetPluginDescriptionStatic(false), PlatformLinux::CreateInstance, PlatformLinux::DebuggerInitialize); } } void PlatformLinux::Terminate () { if (g_initialize_count > 0) { if (--g_initialize_count == 0) { PluginManager::UnregisterPlugin (PlatformLinux::CreateInstance); } } PlatformPOSIX::Terminate (); } Error PlatformLinux::ResolveExecutable (const ModuleSpec &ms, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Error error; // Nothing special to do here, just use the actual file and architecture char exe_path[PATH_MAX]; ModuleSpec resolved_module_spec (ms); if (IsHost()) { // If we have "ls" as the exe_file, resolve the executable location based on // the current path variables if (!resolved_module_spec.GetFileSpec().Exists()) { resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); resolved_module_spec.GetFileSpec().SetFile(exe_path, true); } if (!resolved_module_spec.GetFileSpec().Exists()) resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); if (resolved_module_spec.GetFileSpec().Exists()) error.Clear(); else { error.SetErrorStringWithFormat("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } else { if (m_remote_platform_sp) { error = GetCachedExecutable (resolved_module_spec, exe_module_sp, nullptr, *m_remote_platform_sp); } else { // We may connect to a process and use the provided executable (Don't use local $PATH). if (resolved_module_spec.GetFileSpec().Exists()) error.Clear(); else error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); } } if (error.Success()) { if (resolved_module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule (resolved_module_spec, exe_module_sp, NULL, NULL, NULL); if (error.Fail()) { // If we failed, it may be because the vendor and os aren't known. If that is the // case, try setting them to the host architecture and give it another try. llvm::Triple &module_triple = resolved_module_spec.GetArchitecture().GetTriple(); bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); if (!is_vendor_specified || !is_os_specified) { const llvm::Triple &host_triple = HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); if (!is_vendor_specified) module_triple.setVendorName (host_triple.getVendorName()); if (!is_os_specified) module_triple.setOSName (host_triple.getOSName()); error = ModuleList::GetSharedModule (resolved_module_spec, exe_module_sp, NULL, NULL, NULL); } } // TODO find out why exe_module_sp might be NULL if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) { exe_module_sp.reset(); error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), resolved_module_spec.GetArchitecture().GetArchitectureName()); } } else { // No valid architecture was specified, ask the platform for // the architectures that we should be using (in the correct order) // and see if we can find a match that way StreamString arch_names; for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) { error = ModuleList::GetSharedModule (resolved_module_spec, exe_module_sp, NULL, NULL, NULL); // Did we find an executable using one of the if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; else error.SetErrorToGenericError(); } if (idx > 0) arch_names.PutCString (", "); arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); } if (error.Fail() || !exe_module_sp) { if (resolved_module_spec.GetFileSpec().Readable()) { error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", resolved_module_spec.GetFileSpec().GetPath().c_str(), GetPluginName().GetCString(), arch_names.GetString().c_str()); } else { error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } } return error; } Error PlatformLinux::GetFileWithUUID (const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { if (IsRemote()) { if (m_remote_platform_sp) return m_remote_platform_sp->GetFileWithUUID (platform_file, uuid_ptr, local_file); } // Default to the local case local_file = platform_file; return Error(); } //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ PlatformLinux::PlatformLinux (bool is_host) : PlatformPOSIX(is_host) // This is the local host platform { } //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ PlatformLinux::~PlatformLinux() { } bool PlatformLinux::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) { bool success = false; if (IsHost()) { success = Platform::GetProcessInfo (pid, process_info); } else { if (m_remote_platform_sp) success = m_remote_platform_sp->GetProcessInfo (pid, process_info); } return success; } uint32_t PlatformLinux::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) { uint32_t match_count = 0; if (IsHost()) { // Let the base class figure out the host details match_count = Platform::FindProcesses (match_info, process_infos); } else { // If we are remote, we can only return results if we are connected if (m_remote_platform_sp) match_count = m_remote_platform_sp->FindProcesses (match_info, process_infos); } return match_count; } bool PlatformLinux::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) { if (IsHost()) { ArchSpec hostArch = HostInfo::GetArchitecture(HostInfo::eArchKindDefault); if (hostArch.GetTriple().isOSLinux()) { if (idx == 0) { arch = hostArch; return arch.IsValid(); } else if (idx == 1) { // If the default host architecture is 64-bit, look for a 32-bit variant if (hostArch.IsValid() && hostArch.GetTriple().isArch64Bit()) { arch = HostInfo::GetArchitecture(HostInfo::eArchKind32); return arch.IsValid(); } } } } else { if (m_remote_platform_sp) return m_remote_platform_sp->GetSupportedArchitectureAtIndex(idx, arch); llvm::Triple triple; // Set the OS to linux triple.setOS(llvm::Triple::Linux); // Set the architecture switch (idx) { case 0: triple.setArchName("x86_64"); break; case 1: triple.setArchName("i386"); break; case 2: triple.setArchName("arm"); break; case 3: triple.setArchName("aarch64"); break; case 4: triple.setArchName("mips64"); break; case 5: triple.setArchName("hexagon"); break; case 6: triple.setArchName("mips"); break; case 7: triple.setArchName("mips64el"); break; case 8: triple.setArchName("mipsel"); break; default: return false; } // Leave the vendor as "llvm::Triple:UnknownVendor" and don't specify the vendor by // calling triple.SetVendorName("unknown") so that it is a "unspecified unknown". // This means when someone calls triple.GetVendorName() it will return an empty string // which indicates that the vendor can be set when two architectures are merged // Now set the triple into "arch" and return true arch.SetTriple(triple); return true; } return false; } void PlatformLinux::GetStatus (Stream &strm) { Platform::GetStatus(strm); #ifndef LLDB_DISABLE_POSIX // Display local kernel information only when we are running in host mode. // Otherwise, we would end up printing non-Linux information (when running // on Mac OS for example). if (IsHost()) { struct utsname un; if (uname(&un)) return; strm.Printf (" Kernel: %s\n", un.sysname); strm.Printf (" Release: %s\n", un.release); strm.Printf (" Version: %s\n", un.version); } #endif } size_t PlatformLinux::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite *bp_site) { ArchSpec arch = target.GetArchitecture(); const uint8_t *trap_opcode = NULL; size_t trap_opcode_size = 0; switch (arch.GetMachine()) { default: assert(false && "CPU type not supported!"); break; case llvm::Triple::aarch64: { static const uint8_t g_aarch64_opcode[] = { 0x00, 0x00, 0x20, 0xd4 }; trap_opcode = g_aarch64_opcode; trap_opcode_size = sizeof(g_aarch64_opcode); } break; case llvm::Triple::x86: case llvm::Triple::x86_64: { static const uint8_t g_i386_breakpoint_opcode[] = { 0xCC }; trap_opcode = g_i386_breakpoint_opcode; trap_opcode_size = sizeof(g_i386_breakpoint_opcode); } break; case llvm::Triple::hexagon: { static const uint8_t g_hex_opcode[] = { 0x0c, 0xdb, 0x00, 0x54 }; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::arm: { // The ARM reference recommends the use of 0xe7fddefe and 0xdefe // but the linux kernel does otherwise. static const uint8_t g_arm_breakpoint_opcode[] = { 0xf0, 0x01, 0xf0, 0xe7 }; static const uint8_t g_thumb_breakpoint_opcode[] = { 0x01, 0xde }; lldb::BreakpointLocationSP bp_loc_sp (bp_site->GetOwnerAtIndex (0)); AddressClass addr_class = eAddressClassUnknown; if (bp_loc_sp) addr_class = bp_loc_sp->GetAddress ().GetAddressClass (); if (addr_class == eAddressClassCodeAlternateISA || (addr_class == eAddressClassUnknown && (bp_site->GetLoadAddress() & 1))) { trap_opcode = g_thumb_breakpoint_opcode; trap_opcode_size = sizeof(g_thumb_breakpoint_opcode); } else { trap_opcode = g_arm_breakpoint_opcode; trap_opcode_size = sizeof(g_arm_breakpoint_opcode); } } break; case llvm::Triple::mips: case llvm::Triple::mips64: { static const uint8_t g_hex_opcode[] = { 0x00, 0x00, 0x00, 0x0d }; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; case llvm::Triple::mipsel: case llvm::Triple::mips64el: { static const uint8_t g_hex_opcode[] = { 0x0d, 0x00, 0x00, 0x00 }; trap_opcode = g_hex_opcode; trap_opcode_size = sizeof(g_hex_opcode); } break; } if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) return trap_opcode_size; return 0; } int32_t PlatformLinux::GetResumeCountForLaunchInfo (ProcessLaunchInfo &launch_info) { int32_t resume_count = 0; // Always resume past the initial stop when we use eLaunchFlagDebug if (launch_info.GetFlags ().Test (eLaunchFlagDebug)) { // Resume past the stop for the final exec into the true inferior. ++resume_count; } // If we're not launching a shell, we're done. const FileSpec &shell = launch_info.GetShell(); if (!shell) return resume_count; std::string shell_string = shell.GetPath(); // We're in a shell, so for sure we have to resume past the shell exec. ++resume_count; // Figure out what shell we're planning on using. const char *shell_name = strrchr (shell_string.c_str(), '/'); if (shell_name == NULL) shell_name = shell_string.c_str(); else shell_name++; if (strcmp (shell_name, "csh") == 0 || strcmp (shell_name, "tcsh") == 0 || strcmp (shell_name, "zsh") == 0 || strcmp (shell_name, "sh") == 0) { // These shells seem to re-exec themselves. Add another resume. ++resume_count; } return resume_count; } bool PlatformLinux::CanDebugProcess () { if (IsHost ()) { return true; } else { // If we're connected, we can debug. return IsConnected (); } } // For local debugging, Linux will override the debug logic to use llgs-launch rather than // lldb-launch, llgs-attach. This differs from current lldb-launch, debugserver-attach // approach on MacOSX. lldb::ProcessSP PlatformLinux::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one Error &error) { Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); if (log) log->Printf ("PlatformLinux::%s entered (target %p)", __FUNCTION__, static_cast(target)); // If we're a remote host, use standard behavior from parent class. if (!IsHost ()) return PlatformPOSIX::DebugProcess (launch_info, debugger, target, error); // // For local debugging, we'll insist on having ProcessGDBRemote create the process. // ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags ().Set (eLaunchFlagDebug); // We always launch the process we are going to debug in a separate process // group, since then we can handle ^C interrupts ourselves w/o having to worry // about the target getting them as well. launch_info.SetLaunchInSeparateProcessGroup(true); // Ensure we have a target. if (target == nullptr) { if (log) log->Printf ("PlatformLinux::%s creating new target", __FUNCTION__); TargetSP new_target_sp; error = debugger.GetTargetList().CreateTarget (debugger, nullptr, nullptr, false, nullptr, new_target_sp); if (error.Fail ()) { if (log) log->Printf ("PlatformLinux::%s failed to create new target: %s", __FUNCTION__, error.AsCString ()); return process_sp; } target = new_target_sp.get(); if (!target) { error.SetErrorString ("CreateTarget() returned nullptr"); if (log) log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); return process_sp; } } else { if (log) log->Printf ("PlatformLinux::%s using provided target", __FUNCTION__); } // Mark target as currently selected target. debugger.GetTargetList().SetSelectedTarget(target); // Now create the gdb-remote process. if (log) log->Printf ("PlatformLinux::%s having target create process with gdb-remote plugin", __FUNCTION__); process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", nullptr); if (!process_sp) { error.SetErrorString ("CreateProcess() failed for gdb-remote process"); if (log) log->Printf ("PlatformLinux::%s failed: %s", __FUNCTION__, error.AsCString ()); return process_sp; } else { if (log) log->Printf ("PlatformLinux::%s successfully created process", __FUNCTION__); } // Set the unix signals properly. process_sp->SetUnixSignals (Host::GetUnixSignals ()); // Adjust launch for a hijacker. ListenerSP listener_sp; if (!launch_info.GetHijackListener ()) { if (log) log->Printf ("PlatformLinux::%s setting up hijacker", __FUNCTION__); listener_sp.reset (new Listener("lldb.PlatformLinux.DebugProcess.hijack")); launch_info.SetHijackListener (listener_sp); process_sp->HijackProcessEvents (listener_sp.get ()); } // Log file actions. if (log) { log->Printf ("PlatformLinux::%s launching process with the following file actions:", __FUNCTION__); StreamString stream; size_t i = 0; const FileAction *file_action; while ((file_action = launch_info.GetFileActionAtIndex (i++)) != nullptr) { file_action->Dump (stream); log->PutCString (stream.GetString().c_str ()); stream.Clear(); } } // Do the launch. error = process_sp->Launch(launch_info); if (error.Success ()) { // Handle the hijacking of process events. if (listener_sp) { const StateType state = process_sp->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); if (state == eStateStopped) { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " state %s\n", __FUNCTION__, process_sp->GetID (), StateAsCString (state)); } else { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " state is not stopped - %s\n", __FUNCTION__, process_sp->GetID (), StateAsCString (state)); } } // Hook up process PTY if we have one (which we should for local debugging with llgs). int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor(); if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd) { process_sp->SetSTDIOFileDescriptor(pty_fd); if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " hooked up STDIO pty to process", __FUNCTION__, process_sp->GetID ()); } else { if (log) log->Printf ("PlatformLinux::%s pid %" PRIu64 " not using process STDIO pty", __FUNCTION__, process_sp->GetID ()); } } else { if (log) log->Printf ("PlatformLinux::%s process launch failed: %s", __FUNCTION__, error.AsCString ()); // FIXME figure out appropriate cleanup here. Do we delete the target? Do we delete the process? Does our caller do that? } return process_sp; } void PlatformLinux::CalculateTrapHandlerSymbolNames () { m_trap_handlers.push_back (ConstString ("_sigtramp")); } Error PlatformLinux::LaunchNativeProcess (ProcessLaunchInfo &launch_info, NativeProcessProtocol::NativeDelegate &native_delegate, NativeProcessProtocolSP &process_sp) { #if !defined(__linux__) return Error("Only implemented on Linux hosts"); #else if (!IsHost ()) return Error("PlatformLinux::%s (): cannot launch a debug process when not the host", __FUNCTION__); // Retrieve the exe module. lldb::ModuleSP exe_module_sp; ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); Error error = ResolveExecutable ( exe_module_spec, exe_module_sp, NULL); if (!error.Success ()) return error; if (!exe_module_sp) return Error("exe_module_sp could not be resolved for %s", launch_info.GetExecutableFile ().GetPath ().c_str ()); // Launch it for debugging error = process_linux::NativeProcessLinux::LaunchProcess ( exe_module_sp.get (), launch_info, native_delegate, process_sp); return error; #endif } Error PlatformLinux::AttachNativeProcess (lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, NativeProcessProtocolSP &process_sp) { #if !defined(__linux__) return Error("Only implemented on Linux hosts"); #else if (!IsHost ()) return Error("PlatformLinux::%s (): cannot attach to a debug process when not the host", __FUNCTION__); // Launch it for debugging return process_linux::NativeProcessLinux::AttachToProcess (pid, native_delegate, process_sp); #endif } uint64_t PlatformLinux::ConvertMmapFlagsToPlatform(const ArchSpec &arch, unsigned flags) { uint64_t flags_platform = 0; uint64_t map_anon = MAP_ANON; // To get correct flags for MIPS Architecture if (arch.GetTriple ().getArch () == llvm::Triple::mips64 || arch.GetTriple ().getArch () == llvm::Triple::mips64el || arch.GetTriple ().getArch () == llvm::Triple::mips || arch.GetTriple ().getArch () == llvm::Triple::mipsel) map_anon = 0x800; if (flags & eMmapFlagsPrivate) flags_platform |= MAP_PRIVATE; if (flags & eMmapFlagsAnon) flags_platform |= map_anon; return flags_platform; }