| 1 | //===-- EmulationStateARM.cpp ---------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "EmulationStateARM.h" |
| 10 | |
| 11 | #include "lldb/Interpreter/OptionValueArray.h" |
| 12 | #include "lldb/Interpreter/OptionValueDictionary.h" |
| 13 | #include "lldb/Target/RegisterContext.h" |
| 14 | #include "lldb/Target/StackFrame.h" |
| 15 | #include "lldb/Utility/RegisterValue.h" |
| 16 | #include "lldb/Utility/Scalar.h" |
| 17 | |
| 18 | #include "Utility/ARM_DWARF_Registers.h" |
| 19 | |
| 20 | using namespace lldb; |
| 21 | using namespace lldb_private; |
| 22 | |
| 23 | EmulationStateARM::EmulationStateARM() : m_vfp_regs(), m_memory() { |
| 24 | ClearPseudoRegisters(); |
| 25 | } |
| 26 | |
| 27 | EmulationStateARM::~EmulationStateARM() = default; |
| 28 | |
| 29 | bool EmulationStateARM::StorePseudoRegisterValue(uint32_t reg_num, |
| 30 | uint64_t value) { |
| 31 | if (reg_num <= dwarf_cpsr) |
| 32 | m_gpr[reg_num - dwarf_r0] = (uint32_t)value; |
| 33 | else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) { |
| 34 | uint32_t idx = reg_num - dwarf_s0; |
| 35 | m_vfp_regs.s_regs[idx] = (uint32_t)value; |
| 36 | } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) { |
| 37 | uint32_t idx = reg_num - dwarf_d0; |
| 38 | if (idx < 16) { |
| 39 | m_vfp_regs.s_regs[idx * 2] = (uint32_t)value; |
| 40 | m_vfp_regs.s_regs[idx * 2 + 1] = (uint32_t)(value >> 32); |
| 41 | } else |
| 42 | m_vfp_regs.d_regs[idx - 16] = value; |
| 43 | } else |
| 44 | return false; |
| 45 | |
| 46 | return true; |
| 47 | } |
| 48 | |
| 49 | uint64_t EmulationStateARM::ReadPseudoRegisterValue(uint32_t reg_num, |
| 50 | bool &success) { |
| 51 | uint64_t value = 0; |
| 52 | success = true; |
| 53 | |
| 54 | if (reg_num <= dwarf_cpsr) |
| 55 | value = m_gpr[reg_num - dwarf_r0]; |
| 56 | else if ((dwarf_s0 <= reg_num) && (reg_num <= dwarf_s31)) { |
| 57 | uint32_t idx = reg_num - dwarf_s0; |
| 58 | value = m_vfp_regs.s_regs[idx]; |
| 59 | } else if ((dwarf_d0 <= reg_num) && (reg_num <= dwarf_d31)) { |
| 60 | uint32_t idx = reg_num - dwarf_d0; |
| 61 | if (idx < 16) |
| 62 | value = (uint64_t)m_vfp_regs.s_regs[idx * 2] | |
| 63 | ((uint64_t)m_vfp_regs.s_regs[idx * 2 + 1] << 32); |
| 64 | else |
| 65 | value = m_vfp_regs.d_regs[idx - 16]; |
| 66 | } else |
| 67 | success = false; |
| 68 | |
| 69 | return value; |
| 70 | } |
| 71 | |
| 72 | void EmulationStateARM::ClearPseudoRegisters() { |
| 73 | for (int i = 0; i < 17; ++i) |
| 74 | m_gpr[i] = 0; |
| 75 | |
| 76 | for (int i = 0; i < 32; ++i) |
| 77 | m_vfp_regs.s_regs[i] = 0; |
| 78 | |
| 79 | for (int i = 0; i < 16; ++i) |
| 80 | m_vfp_regs.d_regs[i] = 0; |
| 81 | } |
| 82 | |
| 83 | void EmulationStateARM::ClearPseudoMemory() { m_memory.clear(); } |
| 84 | |
| 85 | bool EmulationStateARM::StoreToPseudoAddress(lldb::addr_t p_address, |
| 86 | uint32_t value) { |
| 87 | m_memory[p_address] = value; |
| 88 | return true; |
| 89 | } |
| 90 | |
| 91 | uint32_t EmulationStateARM::ReadFromPseudoAddress(lldb::addr_t p_address, |
| 92 | bool &success) { |
| 93 | std::map<lldb::addr_t, uint32_t>::iterator pos; |
| 94 | uint32_t ret_val = 0; |
| 95 | |
| 96 | success = true; |
| 97 | pos = m_memory.find(x: p_address); |
| 98 | if (pos != m_memory.end()) |
| 99 | ret_val = pos->second; |
| 100 | else |
| 101 | success = false; |
| 102 | |
| 103 | return ret_val; |
| 104 | } |
| 105 | |
| 106 | size_t EmulationStateARM::ReadPseudoMemory( |
| 107 | EmulateInstruction *instruction, void *baton, |
| 108 | const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, |
| 109 | size_t length) { |
| 110 | if (!baton) |
| 111 | return 0; |
| 112 | |
| 113 | bool success = true; |
| 114 | EmulationStateARM *pseudo_state = (EmulationStateARM *)baton; |
| 115 | if (length <= 4) { |
| 116 | uint32_t value = pseudo_state->ReadFromPseudoAddress(p_address: addr, success); |
| 117 | if (!success) |
| 118 | return 0; |
| 119 | |
| 120 | if (endian::InlHostByteOrder() == lldb::eByteOrderBig) |
| 121 | value = llvm::byteswap<uint32_t>(V: value); |
| 122 | *((uint32_t *)dst) = value; |
| 123 | } else if (length == 8) { |
| 124 | uint32_t value1 = pseudo_state->ReadFromPseudoAddress(p_address: addr, success); |
| 125 | if (!success) |
| 126 | return 0; |
| 127 | |
| 128 | uint32_t value2 = pseudo_state->ReadFromPseudoAddress(p_address: addr + 4, success); |
| 129 | if (!success) |
| 130 | return 0; |
| 131 | |
| 132 | if (endian::InlHostByteOrder() == lldb::eByteOrderBig) { |
| 133 | value1 = llvm::byteswap<uint32_t>(V: value1); |
| 134 | value2 = llvm::byteswap<uint32_t>(V: value2); |
| 135 | } |
| 136 | ((uint32_t *)dst)[0] = value1; |
| 137 | ((uint32_t *)dst)[1] = value2; |
| 138 | } else |
| 139 | success = false; |
| 140 | |
| 141 | if (success) |
| 142 | return length; |
| 143 | |
| 144 | return 0; |
| 145 | } |
| 146 | |
| 147 | size_t EmulationStateARM::WritePseudoMemory( |
| 148 | EmulateInstruction *instruction, void *baton, |
| 149 | const EmulateInstruction::Context &context, lldb::addr_t addr, |
| 150 | const void *dst, size_t length) { |
| 151 | if (!baton) |
| 152 | return 0; |
| 153 | |
| 154 | EmulationStateARM *pseudo_state = (EmulationStateARM *)baton; |
| 155 | |
| 156 | if (length <= 4) { |
| 157 | uint32_t value; |
| 158 | memcpy (dest: &value, src: dst, n: sizeof (uint32_t)); |
| 159 | if (endian::InlHostByteOrder() == lldb::eByteOrderBig) |
| 160 | value = llvm::byteswap<uint32_t>(V: value); |
| 161 | |
| 162 | pseudo_state->StoreToPseudoAddress(p_address: addr, value); |
| 163 | return length; |
| 164 | } else if (length == 8) { |
| 165 | uint32_t value1; |
| 166 | uint32_t value2; |
| 167 | memcpy (dest: &value1, src: dst, n: sizeof (uint32_t)); |
| 168 | memcpy(dest: &value2, src: static_cast<const uint8_t *>(dst) + sizeof(uint32_t), |
| 169 | n: sizeof(uint32_t)); |
| 170 | if (endian::InlHostByteOrder() == lldb::eByteOrderBig) { |
| 171 | value1 = llvm::byteswap<uint32_t>(V: value1); |
| 172 | value2 = llvm::byteswap<uint32_t>(V: value2); |
| 173 | } |
| 174 | |
| 175 | pseudo_state->StoreToPseudoAddress(p_address: addr, value: value1); |
| 176 | pseudo_state->StoreToPseudoAddress(p_address: addr + 4, value: value2); |
| 177 | return length; |
| 178 | } |
| 179 | |
| 180 | return 0; |
| 181 | } |
| 182 | |
| 183 | bool EmulationStateARM::ReadPseudoRegister( |
| 184 | EmulateInstruction *instruction, void *baton, |
| 185 | const lldb_private::RegisterInfo *reg_info, |
| 186 | lldb_private::RegisterValue ®_value) { |
| 187 | if (!baton || !reg_info) |
| 188 | return false; |
| 189 | |
| 190 | bool success = true; |
| 191 | EmulationStateARM *pseudo_state = (EmulationStateARM *)baton; |
| 192 | const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF]; |
| 193 | assert(dwarf_reg_num != LLDB_INVALID_REGNUM); |
| 194 | uint64_t reg_uval = |
| 195 | pseudo_state->ReadPseudoRegisterValue(reg_num: dwarf_reg_num, success); |
| 196 | |
| 197 | if (success) |
| 198 | success = reg_value.SetUInt(uint: reg_uval, byte_size: reg_info->byte_size); |
| 199 | return success; |
| 200 | } |
| 201 | |
| 202 | bool EmulationStateARM::WritePseudoRegister( |
| 203 | EmulateInstruction *instruction, void *baton, |
| 204 | const EmulateInstruction::Context &context, |
| 205 | const lldb_private::RegisterInfo *reg_info, |
| 206 | const lldb_private::RegisterValue ®_value) { |
| 207 | if (!baton || !reg_info) |
| 208 | return false; |
| 209 | |
| 210 | EmulationStateARM *pseudo_state = (EmulationStateARM *)baton; |
| 211 | const uint32_t dwarf_reg_num = reg_info->kinds[eRegisterKindDWARF]; |
| 212 | assert(dwarf_reg_num != LLDB_INVALID_REGNUM); |
| 213 | return pseudo_state->StorePseudoRegisterValue(reg_num: dwarf_reg_num, |
| 214 | value: reg_value.GetAsUInt64()); |
| 215 | } |
| 216 | |
| 217 | bool EmulationStateARM::CompareState(EmulationStateARM &other_state, |
| 218 | Stream &out_stream) { |
| 219 | bool match = true; |
| 220 | |
| 221 | for (int i = 0; match && i < 17; ++i) { |
| 222 | if (m_gpr[i] != other_state.m_gpr[i]) { |
| 223 | match = false; |
| 224 | out_stream.Printf(format: "r%d: 0x%x != 0x%x\n" , i, m_gpr[i], |
| 225 | other_state.m_gpr[i]); |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | for (int i = 0; match && i < 32; ++i) { |
| 230 | if (m_vfp_regs.s_regs[i] != other_state.m_vfp_regs.s_regs[i]) { |
| 231 | match = false; |
| 232 | out_stream.Printf(format: "s%d: 0x%x != 0x%x\n" , i, m_vfp_regs.s_regs[i], |
| 233 | other_state.m_vfp_regs.s_regs[i]); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | for (int i = 0; match && i < 16; ++i) { |
| 238 | if (m_vfp_regs.d_regs[i] != other_state.m_vfp_regs.d_regs[i]) { |
| 239 | match = false; |
| 240 | out_stream.Printf(format: "d%d: 0x%" PRIx64 " != 0x%" PRIx64 "\n" , i + 16, |
| 241 | m_vfp_regs.d_regs[i], other_state.m_vfp_regs.d_regs[i]); |
| 242 | } |
| 243 | } |
| 244 | |
| 245 | // other_state is the expected state. If it has memory, check it. |
| 246 | if (!other_state.m_memory.empty() && m_memory != other_state.m_memory) { |
| 247 | match = false; |
| 248 | out_stream.Printf(format: "memory does not match\n" ); |
| 249 | out_stream.Printf(format: "got memory:\n" ); |
| 250 | for (auto p : m_memory) |
| 251 | out_stream.Printf(format: "0x%08" PRIx64 ": 0x%08x\n" , p.first, p.second); |
| 252 | out_stream.Printf(format: "expected memory:\n" ); |
| 253 | for (auto p : other_state.m_memory) |
| 254 | out_stream.Printf(format: "0x%08" PRIx64 ": 0x%08x\n" , p.first, p.second); |
| 255 | } |
| 256 | |
| 257 | return match; |
| 258 | } |
| 259 | |
| 260 | bool EmulationStateARM::( |
| 261 | OptionValueDictionary *reg_dict, char kind, int first_reg, int num) { |
| 262 | StreamString sstr; |
| 263 | for (int i = 0; i < num; ++i) { |
| 264 | sstr.Clear(); |
| 265 | sstr.Printf(format: "%c%d" , kind, i); |
| 266 | OptionValueSP value_sp = reg_dict->GetValueForKey(key: sstr.GetString()); |
| 267 | if (value_sp.get() == nullptr) |
| 268 | return false; |
| 269 | uint64_t reg_value = value_sp->GetValueAs<uint64_t>().value_or(u: 0); |
| 270 | StorePseudoRegisterValue(reg_num: first_reg + i, value: reg_value); |
| 271 | } |
| 272 | |
| 273 | return true; |
| 274 | } |
| 275 | |
| 276 | bool EmulationStateARM::LoadStateFromDictionary( |
| 277 | OptionValueDictionary *test_data) { |
| 278 | static constexpr llvm::StringLiteral memory_key("memory" ); |
| 279 | static constexpr llvm::StringLiteral registers_key("registers" ); |
| 280 | |
| 281 | if (!test_data) |
| 282 | return false; |
| 283 | |
| 284 | OptionValueSP value_sp = test_data->GetValueForKey(key: memory_key); |
| 285 | |
| 286 | // Load memory, if present. |
| 287 | |
| 288 | if (value_sp.get() != nullptr) { |
| 289 | static constexpr llvm::StringLiteral address_key("address" ); |
| 290 | static constexpr llvm::StringLiteral data_key("data" ); |
| 291 | uint64_t start_address = 0; |
| 292 | |
| 293 | OptionValueDictionary *mem_dict = value_sp->GetAsDictionary(); |
| 294 | value_sp = mem_dict->GetValueForKey(key: address_key); |
| 295 | if (value_sp.get() == nullptr) |
| 296 | return false; |
| 297 | else |
| 298 | start_address = value_sp->GetValueAs<uint64_t>().value_or(u: 0); |
| 299 | |
| 300 | value_sp = mem_dict->GetValueForKey(key: data_key); |
| 301 | OptionValueArray *mem_array = value_sp->GetAsArray(); |
| 302 | if (!mem_array) |
| 303 | return false; |
| 304 | |
| 305 | uint32_t num_elts = mem_array->GetSize(); |
| 306 | uint32_t address = (uint32_t)start_address; |
| 307 | |
| 308 | for (uint32_t i = 0; i < num_elts; ++i) { |
| 309 | value_sp = mem_array->GetValueAtIndex(idx: i); |
| 310 | if (value_sp.get() == nullptr) |
| 311 | return false; |
| 312 | uint64_t value = value_sp->GetValueAs<uint64_t>().value_or(u: 0); |
| 313 | StoreToPseudoAddress(p_address: address, value); |
| 314 | address = address + 4; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | value_sp = test_data->GetValueForKey(key: registers_key); |
| 319 | if (value_sp.get() == nullptr) |
| 320 | return false; |
| 321 | |
| 322 | // Load General Registers |
| 323 | |
| 324 | OptionValueDictionary *reg_dict = value_sp->GetAsDictionary(); |
| 325 | if (!LoadRegistersStateFromDictionary(reg_dict, kind: 'r', first_reg: dwarf_r0, num: 16)) |
| 326 | return false; |
| 327 | |
| 328 | static constexpr llvm::StringLiteral cpsr_name("cpsr" ); |
| 329 | value_sp = reg_dict->GetValueForKey(key: cpsr_name); |
| 330 | if (value_sp.get() == nullptr) |
| 331 | return false; |
| 332 | StorePseudoRegisterValue(reg_num: dwarf_cpsr, |
| 333 | value: value_sp->GetValueAs<uint64_t>().value_or(u: 0)); |
| 334 | |
| 335 | // Load s/d Registers |
| 336 | // To prevent you giving both types in a state and overwriting |
| 337 | // one or the other, we'll expect to get either all S registers, |
| 338 | // or all D registers. Not a mix of the two. |
| 339 | bool found_s_registers = |
| 340 | LoadRegistersStateFromDictionary(reg_dict, kind: 's', first_reg: dwarf_s0, num: 32); |
| 341 | bool found_d_registers = |
| 342 | LoadRegistersStateFromDictionary(reg_dict, kind: 'd', first_reg: dwarf_d0, num: 32); |
| 343 | |
| 344 | return found_s_registers != found_d_registers; |
| 345 | } |
| 346 | |