//===-- RegisterContextMinidump_ARM.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "RegisterContextMinidump_ARM.h" #include "Utility/ARM_DWARF_Registers.h" #include "Utility/ARM_ehframe_Registers.h" #include "lldb/Utility/RegisterValue.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/lldb-enumerations.h" // C includes #include // C++ includes using namespace lldb; using namespace lldb_private; using namespace minidump; #define INV LLDB_INVALID_REGNUM #define OFFSET(r) (offsetof(RegisterContextMinidump_ARM::Context, r)) #define DEF_R(i) \ { \ "r" #i, nullptr, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ {ehframe_r##i, dwarf_r##i, INV, INV, reg_r##i}, nullptr, nullptr, \ nullptr, \ } #define DEF_R_ARG(i, n) \ { \ "r" #i, "arg" #n, 4, OFFSET(r) + i * 4, eEncodingUint, eFormatHex, \ {ehframe_r##i, dwarf_r##i, LLDB_REGNUM_GENERIC_ARG1 + i, INV, \ reg_r##i}, \ nullptr, nullptr, nullptr, \ } #define DEF_D(i) \ { \ "d" #i, nullptr, 8, OFFSET(d) + i * 8, eEncodingVector, \ eFormatVectorOfUInt8, {dwarf_d##i, dwarf_d##i, INV, INV, reg_d##i}, \ nullptr, nullptr, nullptr, \ } #define DEF_S(i) \ { \ "s" #i, nullptr, 4, OFFSET(s) + i * 4, eEncodingIEEE754, eFormatFloat, \ {dwarf_s##i, dwarf_s##i, INV, INV, reg_s##i}, nullptr, nullptr, \ nullptr, \ } #define DEF_Q(i) \ { \ "q" #i, nullptr, 16, OFFSET(q) + i * 16, eEncodingVector, \ eFormatVectorOfUInt8, {dwarf_q##i, dwarf_q##i, INV, INV, reg_q##i}, \ nullptr, nullptr, nullptr, \ } // Zero based LLDB register numbers for this register context enum { // General Purpose Registers reg_r0, reg_r1, reg_r2, reg_r3, reg_r4, reg_r5, reg_r6, reg_r7, reg_r8, reg_r9, reg_r10, reg_r11, reg_r12, reg_sp, reg_lr, reg_pc, reg_cpsr, // Floating Point Registers reg_fpscr, reg_d0, reg_d1, reg_d2, reg_d3, reg_d4, reg_d5, reg_d6, reg_d7, reg_d8, reg_d9, reg_d10, reg_d11, reg_d12, reg_d13, reg_d14, reg_d15, reg_d16, reg_d17, reg_d18, reg_d19, reg_d20, reg_d21, reg_d22, reg_d23, reg_d24, reg_d25, reg_d26, reg_d27, reg_d28, reg_d29, reg_d30, reg_d31, reg_s0, reg_s1, reg_s2, reg_s3, reg_s4, reg_s5, reg_s6, reg_s7, reg_s8, reg_s9, reg_s10, reg_s11, reg_s12, reg_s13, reg_s14, reg_s15, reg_s16, reg_s17, reg_s18, reg_s19, reg_s20, reg_s21, reg_s22, reg_s23, reg_s24, reg_s25, reg_s26, reg_s27, reg_s28, reg_s29, reg_s30, reg_s31, reg_q0, reg_q1, reg_q2, reg_q3, reg_q4, reg_q5, reg_q6, reg_q7, reg_q8, reg_q9, reg_q10, reg_q11, reg_q12, reg_q13, reg_q14, reg_q15, k_num_regs }; static RegisterInfo g_reg_info_apple_fp = { "fp", "r7", 4, OFFSET(r) + 7 * 4, eEncodingUint, eFormatHex, {ehframe_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, INV, reg_r7}, nullptr, nullptr, nullptr, }; static RegisterInfo g_reg_info_fp = { "fp", "r11", 4, OFFSET(r) + 11 * 4, eEncodingUint, eFormatHex, {ehframe_r11, dwarf_r11, LLDB_REGNUM_GENERIC_FP, INV, reg_r11}, nullptr, nullptr, nullptr, }; // Register info definitions for this register context static RegisterInfo g_reg_infos[] = { DEF_R_ARG(0, 1), DEF_R_ARG(1, 2), DEF_R_ARG(2, 3), DEF_R_ARG(3, 4), DEF_R(4), DEF_R(5), DEF_R(6), DEF_R(7), DEF_R(8), DEF_R(9), DEF_R(10), DEF_R(11), DEF_R(12), {"sp", "r13", 4, OFFSET(r) + 13 * 4, eEncodingUint, eFormatHex, {ehframe_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, INV, reg_sp}, nullptr, nullptr, nullptr, }, {"lr", "r14", 4, OFFSET(r) + 14 * 4, eEncodingUint, eFormatHex, {ehframe_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, INV, reg_lr}, nullptr, nullptr, nullptr, }, {"pc", "r15", 4, OFFSET(r) + 15 * 4, eEncodingUint, eFormatHex, {ehframe_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, INV, reg_pc}, nullptr, nullptr, nullptr, }, {"cpsr", "psr", 4, OFFSET(cpsr), eEncodingUint, eFormatHex, {ehframe_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, INV, reg_cpsr}, nullptr, nullptr, nullptr, }, {"fpscr", nullptr, 8, OFFSET(fpscr), eEncodingUint, eFormatHex, {INV, INV, INV, INV, reg_fpscr}, nullptr, nullptr, nullptr, }, DEF_D(0), DEF_D(1), DEF_D(2), DEF_D(3), DEF_D(4), DEF_D(5), DEF_D(6), DEF_D(7), DEF_D(8), DEF_D(9), DEF_D(10), DEF_D(11), DEF_D(12), DEF_D(13), DEF_D(14), DEF_D(15), DEF_D(16), DEF_D(17), DEF_D(18), DEF_D(19), DEF_D(20), DEF_D(21), DEF_D(22), DEF_D(23), DEF_D(24), DEF_D(25), DEF_D(26), DEF_D(27), DEF_D(28), DEF_D(29), DEF_D(30), DEF_D(31), DEF_S(0), DEF_S(1), DEF_S(2), DEF_S(3), DEF_S(4), DEF_S(5), DEF_S(6), DEF_S(7), DEF_S(8), DEF_S(9), DEF_S(10), DEF_S(11), DEF_S(12), DEF_S(13), DEF_S(14), DEF_S(15), DEF_S(16), DEF_S(17), DEF_S(18), DEF_S(19), DEF_S(20), DEF_S(21), DEF_S(22), DEF_S(23), DEF_S(24), DEF_S(25), DEF_S(26), DEF_S(27), DEF_S(28), DEF_S(29), DEF_S(30), DEF_S(31), DEF_Q(0), DEF_Q(1), DEF_Q(2), DEF_Q(3), DEF_Q(4), DEF_Q(5), DEF_Q(6), DEF_Q(7), DEF_Q(8), DEF_Q(9), DEF_Q(10), DEF_Q(11), DEF_Q(12), DEF_Q(13), DEF_Q(14), DEF_Q(15)}; constexpr size_t k_num_reg_infos = std::size(g_reg_infos); // ARM general purpose registers. const uint32_t g_gpr_regnums[] = { reg_r0, reg_r1, reg_r2, reg_r3, reg_r4, reg_r5, reg_r6, reg_r7, reg_r8, reg_r9, reg_r10, reg_r11, reg_r12, reg_sp, reg_lr, reg_pc, reg_cpsr, LLDB_INVALID_REGNUM // register sets need to end with this flag }; const uint32_t g_fpu_regnums[] = { reg_fpscr, reg_d0, reg_d1, reg_d2, reg_d3, reg_d4, reg_d5, reg_d6, reg_d7, reg_d8, reg_d9, reg_d10, reg_d11, reg_d12, reg_d13, reg_d14, reg_d15, reg_d16, reg_d17, reg_d18, reg_d19, reg_d20, reg_d21, reg_d22, reg_d23, reg_d24, reg_d25, reg_d26, reg_d27, reg_d28, reg_d29, reg_d30, reg_d31, reg_s0, reg_s1, reg_s2, reg_s3, reg_s4, reg_s5, reg_s6, reg_s7, reg_s8, reg_s9, reg_s10, reg_s11, reg_s12, reg_s13, reg_s14, reg_s15, reg_s16, reg_s17, reg_s18, reg_s19, reg_s20, reg_s21, reg_s22, reg_s23, reg_s24, reg_s25, reg_s26, reg_s27, reg_s28, reg_s29, reg_s30, reg_s31, reg_q0, reg_q1, reg_q2, reg_q3, reg_q4, reg_q5, reg_q6, reg_q7, reg_q8, reg_q9, reg_q10, reg_q11, reg_q12, reg_q13, reg_q14, reg_q15, LLDB_INVALID_REGNUM // register sets need to end with this flag }; // Skip the last LLDB_INVALID_REGNUM in each count below by subtracting 1 constexpr size_t k_num_gpr_regs = std::size(g_gpr_regnums) - 1; constexpr size_t k_num_fpu_regs = std::size(g_fpu_regnums) - 1; static RegisterSet g_reg_sets[] = { {"General Purpose Registers", "gpr", k_num_gpr_regs, g_gpr_regnums}, {"Floating Point Registers", "fpu", k_num_fpu_regs, g_fpu_regnums}, }; constexpr size_t k_num_reg_sets = std::size(g_reg_sets); RegisterContextMinidump_ARM::RegisterContextMinidump_ARM( lldb_private::Thread &thread, const DataExtractor &data, bool apple) : RegisterContext(thread, 0), m_apple(apple) { lldb::offset_t offset = 0; m_regs.context_flags = data.GetU32(&offset); for (unsigned i = 0; i < std::size(m_regs.r); ++i) m_regs.r[i] = data.GetU32(&offset); m_regs.cpsr = data.GetU32(&offset); m_regs.fpscr = data.GetU64(&offset); for (unsigned i = 0; i < std::size(m_regs.d); ++i) m_regs.d[i] = data.GetU64(&offset); lldbassert(k_num_regs == k_num_reg_infos); } size_t RegisterContextMinidump_ARM::GetRegisterCountStatic() { return k_num_regs; } // Used for unit testing so we can verify register info is filled in for // all register flavors (DWARF, EH Frame, generic, etc). size_t RegisterContextMinidump_ARM::GetRegisterCount() { return GetRegisterCountStatic(); } // Used for unit testing so we can verify register info is filled in. const RegisterInfo * RegisterContextMinidump_ARM::GetRegisterInfoAtIndexStatic(size_t reg, bool apple) { if (reg < k_num_reg_infos) { if (apple) { if (reg == reg_r7) return &g_reg_info_apple_fp; } else { if (reg == reg_r11) return &g_reg_info_fp; } return &g_reg_infos[reg]; } return nullptr; } const RegisterInfo * RegisterContextMinidump_ARM::GetRegisterInfoAtIndex(size_t reg) { return GetRegisterInfoAtIndexStatic(reg, m_apple); } size_t RegisterContextMinidump_ARM::GetRegisterSetCount() { return k_num_reg_sets; } const RegisterSet *RegisterContextMinidump_ARM::GetRegisterSet(size_t set) { if (set < k_num_reg_sets) return &g_reg_sets[set]; return nullptr; } const char *RegisterContextMinidump_ARM::GetRegisterName(unsigned reg) { if (reg < k_num_reg_infos) return g_reg_infos[reg].name; return nullptr; } bool RegisterContextMinidump_ARM::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { Status error; reg_value.SetFromMemoryData( *reg_info, (const uint8_t *)&m_regs + reg_info->byte_offset, reg_info->byte_size, lldb::eByteOrderLittle, error); return error.Success(); } bool RegisterContextMinidump_ARM::WriteRegister(const RegisterInfo *, const RegisterValue &) { return false; } uint32_t RegisterContextMinidump_ARM::ConvertRegisterKindToRegisterNumber( lldb::RegisterKind kind, uint32_t num) { for (size_t i = 0; i < k_num_regs; ++i) { if (g_reg_infos[i].kinds[kind] == num) return i; } return LLDB_INVALID_REGNUM; }