summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Storsjo <martin@martin.st>2019-10-21 08:02:34 +0000
committerMartin Storsjo <martin@martin.st>2019-10-21 08:02:34 +0000
commit963531af8b16f3624532f26c110fa72528417975 (patch)
tree184e84547148afea7b996e37f6119ffafc6642e7
parentcbe8a05a0f66c11f677ec8b5da5d1d2e49dd3b4c (diff)
[LLDB] [Windows] Initial support for ARM register contexts
Differential Revision: https://reviews.llvm.org/D69226 git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@375392 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--source/Plugins/Process/Windows/Common/CMakeLists.txt3
-rw-r--r--source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp644
-rw-r--r--source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h80
-rw-r--r--source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp10
-rw-r--r--source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp432
-rw-r--r--source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h47
-rw-r--r--test/Shell/Register/Inputs/arm-fp-read.cpp19
-rw-r--r--test/Shell/Register/Inputs/arm-gp-read.cpp44
-rw-r--r--test/Shell/Register/arm-fp-read.test21
-rw-r--r--test/Shell/Register/arm-gp-read.test19
10 files changed, 1316 insertions, 3 deletions
diff --git a/source/Plugins/Process/Windows/Common/CMakeLists.txt b/source/Plugins/Process/Windows/Common/CMakeLists.txt
index d0d3fcbee..876bc8cab 100644
--- a/source/Plugins/Process/Windows/Common/CMakeLists.txt
+++ b/source/Plugins/Process/Windows/Common/CMakeLists.txt
@@ -4,6 +4,7 @@ add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN
LocalDebugDelegate.cpp
NativeProcessWindows.cpp
NativeRegisterContextWindows.cpp
+ NativeRegisterContextWindows_arm.cpp
NativeRegisterContextWindows_arm64.cpp
NativeRegisterContextWindows_i386.cpp
NativeRegisterContextWindows_WoW64.cpp
@@ -14,10 +15,10 @@ add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN
ProcessWindowsLog.cpp
RegisterContextWindows.cpp
TargetThreadWindows.cpp
+ arm/RegisterContextWindows_arm.cpp
arm64/RegisterContextWindows_arm64.cpp
x64/RegisterContextWindows_x64.cpp
x86/RegisterContextWindows_x86.cpp
- # TODO add support for ARM (NT)
LINK_LIBS
lldbCore
diff --git a/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp b/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
new file mode 100644
index 000000000..d25b08f7e
--- /dev/null
+++ b/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
@@ -0,0 +1,644 @@
+//===-- NativeRegisterContextWindows_arm.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(__arm__) || defined(_M_ARM)
+
+#include "NativeRegisterContextWindows_arm.h"
+#include "NativeThreadWindows.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.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_arm[] = {
+ gpr_r0_arm, gpr_r1_arm, gpr_r2_arm, gpr_r3_arm, gpr_r4_arm,
+ gpr_r5_arm, gpr_r6_arm, gpr_r7_arm, gpr_r8_arm, gpr_r9_arm,
+ gpr_r10_arm, gpr_r11_arm, gpr_r12_arm, gpr_sp_arm, gpr_lr_arm,
+ gpr_pc_arm, gpr_cpsr_arm,
+ LLDB_INVALID_REGNUM // Register set must be terminated with this flag
+};
+static_assert(((sizeof g_gpr_regnums_arm / sizeof g_gpr_regnums_arm[0]) - 1) ==
+ k_num_gpr_registers_arm,
+ "g_gpr_regnums_arm has wrong number of register infos");
+
+static const uint32_t g_fpr_regnums_arm[] = {
+ fpu_s0_arm, fpu_s1_arm, fpu_s2_arm, fpu_s3_arm, fpu_s4_arm,
+ fpu_s5_arm, fpu_s6_arm, fpu_s7_arm, fpu_s8_arm, fpu_s9_arm,
+ fpu_s10_arm, fpu_s11_arm, fpu_s12_arm, fpu_s13_arm, fpu_s14_arm,
+ fpu_s15_arm, fpu_s16_arm, fpu_s17_arm, fpu_s18_arm, fpu_s19_arm,
+ fpu_s20_arm, fpu_s21_arm, fpu_s22_arm, fpu_s23_arm, fpu_s24_arm,
+ fpu_s25_arm, fpu_s26_arm, fpu_s27_arm, fpu_s28_arm, fpu_s29_arm,
+ fpu_s30_arm, fpu_s31_arm,
+
+ fpu_d0_arm, fpu_d1_arm, fpu_d2_arm, fpu_d3_arm, fpu_d4_arm,
+ fpu_d5_arm, fpu_d6_arm, fpu_d7_arm, fpu_d8_arm, fpu_d9_arm,
+ fpu_d10_arm, fpu_d11_arm, fpu_d12_arm, fpu_d13_arm, fpu_d14_arm,
+ fpu_d15_arm, fpu_d16_arm, fpu_d17_arm, fpu_d18_arm, fpu_d19_arm,
+ fpu_d20_arm, fpu_d21_arm, fpu_d22_arm, fpu_d23_arm, fpu_d24_arm,
+ fpu_d25_arm, fpu_d26_arm, fpu_d27_arm, fpu_d28_arm, fpu_d29_arm,
+ fpu_d30_arm, fpu_d31_arm,
+
+ fpu_q0_arm, fpu_q1_arm, fpu_q2_arm, fpu_q3_arm, fpu_q4_arm,
+ fpu_q5_arm, fpu_q6_arm, fpu_q7_arm, fpu_q8_arm, fpu_q9_arm,
+ fpu_q10_arm, fpu_q11_arm, fpu_q12_arm, fpu_q13_arm, fpu_q14_arm,
+ fpu_q15_arm,
+
+ fpu_fpscr_arm,
+ LLDB_INVALID_REGNUM // Register set must be terminated with this flag
+};
+static_assert(((sizeof g_fpr_regnums_arm / sizeof g_fpr_regnums_arm[0]) - 1) ==
+ k_num_fpr_registers_arm,
+ "g_fpu_regnums_arm has wrong number of register infos");
+
+static const RegisterSet g_reg_sets_arm[] = {
+ {"General Purpose Registers", "gpr",
+ llvm::array_lengthof(g_gpr_regnums_arm) - 1, g_gpr_regnums_arm},
+ {"Floating Point Registers", "fpr",
+ llvm::array_lengthof(g_fpr_regnums_arm) - 1, g_fpr_regnums_arm},
+};
+
+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 RegisterInfoPOSIX_arm(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>
+NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread) {
+ // TODO: Register context for a WoW64 application?
+
+ // Register context for a native 64-bit application.
+ return std::make_unique<NativeRegisterContextWindows_arm>(target_arch,
+ native_thread);
+}
+
+NativeRegisterContextWindows_arm::NativeRegisterContextWindows_arm(
+ const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
+ : NativeRegisterContextWindows(native_thread,
+ CreateRegisterInfoInterface(target_arch)) {}
+
+bool NativeRegisterContextWindows_arm::IsGPR(uint32_t reg_index) const {
+ return (reg_index >= k_first_gpr_arm && reg_index <= k_last_gpr_arm);
+}
+
+bool NativeRegisterContextWindows_arm::IsFPR(uint32_t reg_index) const {
+ return (reg_index >= k_first_fpr_arm && reg_index <= k_last_fpr_arm);
+}
+
+uint32_t NativeRegisterContextWindows_arm::GetRegisterSetCount() const {
+ return k_num_register_sets;
+}
+
+const RegisterSet *
+NativeRegisterContextWindows_arm::GetRegisterSet(uint32_t set_index) const {
+ if (set_index >= k_num_register_sets)
+ return nullptr;
+ return &g_reg_sets_arm[set_index];
+}
+
+Status NativeRegisterContextWindows_arm::GPRRead(const uint32_t reg,
+ RegisterValue &reg_value) {
+ ::CONTEXT tls_context;
+ DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ Status error =
+ GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case gpr_r0_arm:
+ reg_value.SetUInt32(tls_context.R0);
+ break;
+ case gpr_r1_arm:
+ reg_value.SetUInt32(tls_context.R1);
+ break;
+ case gpr_r2_arm:
+ reg_value.SetUInt32(tls_context.R2);
+ break;
+ case gpr_r3_arm:
+ reg_value.SetUInt32(tls_context.R3);
+ break;
+ case gpr_r4_arm:
+ reg_value.SetUInt32(tls_context.R4);
+ break;
+ case gpr_r5_arm:
+ reg_value.SetUInt32(tls_context.R5);
+ break;
+ case gpr_r6_arm:
+ reg_value.SetUInt32(tls_context.R6);
+ break;
+ case gpr_r7_arm:
+ reg_value.SetUInt32(tls_context.R7);
+ break;
+ case gpr_r8_arm:
+ reg_value.SetUInt32(tls_context.R8);
+ break;
+ case gpr_r9_arm:
+ reg_value.SetUInt32(tls_context.R9);
+ break;
+ case gpr_r10_arm:
+ reg_value.SetUInt32(tls_context.R10);
+ break;
+ case gpr_r11_arm:
+ reg_value.SetUInt32(tls_context.R11);
+ break;
+ case gpr_r12_arm:
+ reg_value.SetUInt32(tls_context.R12);
+ break;
+ case gpr_sp_arm:
+ reg_value.SetUInt32(tls_context.Sp);
+ break;
+ case gpr_lr_arm:
+ reg_value.SetUInt32(tls_context.Lr);
+ break;
+ case gpr_pc_arm:
+ reg_value.SetUInt32(tls_context.Pc);
+ break;
+ case gpr_cpsr_arm:
+ reg_value.SetUInt32(tls_context.Cpsr);
+ break;
+ }
+
+ return error;
+}
+
+Status
+NativeRegisterContextWindows_arm::GPRWrite(const uint32_t reg,
+ const RegisterValue &reg_value) {
+ ::CONTEXT tls_context;
+ DWORD context_flag = CONTEXT_CONTROL | CONTEXT_INTEGER;
+ auto thread_handle = GetThreadHandle();
+ Status error =
+ GetThreadContextHelper(thread_handle, &tls_context, context_flag);
+ if (error.Fail())
+ return error;
+
+ switch (reg) {
+ case gpr_r0_arm:
+ tls_context.R0 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r1_arm:
+ tls_context.R1 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r2_arm:
+ tls_context.R2 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r3_arm:
+ tls_context.R3 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r4_arm:
+ tls_context.R4 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r5_arm:
+ tls_context.R5 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r6_arm:
+ tls_context.R6 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r7_arm:
+ tls_context.R7 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r8_arm:
+ tls_context.R8 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r9_arm:
+ tls_context.R9 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r10_arm:
+ tls_context.R10 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r11_arm:
+ tls_context.R11 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r12_arm:
+ tls_context.R12 = reg_value.GetAsUInt32();
+ break;
+ case gpr_sp_arm:
+ tls_context.Sp = reg_value.GetAsUInt32();
+ break;
+ case gpr_lr_arm:
+ tls_context.Lr = reg_value.GetAsUInt32();
+ break;
+ case gpr_pc_arm:
+ tls_context.Pc = reg_value.GetAsUInt32();
+ break;
+ case gpr_cpsr_arm:
+ tls_context.Cpsr = reg_value.GetAsUInt32();
+ break;
+ }
+
+ return SetThreadContextHelper(thread_handle, &tls_context);
+}
+
+Status NativeRegisterContextWindows_arm::FPRRead(const uint32_t reg,
+ RegisterValue &reg_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 fpu_s0_arm:
+ case fpu_s1_arm:
+ case fpu_s2_arm:
+ case fpu_s3_arm:
+ case fpu_s4_arm:
+ case fpu_s5_arm:
+ case fpu_s6_arm:
+ case fpu_s7_arm:
+ case fpu_s8_arm:
+ case fpu_s9_arm:
+ case fpu_s10_arm:
+ case fpu_s11_arm:
+ case fpu_s12_arm:
+ case fpu_s13_arm:
+ case fpu_s14_arm:
+ case fpu_s15_arm:
+ case fpu_s16_arm:
+ case fpu_s17_arm:
+ case fpu_s18_arm:
+ case fpu_s19_arm:
+ case fpu_s20_arm:
+ case fpu_s21_arm:
+ case fpu_s22_arm:
+ case fpu_s23_arm:
+ case fpu_s24_arm:
+ case fpu_s25_arm:
+ case fpu_s26_arm:
+ case fpu_s27_arm:
+ case fpu_s28_arm:
+ case fpu_s29_arm:
+ case fpu_s30_arm:
+ case fpu_s31_arm:
+ reg_value.SetUInt32(tls_context.S[reg - fpu_s0_arm],
+ RegisterValue::eTypeFloat);
+ break;
+
+ case fpu_d0_arm:
+ case fpu_d1_arm:
+ case fpu_d2_arm:
+ case fpu_d3_arm:
+ case fpu_d4_arm:
+ case fpu_d5_arm:
+ case fpu_d6_arm:
+ case fpu_d7_arm:
+ case fpu_d8_arm:
+ case fpu_d9_arm:
+ case fpu_d10_arm:
+ case fpu_d11_arm:
+ case fpu_d12_arm:
+ case fpu_d13_arm:
+ case fpu_d14_arm:
+ case fpu_d15_arm:
+ case fpu_d16_arm:
+ case fpu_d17_arm:
+ case fpu_d18_arm:
+ case fpu_d19_arm:
+ case fpu_d20_arm:
+ case fpu_d21_arm:
+ case fpu_d22_arm:
+ case fpu_d23_arm:
+ case fpu_d24_arm:
+ case fpu_d25_arm:
+ case fpu_d26_arm:
+ case fpu_d27_arm:
+ case fpu_d28_arm:
+ case fpu_d29_arm:
+ case fpu_d30_arm:
+ case fpu_d31_arm:
+ reg_value.SetUInt64(tls_context.D[reg - fpu_d0_arm],
+ RegisterValue::eTypeDouble);
+ break;
+
+ case fpu_q0_arm:
+ case fpu_q1_arm:
+ case fpu_q2_arm:
+ case fpu_q3_arm:
+ case fpu_q4_arm:
+ case fpu_q5_arm:
+ case fpu_q6_arm:
+ case fpu_q7_arm:
+ case fpu_q8_arm:
+ case fpu_q9_arm:
+ case fpu_q10_arm:
+ case fpu_q11_arm:
+ case fpu_q12_arm:
+ case fpu_q13_arm:
+ case fpu_q14_arm:
+ case fpu_q15_arm:
+ reg_value.SetBytes(&tls_context.Q[reg - fpu_q0_arm], 16,
+ endian::InlHostByteOrder());
+ break;
+
+ case fpu_fpscr_arm:
+ reg_value.SetUInt32(tls_context.Fpscr);
+ break;
+ }
+
+ return error;
+}
+
+Status
+NativeRegisterContextWindows_arm::FPRWrite(const uint32_t reg,
+ const RegisterValue &reg_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 fpu_s0_arm:
+ case fpu_s1_arm:
+ case fpu_s2_arm:
+ case fpu_s3_arm:
+ case fpu_s4_arm:
+ case fpu_s5_arm:
+ case fpu_s6_arm:
+ case fpu_s7_arm:
+ case fpu_s8_arm:
+ case fpu_s9_arm:
+ case fpu_s10_arm:
+ case fpu_s11_arm:
+ case fpu_s12_arm:
+ case fpu_s13_arm:
+ case fpu_s14_arm:
+ case fpu_s15_arm:
+ case fpu_s16_arm:
+ case fpu_s17_arm:
+ case fpu_s18_arm:
+ case fpu_s19_arm:
+ case fpu_s20_arm:
+ case fpu_s21_arm:
+ case fpu_s22_arm:
+ case fpu_s23_arm:
+ case fpu_s24_arm:
+ case fpu_s25_arm:
+ case fpu_s26_arm:
+ case fpu_s27_arm:
+ case fpu_s28_arm:
+ case fpu_s29_arm:
+ case fpu_s30_arm:
+ case fpu_s31_arm:
+ tls_context.S[reg - fpu_s0_arm] = reg_value.GetAsUInt32();
+ break;
+
+ case fpu_d0_arm:
+ case fpu_d1_arm:
+ case fpu_d2_arm:
+ case fpu_d3_arm:
+ case fpu_d4_arm:
+ case fpu_d5_arm:
+ case fpu_d6_arm:
+ case fpu_d7_arm:
+ case fpu_d8_arm:
+ case fpu_d9_arm:
+ case fpu_d10_arm:
+ case fpu_d11_arm:
+ case fpu_d12_arm:
+ case fpu_d13_arm:
+ case fpu_d14_arm:
+ case fpu_d15_arm:
+ case fpu_d16_arm:
+ case fpu_d17_arm:
+ case fpu_d18_arm:
+ case fpu_d19_arm:
+ case fpu_d20_arm:
+ case fpu_d21_arm:
+ case fpu_d22_arm:
+ case fpu_d23_arm:
+ case fpu_d24_arm:
+ case fpu_d25_arm:
+ case fpu_d26_arm:
+ case fpu_d27_arm:
+ case fpu_d28_arm:
+ case fpu_d29_arm:
+ case fpu_d30_arm:
+ case fpu_d31_arm:
+ tls_context.D[reg - fpu_d0_arm] = reg_value.GetAsUInt64();
+ break;
+
+ case fpu_q0_arm:
+ case fpu_q1_arm:
+ case fpu_q2_arm:
+ case fpu_q3_arm:
+ case fpu_q4_arm:
+ case fpu_q5_arm:
+ case fpu_q6_arm:
+ case fpu_q7_arm:
+ case fpu_q8_arm:
+ case fpu_q9_arm:
+ case fpu_q10_arm:
+ case fpu_q11_arm:
+ case fpu_q12_arm:
+ case fpu_q13_arm:
+ case fpu_q14_arm:
+ case fpu_q15_arm:
+ memcpy(&tls_context.Q[reg - fpu_q0_arm], reg_value.GetBytes(), 16);
+ break;
+
+ case fpu_fpscr_arm:
+ tls_context.Fpscr = reg_value.GetAsUInt32();
+ break;
+ }
+
+ return SetThreadContextHelper(thread_handle, &tls_context);
+}
+
+Status
+NativeRegisterContextWindows_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_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_arm::WriteRegister(
+ const RegisterInfo *reg_info, const RegisterValue &reg_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 written.
+ error.SetErrorStringWithFormat("register \"%s\" is an internal-only lldb "
+ "register, cannot write 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_arm::ReadAllRegisterValues(
+ lldb::DataBufferSP &data_sp) {
+ const size_t data_size = REG_CONTEXT_SIZE;
+ data_sp = std::make_shared<DataBufferHeap>(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_arm::WriteAllRegisterValues(
+ const lldb::DataBufferSP &data_sp) {
+ Status error;
+ const size_t data_size = REG_CONTEXT_SIZE;
+ if (!data_sp) {
+ error.SetErrorStringWithFormat(
+ "NativeRegisterContextWindows_arm::%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_arm::IsWatchpointHit(uint32_t wp_index,
+ bool &is_hit) {
+ return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::GetWatchpointHitIndex(
+ uint32_t &wp_index, lldb::addr_t trap_addr) {
+ return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::IsWatchpointVacant(uint32_t wp_index,
+ bool &is_vacant) {
+ return Status("unimplemented");
+}
+
+Status NativeRegisterContextWindows_arm::SetHardwareWatchpointWithIndex(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
+ return Status("unimplemented");
+}
+
+bool NativeRegisterContextWindows_arm::ClearHardwareWatchpoint(
+ uint32_t wp_index) {
+ return false;
+}
+
+Status NativeRegisterContextWindows_arm::ClearAllHardwareWatchpoints() {
+ return Status("unimplemented");
+}
+
+uint32_t NativeRegisterContextWindows_arm::SetHardwareWatchpoint(
+ lldb::addr_t addr, size_t size, uint32_t watch_flags) {
+ return LLDB_INVALID_INDEX32;
+}
+
+lldb::addr_t
+NativeRegisterContextWindows_arm::GetWatchpointAddress(uint32_t wp_index) {
+ return LLDB_INVALID_ADDRESS;
+}
+
+uint32_t NativeRegisterContextWindows_arm::NumSupportedHardwareWatchpoints() {
+ // Not implemented
+ return 0;
+}
+
+#endif // defined(__arm__) || defined(_M_ARM)
diff --git a/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h b/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h
new file mode 100644
index 000000000..2778bed9a
--- /dev/null
+++ b/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h
@@ -0,0 +1,80 @@
+//===-- NativeRegisterContextWindows_arm.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(__arm__) || defined(_M_ARM)
+#ifndef liblldb_NativeRegisterContextWindows_arm_h_
+#define liblldb_NativeRegisterContextWindows_arm_h_
+
+#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
+
+#include "NativeRegisterContextWindows.h"
+
+namespace lldb_private {
+
+class NativeThreadWindows;
+
+class NativeRegisterContextWindows_arm : public NativeRegisterContextWindows {
+public:
+ NativeRegisterContextWindows_arm(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 &reg_value) override;
+
+ Status WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_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 &reg_value);
+
+ Status GPRWrite(const uint32_t reg, const RegisterValue &reg_value);
+
+ Status FPRRead(const uint32_t reg, RegisterValue &reg_value);
+
+ Status FPRWrite(const uint32_t reg, const RegisterValue &reg_value);
+
+private:
+ bool IsGPR(uint32_t reg_index) const;
+
+ bool IsFPR(uint32_t reg_index) const;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_NativeRegisterContextWindows_arm_h_
+#endif // defined(__arm__) || defined(_M_ARM)
diff --git a/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
index 416abed88..86a302a87 100644
--- a/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
+++ b/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp
@@ -20,13 +20,14 @@
#include "ProcessWindowsLog.h"
#include "TargetThreadWindows.h"
-// TODO support _M_ARM
#if defined(__x86_64__) || defined(_M_AMD64)
#include "x64/RegisterContextWindows_x64.h"
#elif defined(__i386__) || defined(_M_IX86)
#include "x86/RegisterContextWindows_x86.h"
#elif defined(__aarch64__) || defined(_M_ARM64)
#include "arm64/RegisterContextWindows_arm64.h"
+#elif defined(__arm__) || defined(_M_ARM)
+#include "arm/RegisterContextWindows_arm.h"
#endif
using namespace lldb;
@@ -71,7 +72,12 @@ TargetThreadWindows::CreateRegisterContextForFrame(StackFrame *frame) {
switch (arch.GetMachine()) {
case llvm::Triple::arm:
case llvm::Triple::thumb:
- LLDB_LOG(log, "debugging ARM (NT) targets is currently unsupported");
+#if defined(__arm__) || defined(_M_ARM)
+ m_thread_reg_ctx_sp.reset(
+ new RegisterContextWindows_arm(*this, concrete_frame_idx));
+#else
+ LLDB_LOG(log, "debugging foreign targets is currently unsupported");
+#endif
break;
case llvm::Triple::aarch64:
diff --git a/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp b/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
new file mode 100644
index 000000000..c8bcf71c7
--- /dev/null
+++ b/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp
@@ -0,0 +1,432 @@
+//===-- RegisterContextWindows_arm.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(__arm__) || defined(_M_ARM)
+
+#include "lldb/Host/windows/HostThreadWindows.h"
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-private-types.h"
+
+#include "RegisterContextWindows_arm.h"
+#include "TargetThreadWindows.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define GPR_OFFSET(idx) 0
+#define FPU_OFFSET(idx) 0
+#define FPSCR_OFFSET 0
+#define EXC_OFFSET(reg) 0
+#define DBG_OFFSET_NAME(reg) 0
+
+#define DEFINE_DBG(reg, i) \
+ #reg, NULL, \
+ 0, DBG_OFFSET_NAME(reg[i]), eEncodingUint, eFormatHex, \
+ {LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, \
+ LLDB_INVALID_REGNUM }, \
+ NULL, NULL, NULL, 0
+
+// Include RegisterInfos_arm to declare our g_register_infos_arm structure.
+#define DECLARE_REGISTER_INFOS_ARM_STRUCT
+#include "Plugins/Process/Utility/RegisterInfos_arm.h"
+#undef DECLARE_REGISTER_INFOS_ARM_STRUCT
+
+static size_t k_num_register_infos = llvm::array_lengthof(g_register_infos_arm);
+
+// Array of lldb register numbers used to define the set of all General Purpose
+// Registers
+uint32_t g_gpr_reg_indices[] = {
+ gpr_r0, gpr_r1, gpr_r2, gpr_r3, gpr_r4, gpr_r5, gpr_r6, gpr_r7, gpr_r8,
+ gpr_r9, gpr_r10, gpr_r11, gpr_r12, gpr_sp, gpr_lr, gpr_pc, gpr_cpsr,
+};
+
+uint32_t g_fpu_reg_indices[] = {
+ fpu_s0, fpu_s1, fpu_s2, fpu_s3, fpu_s4, fpu_s5, fpu_s6, fpu_s7,
+ fpu_s8, fpu_s9, fpu_s10, fpu_s11, fpu_s12, fpu_s13, fpu_s14, fpu_s15,
+ fpu_s16, fpu_s17, fpu_s18, fpu_s19, fpu_s20, fpu_s21, fpu_s22, fpu_s23,
+ fpu_s24, fpu_s25, fpu_s26, fpu_s27, fpu_s28, fpu_s29, fpu_s30, fpu_s31,
+
+ fpu_d0, fpu_d1, fpu_d2, fpu_d3, fpu_d4, fpu_d5, fpu_d6, fpu_d7,
+ fpu_d8, fpu_d9, fpu_d10, fpu_d11, fpu_d12, fpu_d13, fpu_d14, fpu_d15,
+ fpu_d16, fpu_d17, fpu_d18, fpu_d19, fpu_d20, fpu_d21, fpu_d22, fpu_d23,
+ fpu_d24, fpu_d25, fpu_d26, fpu_d27, fpu_d28, fpu_d29, fpu_d30, fpu_d31,
+
+ fpu_q0, fpu_q1, fpu_q2, fpu_q3, fpu_q4, fpu_q5, fpu_q6, fpu_q7,
+ fpu_q8, fpu_q9, fpu_q10, fpu_q11, fpu_q12, fpu_q13, fpu_q14, fpu_q15,
+
+ fpu_fpscr,
+};
+
+RegisterSet g_register_sets[] = {
+ {"General Purpose Registers", "gpr",
+ llvm::array_lengthof(g_gpr_reg_indices), g_gpr_reg_indices},
+ {"Floating Point Registers", "fpu", llvm::array_lengthof(g_fpu_reg_indices),
+ g_fpu_reg_indices},
+};
+
+// Constructors and Destructors
+RegisterContextWindows_arm::RegisterContextWindows_arm(
+ Thread &thread, uint32_t concrete_frame_idx)
+ : RegisterContextWindows(thread, concrete_frame_idx) {}
+
+RegisterContextWindows_arm::~RegisterContextWindows_arm() {}
+
+size_t RegisterContextWindows_arm::GetRegisterCount() {
+ return llvm::array_lengthof(g_register_infos_arm);
+}
+
+const RegisterInfo *
+RegisterContextWindows_arm::GetRegisterInfoAtIndex(size_t reg) {
+ if (reg < k_num_register_infos)
+ return &g_register_infos_arm[reg];
+ return NULL;
+}
+
+size_t RegisterContextWindows_arm::GetRegisterSetCount() {
+ return llvm::array_lengthof(g_register_sets);
+}
+
+const RegisterSet *RegisterContextWindows_arm::GetRegisterSet(size_t reg_set) {
+ return &g_register_sets[reg_set];
+}
+
+bool RegisterContextWindows_arm::ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ if (!CacheAllRegisterValues())
+ return false;
+
+ if (reg_info == nullptr)
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ switch (reg) {
+ case gpr_r0:
+ reg_value.SetUInt32(m_context.R0);
+ break;
+ case gpr_r1:
+ reg_value.SetUInt32(m_context.R1);
+ break;
+ case gpr_r2:
+ reg_value.SetUInt32(m_context.R2);
+ break;
+ case gpr_r3:
+ reg_value.SetUInt32(m_context.R3);
+ break;
+ case gpr_r4:
+ reg_value.SetUInt32(m_context.R4);
+ break;
+ case gpr_r5:
+ reg_value.SetUInt32(m_context.R5);
+ break;
+ case gpr_r6:
+ reg_value.SetUInt32(m_context.R6);
+ break;
+ case gpr_r7:
+ reg_value.SetUInt32(m_context.R7);
+ break;
+ case gpr_r8:
+ reg_value.SetUInt32(m_context.R8);
+ break;
+ case gpr_r9:
+ reg_value.SetUInt32(m_context.R9);
+ break;
+ case gpr_r10:
+ reg_value.SetUInt32(m_context.R10);
+ break;
+ case gpr_r11:
+ reg_value.SetUInt32(m_context.R11);
+ break;
+ case gpr_r12:
+ reg_value.SetUInt32(m_context.R12);
+ break;
+ case gpr_sp:
+ reg_value.SetUInt32(m_context.Sp);
+ break;
+ case gpr_lr:
+ reg_value.SetUInt32(m_context.Lr);
+ break;
+ case gpr_pc:
+ reg_value.SetUInt32(m_context.Pc);
+ break;
+ case gpr_cpsr:
+ reg_value.SetUInt32(m_context.Cpsr);
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ reg_value.SetUInt32(m_context.S[reg - fpu_s0], RegisterValue::eTypeFloat);
+ break;
+
+ case fpu_d0:
+ case fpu_d1:
+ case fpu_d2:
+ case fpu_d3:
+ case fpu_d4:
+ case fpu_d5:
+ case fpu_d6:
+ case fpu_d7:
+ case fpu_d8:
+ case fpu_d9:
+ case fpu_d10:
+ case fpu_d11:
+ case fpu_d12:
+ case fpu_d13:
+ case fpu_d14:
+ case fpu_d15:
+ case fpu_d16:
+ case fpu_d17:
+ case fpu_d18:
+ case fpu_d19:
+ case fpu_d20:
+ case fpu_d21:
+ case fpu_d22:
+ case fpu_d23:
+ case fpu_d24:
+ case fpu_d25:
+ case fpu_d26:
+ case fpu_d27:
+ case fpu_d28:
+ case fpu_d29:
+ case fpu_d30:
+ case fpu_d31:
+ reg_value.SetUInt64(m_context.D[reg - fpu_d0], RegisterValue::eTypeDouble);
+ break;
+
+ case fpu_q0:
+ case fpu_q1:
+ case fpu_q2:
+ case fpu_q3:
+ case fpu_q4:
+ case fpu_q5:
+ case fpu_q6:
+ case fpu_q7:
+ case fpu_q8:
+ case fpu_q9:
+ case fpu_q10:
+ case fpu_q11:
+ case fpu_q12:
+ case fpu_q13:
+ case fpu_q14:
+ case fpu_q15:
+ reg_value.SetBytes(&m_context.Q[reg - fpu_q0], reg_info->byte_size,
+ endian::InlHostByteOrder());
+ break;
+
+ case fpu_fpscr:
+ reg_value.SetUInt32(m_context.Fpscr);
+ break;
+
+ default:
+ reg_value.SetValueToInvalid();
+ return false;
+ }
+ return true;
+}
+
+bool RegisterContextWindows_arm::WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ // Since we cannot only write a single register value to the inferior, we
+ // need to make sure our cached copy of the register values are fresh.
+ // Otherwise when writing EAX, for example, we may also overwrite some other
+ // register with a stale value.
+ if (!CacheAllRegisterValues())
+ return false;
+
+ const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+ switch (reg) {
+ case gpr_r0:
+ m_context.R0 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r1:
+ m_context.R1 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r2:
+ m_context.R2 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r3:
+ m_context.R3 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r4:
+ m_context.R4 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r5:
+ m_context.R5 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r6:
+ m_context.R6 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r7:
+ m_context.R7 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r8:
+ m_context.R8 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r9:
+ m_context.R9 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r10:
+ m_context.R10 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r11:
+ m_context.R11 = reg_value.GetAsUInt32();
+ break;
+ case gpr_r12:
+ m_context.R12 = reg_value.GetAsUInt32();
+ break;
+ case gpr_sp:
+ m_context.Sp = reg_value.GetAsUInt32();
+ break;
+ case gpr_lr:
+ m_context.Lr = reg_value.GetAsUInt32();
+ break;
+ case gpr_pc:
+ m_context.Pc = reg_value.GetAsUInt32();
+ break;
+ case gpr_cpsr:
+ m_context.Cpsr = reg_value.GetAsUInt32();
+ break;
+
+ case fpu_s0:
+ case fpu_s1:
+ case fpu_s2:
+ case fpu_s3:
+ case fpu_s4:
+ case fpu_s5:
+ case fpu_s6:
+ case fpu_s7:
+ case fpu_s8:
+ case fpu_s9:
+ case fpu_s10:
+ case fpu_s11:
+ case fpu_s12:
+ case fpu_s13:
+ case fpu_s14:
+ case fpu_s15:
+ case fpu_s16:
+ case fpu_s17:
+ case fpu_s18:
+ case fpu_s19:
+ case fpu_s20:
+ case fpu_s21:
+ case fpu_s22:
+ case fpu_s23:
+ case fpu_s24:
+ case fpu_s25:
+ case fpu_s26:
+ case fpu_s27:
+ case fpu_s28:
+ case fpu_s29:
+ case fpu_s30:
+ case fpu_s31:
+ m_context.S[reg - fpu_s0] = reg_value.GetAsUInt32();
+ break;
+
+ case fpu_d0:
+ case fpu_d1:
+ case fpu_d2:
+ case fpu_d3:
+ case fpu_d4:
+ case fpu_d5:
+ case fpu_d6:
+ case fpu_d7:
+ case fpu_d8:
+ case fpu_d9:
+ case fpu_d10:
+ case fpu_d11:
+ case fpu_d12:
+ case fpu_d13:
+ case fpu_d14:
+ case fpu_d15:
+ case fpu_d16:
+ case fpu_d17:
+ case fpu_d18:
+ case fpu_d19:
+ case fpu_d20:
+ case fpu_d21:
+ case fpu_d22:
+ case fpu_d23:
+ case fpu_d24:
+ case fpu_d25:
+ case fpu_d26:
+ case fpu_d27:
+ case fpu_d28:
+ case fpu_d29:
+ case fpu_d30:
+ case fpu_d31:
+ m_context.D[reg - fpu_d0] = reg_value.GetAsUInt64();
+ break;
+
+ case fpu_q0:
+ case fpu_q1:
+ case fpu_q2:
+ case fpu_q3:
+ case fpu_q4:
+ case fpu_q5:
+ case fpu_q6:
+ case fpu_q7:
+ case fpu_q8:
+ case fpu_q9:
+ case fpu_q10:
+ case fpu_q11:
+ case fpu_q12:
+ case fpu_q13:
+ case fpu_q14:
+ case fpu_q15:
+ memcpy(&m_context.Q[reg - fpu_q0], reg_value.GetBytes(), 16);
+ break;
+
+ case fpu_fpscr:
+ m_context.Fpscr = reg_value.GetAsUInt32();
+ break;
+
+ default:
+ return false;
+ }
+
+ // Physically update the registers in the target process.
+ return ApplyAllRegisterValues();
+}
+
+#endif // defined(__arm__) || defined(_M_ARM)
diff --git a/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h b/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h
new file mode 100644
index 000000000..57050671d
--- /dev/null
+++ b/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h
@@ -0,0 +1,47 @@
+//===-- RegisterContextWindows_arm.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_arm_H_
+#define liblldb_RegisterContextWindows_arm_H_
+
+#if defined(__arm__) || defined(_M_ARM)
+
+#include "RegisterContextWindows.h"
+#include "lldb/lldb-forward.h"
+
+namespace lldb_private {
+
+class Thread;
+
+class RegisterContextWindows_arm : public RegisterContextWindows {
+public:
+ // Constructors and Destructors
+ RegisterContextWindows_arm(Thread &thread, uint32_t concrete_frame_idx);
+
+ virtual ~RegisterContextWindows_arm();
+
+ // Subclasses must override these functions
+ size_t GetRegisterCount() override;
+
+ const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override;
+
+ size_t GetRegisterSetCount() override;
+
+ const RegisterSet *GetRegisterSet(size_t reg_set) override;
+
+ bool ReadRegister(const RegisterInfo *reg_info,
+ RegisterValue &reg_value) override;
+
+ bool WriteRegister(const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) override;
+};
+} // namespace lldb_private
+
+#endif // defined(__arm__) || defined(_M_ARM)
+
+#endif // #ifndef liblldb_RegisterContextWindows_arm_H_
diff --git a/test/Shell/Register/Inputs/arm-fp-read.cpp b/test/Shell/Register/Inputs/arm-fp-read.cpp
new file mode 100644
index 000000000..2dce2ed2d
--- /dev/null
+++ b/test/Shell/Register/Inputs/arm-fp-read.cpp
@@ -0,0 +1,19 @@
+int main() {
+ asm volatile(
+ "vmov.f64 d0, #0.5\n\t"
+ "vmov.f64 d1, #1.5\n\t"
+ "vmov.f64 d2, #2.5\n\t"
+ "vmov.f64 d3, #3.5\n\t"
+ "vmov.f32 s8, #4.5\n\t"
+ "vmov.f32 s9, #5.5\n\t"
+ "vmov.f32 s10, #6.5\n\t"
+ "vmov.f32 s11, #7.5\n\t"
+ "\n\t"
+ "bkpt #0\n\t"
+ :
+ :
+ : "d0", "d1", "d2", "d3", "s8", "s9", "s10", "s11"
+ );
+
+ return 0;
+}
diff --git a/test/Shell/Register/Inputs/arm-gp-read.cpp b/test/Shell/Register/Inputs/arm-gp-read.cpp
new file mode 100644
index 000000000..fd891c49c
--- /dev/null
+++ b/test/Shell/Register/Inputs/arm-gp-read.cpp
@@ -0,0 +1,44 @@
+#include <cstdint>
+
+struct alignas(16) vec_t {
+ uint64_t a, b;
+};
+
+int main() {
+ constexpr uint32_t gprs[] = {
+ 0x00010203,
+ 0x10111213,
+ 0x20212223,
+ 0x30313233,
+ 0x40414243,
+ 0x50515253,
+ 0x60616263,
+ 0x70717273,
+ };
+
+ constexpr vec_t vecs[] = {
+ { 0x0F0E0D0C0B0A0908, 0x1716151413121110, },
+ { 0x100F0E0D0C0B0A09, 0x1817161514131211, },
+ { 0x11100F0E0D0C0B0A, 0x1918171615141312, },
+ { 0x1211100F0E0D0C0B, 0x1A19181716151413, },
+ };
+ const vec_t *vec_ptr = vecs;
+
+ asm volatile(
+ "ldrd r0, r1, [%1]\n\t"
+ "ldrd r2, r3, [%1, #8]\n\t"
+ "ldrd r4, r5, [%1, #16]\n\t"
+ "ldrd r6, r7, [%1, #24]\n\t"
+ "\n\t"
+ "vld1.64 {q0, q1}, [%0]!\n\t"
+ "vld1.64 {q2, q3}, [%0]!\n\t"
+ "\n\t"
+ "bkpt #0\n\t"
+ : "+r"(vec_ptr)
+ : "r"(gprs)
+ : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "q0", "q1", "q2", "q3"
+ );
+
+ return 0;
+}
diff --git a/test/Shell/Register/arm-fp-read.test b/test/Shell/Register/arm-fp-read.test
new file mode 100644
index 000000000..21af9074e
--- /dev/null
+++ b/test/Shell/Register/arm-fp-read.test
@@ -0,0 +1,21 @@
+# REQUIRES: native && target-arm
+# RUN: %clangxx -fomit-frame-pointer %p/Inputs/arm-fp-read.cpp -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+process launch
+
+register read d0
+register read d1
+register read d2
+register read d3
+register read s8
+register read s9
+register read s10
+register read s11
+# CHECK-DAG: d0 = 0.5
+# CHECK-DAG: d1 = 1.5
+# CHECK-DAG: d2 = 2.5
+# CHECK-DAG: d3 = 3.5
+# CHECK-DAG: s8 = 4.5
+# CHECK-DAG: s9 = 5.5
+# CHECK-DAG: s10 = 6.5
+# CHECK-DAG: s11 = 7.5
diff --git a/test/Shell/Register/arm-gp-read.test b/test/Shell/Register/arm-gp-read.test
new file mode 100644
index 000000000..73c1034b6
--- /dev/null
+++ b/test/Shell/Register/arm-gp-read.test
@@ -0,0 +1,19 @@
+# REQUIRES: native && target-arm
+# RUN: %clangxx -fomit-frame-pointer %p/Inputs/arm-gp-read.cpp -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+process launch
+
+register read --all
+# CHECK-DAG: r0 = 0x00010203
+# CHECK-DAG: r1 = 0x10111213
+# CHECK-DAG: r2 = 0x20212223
+# CHECK-DAG: r3 = 0x30313233
+# CHECK-DAG: r4 = 0x40414243
+# CHECK-DAG: r5 = 0x50515253
+# CHECK-DAG: r6 = 0x60616263
+# CHECK-DAG: r7 = 0x70717273
+
+# CHECK-DAG: q0 = {0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17}
+# CHECK-DAG: q1 = {0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18}
+# CHECK-DAG: q2 = {0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19}
+# CHECK-DAG: q3 = {0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a}