blob: 8714953a9cdb85cfd7aea23ff7f5e2651674120e [file] [log] [blame]
//===-- PlatformWindows.cpp -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "PlatformWindows.h"
#include <cstdio>
#if defined(_WIN32)
#include "lldb/Host/windows/windows.h"
#include <winsock2.h>
#endif
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/BreakpointSite.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/Status.h"
using namespace lldb;
using namespace lldb_private;
LLDB_PLUGIN_DEFINE(PlatformWindows)
static uint32_t g_initialize_count = 0;
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 && arch && arch->IsValid()) {
const llvm::Triple &triple = arch->GetTriple();
switch (triple.getVendor()) {
case llvm::Triple::PC:
create = true;
break;
case llvm::Triple::UnknownVendor:
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();
}
llvm::StringRef PlatformWindows::GetPluginDescriptionStatic(bool is_host) {
return is_host ? "Local Windows user platform plug-in."
: "Remote Windows user platform plug-in.";
}
void PlatformWindows::Initialize() {
Platform::Initialize();
if (g_initialize_count++ == 0) {
#if defined(_WIN32)
// 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() {
if (g_initialize_count > 0) {
if (--g_initialize_count == 0) {
PluginManager::UnregisterPlugin(PlatformWindows::CreateInstance);
}
}
Platform::Terminate();
}
/// Default Constructor
PlatformWindows::PlatformWindows(bool is_host) : RemoteAwarePlatform(is_host) {
const auto &AddArch = [&](const ArchSpec &spec) {
if (llvm::any_of(m_supported_architectures, [spec](const ArchSpec &rhs) {
return spec.IsExactMatch(rhs);
}))
return;
if (spec.IsValid())
m_supported_architectures.push_back(spec);
};
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"));
}
Status PlatformWindows::ConnectRemote(Args &args) {
Status error;
if (IsHost()) {
error.SetErrorStringWithFormatv(
"can't connect to the host platform '{0}', always connected",
GetPluginName());
} 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;
}
Status PlatformWindows::DisconnectRemote() {
Status error;
if (IsHost()) {
error.SetErrorStringWithFormatv(
"can't disconnect from the host platform '{0}', always connected",
GetPluginName());
} else {
if (m_remote_platform_sp)
error = m_remote_platform_sp->DisconnectRemote();
else
error.SetErrorString("the platform is not currently connected");
}
return error;
}
ProcessSP PlatformWindows::DebugProcess(ProcessLaunchInfo &launch_info,
Debugger &debugger, Target &target,
Status &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 (IsRemote()) {
if (m_remote_platform_sp)
return m_remote_platform_sp->DebugProcess(launch_info, debugger, target,
error);
else
error.SetErrorString("the platform is not currently connected");
}
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.GetListener(), launch_info.GetProcessPluginName(), nullptr,
false);
// 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,
Status &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 == nullptr) {
TargetSP new_target_sp;
FileSpec emptyFileSpec;
ArchSpec emptyArchSpec;
error = debugger.GetTargetList().CreateTarget(
debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
target = new_target_sp.get();
}
if (!target || error.Fail())
return process_sp;
const char *plugin_name = attach_info.GetProcessPluginName();
process_sp = target->CreateProcess(
attach_info.GetListenerForProcess(debugger), plugin_name, nullptr, false);
process_sp->HijackProcessEvents(attach_info.GetHijackListener());
if (process_sp)
error = process_sp->Attach(attach_info);
return process_sp;
}
void PlatformWindows::GetStatus(Stream &strm) {
Platform::GetStatus(strm);
#ifdef _WIN32
llvm::VersionTuple version = HostInfo::GetOSVersion();
strm << " Host: Windows " << version.getAsString() << '\n';
#endif
}
bool PlatformWindows::CanDebugProcess() { return true; }
ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) {
if (basename.IsEmpty())
return basename;
StreamString stream;
stream.Printf("%s.dll", basename.GetCString());
return ConstString(stream.GetString());
}
size_t
PlatformWindows::GetSoftwareBreakpointTrapOpcode(Target &target,
BreakpointSite *bp_site) {
ArchSpec arch = target.GetArchitecture();
assert(arch.IsValid());
const uint8_t *trap_opcode = nullptr;
size_t trap_opcode_size = 0;
switch (arch.GetMachine()) {
case llvm::Triple::aarch64: {
static const uint8_t g_aarch64_opcode[] = {0x00, 0x00, 0x3e, 0xd4}; // brk #0xf000
trap_opcode = g_aarch64_opcode;
trap_opcode_size = sizeof(g_aarch64_opcode);
if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
return trap_opcode_size;
return 0;
} break;
case llvm::Triple::arm:
case llvm::Triple::thumb: {
static const uint8_t g_thumb_opcode[] = {0xfe, 0xde}; // udf #0xfe
trap_opcode = g_thumb_opcode;
trap_opcode_size = sizeof(g_thumb_opcode);
if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size))
return trap_opcode_size;
return 0;
} break;
default:
return Platform::GetSoftwareBreakpointTrapOpcode(target, bp_site);
}
}