llvm-project/lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp

746 lines
22 KiB
C++

//===-- PlatformWindows.cpp ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "PlatformWindows.h"
// C Includes
#include <stdio.h>
#if defined (_WIN32)
#include "lldb/Host/windows/windows.h"
#include <winsock2.h>
#endif
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Error.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/Module.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/BreakpointSite.h"
#include "lldb/Target/Process.h"
using namespace lldb;
using namespace lldb_private;
static uint32_t g_initialize_count = 0;
namespace
{
class SupportedArchList
{
public:
SupportedArchList()
{
AddArch(ArchSpec("i686-pc-windows"));
AddArch(HostInfo::GetArchitecture(HostInfo::eArchKindDefault));
AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind32));
AddArch(HostInfo::GetArchitecture(HostInfo::eArchKind64));
AddArch(ArchSpec("i386-pc-windows"));
}
size_t Count() const { return m_archs.size(); }
const ArchSpec& operator[](int idx) { return m_archs[idx]; }
private:
void AddArch(const ArchSpec& spec)
{
auto iter = std::find_if(
m_archs.begin(), m_archs.end(),
[spec](const ArchSpec& rhs) { return spec.IsExactMatch(rhs); });
if (iter != m_archs.end())
return;
if (spec.IsValid())
m_archs.push_back(spec);
}
std::vector<ArchSpec> m_archs;
};
}
PlatformSP
PlatformWindows::CreateInstance (bool force, const lldb_private::ArchSpec *arch)
{
// The only time we create an instance is when we are creating a remote
// windows platform
const bool is_host = false;
bool create = force;
if (create == false && arch && arch->IsValid())
{
const llvm::Triple &triple = arch->GetTriple();
switch (triple.getVendor())
{
case llvm::Triple::PC:
create = true;
break;
case llvm::Triple::UnknownArch:
create = !arch->TripleVendorWasSpecified();
break;
default:
break;
}
if (create)
{
switch (triple.getOS())
{
case llvm::Triple::Win32:
break;
case llvm::Triple::UnknownOS:
create = arch->TripleOSWasSpecified();
break;
default:
create = false;
break;
}
}
}
if (create)
return PlatformSP(new PlatformWindows (is_host));
return PlatformSP();
}
lldb_private::ConstString
PlatformWindows::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-windows");
return g_remote_name;
}
}
const char *
PlatformWindows::GetPluginDescriptionStatic(bool is_host)
{
return is_host ?
"Local Windows user platform plug-in." :
"Remote Windows user platform plug-in.";
}
lldb_private::ConstString
PlatformWindows::GetPluginName(void)
{
return GetPluginNameStatic(IsHost());
}
void
PlatformWindows::Initialize(void)
{
Platform::Initialize ();
if (g_initialize_count++ == 0)
{
#if defined (_WIN32)
WSADATA dummy;
WSAStartup(MAKEWORD(2,2), &dummy);
// Force a host flag to true for the default platform object.
PlatformSP default_platform_sp (new PlatformWindows(true));
default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
Platform::SetHostPlatform (default_platform_sp);
#endif
PluginManager::RegisterPlugin(PlatformWindows::GetPluginNameStatic(false),
PlatformWindows::GetPluginDescriptionStatic(false),
PlatformWindows::CreateInstance);
}
}
void
PlatformWindows::Terminate( void )
{
if (g_initialize_count > 0)
{
if (--g_initialize_count == 0)
{
#ifdef _WIN32
WSACleanup();
#endif
PluginManager::UnregisterPlugin (PlatformWindows::CreateInstance);
}
}
Platform::Terminate ();
}
//------------------------------------------------------------------
/// Default Constructor
//------------------------------------------------------------------
PlatformWindows::PlatformWindows (bool is_host) :
Platform(is_host)
{
}
//------------------------------------------------------------------
/// Destructor.
///
/// The destructor is virtual since this class is designed to be
/// inherited from by the plug-in instance.
//------------------------------------------------------------------
PlatformWindows::~PlatformWindows()
{
}
bool
PlatformWindows::GetModuleSpec (const FileSpec& module_file_spec,
const ArchSpec& arch,
ModuleSpec &module_spec)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetModuleSpec (module_file_spec, arch, module_spec);
return Platform::GetModuleSpec (module_file_spec, arch, module_spec);
}
Error
PlatformWindows::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 cant resolve the executable loation 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
{
ms.GetFileSpec().GetPath(exe_path, sizeof(exe_path));
error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path);
}
}
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 (!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;
}
size_t
PlatformWindows::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())
{
case llvm::Triple::x86:
case llvm::Triple::x86_64:
{
static const uint8_t g_i386_opcode[] = { 0xCC };
trap_opcode = g_i386_opcode;
trap_opcode_size = sizeof(g_i386_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;
default:
llvm_unreachable("Unhandled architecture in PlatformWindows::GetSoftwareBreakpointTrapOpcode()");
break;
}
if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
return trap_opcode_size;
return 0;
}
bool
PlatformWindows::GetRemoteOSVersion ()
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetOSVersion (m_major_os_version,
m_minor_os_version,
m_update_os_version);
return false;
}
bool
PlatformWindows::GetRemoteOSBuildString (std::string &s)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSBuildString (s);
s.clear();
return false;
}
bool
PlatformWindows::GetRemoteOSKernelDescription (std::string &s)
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteOSKernelDescription (s);
s.clear();
return false;
}
// Remote Platform subclasses need to override this function
ArchSpec
PlatformWindows::GetRemoteSystemArchitecture ()
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetRemoteSystemArchitecture ();
return ArchSpec();
}
const char *
PlatformWindows::GetHostname ()
{
if (IsHost())
return Platform::GetHostname();
if (m_remote_platform_sp)
return m_remote_platform_sp->GetHostname ();
return NULL;
}
bool
PlatformWindows::IsConnected () const
{
if (IsHost())
return true;
else if (m_remote_platform_sp)
return m_remote_platform_sp->IsConnected();
return false;
}
Error
PlatformWindows::ConnectRemote (Args& args)
{
Error error;
if (IsHost())
{
error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().AsCString() );
}
else
{
if (!m_remote_platform_sp)
m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error);
if (m_remote_platform_sp)
{
if (error.Success())
{
if (m_remote_platform_sp)
{
error = m_remote_platform_sp->ConnectRemote (args);
}
else
{
error.SetErrorString ("\"platform connect\" takes a single argument: <connect-url>");
}
}
}
else
error.SetErrorString ("failed to create a 'remote-gdb-server' platform");
if (error.Fail())
m_remote_platform_sp.reset();
}
return error;
}
Error
PlatformWindows::DisconnectRemote ()
{
Error error;
if (IsHost())
{
error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().AsCString() );
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->DisconnectRemote ();
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
bool
PlatformWindows::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
PlatformWindows::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;
}
Error
PlatformWindows::LaunchProcess (ProcessLaunchInfo &launch_info)
{
Error error;
if (IsHost())
{
error = Platform::LaunchProcess (launch_info);
}
else
{
if (m_remote_platform_sp)
error = m_remote_platform_sp->LaunchProcess (launch_info);
else
error.SetErrorString ("the platform is not currently connected");
}
return error;
}
ProcessSP
PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, Error &error)
{
// Windows has special considerations that must be followed when launching or attaching to a process. The
// key requirement is that when launching or attaching to a process, you must do it from the same the thread
// that will go into a permanent loop which will then receive debug events from the process. In particular,
// this means we can't use any of LLDB's generic mechanisms to do it for us, because it doesn't have the
// special knowledge required for setting up the background thread or passing the right flags.
//
// Another problem is that that LLDB's standard model for debugging a process is to first launch it, have
// it stop at the entry point, and then attach to it. In Windows this doesn't quite work, you have to
// specify as an argument to CreateProcess() that you're going to debug the process. So we override DebugProcess
// here to handle this. Launch operations go directly to the process plugin, and attach operations almost go
// directly to the process plugin (but we hijack the events first). In essence, we encapsulate all the logic
// of Launching and Attaching in the process plugin, and PlatformWindows::DebugProcess is just a pass-through
// to get to the process plugin.
if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
{
// This is a process attach. Don't need to launch anything.
ProcessAttachInfo attach_info(launch_info);
return Attach(attach_info, debugger, target, error);
}
else
{
ProcessSP process_sp = target->CreateProcess(launch_info.GetListenerForProcess(debugger),
launch_info.GetProcessPluginName(),
nullptr);
// We need to launch and attach to the process.
launch_info.GetFlags().Set(eLaunchFlagDebug);
if (process_sp)
error = process_sp->Launch(launch_info);
return process_sp;
}
}
lldb::ProcessSP
PlatformWindows::Attach(ProcessAttachInfo &attach_info,
Debugger &debugger,
Target *target,
Error &error)
{
error.Clear();
lldb::ProcessSP process_sp;
if (!IsHost())
{
if (m_remote_platform_sp)
process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error);
else
error.SetErrorString ("the platform is not currently connected");
return process_sp;
}
if (target == NULL)
{
TargetSP new_target_sp;
FileSpec emptyFileSpec;
ArchSpec emptyArchSpec;
error = debugger.GetTargetList().CreateTarget (debugger,
NULL,
NULL,
false,
NULL,
new_target_sp);
target = new_target_sp.get();
}
if (!target || error.Fail())
return process_sp;
debugger.GetTargetList().SetSelectedTarget(target);
const char *plugin_name = attach_info.GetProcessPluginName();
process_sp = target->CreateProcess(attach_info.GetListenerForProcess(debugger), plugin_name, NULL);
process_sp->HijackProcessEvents(attach_info.GetHijackListener().get());
if (process_sp)
error = process_sp->Attach (attach_info);
return process_sp;
}
const char *
PlatformWindows::GetUserName (uint32_t uid)
{
// Check the cache in Platform in case we have already looked this uid up
const char *user_name = Platform::GetUserName(uid);
if (user_name)
return user_name;
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetUserName(uid);
return NULL;
}
const char *
PlatformWindows::GetGroupName (uint32_t gid)
{
const char *group_name = Platform::GetGroupName(gid);
if (group_name)
return group_name;
if (IsRemote() && m_remote_platform_sp)
return m_remote_platform_sp->GetGroupName(gid);
return NULL;
}
Error
PlatformWindows::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();
}
Error
PlatformWindows::GetSharedModule (const ModuleSpec &module_spec,
Process* process,
ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
ModuleSP *old_module_sp_ptr,
bool *did_create_ptr)
{
Error error;
module_sp.reset();
if (IsRemote())
{
// If we have a remote platform always, let it try and locate
// the shared module first.
if (m_remote_platform_sp)
{
error = m_remote_platform_sp->GetSharedModule (module_spec,
process,
module_sp,
module_search_paths_ptr,
old_module_sp_ptr,
did_create_ptr);
}
}
if (!module_sp)
{
// Fall back to the local platform and find the file locally
error = Platform::GetSharedModule (module_spec,
process,
module_sp,
module_search_paths_ptr,
old_module_sp_ptr,
did_create_ptr);
}
if (module_sp)
module_sp->SetPlatformFileSpec(module_spec.GetFileSpec());
return error;
}
bool
PlatformWindows::GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch)
{
static SupportedArchList architectures;
if (idx >= architectures.Count())
return false;
arch = architectures[idx];
return true;
}
void
PlatformWindows::GetStatus (Stream &strm)
{
Platform::GetStatus(strm);
#ifdef _WIN32
uint32_t major;
uint32_t minor;
uint32_t update;
if (!HostInfo::GetOSVersion(major, minor, update))
{
strm << "Windows";
return;
}
strm << "Host: Windows " << major
<< '.' << minor
<< " Build: " << update << '\n';
#endif
}
bool
PlatformWindows::CanDebugProcess()
{
return true;
}
size_t
PlatformWindows::GetEnvironment(StringList &env)
{
if (IsRemote())
{
if (m_remote_platform_sp)
return m_remote_platform_sp->GetEnvironment(env);
return 0;
}
return Host::GetEnvironment(env);
}