Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp =================================================================== --- lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp +++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/register-reading/main.cpp @@ -1,10 +1,10 @@ #include -struct alignas(16) xmm_t { - uint64_t a, b; -}; - int main() { +#if defined(__x86_64__) + struct alignas(16) xmm_t { + uint64_t a, b; + }; uint64_t r8 = 0x0102030405060708; uint64_t r9 = 0x1112131415161718; uint64_t r10 = 0x2122232425262728; @@ -49,6 +49,6 @@ : "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15"); - +#endif return 0; } Index: lldb/source/Plugins/Process/Utility/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -43,6 +43,8 @@ RegisterContextPOSIX_s390x.cpp RegisterContextPOSIX_x86.cpp RegisterContextThreadMemory.cpp + RegisterContextWindows_i386.cpp + RegisterContextWindows_x86_64.cpp RegisterInfoPOSIX_arm.cpp RegisterInfoPOSIX_arm64.cpp RegisterInfoPOSIX_ppc64le.cpp Index: lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.h @@ -0,0 +1,27 @@ +//===-- RegisterContextWindows_i386.h ---------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_i386_H_ +#define liblldb_RegisterContextWindows_i386_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_i386 : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_i386(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override; + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; +}; + +#endif Index: lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/RegisterContextWindows_i386.cpp @@ -0,0 +1,89 @@ +//===-- RegisterContextWindows_i386.cpp -------------------------*- C++ -*-===// +// +// 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 "RegisterContextWindows_i386.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +using namespace lldb_private; +using namespace lldb; + +namespace { +// Declare our g_register_infos structure. +typedef struct _GPR { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t eip; + uint32_t eflags; + uint32_t cs; + uint32_t fs; + uint32_t gs; + uint32_t ss; + uint32_t ds; + uint32_t es; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) + +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ +#reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_i386 }, nullptr, nullptr, \ + nullptr, 0 \ + } + +// clang-format off +static RegisterInfo g_register_infos_i386[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(eax, nullptr, ehframe_eax_i386, dwarf_eax_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebx, nullptr, ehframe_ebx_i386, dwarf_ebx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ecx, nullptr, ehframe_ecx_i386, dwarf_ecx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edx, nullptr, ehframe_edx_i386, dwarf_edx_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(edi, nullptr, ehframe_edi_i386, dwarf_edi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(esi, nullptr, ehframe_esi_i386, dwarf_esi_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ebp, "fp", ehframe_ebp_i386, dwarf_ebp_i386, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(esp, "sp", ehframe_esp_i386, dwarf_esp_i386, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(eip, "pc", ehframe_eip_i386, dwarf_eip_i386, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(eflags, "flags", ehframe_eflags_i386, dwarf_eflags_i386, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, LLDB_INVALID_REGNUM, dwarf_cs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, LLDB_INVALID_REGNUM, dwarf_fs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, LLDB_INVALID_REGNUM, dwarf_gs_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, LLDB_INVALID_REGNUM, dwarf_ss_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, LLDB_INVALID_REGNUM, dwarf_ds_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, LLDB_INVALID_REGNUM, dwarf_es_i386, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + }; +// clang-format on +} + +RegisterContextWindows_i386::RegisterContextWindows_i386( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch) { + assert(target_arch.GetMachine() == llvm::Triple::x86); +} + +const RegisterInfo *RegisterContextWindows_i386::GetRegisterInfo() const { + return g_register_infos_i386; +} + +uint32_t RegisterContextWindows_i386::GetRegisterCount() const { + return llvm::array_lengthof(g_register_infos_i386); +} + +uint32_t RegisterContextWindows_i386::GetUserRegisterCount() const { + return llvm::array_lengthof(g_register_infos_i386); +} + +size_t RegisterContextWindows_i386::GetGPRSize() const { return sizeof(GPR); } Index: lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.h @@ -0,0 +1,37 @@ +//===-- RegisterContextWindows_x86_64.h -------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextWindows_x86_64_H_ +#define liblldb_RegisterContextWindows_x86_64_H_ + +#include "RegisterInfoInterface.h" + +class RegisterContextWindows_x86_64 + : public lldb_private::RegisterInfoInterface { +public: + RegisterContextWindows_x86_64(const lldb_private::ArchSpec &target_arch); + + size_t GetGPRSize() const override; + + const lldb_private::RegisterInfo *GetRegisterInfo() const override { + return m_register_info_p; + } + + uint32_t GetRegisterCount() const override { return m_register_info_count; } + + uint32_t GetUserRegisterCount() const override { + return m_user_register_count; + } + +private: + const lldb_private::RegisterInfo *m_register_info_p; + uint32_t m_register_info_count; + uint32_t m_user_register_count; +}; + +#endif Index: lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Utility/RegisterContextWindows_x86_64.cpp @@ -0,0 +1,202 @@ +//===-- RegisterContextWindows_x86_64.cpp -----------------------*- C++ -*-===// +// +// 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 "RegisterContextWindows_i386.h" +#include "RegisterContextWindows_x86_64.h" +#include "RegisterContext_x86.h" +#include "lldb-x86-register-enums.h" + +#include + +using namespace lldb_private; +using namespace lldb; + +namespace { +typedef struct _GPR { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint16_t cs; + uint16_t fs; + uint16_t gs; + uint16_t ss; + uint16_t ds; + uint16_t es; +} GPR; + +#define GPR_OFFSET(regname) (LLVM_EXTENSION offsetof(GPR, regname)) +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { \ +#reg, alt, sizeof(((GPR *)nullptr)->reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, \ + {kind1, kind2, kind3, kind4, lldb_##reg##_x86_64 }, nullptr, nullptr, \ + nullptr, 0 \ + } + +typedef struct _FPReg { + XMMReg xmm0; + XMMReg xmm1; + XMMReg xmm2; + XMMReg xmm3; + XMMReg xmm4; + XMMReg xmm5; + XMMReg xmm6; + XMMReg xmm7; + XMMReg xmm8; + XMMReg xmm9; + XMMReg xmm10; + XMMReg xmm11; + XMMReg xmm12; + XMMReg xmm13; + XMMReg xmm14; + XMMReg xmm15; +} FPReg; + +#define FPR_OFFSET(regname) \ + (sizeof(GPR) + LLVM_EXTENSION offsetof(FPReg, regname)) + +#define DEFINE_XMM(reg) \ +#reg, NULL, sizeof(((FPReg *)nullptr)->reg), FPR_OFFSET(reg), eEncodingUint, \ + eFormatVectorOfUInt64, \ + {dwarf_##reg##_x86_64, dwarf_##reg##_x86_64, LLDB_INVALID_REGNUM, \ + LLDB_INVALID_REGNUM, lldb_##reg##_x86_64 }, \ + nullptr, nullptr, nullptr, 0 + +// clang-format off +static RegisterInfo g_register_infos_x86_64[] = { +// General purpose registers EH_Frame DWARF Generic Process Plugin +// =========================== ================== ================ ========================= ==================== + DEFINE_GPR(rax, nullptr, dwarf_rax_x86_64, dwarf_rax_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbx, nullptr, dwarf_rbx_x86_64, dwarf_rbx_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rcx, "arg4", dwarf_rcx_x86_64, dwarf_rcx_x86_64, LLDB_REGNUM_GENERIC_ARG4, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdx, "arg3", dwarf_rdx_x86_64, dwarf_rdx_x86_64, LLDB_REGNUM_GENERIC_ARG3, LLDB_INVALID_REGNUM), + DEFINE_GPR(rdi, "arg1", dwarf_rdi_x86_64, dwarf_rdi_x86_64, LLDB_REGNUM_GENERIC_ARG1, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsi, "arg2", dwarf_rsi_x86_64, dwarf_rsi_x86_64, LLDB_REGNUM_GENERIC_ARG2, LLDB_INVALID_REGNUM), + DEFINE_GPR(rbp, "fp", dwarf_rbp_x86_64, dwarf_rbp_x86_64, LLDB_REGNUM_GENERIC_FP, LLDB_INVALID_REGNUM), + DEFINE_GPR(rsp, "sp", dwarf_rsp_x86_64, dwarf_rsp_x86_64, LLDB_REGNUM_GENERIC_SP, LLDB_INVALID_REGNUM), + DEFINE_GPR(r8, "arg5", dwarf_r8_x86_64, dwarf_r8_x86_64, LLDB_REGNUM_GENERIC_ARG5, LLDB_INVALID_REGNUM), + DEFINE_GPR(r9, "arg6", dwarf_r9_x86_64, dwarf_r9_x86_64, LLDB_REGNUM_GENERIC_ARG6, LLDB_INVALID_REGNUM), + DEFINE_GPR(r10, nullptr, dwarf_r10_x86_64, dwarf_r10_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r11, nullptr, dwarf_r11_x86_64, dwarf_r11_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r12, nullptr, dwarf_r12_x86_64, dwarf_r12_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r13, nullptr, dwarf_r13_x86_64, dwarf_r13_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r14, nullptr, dwarf_r14_x86_64, dwarf_r14_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(r15, nullptr, dwarf_r15_x86_64, dwarf_r15_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(rip, "pc", dwarf_rip_x86_64, dwarf_rip_x86_64, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + DEFINE_GPR(rflags, "flags", dwarf_rflags_x86_64, dwarf_rflags_x86_64, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(cs, nullptr, dwarf_cs_x86_64, dwarf_cs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(fs, nullptr, dwarf_fs_x86_64, dwarf_fs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(gs, nullptr, dwarf_gs_x86_64, dwarf_gs_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ss, nullptr, dwarf_ss_x86_64, dwarf_ss_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ds, nullptr, dwarf_ds_x86_64, dwarf_ds_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(es, nullptr, dwarf_es_x86_64, dwarf_es_x86_64, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_XMM(xmm0), + DEFINE_XMM(xmm1), + DEFINE_XMM(xmm2), + DEFINE_XMM(xmm3), + DEFINE_XMM(xmm4), + DEFINE_XMM(xmm5), + DEFINE_XMM(xmm6), + DEFINE_XMM(xmm7), + DEFINE_XMM(xmm8), + DEFINE_XMM(xmm9), + DEFINE_XMM(xmm10), + DEFINE_XMM(xmm11), + DEFINE_XMM(xmm12), + DEFINE_XMM(xmm13), + DEFINE_XMM(xmm14), + DEFINE_XMM(xmm15) +}; +// clang-format on +} + +static std::vector &GetPrivateRegisterInfoVector() { + static std::vector g_register_infos; + return g_register_infos; +} + +static std::unique_ptr g_private_reg_interface = nullptr; + +static const RegisterInfo * +GetRegisterInfo_WoW64(const lldb_private::ArchSpec &arch) { + // A WoW64 register info is the same as the i386's. + std::vector &g_register_infos = + GetPrivateRegisterInfoVector(); + + if (g_register_infos.empty()) { + g_private_reg_interface.reset( + new RegisterContextWindows_i386(arch)); + const RegisterInfo *base_info = g_private_reg_interface->GetRegisterInfo(); + g_register_infos.insert(g_register_infos.end(), &base_info[0], + &base_info[g_private_reg_interface->GetRegisterCount()]); + } + + return g_register_infos.data(); + } + +static const RegisterInfo *GetRegisterInfoPtr(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + return GetRegisterInfo_WoW64(target_arch); + case llvm::Triple::x86_64: + return g_register_infos_x86_64; + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +static uint32_t GetRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + assert(g_private_reg_interface && + "WoW64 register info not yet filled."); + return g_private_reg_interface->GetRegisterCount(); + case llvm::Triple::x86_64: + return static_cast(llvm::array_lengthof(g_register_infos_x86_64)); + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +static uint32_t GetUserRegisterInfoCount(const ArchSpec &target_arch) { + switch (target_arch.GetMachine()) { + case llvm::Triple::x86: + assert(g_private_reg_interface && + "WoW64 register info not yet filled."); + return g_private_reg_interface->GetUserRegisterCount(); + case llvm::Triple::x86_64: + return static_cast(llvm::array_lengthof(g_register_infos_x86_64)); + default: + llvm_unreachable("Unhandled target architecture."); + } +} + +RegisterContextWindows_x86_64::RegisterContextWindows_x86_64( + const ArchSpec &target_arch) + : lldb_private::RegisterInfoInterface(target_arch), + m_register_info_p(GetRegisterInfoPtr(target_arch)), + m_register_info_count(GetRegisterInfoCount(target_arch)), + m_user_register_count(GetUserRegisterInfoCount(target_arch)) { +} + +size_t RegisterContextWindows_x86_64::GetGPRSize() const { return sizeof(GPR); } Index: lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt =================================================================== --- lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt +++ lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -2,6 +2,12 @@ add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN DebuggerThread.cpp LocalDebugDelegate.cpp + NativeProcessWindows.cpp + NativeRegisterContextWindows.cpp + NativeRegisterContextWindows_i386.cpp + NativeRegisterContextWindows_WoW64.cpp + NativeRegisterContextWindows_x86_64.cpp + NativeThreadWindows.cpp ProcessDebugger.cpp ProcessWindows.cpp ProcessWindowsLog.cpp Index: lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp =================================================================== --- lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp +++ lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp @@ -29,6 +29,10 @@ #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" +#ifndef STATUS_WX86_BREAKPOINT +#define STATUS_WX86_BREAKPOINT 0x4000001FL // For WOW64 +#endif + using namespace lldb; using namespace lldb_private; @@ -350,7 +354,8 @@ // we use simply to wake up the DebuggerThread so that we can close out the // debug loop. if (m_pid_to_detach != 0 && - info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { + (info.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT || + info.ExceptionRecord.ExceptionCode == STATUS_WX86_BREAKPOINT)) { LLDB_LOG(log, "Breakpoint exception is cue to detach from process {0:x}", m_pid_to_detach.load()); ::DebugActiveProcessStop(m_pid_to_detach); Index: lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h @@ -0,0 +1,182 @@ +//===-- NativeProcessWindows.h ----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeProcessWindows_h_ +#define liblldb_NativeProcessWindows_h_ + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/lldb-forward.h" + +#include "IDebugDelegate.h" +#include "ProcessDebugger.h" + +namespace lldb_private { + +class HostProcess; +class NativeProcessWindows; +class NativeThreadWindows; +class NativeDebugDelegate; + +typedef std::shared_ptr NativeDebugDelegateSP; + +//------------------------------------------------------------------ +// NativeProcessWindows +//------------------------------------------------------------------ +class NativeProcessWindows : public NativeProcessProtocol, + public ProcessDebugger { + +public: + class Factory : public NativeProcessProtocol::Factory { + public: + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + + llvm::Expected> + Attach(lldb::pid_t pid, NativeDelegate &native_delegate, + MainLoop &mainloop) const override; + }; + + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + Status Signal(int signo) override; + + Status Interrupt() override; + + Status Kill() override; + + Status IgnoreSignals(llvm::ArrayRef signals) override; + + Status GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + Status AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) override; + + Status DeallocateMemory(lldb::addr_t addr) override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + bool IsAlive() const override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override { return m_arch; } + + void SetArchitecture(const ArchSpec &arch_spec) { m_arch = arch_spec; } + + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override; + + llvm::ErrorOr> + GetAuxvData() const override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + // ProcessDebugger Overrides + void OnExitProcess(uint32_t exit_code) override; + void OnDebuggerConnected(lldb::addr_t image_base) override; + ExceptionResult OnDebugException(bool first_chance, + const ExceptionRecord &record) override; + void OnCreateThread(const HostThread &thread) override; + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) override; + void OnLoadDll(const ModuleSpec &module_spec, + lldb::addr_t module_addr) override; + void OnUnloadDll(lldb::addr_t module_addr) override; + +protected: + NativeThreadWindows *GetThreadByID(lldb::tid_t thread_id); + + bool FindSoftwareBreakpoint(lldb::addr_t addr); + + void StopThread(lldb::tid_t thread_id, lldb::StopReason reason, + std::string description = ""); + + void SetStopReasonForThread(NativeThreadWindows &thread, + lldb::StopReason reason, + std::string description = ""); + +private: + ArchSpec m_arch; + + NativeProcessWindows(ProcessLaunchInfo &launch_info, NativeDelegate &delegate, + llvm::Error &E); + + NativeProcessWindows(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate, llvm::Error &E); + + Status CacheLoadedModules(); + std::map m_loaded_modules; +}; + +//------------------------------------------------------------------ +// NativeDebugDelegate +//------------------------------------------------------------------ +class NativeDebugDelegate : public IDebugDelegate { +public: + NativeDebugDelegate(NativeProcessWindows &process) : m_process(process) {} + + void OnExitProcess(uint32_t exit_code) { m_process.OnExitProcess(exit_code); } + + void OnDebuggerConnected(lldb::addr_t image_base) { + m_process.OnDebuggerConnected(image_base); + } + + ExceptionResult OnDebugException(bool first_chance, + const ExceptionRecord &record) { + return m_process.OnDebugException(first_chance, record); + } + + void OnCreateThread(const HostThread &thread) { + m_process.OnCreateThread(thread); + } + + void OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) { + m_process.OnExitThread(thread_id, exit_code); + } + + void OnLoadDll(const lldb_private::ModuleSpec &module_spec, + lldb::addr_t module_addr) { + m_process.OnLoadDll(module_spec, module_addr); + } + + void OnUnloadDll(lldb::addr_t module_addr) { + m_process.OnUnloadDll(module_addr); + } + + void OnDebugString(const std::string &string) { + m_process.OnDebugString(string); + } + + void OnDebuggerError(const Status &error, uint32_t type) { + return m_process.OnDebuggerError(error, type); + } + +private: + NativeProcessWindows &m_process; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeProcessWindows_h_ Index: lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -0,0 +1,573 @@ +//===-- NativeProcessWindows.cpp --------------------------------*- C++ -*-===// +// +// 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 "lldb/Host/windows/windows.h" +#include + +#include "NativeProcessWindows.h" +#include "NativeThreadWindows.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/windows/AutoHandle.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "ProcessWindowsLog.h" + +#include + +#pragma warning(disable : 4005) +#include "winternl.h" +#include + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace lldb_private { + +NativeProcessWindows::NativeProcessWindows(ProcessLaunchInfo &launch_info, + NativeDelegate &delegate, + llvm::Error &E) + : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID, + launch_info.GetPTY().ReleaseMasterFileDescriptor(), + delegate), + ProcessDebugger(), m_arch(launch_info.GetArchitecture()) { + ErrorAsOutParameter EOut(&E); + DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); + E = LaunchProcess(launch_info, delegate_sp).ToError(); + if (E) + return; + + SetID(GetDebuggedProcessId()); +} + +NativeProcessWindows::NativeProcessWindows(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + llvm::Error &E) + : NativeProcessProtocol(pid, terminal_fd, delegate), ProcessDebugger() { + ErrorAsOutParameter EOut(&E); + DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); + ProcessAttachInfo attach_info; + attach_info.SetProcessID(pid); + E = AttachProcess(pid, attach_info, delegate_sp).ToError(); + if (E) + return; + + SetID(GetDebuggedProcessId()); + + ProcessInstanceInfo info; + if (!Host::GetProcessInfo(pid, info)) { + E = createStringError(inconvertibleErrorCode(), + "Cannot get process information"); + return; + } + m_arch = info.GetArchitecture(); +} + +Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + Status error; + llvm::sys::ScopedLock lock(m_mutex); + + StateType state = GetState(); + if (state == eStateStopped || state == eStateCrashed) { + LLDB_LOG(log, "process {0} is in state {1}. Resuming...", + GetDebuggedProcessId(), state); + LLDB_LOG(log, "resuming {0} threads.", m_threads.size()); + + bool failed = false; + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto thread = static_cast(m_threads[i].get()); + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + Status result = thread->DoResume(action->state); + if (result.Fail()) { + failed = true; + LLDB_LOG(log, + "Trying to resume thread at index {0}, but failed with " + "error {1}.", + i, result); + } + break; + } + case eStateSuspended: + case eStateStopped: + llvm_unreachable("Unexpected state"); + + default: + return Status( + "NativeProcessWindows::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + if (failed) { + error.SetErrorString("NativeProcessWindows::DoResume failed"); + } else { + SetState(eStateRunning); + } + + // Resume the debug loop. + ExceptionRecordSP active_exception = + m_session_data->m_debugger->GetActiveException().lock(); + if (active_exception) { + // Resume the process and continue processing debug events. Mask the + // exception so that from the process's view, there is no indication that + // anything happened. + m_session_data->m_debugger->ContinueAsyncException( + ExceptionResult::MaskException); + } + } else { + LLDB_LOG(log, "error: process {0} is in state {1}. Returning...", + GetDebuggedProcessId(), GetState()); + } + + return error; +} + +NativeThreadWindows * +NativeProcessWindows::GetThreadByID(lldb::tid_t thread_id) { + return static_cast( + NativeProcessProtocol::GetThreadByID(thread_id)); +} + +Status NativeProcessWindows::Halt() { + bool caused_stop = false; + StateType state = GetState(); + if (state != eStateStopped) + return HaltProcess(caused_stop); + return Status(); +} + +Status NativeProcessWindows::Detach() { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + StateType state = GetState(); + if (state != eStateExited && state != eStateDetached) { + error = DetachProcess(); + if (error.Success()) + SetState(eStateDetached); + else + LLDB_LOG(log, "Detaching process error: {0}", error); + } else { + error.SetErrorStringWithFormat("error: process {0} in state = {1}, but " + "cannot detach it in this state.", + GetID(), state); + LLDB_LOG(log, "error: {0}", error); + } + return error; +} + +Status NativeProcessWindows::Signal(int signo) { + Status error; + error.SetErrorString("Windows does not support sending signals to processes"); + return error; +} + +Status NativeProcessWindows::Interrupt() { return Halt(); } + +Status NativeProcessWindows::Kill() { + StateType state = GetState(); + return DestroyProcess(state); +} + +Status NativeProcessWindows::IgnoreSignals(llvm::ArrayRef signals) { + return Status(); +} + +Status NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info); +} + +Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) { + return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read); +} + +Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written); +} + +Status NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) { + return ProcessDebugger::AllocateMemory(size, permissions, addr); +} + +Status NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { + return ProcessDebugger::DeallocateMemory(addr); +} + +lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; } + +bool NativeProcessWindows::IsAlive() const { + StateType state = GetState(); + switch (state) { + case eStateCrashed: + case eStateDetached: + case eStateExited: + case eStateInvalid: + case eStateUnloaded: + return false; + default: + return true; + } +} + +void NativeProcessWindows::SetStopReasonForThread(NativeThreadWindows &thread, + lldb::StopReason reason, + std::string description) { + SetCurrentThreadID(thread.GetID()); + + ThreadStopInfo stop_info; + stop_info.reason = reason; + + // No signal support on Windows but required to provide a 'valid' signum. + if (reason == StopReason::eStopReasonException) { + stop_info.details.exception.type = 0; + stop_info.details.exception.data_count = 0; + } else { + stop_info.details.signal.signo = SIGTRAP; + } + + thread.SetStopReason(stop_info, description); +} + +void NativeProcessWindows::StopThread(lldb::tid_t thread_id, + lldb::StopReason reason, + std::string description) { + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto t = static_cast(m_threads[i].get()); + Status error = t->DoStop(); + if (error.Fail()) + exit(1); + } + SetStopReasonForThread(*thread, reason, description); +} + +size_t NativeProcessWindows::UpdateThreads() { return m_threads.size(); } + +llvm::ErrorOr> +NativeProcessWindows::GetAuxvData() const { + // Not available on this target. + return llvm::errc::not_supported; +} + +bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) { + auto it = m_software_breakpoints.find(addr); + if (it == m_software_breakpoints.end()) + return false; + return true; +} + +Status NativeProcessWindows::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessWindows::RemoveBreakpoint(lldb::addr_t addr, + bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + return RemoveSoftwareBreakpoint(addr); +} + +Status NativeProcessWindows::CacheLoadedModules() { + Status error; + if (!m_loaded_modules.empty()) + return Status(); + + // Retrieve loaded modules by a Target/Module free implemenation. + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetID())); + if (snapshot.IsValid()) { + MODULEENTRY32W me; + me.dwSize = sizeof(MODULEENTRY32W); + if (Module32FirstW(snapshot.get(), &me)) { + do { + std::string path; + if (!llvm::convertWideToUTF8(me.szExePath, path)) + continue; + + FileSpec file_spec(path); + FileSystem::Instance().Resolve(file_spec); + m_loaded_modules[file_spec] = (addr_t)me.modBaseAddr; + } while (Module32Next(snapshot.get(), &me)); + } + + if (!m_loaded_modules.empty()) + return Status(); + } + + error.SetError(::GetLastError(), lldb::ErrorType::eErrorTypeWin32); + return error; +} + +Status NativeProcessWindows::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = CacheLoadedModules(); + if (error.Fail()) + return error; + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + for (auto &it : m_loaded_modules) { + if (it.first == module_file_spec) { + file_spec = it.first; + return Status(); + } + } + return Status("Module (%s) not found in process %" PRIu64 "!", + module_file_spec.GetCString(), GetID()); +} + +Status +NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + Status error = CacheLoadedModules(); + if (error.Fail()) + return error; + + load_addr = LLDB_INVALID_ADDRESS; + FileSpec file_spec(file_name); + FileSystem::Instance().Resolve(file_spec); + for (auto &it : m_loaded_modules) { + if (it.first == file_spec) { + load_addr = it.second; + return Status(); + } + } + return Status("Can't get loaded address of file (%s) in process %" PRIu64 "!", + file_spec.GetCString(), GetID()); +} + +void NativeProcessWindows::OnExitProcess(uint32_t exit_code) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code); + + ProcessDebugger::OnExitProcess(exit_code); + + // No signal involved. It is just an exit event. + WaitStatus wait_status(WaitStatus::Exit, exit_code); + SetExitStatus(wait_status, true); + + // Notify the native delegate. + SetState(eStateExited, true); +} + +void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Debugger connected to process {0}. Image base = {1:x}", + GetDebuggedProcessId(), image_base); + + // This is the earliest chance we can resolve the process ID and + // architecutre if we don't know them yet. + if (GetID() == LLDB_INVALID_PROCESS_ID) + SetID(GetDebuggedProcessId()); + + if (GetArchitecture().GetMachine() == llvm::Triple::UnknownArch) { + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(GetDebuggedProcessId(), process_info)) { + LLDB_LOG(log, "Cannot get process information during debugger connecting " + "to process"); + return; + } + SetArchitecture(process_info.GetArchitecture()); + } + + // The very first one shall always be the main thread. + assert(m_threads.empty()); + m_threads.push_back(llvm::make_unique( + *this, m_session_data->m_debugger->GetMainThread())); +} + +ExceptionResult +NativeProcessWindows::OnDebugException(bool first_chance, + const ExceptionRecord &record) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_EXCEPTION); + llvm::sys::ScopedLock lock(m_mutex); + + // Let the debugger establish the internal status. + ProcessDebugger::OnDebugException(first_chance, record); + + static bool initial_stop = false; + if (!first_chance) { + SetState(eStateStopped, false); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) { + case STATUS_SINGLE_STEP: + case STATUS_WX86_SINGLE_STEP: + StopThread(record.GetThreadID(), StopReason::eStopReasonTrace); + SetState(eStateStopped, true); + + // Continue the debugger. + return ExceptionResult::MaskException; + + case STATUS_BREAKPOINT: + case STATUS_WX86_BREAKPOINT: + if (FindSoftwareBreakpoint(record.GetExceptionAddress())) { + LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.", + record.GetExceptionAddress()); + + StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint); + + if (NativeThreadWindows *stop_thread = + GetThreadByID(record.GetThreadID())) { + auto ®ister_context = stop_thread->GetRegisterContext(); + // The current EIP is AFTER the BP opcode, which is one byte '0xCC' + uint64_t pc = register_context.GetPC() - 1; + register_context.SetPC(pc); + } + + SetState(eStateStopped, true); + return ExceptionResult::MaskException; + } + + if (!initial_stop) { + initial_stop = true; + LLDB_LOG(log, + "Hit loader breakpoint at address {0:x}, setting initial stop " + "event.", + record.GetExceptionAddress()); + + // We are required to report the reason for the first stop after + // launching or being attached. + if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) + SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint); + + // Do not notify the native delegate (e.g. llgs) since at this moment + // the program hasn't returned from Factory::Launch() and the delegate + // might not have an valid native process to operate on. + SetState(eStateStopped, false); + + // Hit the initial stop. Continue the application. + return ExceptionResult::BreakInDebugger; + } + + // Fall through + default: + LLDB_LOG(log, + "Debugger thread reported exception {0:x} at address {1:x} " + "(first_chance={2})", + record.GetExceptionCode(), record.GetExceptionAddress(), + first_chance); + + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex(record.GetExceptionCode(), 8) + << " encountered at address " + << llvm::format_hex(record.GetExceptionAddress(), 8); + StopThread(record.GetThreadID(), StopReason::eStopReasonException, + desc_stream.str().c_str()); + + SetState(eStateStopped, true); + } + + // For non-breakpoints, give the application a chance to handle the + // exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) { + llvm::sys::ScopedLock lock(m_mutex); + m_threads.push_back( + llvm::make_unique(*this, new_thread)); +} + +void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, + uint32_t exit_code) { + llvm::sys::ScopedLock lock(m_mutex); + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (auto t = m_threads.begin(); t != m_threads.end();) { + if ((*t)->GetID() == thread_id) { + t = m_threads.erase(t); + } else { + ++t; + } + } +} + +void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec, + lldb::addr_t module_addr) { + // Simply invalidate the cached loaded modules. + if (!m_loaded_modules.empty()) + m_loaded_modules.clear(); +} + +void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) { + if (!m_loaded_modules.empty()) + m_loaded_modules.clear(); +} + +llvm::Expected> +NativeProcessWindows::Factory::Launch( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Error E = Error::success(); + auto process_up = std::unique_ptr( + new NativeProcessWindows(launch_info, native_delegate, E)); + if (E) + return std::move(E); + return std::move(process_up); +} + +llvm::Expected> +NativeProcessWindows::Factory::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Error E = Error::success(); + // Set pty master fd invalid since it is not available. + auto process_up = std::unique_ptr( + new NativeProcessWindows(pid, -1, native_delegate, E)); + if (E) + return std::move(E); + return std::move(process_up); +} +} // namespace lldb_private Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h @@ -0,0 +1,36 @@ +//===-- NativeRegisterContextWindows.h --------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeRegisterContextWindows_h_ +#define liblldb_NativeRegisterContextWindows_h_ + +#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows : public NativeRegisterContextRegisterInfo { +public: + NativeRegisterContextWindows::NativeRegisterContextWindows( + NativeThreadProtocol &native_thread, + RegisterInfoInterface *reg_info_interface_p); + + static std::unique_ptr + CreateHostNativeRegisterContextWindows(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + +protected: + lldb::thread_t GetThreadHandle() const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_h_ Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp @@ -0,0 +1,28 @@ +//===-- NativeRegisterContextWindows.cpp ------------------------*- C++ -*-===// +// +// 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 "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Utility/Log.h" + +#include "NativeRegisterContextWindows.h" +#include "NativeThreadWindows.h" +#include "ProcessWindowsLog.h" + +using namespace lldb; +using namespace lldb_private; + +NativeRegisterContextWindows::NativeRegisterContextWindows( + NativeThreadProtocol &thread, RegisterInfoInterface *reg_info_interface_p) + : NativeRegisterContextRegisterInfo(thread, reg_info_interface_p) {} + +lldb::thread_t NativeRegisterContextWindows::GetThreadHandle() const { + auto wthread = static_cast(&m_thread); + return wthread->GetHostThread().GetNativeThread().GetSystemHandle(); +} Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h @@ -0,0 +1,74 @@ +//===-- NativeRegisterContextWindows_WoW64.h --------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_WoW64_h_ +#define liblldb_NativeRegisterContextWindows_WoW64_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_WoW64 : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_WoW64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_WoW64_h_ +#endif // defined(_WIN64) Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp @@ -0,0 +1,362 @@ +//===-- NativeRegisterContextWindows_WoW64.cpp ----- ------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) + +#include "NativeRegisterContextWindows_WoW64.h" + +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_i386.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define REG_CONTEXT_SIZE sizeof(::WOW64_CONTEXT) + +namespace { +static const uint32_t g_gpr_regnums_WoW64[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + LLDB_INVALID_REGNUM // Register set must be terminated with this flag. +}; + +static const RegisterSet g_reg_sets_WoW64[] = { + {"General Purpose Registers", "gpr", + llvm::array_lengthof(g_gpr_regnums_WoW64) - 1, g_gpr_regnums_WoW64}, +}; +enum { k_num_register_sets = 1 }; + +static const DWORD kWoW64ContextFlags = + WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS; + +} // namespace + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + // i686 32bit instruction set. + assert((target_arch.GetAddressByteSize() == 4 && + HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextWindows_i386(target_arch); +} + +static Status GetWoW64ThreadContextHelper(lldb::thread_t thread_handle, + PWOW64_CONTEXT context_ptr) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + memset(context_ptr, 0, sizeof(::WOW64_CONTEXT)); + context_ptr->ContextFlags = kWoW64ContextFlags; + if (!::Wow64GetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} Wow64GetThreadContext failed with error {1}", + __FUNCTION__, error); + return error; + } + return Status(); +} + +static Status SetWoW64ThreadContextHelper(lldb::thread_t thread_handle, + PWOW64_CONTEXT context_ptr) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + if (!::Wow64SetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} Wow64SetThreadContext failed with error {1}", + __FUNCTION__, error); + return error; + } + return Status(); +} + +NativeRegisterContextWindows_WoW64::NativeRegisterContextWindows_WoW64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_WoW64::IsGPR(uint32_t reg_index) const { + return (reg_index >= k_first_gpr_i386 && reg_index < k_first_alias_i386); +} + +uint32_t NativeRegisterContextWindows_WoW64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_WoW64::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_WoW64[set_index]; +} + +Status NativeRegisterContextWindows_WoW64::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + ::WOW64_CONTEXT tls_context; + Status error = GetWoW64ThreadContextHelper(GetThreadHandle(), &tls_context); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_eax_i386: + reg_value.SetUInt32(tls_context.Eax); + break; + case lldb_ebx_i386: + reg_value.SetUInt32(tls_context.Ebx); + break; + case lldb_ecx_i386: + reg_value.SetUInt32(tls_context.Ecx); + break; + case lldb_edx_i386: + reg_value.SetUInt32(tls_context.Edx); + break; + case lldb_edi_i386: + reg_value.SetUInt32(tls_context.Edi); + break; + case lldb_esi_i386: + reg_value.SetUInt32(tls_context.Esi); + break; + case lldb_ebp_i386: + reg_value.SetUInt32(tls_context.Ebp); + break; + case lldb_esp_i386: + reg_value.SetUInt32(tls_context.Esp); + break; + case lldb_eip_i386: + reg_value.SetUInt32(tls_context.Eip); + break; + case lldb_eflags_i386: + reg_value.SetUInt32(tls_context.EFlags); + break; + case lldb_cs_i386: + reg_value.SetUInt32(tls_context.SegCs); + break; + case lldb_fs_i386: + reg_value.SetUInt32(tls_context.SegFs); + break; + case lldb_gs_i386: + reg_value.SetUInt32(tls_context.SegGs); + break; + case lldb_ss_i386: + reg_value.SetUInt32(tls_context.SegSs); + break; + case lldb_ds_i386: + reg_value.SetUInt32(tls_context.SegDs); + break; + case lldb_es_i386: + reg_value.SetUInt32(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_WoW64::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::WOW64_CONTEXT tls_context; + auto thread_handle = GetThreadHandle(); + Status error = GetWoW64ThreadContextHelper(thread_handle, &tls_context); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_eax_i386: + tls_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + tls_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + tls_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + tls_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + tls_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + tls_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + tls_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + tls_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + tls_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + tls_context.EFlags = reg_value.GetAsUInt32(); + break; + case lldb_cs_i386: + tls_context.SegCs = reg_value.GetAsUInt32(); + break; + case lldb_fs_i386: + tls_context.SegFs = reg_value.GetAsUInt32(); + break; + case lldb_gs_i386: + tls_context.SegGs = reg_value.GetAsUInt32(); + break; + case lldb_ss_i386: + tls_context.SegSs = reg_value.GetAsUInt32(); + break; + case lldb_ds_i386: + tls_context.SegDs = reg_value.GetAsUInt32(); + break; + case lldb_es_i386: + tls_context.SegEs = reg_value.GetAsUInt32(); + break; + } + + return SetWoW64ThreadContextHelper(thread_handle, &tls_context); +} + +Status +NativeRegisterContextWindows_WoW64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_WoW64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRWrite(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_WoW64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + const size_t data_size = REG_CONTEXT_SIZE; + data_sp = std::make_shared(data_size, 0); + ::WOW64_CONTEXT tls_context; + Status error = GetWoW64ThreadContextHelper(GetThreadHandle(), &tls_context); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &tls_context, data_size); + return error; +} + +Status NativeRegisterContextWindows_WoW64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + const size_t data_size = REG_CONTEXT_SIZE; + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextWindows_WoW64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != data_size) { + error.SetErrorStringWithFormatv( + "data_sp contained mismatched data size, expected {0}, actual {1}", + data_size, data_sp->GetByteSize()); + return error; + } + + ::WOW64_CONTEXT tls_context; + memcpy(&tls_context, data_sp->GetBytes(), data_size); + return SetWoW64ThreadContextHelper(GetThreadHandle(), &tls_context); +} + +Status NativeRegisterContextWindows_WoW64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_WoW64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_WoW64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_WoW64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_WoW64::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_WoW64::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_WoW64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_WoW64::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t NativeRegisterContextWindows_WoW64::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif // defined(_WIN64) Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h @@ -0,0 +1,74 @@ +//===-- NativeRegisterContextWindows_i386.h ---------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_i386_h_ +#define liblldb_NativeRegisterContextWindows_i386_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_i386 : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_i386(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_i386_h_ +#endif // defined(_WIN64) Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp @@ -0,0 +1,376 @@ +//===-- NativeRegisterContextWindows_i386.cpp -------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN32) && !defined(_WIN64) + +#include "NativeRegisterContextWindows_i386.h" + +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_i386.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define REG_CONTEXT_SIZE sizeof(::CONTEXT) + +namespace { +static const uint32_t g_gpr_regnums_i386[] = { + lldb_eax_i386, lldb_ebx_i386, lldb_ecx_i386, lldb_edx_i386, + lldb_edi_i386, lldb_esi_i386, lldb_ebp_i386, lldb_esp_i386, + lldb_eip_i386, lldb_eflags_i386, lldb_cs_i386, lldb_fs_i386, + lldb_gs_i386, lldb_ss_i386, lldb_ds_i386, lldb_es_i386, + LLDB_INVALID_REGNUM // Register sets must be terminated with this flag. +}; + +static const RegisterSet g_reg_sets_i386[] = { + {"General Purpose Registers", "gpr", + llvm::array_lengthof(g_gpr_regnums_i386) - 1, g_gpr_regnums_i386}, +}; + +enum { k_num_register_sets = 1 }; + +} + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 4) && + "Register setting path assumes this is a 32-bit host"); + return new RegisterContextWindows_i386(target_arch); +} + +static Status GetThreadContextHelper(lldb::thread_t thread_handle, + PCONTEXT context_ptr, + const DWORD control_flag) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + memset(context_ptr, 0, sizeof(::CONTEXT)); + context_ptr->ContextFlags = control_flag; + if (!::GetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} GetThreadContext failed with error {1}", __FUNCTION__, + error); + return error; + } + return Status(); +} + +static Status SetThreadContextHelper(lldb::thread_t thread_handle, + PCONTEXT context_ptr) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + if (!::SetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} SetThreadContext failed with error {1}", __FUNCTION__, + error); + return error; + } + return Status(); +} + +std::unique_ptr +NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + return llvm::make_unique(target_arch, + native_thread); +} + +NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const { + return (reg_index < k_first_alias_i386); +} + +uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_i386::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_i386[set_index]; +} + +Status NativeRegisterContextWindows_i386::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_eax_i386: + reg_value.SetUInt32(tls_context.Eax); + break; + case lldb_ebx_i386: + reg_value.SetUInt32(tls_context.Ebx); + break; + case lldb_ecx_i386: + reg_value.SetUInt32(tls_context.Ecx); + break; + case lldb_edx_i386: + reg_value.SetUInt32(tls_context.Edx); + break; + case lldb_edi_i386: + reg_value.SetUInt32(tls_context.Edi); + break; + case lldb_esi_i386: + reg_value.SetUInt32(tls_context.Esi); + break; + case lldb_ebp_i386: + reg_value.SetUInt32(tls_context.Ebp); + break; + case lldb_esp_i386: + reg_value.SetUInt32(tls_context.Esp); + break; + case lldb_eip_i386: + reg_value.SetUInt32(tls_context.Eip); + break; + case lldb_eflags_i386: + reg_value.SetUInt32(tls_context.EFlags); + break; + case lldb_cs_i386: + reg_value.SetUInt32(tls_context.SegCs); + break; + case lldb_fs_i386: + reg_value.SetUInt32(tls_context.SegFs); + break; + case lldb_gs_i386: + reg_value.SetUInt32(tls_context.SegGs); + break; + case lldb_ss_i386: + reg_value.SetUInt32(tls_context.SegSs); + break; + case lldb_ds_i386: + reg_value.SetUInt32(tls_context.SegDs); + break; + case lldb_es_i386: + reg_value.SetUInt32(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_i386::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + auto thread_handle = GetThreadHandle(); + Status error = + GetThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_eax_i386: + tls_context.Eax = reg_value.GetAsUInt32(); + break; + case lldb_ebx_i386: + tls_context.Ebx = reg_value.GetAsUInt32(); + break; + case lldb_ecx_i386: + tls_context.Ecx = reg_value.GetAsUInt32(); + break; + case lldb_edx_i386: + tls_context.Edx = reg_value.GetAsUInt32(); + break; + case lldb_edi_i386: + tls_context.Edi = reg_value.GetAsUInt32(); + break; + case lldb_esi_i386: + tls_context.Esi = reg_value.GetAsUInt32(); + break; + case lldb_ebp_i386: + tls_context.Ebp = reg_value.GetAsUInt32(); + break; + case lldb_esp_i386: + tls_context.Esp = reg_value.GetAsUInt32(); + break; + case lldb_eip_i386: + tls_context.Eip = reg_value.GetAsUInt32(); + break; + case lldb_eflags_i386: + tls_context.EFlags = reg_value.GetAsUInt32(); + break; + case lldb_cs_i386: + tls_context.SegCs = reg_value.GetAsUInt32(); + break; + case lldb_fs_i386: + tls_context.SegFs = reg_value.GetAsUInt32(); + break; + case lldb_gs_i386: + tls_context.SegGs = reg_value.GetAsUInt32(); + break; + case lldb_ss_i386: + tls_context.SegSs = reg_value.GetAsUInt32(); + break; + case lldb_ds_i386: + tls_context.SegDs = reg_value.GetAsUInt32(); + break; + case lldb_es_i386: + tls_context.SegEs = reg_value.GetAsUInt32(); + break; + } + + return SetThreadContextHelper(thread_handle, &tls_context); +} + +Status +NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + const size_t data_size = REG_CONTEXT_SIZE; + data_sp = std::make_shared(data_size, 0); + ::CONTEXT tls_context; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &tls_context, data_size); + return error; +} + +Status NativeRegisterContextWindows_i386::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + const size_t data_size = REG_CONTEXT_SIZE; + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextWindows_i386::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != data_size) { + error.SetErrorStringWithFormatv( + "data_sp contained mismatched data size, expected {0}, actual {1}", + data_size, data_sp->GetByteSize()); + return error; + } + + ::CONTEXT tls_context; + memcpy(&tls_context, data_sp->GetBytes(), data_size); + return SetThreadContextHelper(GetThreadHandle(), &tls_context); +} + +Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_i386::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h @@ -0,0 +1,82 @@ +//===-- NativeRegisterContextWindows_x86_64.h -------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) +#ifndef liblldb_NativeRegisterContextWindows_x86_64_h_ +#define liblldb_NativeRegisterContextWindows_x86_64_h_ + +#include "Plugins/Process/Utility/RegisterContext_x86.h" +#include "Plugins/Process/Utility/lldb-x86-register-enums.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeThreadWindows; + +class NativeRegisterContextWindows_x86_64 + : public NativeRegisterContextWindows { +public: + NativeRegisterContextWindows_x86_64(const ArchSpec &target_arch, + NativeThreadProtocol &native_thread); + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::DataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override; + + Status GetWatchpointHitIndex(uint32_t &wp_index, + lldb::addr_t trap_addr) override; + + Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override; + + bool ClearHardwareWatchpoint(uint32_t wp_index) override; + + Status ClearAllHardwareWatchpoints() override; + + Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, + uint32_t watch_flags, + uint32_t wp_index); + + uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags) override; + + lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; + + uint32_t NumSupportedHardwareWatchpoints() override; + +protected: + Status GPRRead(const uint32_t reg, RegisterValue ®_value); + + Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + + Status FPRRead(const uint32_t reg, RegisterValue ®_value); + + Status FPRWrite(const uint32_t reg, const RegisterValue ®_value); + +private: + bool IsGPR(uint32_t reg_index) const; + + bool IsFPR(uint32_t reg_index) const; +}; + +} // namespace lldb_private + +#endif // liblldb_NativeRegisterContextWindows_x86_64_h_ +#endif // defined(_WIN64) Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp @@ -0,0 +1,579 @@ +//===-- NativeRegisterContextWindows_x86_64.cpp -----------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if defined(_WIN64) + +#include "NativeRegisterContextWindows_x86_64.h" +#include "NativeRegisterContextWindows_WoW64.h" +#include "NativeThreadWindows.h" +#include "Plugins/Process/Utility/RegisterContextWindows_i386.h" +#include "Plugins/Process/Utility/RegisterContextWindows_x86_64.h" +#include "ProcessWindowsLog.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +#define REG_CONTEXT_SIZE sizeof(::CONTEXT) + +namespace { +static const uint32_t g_gpr_regnums_x86_64[] = { + lldb_rax_x86_64, lldb_rbx_x86_64, lldb_rcx_x86_64, lldb_rdx_x86_64, + lldb_rdi_x86_64, lldb_rsi_x86_64, lldb_rbp_x86_64, lldb_rsp_x86_64, + lldb_r8_x86_64, lldb_r9_x86_64, lldb_r10_x86_64, lldb_r11_x86_64, + lldb_r12_x86_64, lldb_r13_x86_64, lldb_r14_x86_64, lldb_r15_x86_64, + lldb_rip_x86_64, lldb_rflags_x86_64, lldb_cs_x86_64, lldb_fs_x86_64, + lldb_gs_x86_64, lldb_ss_x86_64, lldb_ds_x86_64, lldb_es_x86_64, + LLDB_INVALID_REGNUM // Register set must be terminated with this flag +}; + +static const uint32_t g_fpr_regnums_x86_64[] = { + lldb_xmm0_x86_64, lldb_xmm1_x86_64, lldb_xmm2_x86_64, lldb_xmm3_x86_64, + lldb_xmm4_x86_64, lldb_xmm5_x86_64, lldb_xmm6_x86_64, lldb_xmm7_x86_64, + lldb_xmm8_x86_64, lldb_xmm9_x86_64, lldb_xmm10_x86_64, lldb_xmm11_x86_64, + lldb_xmm12_x86_64, lldb_xmm13_x86_64, lldb_xmm14_x86_64, lldb_xmm15_x86_64, + LLDB_INVALID_REGNUM // Register set must be terminated with this flag +}; + +static const RegisterSet g_reg_sets_x86_64[] = { + {"General Purpose Registers", "gpr", + llvm::array_lengthof(g_gpr_regnums_x86_64) - 1, g_gpr_regnums_x86_64}, + {"Floating Point Registers", "fpr", + llvm::array_lengthof(g_fpr_regnums_x86_64) - 1, g_fpr_regnums_x86_64}}; + +enum { k_num_register_sets = 2 }; + +} // namespace + +static RegisterInfoInterface * +CreateRegisterInfoInterface(const ArchSpec &target_arch) { + assert((HostInfo::GetArchitecture().GetAddressByteSize() == 8) && + "Register setting path assumes this is a 64-bit host"); + return new RegisterContextWindows_x86_64(target_arch); +} + +static Status GetThreadContextHelper(lldb::thread_t thread_handle, + PCONTEXT context_ptr, + const DWORD control_flag) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + + memset(context_ptr, 0, sizeof(::CONTEXT)); + context_ptr->ContextFlags = control_flag; + if (!::GetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} GetThreadContext failed with error {1}", __FUNCTION__, + error); + return error; + } + return Status(); +} + +static Status SetThreadContextHelper(lldb::thread_t thread_handle, + PCONTEXT context_ptr) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); + Status error; + // It's assumed that the thread has stopped. + if (!::SetThreadContext(thread_handle, context_ptr)) { + error.SetError(GetLastError(), eErrorTypeWin32); + LLDB_LOG(log, "{0} SetThreadContext failed with error {1}", __FUNCTION__, + error); + return error; + } + return Status(); +} + +std::unique_ptr +NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { + // Register context for a WoW64 application. + if (target_arch.GetAddressByteSize() == 4) + return llvm::make_unique(target_arch, + native_thread); + + // Register context for a native 64-bit application. + return llvm::make_unique(target_arch, + native_thread); +} + +NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64( + const ArchSpec &target_arch, NativeThreadProtocol &native_thread) + : NativeRegisterContextWindows(native_thread, + CreateRegisterInfoInterface(target_arch)) {} + +bool NativeRegisterContextWindows_x86_64::IsGPR(uint32_t reg_index) const { + return (reg_index >= k_first_gpr_x86_64 && reg_index < k_first_alias_x86_64); +} + +bool NativeRegisterContextWindows_x86_64::IsFPR(uint32_t reg_index) const { + return (reg_index >= lldb_xmm0_x86_64 && reg_index <= k_last_fpr_x86_64); +} + +uint32_t NativeRegisterContextWindows_x86_64::GetRegisterSetCount() const { + return k_num_register_sets; +} + +const RegisterSet * +NativeRegisterContextWindows_x86_64::GetRegisterSet(uint32_t set_index) const { + if (set_index >= k_num_register_sets) + return nullptr; + return &g_reg_sets_x86_64[set_index]; +} + +Status NativeRegisterContextWindows_x86_64::GPRRead(const uint32_t reg, + RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_rax_x86_64: + reg_value.SetUInt64(tls_context.Rax); + break; + case lldb_rbx_x86_64: + reg_value.SetUInt64(tls_context.Rbx); + break; + case lldb_rcx_x86_64: + reg_value.SetUInt64(tls_context.Rcx); + break; + case lldb_rdx_x86_64: + reg_value.SetUInt64(tls_context.Rdx); + break; + case lldb_rdi_x86_64: + reg_value.SetUInt64(tls_context.Rdi); + break; + case lldb_rsi_x86_64: + reg_value.SetUInt64(tls_context.Rsi); + break; + case lldb_rbp_x86_64: + reg_value.SetUInt64(tls_context.Rbp); + break; + case lldb_rsp_x86_64: + reg_value.SetUInt64(tls_context.Rsp); + break; + case lldb_r8_x86_64: + reg_value.SetUInt64(tls_context.R8); + break; + case lldb_r9_x86_64: + reg_value.SetUInt64(tls_context.R9); + break; + case lldb_r10_x86_64: + reg_value.SetUInt64(tls_context.R10); + break; + case lldb_r11_x86_64: + reg_value.SetUInt64(tls_context.R11); + break; + case lldb_r12_x86_64: + reg_value.SetUInt64(tls_context.R12); + break; + case lldb_r13_x86_64: + reg_value.SetUInt64(tls_context.R13); + break; + case lldb_r14_x86_64: + reg_value.SetUInt64(tls_context.R14); + break; + case lldb_r15_x86_64: + reg_value.SetUInt64(tls_context.R15); + break; + case lldb_rip_x86_64: + reg_value.SetUInt64(tls_context.Rip); + break; + case lldb_rflags_x86_64: + reg_value.SetUInt64(tls_context.EFlags | 0x2); // Bit #1 always 1 + break; + case lldb_cs_x86_64: + reg_value.SetUInt16(tls_context.SegCs); + break; + case lldb_fs_x86_64: + reg_value.SetUInt16(tls_context.SegFs); + break; + case lldb_gs_x86_64: + reg_value.SetUInt16(tls_context.SegGs); + break; + case lldb_ss_x86_64: + reg_value.SetUInt16(tls_context.SegSs); + break; + case lldb_ds_x86_64: + reg_value.SetUInt16(tls_context.SegDs); + break; + case lldb_es_x86_64: + reg_value.SetUInt16(tls_context.SegEs); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_x86_64::GPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS; + auto thread_handle = GetThreadHandle(); + Status error = + GetThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_rax_x86_64: + tls_context.Rax = reg_value.GetAsUInt64(); + break; + case lldb_rbx_x86_64: + tls_context.Rbx = reg_value.GetAsUInt64(); + break; + case lldb_rcx_x86_64: + tls_context.Rcx = reg_value.GetAsUInt64(); + break; + case lldb_rdx_x86_64: + tls_context.Rdx = reg_value.GetAsUInt64(); + break; + case lldb_rdi_x86_64: + tls_context.Rdi = reg_value.GetAsUInt64(); + break; + case lldb_rsi_x86_64: + tls_context.Rsi = reg_value.GetAsUInt64(); + break; + case lldb_rbp_x86_64: + tls_context.Rbp = reg_value.GetAsUInt64(); + break; + case lldb_rsp_x86_64: + tls_context.Rsp = reg_value.GetAsUInt64(); + break; + case lldb_r8_x86_64: + tls_context.R8 = reg_value.GetAsUInt64(); + break; + case lldb_r9_x86_64: + tls_context.R9 = reg_value.GetAsUInt64(); + break; + case lldb_r10_x86_64: + tls_context.R10 = reg_value.GetAsUInt64(); + break; + case lldb_r11_x86_64: + tls_context.R11 = reg_value.GetAsUInt64(); + break; + case lldb_r12_x86_64: + tls_context.R12 = reg_value.GetAsUInt64(); + break; + case lldb_r13_x86_64: + tls_context.R13 = reg_value.GetAsUInt64(); + break; + case lldb_r14_x86_64: + tls_context.R14 = reg_value.GetAsUInt64(); + break; + case lldb_r15_x86_64: + tls_context.R15 = reg_value.GetAsUInt64(); + break; + case lldb_rip_x86_64: + tls_context.Rip = reg_value.GetAsUInt64(); + break; + case lldb_rflags_x86_64: + tls_context.EFlags = reg_value.GetAsUInt64(); + break; + case lldb_cs_x86_64: + tls_context.SegCs = reg_value.GetAsUInt16(); + break; + case lldb_fs_x86_64: + tls_context.SegFs = reg_value.GetAsUInt16(); + break; + case lldb_gs_x86_64: + tls_context.SegGs = reg_value.GetAsUInt16(); + break; + case lldb_ss_x86_64: + tls_context.SegSs = reg_value.GetAsUInt16(); + break; + case lldb_ds_x86_64: + tls_context.SegDs = reg_value.GetAsUInt16(); + break; + case lldb_es_x86_64: + tls_context.SegEs = reg_value.GetAsUInt16(); + break; + } + + return SetThreadContextHelper(thread_handle, &tls_context); +} + +Status NativeRegisterContextWindows_x86_64::FPRRead(const uint32_t reg, + RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_FLOATING_POINT; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_xmm0_x86_64: + reg_value.SetBytes(&tls_context.Xmm0, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm1_x86_64: + reg_value.SetBytes(&tls_context.Xmm1, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm2_x86_64: + reg_value.SetBytes(&tls_context.Xmm2, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm3_x86_64: + reg_value.SetBytes(&tls_context.Xmm3, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm4_x86_64: + reg_value.SetBytes(&tls_context.Xmm4, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm5_x86_64: + reg_value.SetBytes(&tls_context.Xmm5, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm6_x86_64: + reg_value.SetBytes(&tls_context.Xmm6, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm7_x86_64: + reg_value.SetBytes(&tls_context.Xmm7, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm8_x86_64: + reg_value.SetBytes(&tls_context.Xmm8, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm9_x86_64: + reg_value.SetBytes(&tls_context.Xmm9, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm10_x86_64: + reg_value.SetBytes(&tls_context.Xmm10, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm11_x86_64: + reg_value.SetBytes(&tls_context.Xmm11, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm12_x86_64: + reg_value.SetBytes(&tls_context.Xmm12, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm13_x86_64: + reg_value.SetBytes(&tls_context.Xmm13, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm14_x86_64: + reg_value.SetBytes(&tls_context.Xmm14, 16, endian::InlHostByteOrder()); + break; + case lldb_xmm15_x86_64: + reg_value.SetBytes(&tls_context.Xmm15, 16, endian::InlHostByteOrder()); + break; + } + + return error; +} + +Status +NativeRegisterContextWindows_x86_64::FPRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_CONTROL | CONTEXT_FLOATING_POINT; + auto thread_handle = GetThreadHandle(); + Status error = + GetThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_xmm0_x86_64: + memcpy(&tls_context.Xmm0, reg_value.GetBytes(), 16); + break; + case lldb_xmm1_x86_64: + memcpy(&tls_context.Xmm1, reg_value.GetBytes(), 16); + break; + case lldb_xmm2_x86_64: + memcpy(&tls_context.Xmm2, reg_value.GetBytes(), 16); + break; + case lldb_xmm3_x86_64: + memcpy(&tls_context.Xmm3, reg_value.GetBytes(), 16); + break; + case lldb_xmm4_x86_64: + memcpy(&tls_context.Xmm4, reg_value.GetBytes(), 16); + break; + case lldb_xmm5_x86_64: + memcpy(&tls_context.Xmm5, reg_value.GetBytes(), 16); + break; + case lldb_xmm6_x86_64: + memcpy(&tls_context.Xmm6, reg_value.GetBytes(), 16); + break; + case lldb_xmm7_x86_64: + memcpy(&tls_context.Xmm7, reg_value.GetBytes(), 16); + break; + case lldb_xmm8_x86_64: + memcpy(&tls_context.Xmm8, reg_value.GetBytes(), 16); + break; + case lldb_xmm9_x86_64: + memcpy(&tls_context.Xmm9, reg_value.GetBytes(), 16); + break; + case lldb_xmm10_x86_64: + memcpy(&tls_context.Xmm10, reg_value.GetBytes(), 16); + break; + case lldb_xmm11_x86_64: + memcpy(&tls_context.Xmm11, reg_value.GetBytes(), 16); + break; + case lldb_xmm12_x86_64: + memcpy(&tls_context.Xmm12, reg_value.GetBytes(), 16); + break; + case lldb_xmm13_x86_64: + memcpy(&tls_context.Xmm13, reg_value.GetBytes(), 16); + break; + case lldb_xmm14_x86_64: + memcpy(&tls_context.Xmm14, reg_value.GetBytes(), 16); + break; + case lldb_xmm15_x86_64: + memcpy(&tls_context.Xmm15, reg_value.GetBytes(), 16); + break; + } + + return SetThreadContextHelper(thread_handle, &tls_context); +} + +Status +NativeRegisterContextWindows_x86_64::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRRead(reg, reg_value); + + if (IsFPR(reg)) + return FPRRead(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::WriteRegister( + const RegisterInfo *reg_info, const RegisterValue ®_value) { + Status error; + + if (!reg_info) { + error.SetErrorString("reg_info NULL"); + return error; + } + + const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB]; + if (reg == LLDB_INVALID_REGNUM) { + // This is likely an internal register for lldb use only and should not be + // directly queried. + error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb " + "register, cannot read directly", + reg_info->name); + return error; + } + + if (IsGPR(reg)) + return GPRWrite(reg, reg_value); + + if (IsFPR(reg)) + return FPRWrite(reg, reg_value); + + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::ReadAllRegisterValues( + lldb::DataBufferSP &data_sp) { + const size_t data_size = REG_CONTEXT_SIZE; + data_sp = std::make_shared(data_size, 0); + ::CONTEXT tls_context; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_ALL); + if (error.Fail()) + return error; + + uint8_t *dst = data_sp->GetBytes(); + ::memcpy(dst, &tls_context, data_size); + return error; +} + +Status NativeRegisterContextWindows_x86_64::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + Status error; + const size_t data_size = REG_CONTEXT_SIZE; + if (!data_sp) { + error.SetErrorStringWithFormat( + "NativeRegisterContextWindows_x86_64::%s invalid data_sp provided", + __FUNCTION__); + return error; + } + + if (data_sp->GetByteSize() != data_size) { + error.SetErrorStringWithFormatv( + "data_sp contained mismatched data size, expected {0}, actual {1}", + data_size, data_sp->GetByteSize()); + return error; + } + + ::CONTEXT tls_context; + memcpy(&tls_context, data_sp->GetBytes(), data_size); + return SetThreadContextHelper(GetThreadHandle(), &tls_context); +} + +Status NativeRegisterContextWindows_x86_64::IsWatchpointHit(uint32_t wp_index, + bool &is_hit) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::GetWatchpointHitIndex( + uint32_t &wp_index, lldb::addr_t trap_addr) { + return Status("unimplemented"); +} + +Status +NativeRegisterContextWindows_x86_64::IsWatchpointVacant(uint32_t wp_index, + bool &is_vacant) { + return Status("unimplemented"); +} + +Status NativeRegisterContextWindows_x86_64::SetHardwareWatchpointWithIndex( + lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { + return Status("unimplemented"); +} + +bool NativeRegisterContextWindows_x86_64::ClearHardwareWatchpoint( + uint32_t wp_index) { + return false; +} + +Status NativeRegisterContextWindows_x86_64::ClearAllHardwareWatchpoints() { + return Status("unimplemented"); +} + +uint32_t NativeRegisterContextWindows_x86_64::SetHardwareWatchpoint( + lldb::addr_t addr, size_t size, uint32_t watch_flags) { + return LLDB_INVALID_INDEX32; +} + +lldb::addr_t +NativeRegisterContextWindows_x86_64::GetWatchpointAddress(uint32_t wp_index) { + return LLDB_INVALID_ADDRESS; +} + +uint32_t +NativeRegisterContextWindows_x86_64::NumSupportedHardwareWatchpoints() { + // Not implemented + return 0; +} + +#endif // defined(_WIN64) Index: lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h @@ -0,0 +1,70 @@ +//===-- NativeThreadWindows.h -----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_NativeThreadWindows_h_ +#define liblldb_NativeThreadWindows_h_ + +#include "lldb/Host/HostThread.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" + +#include "NativeRegisterContextWindows.h" + +namespace lldb_private { + +class NativeProcessWindows; + +class NativeThreadWindows : public NativeThreadProtocol { +public: + NativeThreadWindows(NativeProcessWindows &process, const HostThread &thread); + + ~NativeThreadWindows() {} + + Status DoStop(); + Status DoResume(lldb::StateType resume_state); + + std::string GetName() override; + + lldb::StateType GetState() override { return m_state; } + + NativeRegisterContextWindows &GetRegisterContext() override { + return *m_reg_context_up; + } + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + void SetStopReason(ThreadStopInfo stop_info, std::string description); + + const HostThread &GetHostThread() { return m_host_thread; } + +protected: + lldb::StateType m_state = lldb::StateType::eStateInvalid; + std::string m_name; + ThreadStopInfo m_stop_info; + std::string m_stop_description; + std::unique_ptr m_reg_context_up; + // Cache address and index of the watchpoints and hardware breakpoints since + // the register context does not. + using IndexMap = std::map; + IndexMap m_watchpoint_index_map; + IndexMap m_hw_breakpoint_index_map; + HostThread m_host_thread; +}; +} // namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadWindows_h_ Index: lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp =================================================================== --- /dev/null +++ lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp @@ -0,0 +1,149 @@ +//===-- NativeThreadWindows.cpp ---------------------------------*- C++ -*-===// +// +// 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 "NativeThreadWindows.h" +#include "NativeProcessWindows.h" + +#include "lldb/Host/HostThread.h" +#include "lldb/Host/Windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" + +#include "lldb/lldb-forward.h" + +using namespace lldb; +using namespace lldb_private; + +NativeThreadWindows::NativeThreadWindows(NativeProcessWindows &process, + const HostThread &thread) + : NativeThreadProtocol(process, thread.GetNativeThread().GetThreadId()), + m_stop_info(), m_stop_description(), m_host_thread(thread) { + m_reg_context_up = + (NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( + process.GetArchitecture(), *this)); +} + +Status NativeThreadWindows::DoStop() { + if (m_state != eStateStopped) { + DWORD previous_suspend_count = + ::SuspendThread(m_host_thread.GetNativeThread().GetSystemHandle()); + if (previous_suspend_count == (DWORD)-1) + return Status(::GetLastError(), eErrorTypeWin32); + + m_state = eStateStopped; + } + return Status(); +} + +Status NativeThreadWindows::DoResume(lldb::StateType resume_state) { + StateType current_state = GetState(); + if (resume_state == current_state) + return Status(); + + if (resume_state == eStateStepping) { + uint32_t flags_index = + GetRegisterContext().ConvertRegisterKindToRegisterNumber( + eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); + uint64_t flags_value = + GetRegisterContext().ReadRegisterAsUnsigned(flags_index, 0); + flags_value |= 0x100; // Set the trap flag on the CPU + GetRegisterContext().WriteRegisterFromUnsigned(flags_index, flags_value); + } + + if (resume_state == eStateStepping || resume_state == eStateRunning) { + DWORD previous_suspend_count = 0; + HANDLE thread_handle = m_host_thread.GetNativeThread().GetSystemHandle(); + do { + // ResumeThread returns -1 on error, or the thread's *previous* suspend + // count on success. This means that the return value is 1 when the thread + // was restarted. Note that DWORD is an unsigned int, so we need to + // explicitly compare with -1. + previous_suspend_count = ::ResumeThread(thread_handle); + + if (previous_suspend_count == (DWORD)-1) + return Status(::GetLastError(), eErrorTypeWin32); + + } while (previous_suspend_count > 1); + m_state = eStateRunning; + } + + return Status(); +} + +std::string NativeThreadWindows::GetName() { + if (!m_name.empty()) + return m_name; + + // Name is not a property of the Windows thread. Create one with the + // process's. + NativeProcessProtocol &process = GetProcess(); + ProcessInstanceInfo process_info; + if (Host::GetProcessInfo(process.GetID(), process_info)) { + std::string process_name(process_info.GetName()); + m_name = process_name; + } + return m_name; +} + +void NativeThreadWindows::SetStopReason(ThreadStopInfo stop_info, + std::string description) { + m_state = eStateStopped; + m_stop_info = stop_info; + m_stop_description = description; +} + +bool NativeThreadWindows::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + switch (m_state) { + case eStateStopped: + case eStateCrashed: + case eStateExited: + case eStateSuspended: + case eStateUnloaded: + stop_info = m_stop_info; + description = m_stop_description; + return true; + + case eStateInvalid: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateRunning: + case eStateStepping: + case eStateDetached: + if (log) { + log->Printf("NativeThreadWindows::%s tid %" PRIu64 + " in state %s cannot answer stop reason", + __FUNCTION__, GetID(), StateAsCString(m_state)); + } + return false; + } + llvm_unreachable("unhandled StateType!"); +} + +Status NativeThreadWindows::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + return Status("unimplemented."); +} + +Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) { + return Status("unimplemented"); +} + +Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr, + size_t size) { + return Status("unimplemented."); +} + +Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) { + return Status("unimplemented."); +} Index: lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp =================================================================== --- lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp +++ lldb/source/Plugins/Process/Windows/Common/RegisterContextWindows.cpp @@ -123,7 +123,9 @@ return false; } memcpy(&m_context, tmpContext, sizeof(m_context)); - if (!::SuspendThread(wthread.GetHostThread().GetNativeThread().GetSystemHandle())) { + if (::SuspendThread( + wthread.GetHostThread().GetNativeThread().GetSystemHandle()) == + (DWORD)-1) { return false; } if (!::GetThreadContext( @@ -135,7 +137,9 @@ ::GetLastError()); return false; } - if (!::ResumeThread(wthread.GetHostThread().GetNativeThread().GetSystemHandle())) { + if (::ResumeThread( + wthread.GetHostThread().GetNativeThread().GetSystemHandle()) == + (DWORD)-1) { return false; } LLDB_LOG(log, "successfully updated the register values."); Index: lldb/third_party/Python/module/unittest2/unittest2/case.py =================================================================== --- lldb/third_party/Python/module/unittest2/unittest2/case.py +++ lldb/third_party/Python/module/unittest2/unittest2/case.py @@ -753,7 +753,7 @@ elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr) differing = '%ss differ: %s != %s\n' % elements - for i in xrange(min(len1, len2)): + for i in range(min(len1, len2)): try: item1 = seq1[i] except (TypeError, IndexError, NotImplementedError): Index: lldb/unittests/tools/lldb-server/tests/LLGSTest.cpp =================================================================== --- lldb/unittests/tools/lldb-server/tests/LLGSTest.cpp +++ lldb/unittests/tools/lldb-server/tests/LLGSTest.cpp @@ -14,6 +14,10 @@ using namespace lldb_private; using namespace llvm; +#ifdef SendMessage +#undef SendMessage +#endif + TEST_F(TestBase, LaunchModePreservesEnvironment) { putenv(const_cast("LLDB_TEST_MAGIC_VARIABLE=LLDB_TEST_MAGIC_VALUE")); Index: lldb/unittests/tools/lldb-server/tests/TestBase.h =================================================================== --- lldb/unittests/tools/lldb-server/tests/TestBase.h +++ lldb/unittests/tools/lldb-server/tests/TestBase.h @@ -12,6 +12,7 @@ #include "TestClient.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/Socket.h" #include "llvm/Support/Path.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" @@ -23,9 +24,11 @@ static void SetUpTestCase() { lldb_private::FileSystem::Initialize(); lldb_private::HostInfo::Initialize(); + ASSERT_THAT_ERROR(lldb_private::Socket::Initialize(), llvm::Succeeded()); } static void TearDownTestCase() { + lldb_private::Socket::Terminate(); lldb_private::HostInfo::Terminate(); lldb_private::FileSystem::Terminate(); } Index: lldb/unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- lldb/unittests/tools/lldb-server/tests/TestClient.cpp +++ lldb/unittests/tools/lldb-server/tests/TestClient.cpp @@ -25,6 +25,10 @@ using namespace llvm; using namespace llgs_tests; +#ifdef SendMessage +#undef SendMessage +#endif + TestClient::TestClient(std::unique_ptr Conn) { SetConnection(Conn.release()); SetPacketTimeout(std::chrono::seconds(10));