diff --git a/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt --- a/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt @@ -4,6 +4,7 @@ LocalDebugDelegate.cpp NativeProcessWindows.cpp NativeRegisterContextWindows.cpp + NativeRegisterContextWindows_arm.cpp NativeRegisterContextWindows_arm64.cpp NativeRegisterContextWindows_i386.cpp NativeRegisterContextWindows_WoW64.cpp @@ -14,10 +15,10 @@ 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/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.h new file mode 100644 --- /dev/null +++ b/lldb/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 ®_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_arm_h_ +#endif // defined(__arm__) || defined(_M_ARM) diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp new file mode 100644 --- /dev/null +++ b/lldb/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::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(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 ®_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 ®_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 ®_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 ®_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 ®_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 ®_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(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/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp --- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp +++ b/lldb/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 @@ 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/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.h new file mode 100644 --- /dev/null +++ b/lldb/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 ®_value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; +}; +} // namespace lldb_private + +#endif // defined(__arm__) || defined(_M_ARM) + +#endif // #ifndef liblldb_RegisterContextWindows_arm_H_ diff --git a/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp b/lldb/source/Plugins/Process/Windows/Common/arm/RegisterContextWindows_arm.cpp new file mode 100644 --- /dev/null +++ b/lldb/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 ®_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 ®_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/lldb/test/Shell/Register/Inputs/arm-fp-read.cpp b/lldb/test/Shell/Register/Inputs/arm-fp-read.cpp new file mode 100644 --- /dev/null +++ b/lldb/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/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp b/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/Register/Inputs/arm-gp-read.cpp @@ -0,0 +1,44 @@ +#include + +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/lldb/test/Shell/Register/arm-fp-read.test b/lldb/test/Shell/Register/arm-fp-read.test new file mode 100644 --- /dev/null +++ b/lldb/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/lldb/test/Shell/Register/arm-gp-read.test b/lldb/test/Shell/Register/arm-gp-read.test new file mode 100644 --- /dev/null +++ b/lldb/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} diff --git a/llvm/utils/lit/lit/llvm/config.py b/llvm/utils/lit/lit/llvm/config.py --- a/llvm/utils/lit/lit/llvm/config.py +++ b/llvm/utils/lit/lit/llvm/config.py @@ -99,6 +99,8 @@ features.add('target-x86_64') elif re.match(r'^aarch64.*', target_triple): features.add('target-aarch64') + elif re.match(r'^arm.*', target_triple): + features.add('target-arm') use_gmalloc = lit_config.params.get('use_gmalloc', None) if lit.util.pythonize_bool(use_gmalloc):