//===-- EmulateInstructionARM.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 #include #include "EmulateInstructionARM.h" #include "EmulationStateARM.h" #include "lldb/Core/Address.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/PosixApi.h" #include "lldb/Interpreter/OptionValueArray.h" #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Stream.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/ARMUtils.h" #include "Utility/ARM_DWARF_Registers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/MathExtras.h" using namespace lldb; using namespace lldb_private; LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionARM, InstructionARM) // Convenient macro definitions. #define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS) #define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS) #define AlignPC(pc_val) (pc_val & 0xFFFFFFFC) // // ITSession implementation // static std::optional GetARMDWARFRegisterInfo(unsigned reg_num) { RegisterInfo reg_info; ::memset(®_info, 0, sizeof(RegisterInfo)); ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); if (reg_num >= dwarf_q0 && reg_num <= dwarf_q15) { reg_info.byte_size = 16; reg_info.format = eFormatVectorOfUInt8; reg_info.encoding = eEncodingVector; } if (reg_num >= dwarf_d0 && reg_num <= dwarf_d31) { reg_info.byte_size = 8; reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } else if (reg_num >= dwarf_s0 && reg_num <= dwarf_s31) { reg_info.byte_size = 4; reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } else if (reg_num >= dwarf_f0 && reg_num <= dwarf_f7) { reg_info.byte_size = 12; reg_info.format = eFormatFloat; reg_info.encoding = eEncodingIEEE754; } else { reg_info.byte_size = 4; reg_info.format = eFormatHex; reg_info.encoding = eEncodingUint; } reg_info.kinds[eRegisterKindDWARF] = reg_num; switch (reg_num) { case dwarf_r0: reg_info.name = "r0"; break; case dwarf_r1: reg_info.name = "r1"; break; case dwarf_r2: reg_info.name = "r2"; break; case dwarf_r3: reg_info.name = "r3"; break; case dwarf_r4: reg_info.name = "r4"; break; case dwarf_r5: reg_info.name = "r5"; break; case dwarf_r6: reg_info.name = "r6"; break; case dwarf_r7: reg_info.name = "r7"; reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; break; case dwarf_r8: reg_info.name = "r8"; break; case dwarf_r9: reg_info.name = "r9"; break; case dwarf_r10: reg_info.name = "r10"; break; case dwarf_r11: reg_info.name = "r11"; break; case dwarf_r12: reg_info.name = "r12"; break; case dwarf_sp: reg_info.name = "sp"; reg_info.alt_name = "r13"; reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; break; case dwarf_lr: reg_info.name = "lr"; reg_info.alt_name = "r14"; reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; break; case dwarf_pc: reg_info.name = "pc"; reg_info.alt_name = "r15"; reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; break; case dwarf_cpsr: reg_info.name = "cpsr"; reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; break; case dwarf_s0: reg_info.name = "s0"; break; case dwarf_s1: reg_info.name = "s1"; break; case dwarf_s2: reg_info.name = "s2"; break; case dwarf_s3: reg_info.name = "s3"; break; case dwarf_s4: reg_info.name = "s4"; break; case dwarf_s5: reg_info.name = "s5"; break; case dwarf_s6: reg_info.name = "s6"; break; case dwarf_s7: reg_info.name = "s7"; break; case dwarf_s8: reg_info.name = "s8"; break; case dwarf_s9: reg_info.name = "s9"; break; case dwarf_s10: reg_info.name = "s10"; break; case dwarf_s11: reg_info.name = "s11"; break; case dwarf_s12: reg_info.name = "s12"; break; case dwarf_s13: reg_info.name = "s13"; break; case dwarf_s14: reg_info.name = "s14"; break; case dwarf_s15: reg_info.name = "s15"; break; case dwarf_s16: reg_info.name = "s16"; break; case dwarf_s17: reg_info.name = "s17"; break; case dwarf_s18: reg_info.name = "s18"; break; case dwarf_s19: reg_info.name = "s19"; break; case dwarf_s20: reg_info.name = "s20"; break; case dwarf_s21: reg_info.name = "s21"; break; case dwarf_s22: reg_info.name = "s22"; break; case dwarf_s23: reg_info.name = "s23"; break; case dwarf_s24: reg_info.name = "s24"; break; case dwarf_s25: reg_info.name = "s25"; break; case dwarf_s26: reg_info.name = "s26"; break; case dwarf_s27: reg_info.name = "s27"; break; case dwarf_s28: reg_info.name = "s28"; break; case dwarf_s29: reg_info.name = "s29"; break; case dwarf_s30: reg_info.name = "s30"; break; case dwarf_s31: reg_info.name = "s31"; break; // FPA Registers 0-7 case dwarf_f0: reg_info.name = "f0"; break; case dwarf_f1: reg_info.name = "f1"; break; case dwarf_f2: reg_info.name = "f2"; break; case dwarf_f3: reg_info.name = "f3"; break; case dwarf_f4: reg_info.name = "f4"; break; case dwarf_f5: reg_info.name = "f5"; break; case dwarf_f6: reg_info.name = "f6"; break; case dwarf_f7: reg_info.name = "f7"; break; // Intel wireless MMX general purpose registers 0 - 7 XScale accumulator // register 0 - 7 (they do overlap with wCGR0 - wCGR7) case dwarf_wCGR0: reg_info.name = "wCGR0/ACC0"; break; case dwarf_wCGR1: reg_info.name = "wCGR1/ACC1"; break; case dwarf_wCGR2: reg_info.name = "wCGR2/ACC2"; break; case dwarf_wCGR3: reg_info.name = "wCGR3/ACC3"; break; case dwarf_wCGR4: reg_info.name = "wCGR4/ACC4"; break; case dwarf_wCGR5: reg_info.name = "wCGR5/ACC5"; break; case dwarf_wCGR6: reg_info.name = "wCGR6/ACC6"; break; case dwarf_wCGR7: reg_info.name = "wCGR7/ACC7"; break; // Intel wireless MMX data registers 0 - 15 case dwarf_wR0: reg_info.name = "wR0"; break; case dwarf_wR1: reg_info.name = "wR1"; break; case dwarf_wR2: reg_info.name = "wR2"; break; case dwarf_wR3: reg_info.name = "wR3"; break; case dwarf_wR4: reg_info.name = "wR4"; break; case dwarf_wR5: reg_info.name = "wR5"; break; case dwarf_wR6: reg_info.name = "wR6"; break; case dwarf_wR7: reg_info.name = "wR7"; break; case dwarf_wR8: reg_info.name = "wR8"; break; case dwarf_wR9: reg_info.name = "wR9"; break; case dwarf_wR10: reg_info.name = "wR10"; break; case dwarf_wR11: reg_info.name = "wR11"; break; case dwarf_wR12: reg_info.name = "wR12"; break; case dwarf_wR13: reg_info.name = "wR13"; break; case dwarf_wR14: reg_info.name = "wR14"; break; case dwarf_wR15: reg_info.name = "wR15"; break; case dwarf_spsr: reg_info.name = "spsr"; break; case dwarf_spsr_fiq: reg_info.name = "spsr_fiq"; break; case dwarf_spsr_irq: reg_info.name = "spsr_irq"; break; case dwarf_spsr_abt: reg_info.name = "spsr_abt"; break; case dwarf_spsr_und: reg_info.name = "spsr_und"; break; case dwarf_spsr_svc: reg_info.name = "spsr_svc"; break; case dwarf_r8_usr: reg_info.name = "r8_usr"; break; case dwarf_r9_usr: reg_info.name = "r9_usr"; break; case dwarf_r10_usr: reg_info.name = "r10_usr"; break; case dwarf_r11_usr: reg_info.name = "r11_usr"; break; case dwarf_r12_usr: reg_info.name = "r12_usr"; break; case dwarf_r13_usr: reg_info.name = "r13_usr"; break; case dwarf_r14_usr: reg_info.name = "r14_usr"; break; case dwarf_r8_fiq: reg_info.name = "r8_fiq"; break; case dwarf_r9_fiq: reg_info.name = "r9_fiq"; break; case dwarf_r10_fiq: reg_info.name = "r10_fiq"; break; case dwarf_r11_fiq: reg_info.name = "r11_fiq"; break; case dwarf_r12_fiq: reg_info.name = "r12_fiq"; break; case dwarf_r13_fiq: reg_info.name = "r13_fiq"; break; case dwarf_r14_fiq: reg_info.name = "r14_fiq"; break; case dwarf_r13_irq: reg_info.name = "r13_irq"; break; case dwarf_r14_irq: reg_info.name = "r14_irq"; break; case dwarf_r13_abt: reg_info.name = "r13_abt"; break; case dwarf_r14_abt: reg_info.name = "r14_abt"; break; case dwarf_r13_und: reg_info.name = "r13_und"; break; case dwarf_r14_und: reg_info.name = "r14_und"; break; case dwarf_r13_svc: reg_info.name = "r13_svc"; break; case dwarf_r14_svc: reg_info.name = "r14_svc"; break; // Intel wireless MMX control register in co-processor 0 - 7 case dwarf_wC0: reg_info.name = "wC0"; break; case dwarf_wC1: reg_info.name = "wC1"; break; case dwarf_wC2: reg_info.name = "wC2"; break; case dwarf_wC3: reg_info.name = "wC3"; break; case dwarf_wC4: reg_info.name = "wC4"; break; case dwarf_wC5: reg_info.name = "wC5"; break; case dwarf_wC6: reg_info.name = "wC6"; break; case dwarf_wC7: reg_info.name = "wC7"; break; // VFP-v3/Neon case dwarf_d0: reg_info.name = "d0"; break; case dwarf_d1: reg_info.name = "d1"; break; case dwarf_d2: reg_info.name = "d2"; break; case dwarf_d3: reg_info.name = "d3"; break; case dwarf_d4: reg_info.name = "d4"; break; case dwarf_d5: reg_info.name = "d5"; break; case dwarf_d6: reg_info.name = "d6"; break; case dwarf_d7: reg_info.name = "d7"; break; case dwarf_d8: reg_info.name = "d8"; break; case dwarf_d9: reg_info.name = "d9"; break; case dwarf_d10: reg_info.name = "d10"; break; case dwarf_d11: reg_info.name = "d11"; break; case dwarf_d12: reg_info.name = "d12"; break; case dwarf_d13: reg_info.name = "d13"; break; case dwarf_d14: reg_info.name = "d14"; break; case dwarf_d15: reg_info.name = "d15"; break; case dwarf_d16: reg_info.name = "d16"; break; case dwarf_d17: reg_info.name = "d17"; break; case dwarf_d18: reg_info.name = "d18"; break; case dwarf_d19: reg_info.name = "d19"; break; case dwarf_d20: reg_info.name = "d20"; break; case dwarf_d21: reg_info.name = "d21"; break; case dwarf_d22: reg_info.name = "d22"; break; case dwarf_d23: reg_info.name = "d23"; break; case dwarf_d24: reg_info.name = "d24"; break; case dwarf_d25: reg_info.name = "d25"; break; case dwarf_d26: reg_info.name = "d26"; break; case dwarf_d27: reg_info.name = "d27"; break; case dwarf_d28: reg_info.name = "d28"; break; case dwarf_d29: reg_info.name = "d29"; break; case dwarf_d30: reg_info.name = "d30"; break; case dwarf_d31: reg_info.name = "d31"; break; // NEON 128-bit vector registers (overlays the d registers) case dwarf_q0: reg_info.name = "q0"; break; case dwarf_q1: reg_info.name = "q1"; break; case dwarf_q2: reg_info.name = "q2"; break; case dwarf_q3: reg_info.name = "q3"; break; case dwarf_q4: reg_info.name = "q4"; break; case dwarf_q5: reg_info.name = "q5"; break; case dwarf_q6: reg_info.name = "q6"; break; case dwarf_q7: reg_info.name = "q7"; break; case dwarf_q8: reg_info.name = "q8"; break; case dwarf_q9: reg_info.name = "q9"; break; case dwarf_q10: reg_info.name = "q10"; break; case dwarf_q11: reg_info.name = "q11"; break; case dwarf_q12: reg_info.name = "q12"; break; case dwarf_q13: reg_info.name = "q13"; break; case dwarf_q14: reg_info.name = "q14"; break; case dwarf_q15: reg_info.name = "q15"; break; default: return {}; } return reg_info; } // A8.6.50 // Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition. static uint32_t CountITSize(uint32_t ITMask) { // First count the trailing zeros of the IT mask. uint32_t TZ = llvm::countr_zero(ITMask); if (TZ > 3) { return 0; } return (4 - TZ); } // Init ITState. Note that at least one bit is always 1 in mask. bool ITSession::InitIT(uint32_t bits7_0) { ITCounter = CountITSize(Bits32(bits7_0, 3, 0)); if (ITCounter == 0) return false; // A8.6.50 IT unsigned short FirstCond = Bits32(bits7_0, 7, 4); if (FirstCond == 0xF) { return false; } if (FirstCond == 0xE && ITCounter != 1) { return false; } ITState = bits7_0; return true; } // Update ITState if necessary. void ITSession::ITAdvance() { // assert(ITCounter); --ITCounter; if (ITCounter == 0) ITState = 0; else { unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1; SetBits32(ITState, 4, 0, NewITState4_0); } } // Return true if we're inside an IT Block. bool ITSession::InITBlock() { return ITCounter != 0; } // Return true if we're the last instruction inside an IT Block. bool ITSession::LastInITBlock() { return ITCounter == 1; } // Get condition bits for the current thumb instruction. uint32_t ITSession::GetCond() { if (InITBlock()) return Bits32(ITState, 7, 4); else return COND_AL; } // ARM constants used during decoding #define REG_RD 0 #define LDM_REGLIST 1 #define SP_REG 13 #define LR_REG 14 #define PC_REG 15 #define PC_REGLIST_BIT 0x8000 #define ARMv4 (1u << 0) #define ARMv4T (1u << 1) #define ARMv5T (1u << 2) #define ARMv5TE (1u << 3) #define ARMv5TEJ (1u << 4) #define ARMv6 (1u << 5) #define ARMv6K (1u << 6) #define ARMv6T2 (1u << 7) #define ARMv7 (1u << 8) #define ARMv7S (1u << 9) #define ARMv8 (1u << 10) #define ARMvAll (0xffffffffu) #define ARMV4T_ABOVE \ (ARMv4T | ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | \ ARMv7S | ARMv8) #define ARMV5_ABOVE \ (ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | \ ARMv8) #define ARMV5TE_ABOVE \ (ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) #define ARMV5J_ABOVE \ (ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) #define ARMV6_ABOVE (ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) #define ARMV6T2_ABOVE (ARMv6T2 | ARMv7 | ARMv7S | ARMv8) #define ARMV7_ABOVE (ARMv7 | ARMv7S | ARMv8) #define No_VFP 0 #define VFPv1 (1u << 1) #define VFPv2 (1u << 2) #define VFPv3 (1u << 3) #define AdvancedSIMD (1u << 4) #define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD) #define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD) #define VFPv2v3 (VFPv2 | VFPv3) // // EmulateInstructionARM implementation // void EmulateInstructionARM::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance); } void EmulateInstructionARM::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } llvm::StringRef EmulateInstructionARM::GetPluginDescriptionStatic() { return "Emulate instructions for the ARM architecture."; } EmulateInstruction * EmulateInstructionARM::CreateInstance(const ArchSpec &arch, InstructionType inst_type) { if (EmulateInstructionARM::SupportsEmulatingInstructionsOfTypeStatic( inst_type)) { if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::unique_ptr emulate_insn_up( new EmulateInstructionARM(arch)); if (emulate_insn_up) return emulate_insn_up.release(); } else if (arch.GetTriple().getArch() == llvm::Triple::thumb) { std::unique_ptr emulate_insn_up( new EmulateInstructionARM(arch)); if (emulate_insn_up) return emulate_insn_up.release(); } } return nullptr; } bool EmulateInstructionARM::SetTargetTriple(const ArchSpec &arch) { if (arch.GetTriple().getArch() == llvm::Triple::arm) return true; else if (arch.GetTriple().getArch() == llvm::Triple::thumb) return true; return false; } // Write "bits (32) UNKNOWN" to memory address "address". Helper function for // many ARM instructions. bool EmulateInstructionARM::WriteBits32UnknownToMemory(addr_t address) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextWriteMemoryRandomBits; context.SetNoArgs(); uint32_t random_data = rand(); const uint32_t addr_byte_size = GetAddressByteSize(); return MemAWrite(context, address, random_data, addr_byte_size); } // Write "bits (32) UNKNOWN" to register n. Helper function for many ARM // instructions. bool EmulateInstructionARM::WriteBits32Unknown(int n) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextWriteRegisterRandomBits; context.SetNoArgs(); bool success; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; return true; } std::optional EmulateInstructionARM::GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num) { if (reg_kind == eRegisterKindGeneric) { switch (reg_num) { case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = dwarf_pc; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sp; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_r7; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = dwarf_lr; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = dwarf_cpsr; break; default: return {}; } } if (reg_kind == eRegisterKindDWARF) return GetARMDWARFRegisterInfo(reg_num); return {}; } uint32_t EmulateInstructionARM::GetFramePointerRegisterNumber() const { if (m_arch.GetTriple().isAndroid()) return LLDB_INVALID_REGNUM; // Don't use frame pointer on android bool is_apple = false; if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple) is_apple = true; switch (m_arch.GetTriple().getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: case llvm::Triple::TvOS: case llvm::Triple::WatchOS: case llvm::Triple::XROS: case llvm::Triple::BridgeOS: is_apple = true; break; default: break; } /* On Apple iOS et al, the frame pointer register is always r7. * Typically on other ARM systems, thumb code uses r7; arm code uses r11. * Windows on ARM, which is in thumb mode, uses r11 though. */ uint32_t fp_regnum = 11; if (is_apple) fp_regnum = 7; if (m_opcode_mode == eModeThumb && !m_arch.GetTriple().isOSWindows()) fp_regnum = 7; return fp_regnum; } uint32_t EmulateInstructionARM::GetFramePointerDWARFRegisterNumber() const { bool is_apple = false; if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple) is_apple = true; switch (m_arch.GetTriple().getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: is_apple = true; break; default: break; } /* On Apple iOS et al, the frame pointer register is always r7. * Typically on other ARM systems, thumb code uses r7; arm code uses r11. * Windows on ARM, which is in thumb mode, uses r11 though. */ uint32_t fp_regnum = dwarf_r11; if (is_apple) fp_regnum = dwarf_r7; if (m_opcode_mode == eModeThumb && !m_arch.GetTriple().isOSWindows()) fp_regnum = dwarf_r7; return fp_regnum; } // Push Multiple Registers stores multiple registers to the stack, storing to // consecutive memory locations ending just below the address in SP, and // updates // SP to point to the start of the stored data. bool EmulateInstructionARM::EmulatePUSH(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(13); address = SP - 4*BitCount(registers); for (i = 0 to 14) { if (registers == '1') { if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1 MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; } } if (registers<15> == '1') // Only possible for encoding A1 or A2 MemA[address,4] = PCStoreValue(); SP = SP - 4*BitCount(registers); } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t registers = 0; uint32_t Rt; // the source register switch (encoding) { case eEncodingT1: registers = Bits32(opcode, 7, 0); // The M bit represents LR. if (Bit32(opcode, 8)) registers |= (1u << 14); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // Ignore bits 15 & 13. registers = Bits32(opcode, 15, 0) & ~0xa000; // if BitCount(registers) < 2 then UNPREDICTABLE; if (BitCount(registers) < 2) return false; break; case eEncodingT3: Rt = Bits32(opcode, 15, 12); // if BadReg(t) then UNPREDICTABLE; if (BadReg(Rt)) return false; registers = (1u << Rt); break; case eEncodingA1: registers = Bits32(opcode, 15, 0); // Instead of return false, let's handle the following case as well, // which amounts to pushing one reg onto the full descending stacks. // if BitCount(register_list) < 2 then SEE STMDB / STMFD; break; case eEncodingA2: Rt = Bits32(opcode, 15, 12); // if t == 13 then UNPREDICTABLE; if (Rt == dwarf_sp) return false; registers = (1u << Rt); break; default: return false; } addr_t sp_offset = addr_byte_size * BitCount(registers); addr_t addr = sp - sp_offset; uint32_t i; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextPushRegisterOnStack; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); for (i = 0; i < 15; ++i) { if (BitIsSet(registers, i)) { std::optional reg_info = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); context.SetRegisterToRegisterPlusOffset(*reg_info, *sp_reg, addr - sp); uint32_t reg_value = ReadCoreReg(i, &success); if (!success) return false; if (!MemAWrite(context, addr, reg_value, addr_byte_size)) return false; addr += addr_byte_size; } } if (BitIsSet(registers, 15)) { std::optional reg_info = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterToRegisterPlusOffset(*reg_info, *sp_reg, addr - sp); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite(context, addr, pc, addr_byte_size)) return false; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(-sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) return false; } return true; } // Pop Multiple Registers loads multiple registers from the stack, loading from // consecutive memory locations staring at the address in SP, and updates // SP to point just above the loaded data. bool EmulateInstructionARM::EmulatePOP(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(13); address = SP; for i = 0 to 14 if registers == '1' then R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4; if registers<15> == '1' then if UnalignedAllowed then LoadWritePC(MemU[address,4]); else LoadWritePC(MemA[address,4]); if registers<13> == '0' then SP = SP + 4*BitCount(registers); if registers<13> == '1' then SP = bits(32) UNKNOWN; } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t registers = 0; uint32_t Rt; // the destination register switch (encoding) { case eEncodingT1: registers = Bits32(opcode, 7, 0); // The P bit represents PC. if (Bit32(opcode, 8)) registers |= (1u << 15); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // Ignore bit 13. registers = Bits32(opcode, 15, 0) & ~0x2000; // if BitCount(registers) < 2 || (P == '1' && M == '1') then // UNPREDICTABLE; if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then // UNPREDICTABLE; if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT3: Rt = Bits32(opcode, 15, 12); // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then // UNPREDICTABLE; if (Rt == 13) return false; if (Rt == 15 && InITBlock() && !LastInITBlock()) return false; registers = (1u << Rt); break; case eEncodingA1: registers = Bits32(opcode, 15, 0); // Instead of return false, let's handle the following case as well, // which amounts to popping one reg from the full descending stacks. // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD; // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE; if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7) return false; break; case eEncodingA2: Rt = Bits32(opcode, 15, 12); // if t == 13 then UNPREDICTABLE; if (Rt == dwarf_sp) return false; registers = (1u << Rt); break; default: return false; } addr_t sp_offset = addr_byte_size * BitCount(registers); addr_t addr = sp; uint32_t i, data; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextPopRegisterOffStack; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); for (i = 0; i < 15; ++i) { if (BitIsSet(registers, i)) { context.SetAddress(addr); data = MemARead(context, addr, 4, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; addr += addr_byte_size; } } if (BitIsSet(registers, 15)) { context.SetRegisterPlusOffset(*sp_reg, addr - sp); data = MemARead(context, addr, 4, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; // addr += addr_byte_size; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) return false; } return true; } // Set r7 or ip to point to saved value residing within the stack. // ADD (SP plus immediate) bool EmulateInstructionARM::EmulateADDRdSPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t Rd; // the destination register uint32_t imm32; switch (encoding) { case eEncodingT1: Rd = 7; imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32) break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t sp_offset = imm32; addr_t addr = sp + sp_offset; // a pointer to the stack area EmulateInstruction::Context context; if (Rd == GetFramePointerRegisterNumber()) context.type = eContextSetFramePointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); context.SetRegisterPlusOffset(*sp_reg, sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, addr)) return false; } return true; } // Set r7 or ip to the current stack pointer. // MOV (register) bool EmulateInstructionARM::EmulateMOVRdSP(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = R[m]; if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); // APSR.C unchanged // APSR.V unchanged } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t Rd; // the destination register switch (encoding) { case eEncodingT1: Rd = 7; break; case eEncodingA1: Rd = 12; break; default: return false; } EmulateInstruction::Context context; if (Rd == GetFramePointerRegisterNumber()) context.type = EmulateInstruction::eContextSetFramePointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); context.SetRegisterPlusOffset(*sp_reg, 0); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, sp)) return false; } return true; } // Move from high register (r8-r15) to low register (r0-r7). // MOV (register) bool EmulateInstructionARM::EmulateMOVLowHigh(const uint32_t opcode, const ARMEncoding encoding) { return EmulateMOVRdRm(opcode, encoding); } // Move from register to register. // MOV (register) bool EmulateInstructionARM::EmulateMOVRdRm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = R[m]; if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); // APSR.C unchanged // APSR.V unchanged } #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rm; // the source register uint32_t Rd; // the destination register bool setflags; switch (encoding) { case eEncodingT1: Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); setflags = false; if (Rd == 15 && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT2: Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = true; if (InITBlock()) return false; break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (setflags && (BadReg(Rd) || BadReg(Rm))) return false; // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then // UNPREDICTABLE; if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13))) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } uint32_t result = ReadCoreReg(Rm, &success); if (!success) return false; // The context specifies that Rm is to be moved into Rd. EmulateInstruction::Context context; if (Rd == 13) context.type = EmulateInstruction::eContextAdjustStackPointer; else if (Rd == GetFramePointerRegisterNumber() && Rm == 13) context.type = EmulateInstruction::eContextSetFramePointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegisterPlusOffset(*dwarf_reg, 0); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags)) return false; } return true; } // Move (immediate) writes an immediate value to the destination register. It // can optionally update the condition flags based on the value. // MOV (immediate) bool EmulateInstructionARM::EmulateMOVRdImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t imm32; // the immediate value to be written to Rd uint32_t carry = 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C. // for setflags == false, this value is a don't care initialized to // 0 to silence the static analyzer bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) carry = APSR_C; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); if (BadReg(Rd)) return false; break; case eEncodingT3: { // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8, // 32); Rd = Bits32(opcode, 11, 8); setflags = false; uint32_t imm4 = Bits32(opcode, 19, 16); uint32_t imm3 = Bits32(opcode, 14, 12); uint32_t i = Bit32(opcode, 26); uint32_t imm8 = Bits32(opcode, 7, 0); imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; // if BadReg(d) then UNPREDICTABLE; if (BadReg(Rd)) return false; } break; case eEncodingA1: // d = UInt(Rd); setflags = (S == '1'); (imm32, carry) = // ARMExpandImm_C(imm12, APSR.C); Rd = Bits32(opcode, 15, 12); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if ((Rd == 15) && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; case eEncodingA2: { // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32); Rd = Bits32(opcode, 15, 12); setflags = false; uint32_t imm4 = Bits32(opcode, 19, 16); uint32_t imm12 = Bits32(opcode, 11, 0); imm32 = (imm4 << 12) | imm12; // if d == 15 then UNPREDICTABLE; if (Rd == 15) return false; } break; default: return false; } uint32_t result = imm32; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // MUL multiplies two register values. The least significant 32 bits of the // result are written to the destination // register. These 32 bits do not depend on whether the source register values // are considered to be signed values or unsigned values. // // Optionally, it can update the condition flags based on the result. In the // Thumb instruction set, this option is limited to only a few forms of the // instruction. bool EmulateInstructionARM::EmulateMUL(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results result = operand1 * operand2; R[d] = result<31:0>; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); if ArchVersion() == 4 then APSR.C = bit UNKNOWN; // else APSR.C unchanged // APSR.V always unchanged #endif if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; bool setflags; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock(); d = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 2, 0); setflags = !InITBlock(); // if ArchVersion() < 6 && d == n then UNPREDICTABLE; if ((ArchVersion() < ARMv6) && (d == n)) return false; break; case eEncodingT2: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE; d = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); setflags = false; // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE; if (BadReg(d) || BadReg(n) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); d = Bits32(opcode, 19, 16); n = Bits32(opcode, 3, 0); m = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (n == 15) || (m == 15)) return false; // if ArchVersion() < 6 && d == n then UNPREDICTABLE; if ((ArchVersion() < ARMv6) && (d == n)) return false; break; default: return false; } bool success = false; // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final // results uint64_t operand1 = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final // results uint64_t operand2 = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // result = operand1 * operand2; uint64_t result = operand1 * operand2; // R[d] = result<31:0>; std::optional op1_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional op2_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextArithmetic; context.SetRegisterRegisterOperands(*op1_reg, *op2_reg); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, (0x0000ffff & result))) return false; // if setflags then if (setflags) { // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); m_new_inst_cpsr = m_opcode_cpsr; SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, 31)); SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); if (m_new_inst_cpsr != m_opcode_cpsr) { if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) return false; } // if ArchVersion() == 4 then // APSR.C = bit UNKNOWN; } } return true; } // Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to // the destination register. It can optionally update the condition flags based // on the value. bool EmulateInstructionARM::EmulateMVNImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = NOT(imm32); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } uint32_t result = ~imm32; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise NOT (register) writes the bitwise inverse of a register value to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateMVNReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = NOT(shifted); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rm; // the source register uint32_t Rd; // the destination register ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; // the carry bit after the shift operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; if (InITBlock()) return false; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (BadReg(Rd) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } bool success = false; uint32_t value = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(value, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = ~shifted; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // PC relative immediate load into register, possibly followed by ADD (SP plus // register). // LDR (literal) bool EmulateInstructionARM::EmulateLDRRtPCRelative(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,4]; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 if CurrentInstrSet() == InstrSet_ARM then R[t] = ROR(data, 8*UInt(address<1:0>)); else R[t] = bits(32) UNKNOWN; } #endif if (ConditionPassed(opcode)) { bool success = false; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; // PC relative immediate load context EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional pc_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterPlusOffset(*pc_reg, 0); uint32_t Rt; // the destination register uint32_t imm32; // immediate offset from the PC bool add; // +imm32 or -imm32? addr_t base; // the base address addr_t address; // the PC relative address uint32_t data; // the literal data value from the PC relative load switch (encoding) { case eEncodingT1: Rt = Bits32(opcode, 10, 8); imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32); add = true; break; case eEncodingT2: Rt = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32); add = BitIsSet(opcode, 23); if (Rt == 15 && InITBlock() && !LastInITBlock()) return false; break; default: return false; } base = Align(pc, 4); if (add) address = base + imm32; else address = base - imm32; context.SetRegisterPlusOffset(*pc_reg, address - base); data = MemURead(context, address, 4, 0, &success); if (!success) return false; if (Rt == 15) { if (Bits32(address, 1, 0) == 0) { // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } else return false; } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) return false; } else // We don't handle ARM for now. return false; } return true; } // An add operation to adjust the SP. // ADD (SP plus immediate) bool EmulateInstructionARM::EmulateADDSPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t imm32; // the immediate operand uint32_t d; bool setflags; switch (encoding) { case eEncodingT1: // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32); d = Bits32(opcode, 10, 8); imm32 = (Bits32(opcode, 7, 0) << 2); setflags = false; break; case eEncodingT2: // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); d = 13; imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) setflags = false; break; case eEncodingT3: // d = UInt(Rd); setflags = (S == "1"); imm32 = // ThumbExpandImm(i:imm3:imm8); d = Bits32(opcode, 11, 8); imm32 = ThumbExpandImm(opcode); setflags = Bit32(opcode, 20); // if Rd == "1111" && S == "1" then SEE CMN (immediate); if (d == 15 && setflags == 1) return false; // CMN (immediate) not yet supported // if d == 15 && S == "0" then UNPREDICTABLE; if (d == 15 && setflags == 0) return false; break; case eEncodingT4: { // if Rn == '1111' then SEE ADR; // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32); d = Bits32(opcode, 11, 8); setflags = false; uint32_t i = Bit32(opcode, 26); uint32_t imm3 = Bits32(opcode, 14, 12); uint32_t imm8 = Bits32(opcode, 7, 0); imm32 = (i << 11) | (imm3 << 8) | imm8; // if d == 15 then UNPREDICTABLE; if (d == 15) return false; } break; default: return false; } // (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); AddWithCarryResult res = AddWithCarry(sp, imm32, 0); EmulateInstruction::Context context; if (d == 13) context.type = EmulateInstruction::eContextAdjustStackPointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); context.SetRegisterPlusOffset(*sp_reg, res.result - sp); if (d == 15) { if (!ALUWritePC(context, res.result)) return false; } else { // R[d] = result; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags, res.carry_out, res.overflow)) return false; } } return true; } // An add operation to adjust the SP. // ADD (SP plus register) bool EmulateInstructionARM::EmulateADDSPRm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(SP, shifted, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t Rm; // the second operand switch (encoding) { case eEncodingT2: Rm = Bits32(opcode, 6, 3); break; default: return false; } int32_t reg_value = ReadCoreReg(Rm, &success); if (!success) return false; addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value EmulateInstruction::Context context; context.type = eContextArithmetic; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); std::optional other_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegisterRegisterOperands(*sp_reg, *other_reg); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr)) return false; } return true; } // Branch with Link and Exchange Instruction Sets (immediate) calls a // subroutine at a PC-relative address, and changes instruction set from ARM to // Thumb, or from Thumb to ARM. // BLX (immediate) bool EmulateInstructionARM::EmulateBLXImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); if CurrentInstrSet() == InstrSet_ARM then LR = PC - 4; else LR = PC<31:1> : '1'; if targetInstrSet == InstrSet_ARM then targetAddress = Align(PC,4) + imm32; else targetAddress = PC + imm32; SelectInstrSet(targetInstrSet); BranchWritePC(targetAddress); } #endif bool success = true; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t lr; // next instruction address addr_t target; // target address int32_t imm32; // PC-relative offset switch (encoding) { case eEncodingT1: { lr = pc | 1u; // return address uint32_t S = Bit32(opcode, 26); uint32_t imm10 = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<25>(imm25); target = pc + imm32; SelectInstrSet(eModeThumb); context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); if (InITBlock() && !LastInITBlock()) return false; break; } case eEncodingT2: { lr = pc | 1u; // return address uint32_t S = Bit32(opcode, 26); uint32_t imm10H = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm10L = Bits32(opcode, 10, 1); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2); imm32 = llvm::SignExtend32<25>(imm25); target = Align(pc, 4) + imm32; SelectInstrSet(eModeARM); context.SetISAAndImmediateSigned(eModeARM, 4 + imm32); if (InITBlock() && !LastInITBlock()) return false; break; } case eEncodingA1: lr = pc - 4; // return address imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); target = Align(pc, 4) + imm32; SelectInstrSet(eModeARM); context.SetISAAndImmediateSigned(eModeARM, 8 + imm32); break; case eEncodingA2: lr = pc - 4; // return address imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | Bits32(opcode, 24, 24) << 1); target = pc + imm32; SelectInstrSet(eModeThumb); context.SetISAAndImmediateSigned(eModeThumb, 8 + imm32); break; default: return false; } if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) return false; if (!BranchWritePC(context, target)) return false; if (m_opcode_cpsr != m_new_inst_cpsr) if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) return false; } return true; } // Branch with Link and Exchange (register) calls a subroutine at an address // and instruction set specified by a register. // BLX (register) bool EmulateInstructionARM::EmulateBLXRm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); target = R[m]; if CurrentInstrSet() == InstrSet_ARM then next_instr_addr = PC - 4; LR = next_instr_addr; else next_instr_addr = PC - 2; LR = next_instr_addr<31:1> : '1'; BXWritePC(target); } #endif bool success = false; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; const uint32_t pc = ReadCoreReg(PC_REG, &success); addr_t lr; // next instruction address if (!success) return false; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: lr = (pc - 2) | 1u; // return address Rm = Bits32(opcode, 6, 3); // if m == 15 then UNPREDICTABLE; if (Rm == 15) return false; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: lr = pc - 4; // return address Rm = Bits32(opcode, 3, 0); // if m == 15 then UNPREDICTABLE; if (Rm == 15) return false; break; default: return false; } addr_t target = ReadCoreReg(Rm, &success); if (!success) return false; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegister(*dwarf_reg); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) return false; if (!BXWritePC(context, target)) return false; } return true; } // Branch and Exchange causes a branch to an address and instruction set // specified by a register. bool EmulateInstructionARM::EmulateBXRm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); BXWritePC(R[m]); } #endif if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: Rm = Bits32(opcode, 6, 3); if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rm = Bits32(opcode, 3, 0); break; default: return false; } bool success = false; addr_t target = ReadCoreReg(Rm, &success); if (!success) return false; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegister(*dwarf_reg); if (!BXWritePC(context, target)) return false; } return true; } // Branch and Exchange Jazelle attempts to change to Jazelle state. If the // attempt fails, it branches to an address and instruction set specified by a // register as though it were a BX instruction. // // TODO: Emulate Jazelle architecture? // We currently assume that switching to Jazelle state fails, thus // treating BXJ as a BX operation. bool EmulateInstructionARM::EmulateBXJRm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then BXWritePC(R[m]); else if JazelleAcceptsExecution() then SwitchToJazelleExecution(); else SUBARCHITECTURE_DEFINED handler call; } #endif if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: Rm = Bits32(opcode, 19, 16); if (BadReg(Rm)) return false; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rm = Bits32(opcode, 3, 0); if (Rm == 15) return false; break; default: return false; } bool success = false; addr_t target = ReadCoreReg(Rm, &success); if (!success) return false; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegister(*dwarf_reg); if (!BXWritePC(context, target)) return false; } return true; } // Set r7 to point to some ip offset. // SUB (immediate) bool EmulateInstructionARM::EmulateSUBR7IPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif if (ConditionPassed(opcode)) { bool success = false; const addr_t ip = ReadCoreReg(12, &success); if (!success) return false; uint32_t imm32; switch (encoding) { case eEncodingA1: imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t ip_offset = imm32; addr_t addr = ip - ip_offset; // the adjusted ip value EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r12); context.SetRegisterPlusOffset(*dwarf_reg, -ip_offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r7, addr)) return false; } return true; } // Set ip to point to some stack offset. // SUB (SP minus immediate) bool EmulateInstructionARM::EmulateSUBIPSPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif if (ConditionPassed(opcode)) { bool success = false; const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t imm32; switch (encoding) { case eEncodingA1: imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t sp_offset = imm32; addr_t addr = sp - sp_offset; // the adjusted stack pointer value EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); context.SetRegisterPlusOffset(*dwarf_reg, -sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r12, addr)) return false; } return true; } // This instruction subtracts an immediate value from the SP value, and writes // the result to the destination register. // // If Rd == 13 => A sub operation to adjust the SP -- allocate space for local // storage. bool EmulateInstructionARM::EmulateSUBSPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t Rd; bool setflags; uint32_t imm32; switch (encoding) { case eEncodingT1: Rd = 13; setflags = false; imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rd == 15 && setflags) return EmulateCMPImm(opcode, eEncodingT2); if (Rd == 15 && !setflags) return false; break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); setflags = false; imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) if (Rd == 15) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1); EmulateInstruction::Context context; if (Rd == 13) { uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting // to negate it, or the wrong // value gets passed down to context.SetImmediateSigned. context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(-imm64); // the stack pointer offset } else { context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); } if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // A store operation to the stack that also updates the SP. bool EmulateInstructionARM::EmulateSTRRtSP(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; if wback then R[n] = offset_addr; } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; uint32_t Rt; // the source register uint32_t imm12; uint32_t Rn; // This function assumes Rn is the SP, but we should verify that. bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: Rt = Bits32(opcode, 15, 12); imm12 = Bits32(opcode, 11, 0); Rn = Bits32(opcode, 19, 16); if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP. return false; index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); if (wback && ((Rn == 15) || (Rn == Rt))) return false; break; default: return false; } addr_t offset_addr; if (add) offset_addr = sp + imm12; else offset_addr = sp - imm12; addr_t addr; if (index) addr = offset_addr; else addr = sp; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextPushRegisterOnStack; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rt); context.SetRegisterToRegisterPlusOffset(*dwarf_reg, *sp_reg, addr - sp); if (Rt != 15) { uint32_t reg_value = ReadCoreReg(Rt, &success); if (!success) return false; if (!MemUWrite(context, addr, reg_value, addr_byte_size)) return false; } else { const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemUWrite(context, addr, pc, addr_byte_size)) return false; } if (wback) { context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(addr - sp); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, offset_addr)) return false; } } return true; } // Vector Push stores multiple extension registers to the stack. It also // updates SP to point to the start of the stored data. bool EmulateInstructionARM::EmulateVPUSH(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); address = SP - imm32; SP = SP - imm32; if single_regs then for r = 0 to regs-1 MemA[address,4] = S[d+r]; address = address+4; else for r = 0 to regs-1 // Store as two word-aligned words in the correct order for // current endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; address = address+8; } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; bool single_regs; uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register uint32_t imm32; // stack offset uint32_t regs; // number of registers switch (encoding) { case eEncodingT1: case eEncodingA1: single_regs = false; d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; // If UInt(imm8) is odd, see "FSTMX". regs = Bits32(opcode, 7, 0) / 2; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; case eEncodingT2: case eEncodingA2: single_regs = true; d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; regs = Bits32(opcode, 7, 0); // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; default: return false; } uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; addr_t sp_offset = imm32; addr_t addr = sp - sp_offset; uint32_t i; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextPushRegisterOnStack; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); for (i = 0; i < regs; ++i) { std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i); context.SetRegisterToRegisterPlusOffset(*dwarf_reg, *sp_reg, addr - sp); // uint64_t to accommodate 64-bit registers. uint64_t reg_value = ReadRegisterUnsigned(*dwarf_reg, 0, &success); if (!success) return false; if (!MemAWrite(context, addr, reg_value, reg_byte_size)) return false; addr += reg_byte_size; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(-sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) return false; } return true; } // Vector Pop loads multiple extension registers from the stack. It also // updates SP to point just above the loaded data. bool EmulateInstructionARM::EmulateVPOP(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); address = SP; SP = SP + imm32; if single_regs then for r = 0 to regs-1 S[d+r] = MemA[address,4]; address = address+4; else for r = 0 to regs-1 word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; // Combine the word-aligned words in the correct order for // current endianness. D[d+r] = if BigEndian() then word1:word2 else word2:word1; } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg(SP_REG, &success); if (!success) return false; bool single_regs; uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register uint32_t imm32; // stack offset uint32_t regs; // number of registers switch (encoding) { case eEncodingT1: case eEncodingA1: single_regs = false; d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; // If UInt(imm8) is odd, see "FLDMX". regs = Bits32(opcode, 7, 0) / 2; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; case eEncodingT2: case eEncodingA2: single_regs = true; d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; regs = Bits32(opcode, 7, 0); // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; default: return false; } uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; addr_t sp_offset = imm32; addr_t addr = sp; uint32_t i; uint64_t data; // uint64_t to accommodate 64-bit registers. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextPopRegisterOffStack; for (i = 0; i < regs; ++i) { std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i); context.SetAddress(addr); data = MemARead(context, addr, reg_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, *dwarf_reg, data)) return false; addr += reg_byte_size; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned(sp_offset); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) return false; } return true; } // SVC (previously SWI) bool EmulateInstructionARM::EmulateSVC(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); CallSupervisor(); } #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t pc = ReadCoreReg(PC_REG, &success); addr_t lr; // next instruction address if (!success) return false; uint32_t imm32; // the immediate constant uint32_t mode; // ARM or Thumb mode switch (encoding) { case eEncodingT1: lr = (pc + 2) | 1u; // return address imm32 = Bits32(opcode, 7, 0); mode = eModeThumb; break; case eEncodingA1: lr = pc + 4; // return address imm32 = Bits32(opcode, 23, 0); mode = eModeARM; break; default: return false; } EmulateInstruction::Context context; context.type = EmulateInstruction::eContextSupervisorCall; context.SetISAAndImmediate(mode, imm32); if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) return false; } return true; } // If Then makes up to four following instructions (the IT block) conditional. bool EmulateInstructionARM::EmulateIT(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... EncodingSpecificOperations(); ITSTATE.IT<7:0> = firstcond:mask; #endif m_it_session.InitIT(Bits32(opcode, 7, 0)); return true; } bool EmulateInstructionARM::EmulateNop(const uint32_t opcode, const ARMEncoding encoding) { // NOP, nothing to do... return true; } // Branch causes a branch to a target address. bool EmulateInstructionARM::EmulateB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); BranchWritePC(PC + imm32); } #endif bool success = false; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t target; // target address int32_t imm32; // PC-relative offset switch (encoding) { case eEncodingT1: // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1); target = pc + imm32; context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); break; case eEncodingT2: imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0) << 1); target = pc + imm32; context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); break; case eEncodingT3: // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). { if (Bits32(opcode, 25, 23) == 7) return false; // See Branches and miscellaneous control on page // A6-235. uint32_t S = Bit32(opcode, 26); uint32_t imm6 = Bits32(opcode, 21, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t imm21 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<21>(imm21); target = pc + imm32; context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); break; } case eEncodingT4: { uint32_t S = Bit32(opcode, 26); uint32_t imm10 = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<25>(imm25); target = pc + imm32; context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); break; } case eEncodingA1: imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); target = pc + imm32; context.SetISAAndImmediateSigned(eModeARM, 8 + imm32); break; default: return false; } if (!BranchWritePC(context, target)) return false; } return true; } // Compare and Branch on Nonzero and Compare and Branch on Zero compare the // value in a register with zero and conditionally branch forward a constant // value. They do not affect the condition flags. CBNZ, CBZ bool EmulateInstructionARM::EmulateCB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... EncodingSpecificOperations(); if nonzero ^ IsZero(R[n]) then BranchWritePC(PC + imm32); #endif bool success = false; // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t target; // target address uint32_t imm32; // PC-relative offset to branch forward bool nonzero; switch (encoding) { case eEncodingT1: imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1; nonzero = BitIsSet(opcode, 11); target = pc + imm32; context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); break; default: return false; } if (m_ignore_conditions || (nonzero ^ (reg_val == 0))) if (!BranchWritePC(context, target)) return false; return true; } // Table Branch Byte causes a PC-relative forward branch using a table of // single byte offsets. // A base register provides a pointer to the table, and a second register // supplies an index into the table. // The branch length is twice the value of the byte returned from the table. // // Table Branch Halfword causes a PC-relative forward branch using a table of // single halfword offsets. // A base register provides a pointer to the table, and a second register // supplies an index into the table. // The branch length is twice the value of the halfword returned from the // table. TBB, TBH bool EmulateInstructionARM::EmulateTB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... EncodingSpecificOperations(); NullCheckIfThumbEE(n); if is_tbh then halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]); else halfwords = UInt(MemU[R[n]+R[m], 1]); BranchWritePC(PC + 2*halfwords); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn; // the base register which contains the address of the table of // branch lengths uint32_t Rm; // the index register which contains an integer pointing to a // byte/halfword in the table bool is_tbh; // true if table branch halfword switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); is_tbh = BitIsSet(opcode, 4); if (Rn == 13 || BadReg(Rm)) return false; if (InITBlock() && !LastInITBlock()) return false; break; default: return false; } // Read the address of the table from the operand register Rn. The PC can // be used, in which case the table immediately follows this instruction. uint32_t base = ReadCoreReg(Rn, &success); if (!success) return false; // the table index uint32_t index = ReadCoreReg(Rm, &success); if (!success) return false; // the offsetted table address addr_t addr = base + (is_tbh ? index * 2 : index); // PC-relative offset to branch forward EmulateInstruction::Context context; context.type = EmulateInstruction::eContextTableBranchReadMemory; uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2; if (!success) return false; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; // target address addr_t target = pc + offset; context.type = EmulateInstruction::eContextRelativeBranchImmediate; context.SetISAAndImmediateSigned(eModeThumb, 4 + offset); if (!BranchWritePC(context, target)) return false; } return true; } // This instruction adds an immediate value to a register value, and writes the // result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateADDImmThumb(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; bool setflags; uint32_t imm32; uint32_t carry_out; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 = // ZeroExtend(imm3, 32); d = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm32 = Bits32(opcode, 8, 6); break; case eEncodingT2: // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 = // ZeroExtend(imm8, 32); d = Bits32(opcode, 10, 8); n = Bits32(opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32(opcode, 7, 0); break; case eEncodingT3: // if Rd == '1111' && S == '1' then SEE CMN (immediate); // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 = // ThumbExpandImm(i:imm3:imm8); d = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry_out); // if Rn == '1101' then SEE ADD (SP plus immediate); if (n == 13) return EmulateADDSPImm(opcode, eEncodingT3); // if BadReg(d) || n == 15 then UNPREDICTABLE; if (BadReg(d) || (n == 15)) return false; break; case eEncodingT4: { // if Rn == '1111' then SEE ADR; // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 = // ZeroExtend(i:imm3:imm8, 32); d = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); setflags = false; uint32_t i = Bit32(opcode, 26); uint32_t imm3 = Bits32(opcode, 14, 12); uint32_t imm8 = Bits32(opcode, 7, 0); imm32 = (i << 11) | (imm3 << 8) | imm8; // if Rn == '1101' then SEE ADD (SP plus immediate); if (n == 13) return EmulateADDSPImm(opcode, eEncodingT4); // if BadReg(d) then UNPREDICTABLE; if (BadReg(d)) return false; break; } default: return false; } uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); AddWithCarryResult res = AddWithCarry(Rn, imm32, 0); std::optional reg_n = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextArithmetic; context.SetRegisterPlusOffset(*reg_n, imm32); // R[d] = result; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds an immediate value to a register value, and writes the // result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateADDImmARM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be added to the value obtained from Rn bool setflags; switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, imm32, 0); EmulateInstruction::Context context; if (Rd == 13) context.type = EmulateInstruction::eContextAdjustStackPointer; else if (Rd == GetFramePointerRegisterNumber()) context.type = EmulateInstruction::eContextSetFramePointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, Rn); context.SetRegisterPlusOffset(*dwarf_reg, imm32); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds a register value and an optionally-shifted register // value, and writes the result to the destination register. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateADDReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); Rm = Bits32(opcode, 8, 6); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); setflags = false; shift_t = SRType_LSL; shift_n = 0; if (Rn == 15 && Rm == 15) return false; if (Rd == 15 && InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, 0); EmulateInstruction::Context context; context.type = eContextArithmetic; std::optional op1_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn); std::optional op2_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); context.SetRegisterRegisterOperands(*op1_reg, *op2_reg); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // Compare Negative (immediate) adds a register value and an immediate value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMNImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t imm32; // the immediate value to be compared with switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rn == 15) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteFlags(context, res.result, res.carry_out, res.overflow); } // Compare Negative (register) adds a register value and an optionally-shifted // register value. It updates the condition flags based on the result, and // discards the result. bool EmulateInstructionARM::EmulateCMNReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t Rm; // the second operand ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if n == 15 || BadReg(m) then UNPREDICTABLE; if (Rn == 15 || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, 0); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteFlags(context, res.result, res.carry_out, res.overflow); } // Compare (immediate) subtracts an immediate value from a register value. It // updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMPImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t imm32; // the immediate value to be compared with switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 10, 8); imm32 = Bits32(opcode, 7, 0); break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rn == 15) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteFlags(context, res.result, res.carry_out, res.overflow); } // Compare (register) subtracts an optionally-shifted register value from a // register value. It updates the condition flags based on the result, and // discards the result. bool EmulateInstructionARM::EmulateCMPReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t Rm; // the second operand ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); shift_t = SRType_LSL; shift_n = 0; if (Rn < 8 && Rm < 8) return false; if (Rn == 15 || Rm == 15) return false; break; case eEncodingT3: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (Rn == 15 || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteFlags(context, res.result, res.carry_out, res.overflow); } // Arithmetic Shift Right (immediate) shifts a register value right by an // immediate number of bits, shifting in copies of its sign bit, and writes the // result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateASRImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm(opcode, encoding, SRType_ASR); } // Arithmetic Shift Right (register) shifts a register value right by a // variable number of bits, shifting in copies of its sign bit, and writes the // result to the destination register. The variable number of bits is read from // the bottom byte of a register. It can optionally update the condition flags // based on the result. bool EmulateInstructionARM::EmulateASRReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg(opcode, encoding, SRType_ASR); } // Logical Shift Left (immediate) shifts a register value left by an immediate // number of bits, shifting in zeros, and writes the result to the destination // register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateLSLImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm(opcode, encoding, SRType_LSL); } // Logical Shift Left (register) shifts a register value left by a variable // number of bits, shifting in zeros, and writes the result to the destination // register. The variable number of bits is read from the bottom byte of a // register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateLSLReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg(opcode, encoding, SRType_LSL); } // Logical Shift Right (immediate) shifts a register value right by an // immediate number of bits, shifting in zeros, and writes the result to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateLSRImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm(opcode, encoding, SRType_LSR); } // Logical Shift Right (register) shifts a register value right by a variable // number of bits, shifting in zeros, and writes the result to the destination // register. The variable number of bits is read from the bottom byte of a // register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateLSRReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg(opcode, encoding, SRType_LSR); } // Rotate Right (immediate) provides the value of the contents of a register // rotated by a constant value. The bits that are rotated off the right end are // inserted into the vacated bit positions on the left. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateRORImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm(opcode, encoding, SRType_ROR); } // Rotate Right (register) provides the value of the contents of a register // rotated by a variable number of bits. The bits that are rotated off the // right end are inserted into the vacated bit positions on the left. The // variable number of bits is read from the bottom byte of a register. It can // optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateRORReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg(opcode, encoding, SRType_ROR); } // Rotate Right with Extend provides the value of the contents of a register // shifted right by one place, with the carry flag shifted into bit [31]. // // RRX can optionally update the condition flags based on the result. // In that case, bit [0] is shifted into the carry flag. bool EmulateInstructionARM::EmulateRRX(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm(opcode, encoding, SRType_RRX); } bool EmulateInstructionARM::EmulateShiftImm(const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) { // assert(shift_type == SRType_ASR // || shift_type == SRType_LSL // || shift_type == SRType_LSR // || shift_type == SRType_ROR // || shift_type == SRType_RRX); bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t Rm; // the first operand register uint32_t imm5; // encoding for the shift amount uint32_t carry; // the carry bit after the shift operation bool setflags; // Special case handling! // A8.6.139 ROR (immediate) -- Encoding T1 ARMEncoding use_encoding = encoding; if (shift_type == SRType_ROR && use_encoding == eEncodingT1) { // Morph the T1 encoding from the ARM Architecture Manual into T2 // encoding to have the same decoding of bit fields as the other Thumb2 // shift operations. use_encoding = eEncodingT2; } switch (use_encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm5 = Bits32(opcode, 10, 6); break; case eEncodingT2: // A8.6.141 RRX // There's no imm form of RRX instructions. if (shift_type == SRType_RRX) return false; Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6); if (BadReg(Rd) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); imm5 = Bits32(opcode, 11, 7); break; default: return false; } // A8.6.139 ROR (immediate) if (shift_type == SRType_ROR && imm5 == 0) shift_type = SRType_RRX; // Get the first operand. uint32_t value = ReadCoreReg(Rm, &success); if (!success) return false; // Decode the shift amount if not RRX. uint32_t amt = (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5)); uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); if (!success) return false; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } bool EmulateInstructionARM::EmulateShiftReg(const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) { // assert(shift_type == SRType_ASR // || shift_type == SRType_LSL // || shift_type == SRType_LSR // || shift_type == SRType_ROR); bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t Rn; // the first operand register uint32_t Rm; // the register whose bottom byte contains the amount to shift by uint32_t carry; // the carry bit after the shift operation bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Rd; Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 3, 0); Rm = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); if (Rd == 15 || Rn == 15 || Rm == 15) return false; break; default: return false; } // Get the first operand. uint32_t value = ReadCoreReg(Rn, &success); if (!success) return false; // Get the Rm register content. uint32_t val = ReadCoreReg(Rm, &success); if (!success) return false; // Get the shift amount. uint32_t amt = Bits32(val, 7, 0); uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); if (!success) return false; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // LDM loads multiple registers from consecutive memory locations, using an // address from a base register. Optionally the address just above the highest // of those locations can be written back to the base register. bool EmulateInstructionARM::EmulateLDM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() EncodingSpecificOperations(); NullCheckIfThumbEE (n); address = R[n]; for i = 0 to 14 if registers == '1' then R[i] = MemA[address, 4]; address = address + 4; if registers<15> == '1' then LoadWritePC (MemA[address, 4]); if wback && registers == '0' then R[n] = R[n] + 4 * BitCount (registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = '00000000':register_list; wback = // (registers == '0'); n = Bits32(opcode, 10, 8); registers = Bits32(opcode, 7, 0); registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. wback = BitIsClear(registers, n); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // if W == '1' && Rn == '1101' then SEE POP; // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); registers = registers & 0xdfff; // Make sure bit 13 is zero. wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then // UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 2) || (BitIsSet(opcode, 14) && BitIsSet(opcode, 15))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then // UNPREDICTABLE; if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet(registers, n)) return false; break; case eEncodingA1: n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } int32_t offset = 0; const addr_t base_address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*dwarf_reg, offset); for (int i = 0; i < 14; ++i) { if (BitIsSet(registers, i)) { context.type = EmulateInstruction::eContextRegisterPlusOffset; context.SetRegisterPlusOffset(*dwarf_reg, offset); if (wback && (n == 13)) // Pop Instruction { context.type = EmulateInstruction::eContextPopRegisterOffStack; context.SetAddress(base_address + offset); } // R[i] = MemA [address, 4]; address = address + 4; uint32_t data = MemARead(context, base_address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } if (BitIsSet(registers, 15)) { // LoadWritePC (MemA [address, 4]); context.type = EmulateInstruction::eContextRegisterPlusOffset; context.SetRegisterPlusOffset(*dwarf_reg, offset); uint32_t data = MemARead(context, base_address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } if (wback && BitIsClear(registers, n)) { // R[n] = R[n] + 4 * BitCount (registers) int32_t offset = addr_byte_size * BitCount(registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetRegisterPlusOffset(*dwarf_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, base_address + offset)) return false; } if (wback && BitIsSet(registers, n)) // R[n] bits(32) UNKNOWN; return WriteBits32Unknown(n); } return true; } // LDMDA loads multiple registers from consecutive memory locations using an // address from a base register. // The consecutive memory locations end at this address and the address just // below the lowest of those locations can optionally be written back to the // base register. bool EmulateInstructionARM::EmulateLDMDA(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); address = R[n] - 4*BitCount(registers) + 4; for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers) + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount(registers)) + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*dwarf_reg, offset); // for i = 0 to 14 for (int i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet(registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset(*dwarf_reg, Rn - (address + offset)); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet(registers, 15)) { context.SetRegisterPlusOffset(*dwarf_reg, offset); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if (wback && BitIsClear(registers, n)) { offset = (addr_byte_size * BitCount(registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; if (wback && BitIsSet(registers, n)) return WriteBits32Unknown(n); } return true; } // LDMDB loads multiple registers from consecutive memory locations using an // address from a base register. The // consecutive memory locations end just below this address, and the address of // the lowest of those locations can be optionally written back to the base // register. bool EmulateInstructionARM::EmulateLDMDB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] - 4*BitCount(registers); for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); registers = registers & 0xdfff; // Make sure bit 13 is a zero. wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then // UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 2) || (BitIsSet(opcode, 14) && BitIsSet(opcode, 15))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then // UNPREDICTABLE; if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet(registers, n)) return false; break; case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers); int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount(registers)); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*dwarf_reg, Rn - address); for (int i = 0; i < 14; ++i) { if (BitIsSet(registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset(*dwarf_reg, Rn - (address + offset)); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet(registers, 15)) { context.SetRegisterPlusOffset(*dwarf_reg, offset); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if (wback && BitIsClear(registers, n)) { offset = (addr_byte_size * BitCount(registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only // possible for encoding A1 if (wback && BitIsSet(registers, n)) return WriteBits32Unknown(n); } return true; } // LDMIB loads multiple registers from consecutive memory locations using an // address from a base register. The // consecutive memory locations start just above this address, and thea ddress // of the last of those locations can optinoally be written back to the base // register. bool EmulateInstructionARM::EmulateLDMIB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] + 4; for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n] + 4; int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*dwarf_reg, offset); for (int i = 0; i < 14; ++i) { if (BitIsSet(registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset(*dwarf_reg, offset + addr_byte_size); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet(registers, 15)) { context.SetRegisterPlusOffset(*dwarf_reg, offset); uint32_t data = MemARead(context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); if (wback && BitIsClear(registers, n)) { offset = addr_byte_size * BitCount(registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only // possible for encoding A1 if (wback && BitIsSet(registers, n)) return WriteBits32Unknown(n); } return true; } // Load Register (immediate) calculates an address from a base register value // and an immediate offset, loads a word from memory, and writes to a register. // LDR (immediate, Thumb) bool EmulateInstructionARM::EmulateLDRRtRnImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(15); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7 } #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rt; // the destination register uint32_t Rn; // the base register uint32_t imm32; // the immediate offset used to form the address addr_t offset_addr; // the offset address addr_t address; // the calculated address uint32_t data; // the literal data value from memory load bool add, index, wback; switch (encoding) { case eEncodingT1: Rt = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32); // index = TRUE; add = TRUE; wback = FALSE add = true; index = true; wback = false; break; case eEncodingT2: // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); Rt = Bits32(opcode, 10, 8); Rn = 13; imm32 = Bits32(opcode, 7, 0) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT3: // if Rn == '1111' then SEE LDR (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); Rt = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if ((Rt == 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT4: // if Rn == '1111' then SEE LDR (literal); // if P == '1' && U == '1' && W == '0' then SEE LDRT; // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 == // '00000100' then SEE POP; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); Rt = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock()) // then UNPREDICTABLE; if ((wback && (Rn == Rt)) || ((Rt == 15) && InITBlock() && !LastInITBlock())) return false; break; default: return false; } uint32_t base = ReadCoreReg(Rn, &success); if (!success) return false; if (add) offset_addr = base + imm32; else offset_addr = base - imm32; address = (index ? offset_addr : base); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn); if (wback) { EmulateInstruction::Context ctx; if (Rn == 13) { ctx.type = eContextAdjustStackPointer; ctx.SetImmediateSigned((int32_t)(offset_addr - base)); } else if (Rn == GetFramePointerRegisterNumber()) { ctx.type = eContextSetFramePointer; ctx.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); } else { ctx.type = EmulateInstruction::eContextAdjustBaseRegister; ctx.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); } if (!WriteRegisterUnsigned(ctx, eRegisterKindDWARF, dwarf_r0 + Rn, offset_addr)) return false; } // Prepare to write to the Rt register. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); // Read memory from the address. data = MemURead(context, address, 4, 0, &success); if (!success) return false; if (Rt == 15) { if (Bits32(address, 1, 0) == 0) { if (!LoadWritePC(context, data)) return false; } else return false; } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) return false; } else WriteBits32Unknown(Rt); } return true; } // STM (Store Multiple Increment After) stores multiple registers to consecutive // memory locations using an address // from a base register. The consecutive memory locations start at this // address, and the address just above the last of those locations can // optionally be written back to the base register. bool EmulateInstructionARM::EmulateSTM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n]; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then // Only possible for encoding A1 MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] + 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE; n = Bits32(opcode, 10, 8); registers = Bits32(opcode, 7, 0); registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. wback = true; // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 2)) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet(registers, n)) return false; break; case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n]; int32_t offset = 0; const addr_t address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); // for i = 0 to 14 uint32_t lowest_set_bit = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet(registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings // T1 and A1 WriteBits32UnknownToMemory(address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, offset); if (!MemAWrite(context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // Only possible for encoding A1 // MemA[address,4] = PCStoreValue(); if (BitIsSet(registers, 15)) { std::optional pc_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterPlusOffset(*pc_reg, 8); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite(context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] + 4*BitCount(registers); if (wback) { offset = addr_byte_size * BitCount(registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t data = address + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMDA (Store Multiple Decrement After) stores multiple registers to // consecutive memory locations using an address from a base register. The // consecutive memory locations end at this address, and the address just below // the lowest of those locations can optionally be written back to the base // register. bool EmulateInstructionARM::EmulateSTMDA(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] - 4*BitCount(registers) + 4; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] - 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers) + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount(registers)) + 4; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); // for i = 0 to 14 uint32_t lowest_bit_set = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet(registers, i)) { if (i < lowest_bit_set) lowest_bit_set = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_bit_set)) // MemA[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory(address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, Rn - (address + offset)); if (!MemAWrite(context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // MemA[address,4] = PCStoreValue(); if (BitIsSet(registers, 15)) { std::optional pc_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterPlusOffset(*pc_reg, 8); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite(context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] - 4*BitCount(registers); if (wback) { offset = (addr_byte_size * BitCount(registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMDB (Store Multiple Decrement Before) stores multiple registers to // consecutive memory locations using an address from a base register. The // consecutive memory locations end just below this address, and the address of // the first of those locations can optionally be written back to the base // register. bool EmulateInstructionARM::EmulateSTMDB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] - 4*BitCount(registers); for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then // Only possible for encoding A1 MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] - 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if W == '1' && Rn == '1101' then SEE PUSH; if ((BitIsSet(opcode, 21)) && (Bits32(opcode, 19, 16) == 13)) { // See PUSH } // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; if ((n == 15) || BitCount(registers) < 2) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet(registers, n)) return false; break; case eEncodingA1: // if W == '1' && Rn == '1101' && BitCount(register_list) >= 2 then SEE // PUSH; if (BitIsSet(opcode, 21) && (Bits32(opcode, 19, 16) == 13) && BitCount(Bits32(opcode, 15, 0)) >= 2) { // See Push } // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || BitCount(registers) < 1) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers); int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount(registers)); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); // for i = 0 to 14 uint32_t lowest_set_bit = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet(registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding // A1 WriteBits32UnknownToMemory(address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, Rn - (address + offset)); if (!MemAWrite(context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // Only possible for encoding A1 // MemA[address,4] = PCStoreValue(); if (BitIsSet(registers, 15)) { std::optional pc_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterPlusOffset(*pc_reg, 8); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite(context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] - 4*BitCount(registers); if (wback) { offset = (addr_byte_size * BitCount(registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMIB (Store Multiple Increment Before) stores multiple registers to // consecutive memory locations using an address from a base register. The // consecutive memory locations start just above this address, and the address // of the last of those locations can optionally be written back to the base // register. bool EmulateInstructionARM::EmulateSTMIB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] + 4; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] + 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32(opcode, 19, 16); registers = Bits32(opcode, 15, 0); wback = BitIsSet(opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) && (BitCount(registers) < 1)) return false; break; default: return false; } // address = R[n] + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t address = Rn + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t lowest_set_bit = 14; // for i = 0 to 14 for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet(registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory(address + offset); // else else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, offset + addr_byte_size); if (!MemAWrite(context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // MemA[address,4] = PCStoreValue(); if (BitIsSet(registers, 15)) { std::optional pc_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); context.SetRegisterPlusOffset(*pc_reg, 8); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite(context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] + 4*BitCount(registers); if (wback) { offset = addr_byte_size * BitCount(registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned(offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STR (store immediate) calculates an address from a base register value and an // immediate offset, and stores a word // from a register to memory. It can use offset, post-indexed, or pre-indexed // addressing. bool EmulateInstructionARM::EmulateSTRThumb(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; if UnalignedSupport() || address<1:0> == '00' then MemU[address,4] = R[t]; else // Can only occur before ARMv7 MemU[address,4] = bits(32) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations (); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = false; wback = false; break; case eEncodingT2: // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); t = Bits32(opcode, 10, 8); n = 13; imm32 = Bits32(opcode, 7, 0) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT3: // if Rn == '1111' then UNDEFINED; if (Bits32(opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; case eEncodingT4: // if P == '1' && U == '1' && W == '0' then SEE STRT; // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 == // '00000100' then SEE PUSH; // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; if ((Bits32(opcode, 19, 16) == 15) || (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; default: return false; } addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t base_address = ReadCoreReg(n, &success); if (!success) return false; if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; EmulateInstruction::Context context; if (n == 13) context.type = eContextPushRegisterOnStack; else context.type = eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); // if UnalignedSupport() || address<1:0> == '00' then if (UnalignedSupport() || (BitIsClear(address, 1) && BitIsClear(address, 0))) { // MemU[address,4] = R[t]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); int32_t offset = address - base_address; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, offset); if (!MemUWrite(context, address, data, addr_byte_size)) return false; } else { // MemU[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory(address); } // if wback then R[n] = offset_addr; if (wback) { if (n == 13) context.type = eContextAdjustStackPointer; else context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // STR (Store Register) calculates an address from a base register value and an // offset register value, stores a // word from a register to memory. The offset register value can optionally // be shifted. bool EmulateInstructionARM::EmulateSTRRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; if t == 15 then // Only possible for encoding A1 data = PCStoreValue(); else data = R[t]; if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then MemU[address,4] = data; else // Can only occur before ARMv7 MemU[address,4] = bits(32) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t m; ARM_ShifterType shift_t; uint32_t shift_n; bool index; bool add; bool wback; // EncodingSpecificOperations (); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation // in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; if (Bits32(opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if t == 15 || BadReg(m) then UNPREDICTABLE; if ((t == 15) || (BadReg(m))) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE STRT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t typ = Bits32(opcode, 6, 5); uint32_t imm5 = Bits32(opcode, 11, 7); shift_n = DecodeImmShift(typ, imm5, shift_t); // if m == 15 then UNPREDICTABLE; if (m == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; } default: return false; } addr_t offset_addr; addr_t address; int32_t offset = 0; addr_t base_address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; uint32_t Rm_data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); offset = Shift(Rm_data, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = base_address + offset; else offset_addr = base_address - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; uint32_t data; // if t == 15 then // Only possible for encoding A1 if (t == 15) // data = PCStoreValue(); data = ReadCoreReg(PC_REG, &success); else // data = R[t]; data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == // InstrSet_ARM then if (UnalignedSupport() || (BitIsClear(address, 1) && BitIsClear(address, 0)) || CurrentInstrSet() == eModeARM) { // MemU[address,4] = data; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - base_address); if (!MemUWrite(context, address, data, addr_byte_size)) return false; } else // MemU[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory(address); // if wback then R[n] = offset_addr; if (wback) { context.type = eContextRegisterLoad; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } bool EmulateInstructionARM::EmulateSTRBThumb(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,1] = R[t]<7:0>; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; if (Bits32(opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if BadReg(t) then UNPREDICTABLE; if (BadReg(t)) return false; break; case eEncodingT3: // if P == '1' && U == '1' && W == '0' then SEE STRBT; // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; if (Bits32(opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE if ((BadReg(t)) || (wback && (n == t))) return false; break; default: return false; } addr_t offset_addr; addr_t address; addr_t base_address = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; // MemU[address,1] = R[t]<7:0> std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - base_address); uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; data = Bits32(data, 7, 0); if (!MemUWrite(context, address, data, 1)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextRegisterLoad; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // STRH (register) calculates an address from a base register value and an // offset register value, and stores a // halfword from a register to memory. The offset register value can be // shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateSTRHRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; if UnalignedSupport() || address<0> == '0' then MemU[address,2] = R[t]<15:0>; else // Can only occur before ARMv7 MemU[address,2] = bits(16) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation // in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); if (n == 15) return false; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if BadReg(t) || BadReg(m) then UNPREDICTABLE; if (BadReg(t) || BadReg(m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE STRHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); addr_t offset_addr; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; EmulateInstruction::Context context; context.type = eContextRegisterStore; // if UnalignedSupport() || address<0> == '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // MemU[address,2] = R[t]<15:0>; uint32_t Rt = ReadCoreReg(t, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional offset_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, *data_reg); if (!MemUWrite(context, address, Bits32(Rt, 15, 0), 2)) return false; } else // Can only occur before ARMv7 { // MemU[address,2] = bits(16) UNKNOWN; } // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // Add with Carry (immediate) adds an immediate value and the carry flag value // to a register value, and writes the result to the destination register. It // can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateADCImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be added to the value obtained from Rn bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. int32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // Add with Carry (register) adds a register value, the carry flag value, and // an optionally-shifted register value, and writes the result to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateADCReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. int32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. int32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds an immediate value to the PC value to form a PC- // relative address, and writes the result to the destination register. bool EmulateInstructionARM::EmulateADR(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32); if d == 15 then // Can only occur for ARM encodings ALUWritePC(result); else R[d] = result; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; uint32_t imm32; // the immediate value to be added/subtracted to/from the PC bool add; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 10, 8); imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32) add = true; break; case eEncodingT2: case eEncodingT3: Rd = Bits32(opcode, 11, 8); imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB if (BadReg(Rd)) return false; break; case eEncodingA1: case eEncodingA2: Rd = Bits32(opcode, 15, 12); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB break; default: return false; } // Read the PC value. uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreReg(context, result, Rd)) return false; } return true; } // This instruction performs a bitwise AND of a register value and an immediate // value, and writes the result to the destination register. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateANDImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rd == '1111' && S == '1' then SEE TST (immediate); if (Rd == 15 && setflags) return EmulateTSTImm(opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // This instruction performs a bitwise AND of a register value and an // optionally-shifted register value, and writes the result to the destination // register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateANDReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE TST (register); if (Rd == 15 && setflags) return EmulateTSTReg(opcode, eEncodingT2); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and // the complement of an immediate value, and writes the result to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateBICImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND NOT(imm32); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to // the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & ~imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Bit Clear (register) performs a bitwise AND of a register value and // the complement of an optionally-shifted register value, and writes the // result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateBICReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND NOT(shifted); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & ~shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // LDR (immediate, ARM) calculates an address from a base register value and an // immediate offset, loads a word // from memory, and writes it to a register. It can use offset, post-indexed, // or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRImmediateARM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 R[t] = ROR(data, 8*UInt(address<1:0>)); #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rn == '1111' then SEE LDR (literal); // if P == '0' && W == '1' then SEE LDRT; // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 == // '000000000100' then SEE POP; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // if wback && n == t then UNPREDICTABLE; if (wback && (n == t)) return false; break; default: return false; } addr_t address; addr_t offset_addr; addr_t base_address = ReadCoreReg(n, &success); if (!success) return false; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; // data = MemU[address,4]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base_address); uint64_t data = MemURead(context, address, addr_byte_size, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if t == 15 then if (t == 15) { // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; if (BitIsClear(address, 1) && BitIsClear(address, 0)) { // LoadWritePC (data); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base_address); LoadWritePC(context, data); } else return false; } // elsif UnalignedSupport() || address<1:0> = '00' then else if (UnalignedSupport() || (BitIsClear(address, 1) && BitIsClear(address, 0))) { // R[t] = data; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base_address); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } // else // Can only apply before ARMv7 else { // R[t] = ROR(data, 8*UInt(address<1:0>)); data = ROR(data, Bits32(address, 1, 0), &success); if (!success) return false; context.type = eContextRegisterLoad; context.SetImmediate(data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } } return true; } // LDR (register) calculates an address from a base register value and an offset // register value, loads a word // from memory, and writes it to a register. The offset register value can // optionally be shifted. bool EmulateInstructionARM::EmulateLDRRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 if CurrentInstrSet() == InstrSet_ARM then R[t] = ROR(data, 8*UInt(address<1:0>)); else R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation // in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDR (literal); // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if BadReg(m) then UNPREDICTABLE; if (BadReg(m)) return false; // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if ((t == 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE LDRT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t type = Bits32(opcode, 6, 5); uint32_t imm5 = Bits32(opcode, 11, 7); shift_n = DecodeImmShift(type, imm5, shift_t); // if m == 15 then UNPREDICTABLE; if (m == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; } break; default: return false; } uint32_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; uint32_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is // an application level alias for the CPSR". addr_t offset = Shift(Rm, shift_t, shift_n, Bit32(m_opcode_cpsr, APSR_C), &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,4]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemURead(context, address, addr_byte_size, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if t == 15 then if (t == 15) { // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; if (BitIsClear(address, 1) && BitIsClear(address, 0)) { context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); LoadWritePC(context, data); } else return false; } // elsif UnalignedSupport() || address<1:0> = '00' then else if (UnalignedSupport() || (BitIsClear(address, 1) && BitIsClear(address, 0))) { // R[t] = data; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // if CurrentInstrSet() == InstrSet_ARM then if (CurrentInstrSet() == eModeARM) { // R[t] = ROR(data, 8*UInt(address<1:0>)); data = ROR(data, Bits32(address, 1, 0), &success); if (!success) return false; context.type = eContextRegisterLoad; context.SetImmediate(data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } } return true; } // LDRB (immediate, Thumb) bool EmulateInstructionARM::EmulateLDRBImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = ZeroExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT2: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if Rt == '1111' then SEE PLD; if (t == 15) return false; // PLD is not implemented yet // if Rn == '1111' then SEE LDRB (literal); if (n == 15) return EmulateLDRBLiteral(opcode, eEncodingT1); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT3: // if P == '1' && U == '1' && W == '0' then SEE LDRBT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; if (t == 15) return false; // PLD is not implemented yet // if Rn == '1111' then SEE LDRB (literal); if (n == 15) return EmulateLDRBLiteral(opcode, eEncodingT1); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg(t) || (wback && (n == t))) return false; break; default: return false; } uint32_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address; addr_t offset_addr; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = ZeroExtend(MemU[address,1], 32); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); uint64_t data = MemURead(context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRB (literal) calculates an address from the PC value and an immediate // offset, loads a byte from memory, // zero-extends it to form a 32-bit word and writes it to a register. bool EmulateInstructionARM::EmulateLDRBLiteral(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); R[t] = ZeroExtend(MemU[address,1], 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; switch (encoding) { case eEncodingT1: // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0); add = BitIsSet(opcode, 23); // if Rt == '1111' then SEE PLD; if (t == 15) return false; // PLD is not implemented yet // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0); add = BitIsSet(opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; default: return false; } // base = Align(PC,4); uint32_t pc_val = ReadCoreReg(PC_REG, &success); if (!success) return false; uint32_t base = AlignPC(pc_val); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // R[t] = ZeroExtend(MemU[address,1], 32); EmulateInstruction::Context context; context.type = eContextRelativeBranchImmediate; context.SetImmediate(address - base); uint64_t data = MemURead(context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } return true; } // LDRB (register) calculates an address from a base register value and an // offset rigister value, loads a byte from memory, zero-extends it to form a // 32-bit word, and writes it to a register. The offset register value can // optionally be shifted. bool EmulateInstructionARM::EmulateLDRBRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; R[t] = ZeroExtend(MemU[address,1],32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if Rt == '1111' then SEE PLD; if (t == 15) return false; // PLD is not implemented yet // if Rn == '1111' then SEE LDRB (literal); if (n == 15) return EmulateLDRBLiteral(opcode, eEncodingT1); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg(m)) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE LDRBT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t type = Bits32(opcode, 6, 5); uint32_t imm5 = Bits32(opcode, 11, 7); shift_n = DecodeImmShift(type, imm5, shift_t); // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; } break; default: return false; } addr_t offset_addr; addr_t address; // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint32_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = ZeroExtend(MemU[address,1],32); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemURead(context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRH (immediate, Thumb) calculates an address from a base register value and // an immediate offset, loads a // halfword from memory, zero-extends it to form a 32-bit word, and writes it // to a register. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRHImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6) << 1; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT2: // if Rt == '1111' then SEE "Unallocated memory hints"; // if Rn == '1111' then SEE LDRH (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT3: // if Rn == '1111' then SEE LDRH (literal); // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE // "Unallocated memory hints"; // if P == '1' && U == '1' && W == '0' then SEE LDRHT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg(t) || (wback && (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; addr_t address; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // LDRH (literal) calculates an address from the PC value and an immediate // offset, loads a halfword from memory, // zero-extends it to form a 32-bit word, and writes it to a register. bool EmulateInstructionARM::EmulateLDRHLiteral(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,2]; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0); add = BitIsSet(opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { uint32_t imm4H = Bits32(opcode, 11, 8); uint32_t imm4L = Bits32(opcode, 3, 0); // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = (imm4H << 4) | imm4L; add = BitIsSet(opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t base = AlignPC(pc_value); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // LDRH (literal) calculates an address from a base register value and an offset // register value, loads a halfword // from memory, zero-extends it to form a 32-bit word, and writes it to a // register. The offset register value can be shifted left by 0, 1, 2, or 3 // bits. bool EmulateInstructionARM::EmulateLDRHRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation // in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDRH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg(m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional offset_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // LDRSB (immediate) calculates an address from a base register value and an // immediate offset, loads a byte from // memory, sign-extends it to form a 32-bit word, and writes it to a register. // It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRSBImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = SignExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT2: // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // if P == '1' && U == '1' && W == '0' then SEE LDRSBT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (((t == 13) || ((t == 15) && (BitIsClear(opcode, 10) || BitIsSet(opcode, 9) || BitIsSet(opcode, 8)))) || (wback && (n == t))) return false; break; case eEncodingA1: { // if Rn == '1111' then SEE LDRSB (literal); // if P == '0' && W == '1' then SEE LDRSBT; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); uint32_t imm4H = Bits32(opcode, 11, 8); uint32_t imm4L = Bits32(opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; } default: return false; } uint64_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = SignExtend(MemU[address,1], 32); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRSB (literal) calculates an address from the PC value and an immediate // offset, loads a byte from memory, // sign-extends it to form a 32-bit word, and writes tit to a register. bool EmulateInstructionARM::EmulateLDRSBLiteral(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); R[t] = SignExtend(MemU[address,1], 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE PLI; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0); add = BitIsSet(opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); uint32_t imm4H = Bits32(opcode, 11, 8); uint32_t imm4L = Bits32(opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; add = BitIsSet(opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg(PC_REG, &success); if (!success) return false; uint64_t base = AlignPC(pc_value); // address = if add then (base + imm32) else (base - imm32); addr_t address; if (add) address = base + imm32; else address = base - imm32; // R[t] = SignExtend(MemU[address,1], 32); std::optional base_reg = GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base); uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; } return true; } // LDRSB (register) calculates an address from a base register value and an // offset register value, loadsa byte from // memory, sign-extends it to form a 32-bit word, and writes it to a register. // The offset register value can be shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateLDRSBRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; R[t] = SignExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rt == '1111' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg(m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRSBT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = SignExtend(MemU[address,1], 32); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional offset_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRSH (immediate) calculates an address from a base register value and an // immediate offset, loads a halfword from // memory, sign-extends it to form a 32-bit word, and writes it to a register. // It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRSHImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT2: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE // "Unallocated memory hints"; // if P == '1' && U == '1' && W == '0' then SEE LDRSHT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 10); add = BitIsSet(opcode, 9); wback = BitIsSet(opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg(t) || (wback && (n == t))) return false; break; case eEncodingA1: { // if Rn == '1111' then SEE LDRSH (literal); // if P == '0' && W == '1' then SEE LDRSHT; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); uint32_t imm4H = Bits32(opcode, 11, 8); uint32_t imm4L = Bits32(opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; } default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = SignExtend(data, 32); int64_t signed_data = llvm::SignExtend64<16>(data); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // LDRSH (literal) calculates an address from the PC value and an immediate // offset, loads a halfword from memory, // sign-extends it to from a 32-bit word, and writes it to a register. bool EmulateInstructionARM::EmulateLDRSHLiteral(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,2]; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0); add = BitIsSet(opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32(opcode, 15, 12); uint32_t imm4H = Bits32(opcode, 11, 8); uint32_t imm4L = Bits32(opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; add = BitIsSet(opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg(PC_REG, &success); if (!success) return false; uint64_t base = AlignPC(pc_value); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, imm32); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = SignExtend(data, 32); int64_t signed_data = llvm::SignExtend64<16>(data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // LDRSH (register) calculates an address from a base register value and an // offset register value, loads a halfword // from memory, sign-extends it to form a 32-bit word, and writes it to a // register. The offset register value can be shifted left by 0, 1, 2, or 3 // bits. bool EmulateInstructionARM::EmulateLDRSHRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation // in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32(opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg(m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRSHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || // (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional offset_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); uint64_t data = MemURead(context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear(address, 0)) { // R[t] = SignExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); int64_t signed_data = llvm::SignExtend64<16>(data); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t)signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown(t); } } return true; } // SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and // writes the result to the destination // register. You can specifiy a rotation by 0, 8, 16, or 24 bits before // extracting the 8-bit value. bool EmulateInstructionARM::EmulateSXTB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = SignExtend(rotated<7:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32(opcode, 2, 0); m = Bits32(opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 11, 8); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg(d) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 15, 12); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR(Rm, rotation, &success); if (!success) return false; // R[d] = SignExtend(rotated<7:0>, 32); int64_t data = llvm::SignExtend64<8>(rotated); std::optional source_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister(*source_reg); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t)data)) return false; } return true; } // SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and // writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before // extracting the 16-bit value. bool EmulateInstructionARM::EmulateSXTH(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = SignExtend(rotated<15:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32(opcode, 2, 0); m = Bits32(opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 11, 8); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg(d) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 15, 12); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR(Rm, rotation, &success); if (!success) return false; // R[d] = SignExtend(rotated<15:0>, 32); std::optional source_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister(*source_reg); int64_t data = llvm::SignExtend64<16>(rotated); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t)data)) return false; } return true; } // UXTB extracts an 8-bit value from a register, zero-extends it to 32 bits, and // writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before // extracting the 8-bit value. bool EmulateInstructionARM::EmulateUXTB(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = ZeroExtend(rotated<7:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32(opcode, 2, 0); m = Bits32(opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 11, 8); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg(d) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 15, 12); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR(Rm, rotation, &success); if (!success) return false; // R[d] = ZeroExtend(rotated<7:0>, 32); std::optional source_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister(*source_reg); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, Bits32(rotated, 7, 0))) return false; } return true; } // UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and // writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before // extracting the 16-bit value. bool EmulateInstructionARM::EmulateUXTH(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = ZeroExtend(rotated<15:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32(opcode, 2, 0); m = Bits32(opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 11, 8); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg(d) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32(opcode, 15, 12); m = Bits32(opcode, 3, 0); rotation = Bits32(opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR(Rm, rotation, &success); if (!success) return false; // R[d] = ZeroExtend(rotated<15:0>, 32); std::optional source_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister(*source_reg); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, Bits32(rotated, 15, 0))) return false; } return true; } // RFE (Return From Exception) loads the PC and the CPSR from the word at the // specified address and the following // word respectively. bool EmulateInstructionARM::EmulateRFE(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE; else address = if increment then R[n] else R[n]-8; if wordhigher then address = address+4; CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); BranchWritePC(MemA[address,4]); if wback then R[n] = if increment then R[n]+8 else R[n]-8; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; bool wback; bool increment; bool wordhigher; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher = // FALSE; n = Bits32(opcode, 19, 16); wback = BitIsSet(opcode, 21); increment = false; wordhigher = false; // if n == 15 then UNPREDICTABLE; if (n == 15) return false; // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingT2: // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE; n = Bits32(opcode, 19, 16); wback = BitIsSet(opcode, 21); increment = true; wordhigher = false; // if n == 15 then UNPREDICTABLE; if (n == 15) return false; // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: // n = UInt(Rn); n = Bits32(opcode, 19, 16); // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U); wback = BitIsSet(opcode, 21); increment = BitIsSet(opcode, 23); wordhigher = (Bit32(opcode, 24) == Bit32(opcode, 23)); // if n == 15 then UNPREDICTABLE; if (n == 15) return false; break; default: return false; } // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE // then if (!CurrentModeIsPrivileged()) // UNPREDICTABLE; return false; else { uint64_t Rn = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address; // address = if increment then R[n] else R[n]-8; if (increment) address = Rn; else address = Rn - 8; // if wordhigher then address = address+4; if (wordhigher) address = address + 4; // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); EmulateInstruction::Context context; context.type = eContextReturnFromException; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemARead(context, address + 4, 4, 0, &success); if (!success) return false; CPSRWriteByInstr(data, 15, true); // BranchWritePC(MemA[address,4]); uint64_t data2 = MemARead(context, address, 4, 0, &success); if (!success) return false; BranchWritePC(context, data2); // if wback then R[n] = if increment then R[n]+8 else R[n]-8; if (wback) { context.type = eContextAdjustBaseRegister; if (increment) { context.SetOffset(8); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, Rn + 8)) return false; } else { context.SetOffset(-8); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, Rn - 8)) return false; } } // if wback } } // if ConditionPassed() return true; } // Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a // register value and an immediate value, and writes the result to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateEORImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] EOR imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rd == '1111' && S == '1' then SEE TEQ (immediate); if (Rd == 15 && setflags) return EmulateTEQImm(opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 ^ imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a // register value and an optionally-shifted register value, and writes the // result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateEORReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] EOR shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE TEQ (register); if (Rd == 15 && setflags) return EmulateTEQReg(opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 ^ shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value // and an immediate value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateORRImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] OR imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rn == '1111' then SEE MOV (immediate); if (Rn == 15) return EmulateMOVRdImm(opcode, eEncodingT2); if (BadReg(Rd) || Rn == 13) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 | imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise OR (register) performs a bitwise (inclusive) OR of a register value // and an optionally-shifted register value, and writes the result to the // destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateORRReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] OR shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rn == '1111' then SEE MOV (register); if (Rn == 15) return EmulateMOVRdRm(opcode, eEncodingT3); if (BadReg(Rd) || Rn == 13 || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 | shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Reverse Subtract (immediate) subtracts a register value from an immediate // value, and writes the result to the destination register. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateRSBImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm32 = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // Reverse Subtract (register) subtracts a register value from an optionally- // shifted register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateRSBReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~val1, shifted, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // Reverse Subtract with Carry (immediate) subtracts a register value and the // value of NOT (Carry flag) from an immediate value, and writes the result to // the destination register. It can optionally update the condition flags based // on the result. bool EmulateInstructionARM::EmulateRSCImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // Reverse Subtract with Carry (register) subtracts a register value and the // value of NOT (Carry flag) from an optionally-shifted register value, and // writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateRSCReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // Subtract with Carry (immediate) subtracts an immediate value and the value // of // NOT (Carry flag) from a register value, and writes the result to the // destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSBCImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // Subtract with Carry (register) subtracts an optionally-shifted register // value and the value of // NOT (Carry flag) from a register value, and writes the result to the // destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSBCReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // This instruction subtracts an immediate value from a register value, and // writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateSUBImmThumb(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be subtracted from the value // obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32) break; case eEncodingT2: Rd = Rn = Bits32(opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) // if Rd == '1111' && S == '1' then SEE CMP (immediate); if (Rd == 15 && setflags) return EmulateCMPImm(opcode, eEncodingT2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm(opcode, eEncodingT2); // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE; if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15) return false; break; case eEncodingT4: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) // if Rn == '1111' then SEE ADR; if (Rn == 15) return EmulateADR(opcode, eEncodingT2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm(opcode, eEncodingT3); if (BadReg(Rd)) return false; break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow); } // This instruction subtracts an immediate value from a register value, and // writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateSUBImmARM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be subtracted from the value // obtained from Rn switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rn == '1111' && S == '0' then SEE ADR; if (Rn == 15 && !setflags) return EmulateADR(opcode, eEncodingA2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm(opcode, eEncodingA1); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc(opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; if (Rd == 13) context.type = EmulateInstruction::eContextAdjustStackPointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, Rn); int64_t imm32_signed = imm32; context.SetRegisterPlusOffset(*dwarf_reg, -imm32_signed); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // Test Equivalence (immediate) performs a bitwise exclusive OR operation on a // register value and an immediate value. It updates the condition flags based // on the result, and discards the result. bool EmulateInstructionARM::EmulateTEQImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] EOR imm32; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rn)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 ^ imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test Equivalence (register) performs a bitwise exclusive OR operation on a // register value and an optionally-shifted register value. It updates the // condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateTEQReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] EOR shifted; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm uint32_t carry; switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 ^ shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test (immediate) performs a bitwise AND operation on a register value and an // immediate value. It updates the condition flags based on the result, and // discards the result. bool EmulateInstructionARM::EmulateTSTImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND imm32; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm_C( opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rn)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test (register) performs a bitwise AND operation on a register value and an // optionally-shifted register value. It updates the condition flags based on // the result, and discards the result. bool EmulateInstructionARM::EmulateTSTReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND shifted; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm uint32_t carry; switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, result, carry)) return false; } return true; } // A8.6.216 SUB (SP minus register) bool EmulateInstructionARM::EmulateSUBSPReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1'); d = Bits32(opcode, 11, 8); m = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then // UNPREDICTABLE; if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3))) return false; // if d == 15 || BadReg(m) then UNPREDICTABLE; if ((d == 15) || BadReg(m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1'); d = Bits32(opcode, 15, 12); m = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if (d == 15 && setflags) EmulateSUBSPcLrEtc(opcode, encoding); // (shift_t, shift_n) = DecodeImmShift(type, imm5); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1'); uint32_t sp_val = ReadCoreReg(SP_REG, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(sp_val, ~shifted, 1); EmulateInstruction::Context context; context.type = eContextArithmetic; std::optional sp_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); std::optional dwarf_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); context.SetRegisterRegisterOperands(*sp_reg, *dwarf_reg); if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) return false; } return true; } // A8.6.7 ADD (register-shifted register) bool EmulateInstructionARM::EmulateADDRegShift(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[s]<7:0>); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; uint32_t s; bool setflags; ARM_ShifterType shift_t; switch (encoding) { case eEncodingA1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs); d = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); s = Bits32(opcode, 11, 8); // setflags = (S == '1'); shift_t = DecodeRegShift(type); setflags = BitIsSet(opcode, 20); shift_t = DecodeRegShift(Bits32(opcode, 6, 5)); // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE; if ((d == 15) || (n == 15) || (m == 15) || (s == 15)) return false; break; default: return false; } // shift_n = UInt(R[s]<7:0>); uint32_t Rs = ReadCoreReg(s, &success); if (!success) return false; uint32_t shift_n = Bits32(Rs, 7, 0); // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(Rn, shifted, 0); // R[d] = result; EmulateInstruction::Context context; context.type = eContextArithmetic; std::optional reg_n = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional reg_m = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); context.SetRegisterRegisterOperands(*reg_n, *reg_m); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, res.result)) return false; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; if (setflags) return WriteFlags(context, res.result, res.carry_out, res.overflow); } return true; } // A8.6.213 SUB (register) bool EmulateInstructionARM::EmulateSUBReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock(); d = Bits32(opcode, 2, 0); n = Bits32(opcode, 5, 3); m = Bits32(opcode, 8, 6); setflags = !InITBlock(); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S =="1"); d = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if Rd == "1111" && S == "1" then SEE CMP (register); if (d == 15 && setflags == 1) return EmulateCMPImm(opcode, eEncodingT3); // if Rn == "1101" then SEE SUB (SP minus register); if (n == 13) return EmulateSUBSPReg(opcode, eEncodingT1); // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then // UNPREDICTABLE; if ((d == 13) || ((d == 15) && BitIsClear(opcode, 20)) || (n == 15) || BadReg(m)) return false; break; case eEncodingA1: // if Rn == '1101' then SEE SUB (SP minus register); // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); d = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related // instructions; if ((d == 15) && setflags) EmulateSUBSPcLrEtc(opcode, encoding); // (shift_t, shift_n) = DecodeImmShift(type, imm5); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(Rn, ~shifted, 1); // if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // // setflags is always FALSE here else // R[d] = result; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; EmulateInstruction::Context context; context.type = eContextArithmetic; std::optional reg_n = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional reg_m = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); context.SetRegisterRegisterOperands(*reg_n, *reg_m); if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) return false; } return true; } // A8.6.202 STREX // Store Register Exclusive calculates an address from a base register value // and an immediate offset, and stores a word from a register to memory if the // executing processor has exclusive access to the memory addressed. bool EmulateInstructionARM::EmulateSTREX(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] + imm32; if ExclusiveMonitorsPass(address,4) then MemA[address,4] = R[t]; R[d] = 0; else R[d] = 1; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t t; uint32_t n; uint32_t imm32; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = // ZeroExtend(imm8:'00', // 32); d = Bits32(opcode, 11, 8); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0) << 2; // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE; if (BadReg(d) || BadReg(t) || (n == 15)) return false; // if d == n || d == t then UNPREDICTABLE; if ((d == n) || (d == t)) return false; break; case eEncodingA1: // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero // offset d = Bits32(opcode, 15, 12); t = Bits32(opcode, 3, 0); n = Bits32(opcode, 19, 16); imm32 = 0; // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE; if ((d == 15) || (t == 15) || (n == 15)) return false; // if d == n || d == t then UNPREDICTABLE; if ((d == n) || (d == t)) return false; break; default: return false; } // address = R[n] + imm32; uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t address = Rn + imm32; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, imm32); // if ExclusiveMonitorsPass(address,4) then if (ExclusiveMonitorsPass // (address, addr_byte_size)) -- For now, for the sake of emulation, we // will say this // always return // true. if (true) { // MemA[address,4] = R[t]; uint32_t Rt = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; if (!MemAWrite(context, address, Rt, addr_byte_size)) return false; // R[d] = 0; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, 0)) return false; } #if 0 // unreachable because if true else { // R[d] = 1; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) return false; } #endif // unreachable because if true } return true; } // A8.6.197 STRB (immediate, ARM) bool EmulateInstructionARM::EmulateSTRBImmARM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,1] = R[t]<7:0>; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if P == '0' && W == '1' then SEE STRBT; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // MemU[address,1] = R[t]<7:0>; uint32_t Rt = ReadCoreReg(t, &success); if (!success) return false; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemUWrite(context, address, Bits32(Rt, 7, 0), 1)) return false; // if wback then R[n] = offset_addr; if (wback) { if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.194 STR (immediate, ARM) bool EmulateInstructionARM::EmulateSTRImmARM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingA1: // if P == '0' && W == '1' then SEE STRT; // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm12 == // '000000000100' then SEE PUSH; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 11, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); // MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; uint32_t Rt = ReadCoreReg(t, &success); if (!success) return false; if (t == 15) { uint32_t pc_value = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemUWrite(context, address, pc_value, addr_byte_size)) return false; } else { if (!MemUWrite(context, address, Rt, addr_byte_size)) return false; } // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetImmediate(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.66 LDRD (immediate) // Load Register Dual (immediate) calculates an address from a base register // value and an immediate offset, loads two words from memory, and writes them // to two registers. It can use offset, post-indexed, or pre-indexed // addressing. bool EmulateInstructionARM::EmulateLDRDImmediate(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = MemA[address,4]; R[t2] = MemA[address+4,4]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingT1: // if P == '0' && W == '0' then SEE 'Related encodings'; // if Rn == '1111' then SEE LDRD (literal); // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = // ZeroExtend(imm8:'00', 32); t = Bits32(opcode, 15, 12); t2 = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0) << 2; // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); // if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; // if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE; if (BadReg(t) || BadReg(t2) || (t == t2)) return false; break; case eEncodingA1: // if Rn == '1111' then SEE LDRD (literal); // if Rt<0> == '1' then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, // 32); t = Bits32(opcode, 15, 12); if (BitIsSet(t, 0)) return false; t2 = t + 1; n = Bits32(opcode, 19, 16); imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if P == '0' && W == '1' then UNPREDICTABLE; if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) return false; // if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; // if t2 == 15 then UNPREDICTABLE; if (t2 == 15) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // R[t] = MemA[address,4]; EmulateInstruction::Context context; if (n == 13) context.type = eContextPopRegisterOffStack; else context.type = eContextRegisterLoad; context.SetAddress(address); const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // R[t2] = MemA[address+4,4]; context.SetAddress(address + 4); data = MemARead(context, address + 4, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.68 LDRD (register) // Load Register Dual (register) calculates an address from a base register // value and a register offset, loads two words from memory, and writes them to // two registers. It can use offset, post-indexed or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRDRegister(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); address = if index then offset_addr else R[n]; R[t] = MemA[address,4]; R[t2] = MemA[address+4,4]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t m; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rt<0> == '1' then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); if (BitIsSet(t, 0)) return false; t2 = t + 1; n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if P == '0' && W == '1' then UNPREDICTABLE; if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) return false; // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE; if ((t2 == 15) || (m == 15) || (m == t) || (m == t2)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; if ((ArchVersion() < 6) && wback && (m == n)) return false; break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); addr_t offset_addr; if (add) offset_addr = Rn + Rm; else offset_addr = Rn - Rm; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; EmulateInstruction::Context context; if (n == 13) context.type = eContextPopRegisterOffStack; else context.type = eContextRegisterLoad; context.SetAddress(address); // R[t] = MemA[address,4]; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // R[t2] = MemA[address+4,4]; data = MemARead(context, address + 4, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.200 STRD (immediate) // Store Register Dual (immediate) calculates an address from a base register // value and an immediate offset, and stores two words from two registers to // memory. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateSTRDImm(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemA[address,4] = R[t]; MemA[address+4,4] = R[t2]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingT1: // if P == '0' && W == '0' then SEE 'Related encodings'; // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = // ZeroExtend(imm8:'00', 32); t = Bits32(opcode, 15, 12); t2 = Bits32(opcode, 11, 8); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0) << 2; // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); // if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE; if ((n == 15) || BadReg(t) || BadReg(t2)) return false; break; case eEncodingA1: // if Rt<0> == '1' then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, // 32); t = Bits32(opcode, 15, 12); if (BitIsSet(t, 0)) return false; t2 = t + 1; n = Bits32(opcode, 19, 16); imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if P == '0' && W == '1' then UNPREDICTABLE; if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if t2 == 15 then UNPREDICTABLE; if (t2 == 15) return false; break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // MemA[address,4] = R[t]; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); uint32_t data = ReadCoreReg(t, &success); if (!success) return false; EmulateInstruction::Context context; if (n == 13) context.type = eContextPushRegisterOnStack; else context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); const uint32_t addr_byte_size = GetAddressByteSize(); if (!MemAWrite(context, address, data, addr_byte_size)) return false; // MemA[address+4,4] = R[t2]; data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, (address + 4) - Rn); data = ReadCoreReg(t2, &success); if (!success) return false; if (!MemAWrite(context, address + 4, data, addr_byte_size)) return false; // if wback then R[n] = offset_addr; if (wback) { if (n == 13) context.type = eContextAdjustStackPointer; else context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.201 STRD (register) bool EmulateInstructionARM::EmulateSTRDReg(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); address = if index then offset_addr else R[n]; MemA[address,4] = R[t]; MemA[address+4,4] = R[t2]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t m; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rt<0> == '1' then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); t = Bits32(opcode, 15, 12); if (BitIsSet(t, 0)) return false; t2 = t + 1; n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet(opcode, 24); add = BitIsSet(opcode, 23); wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); // if P == '0' && W == '1' then UNPREDICTABLE; if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) return false; // if t2 == 15 || m == 15 then UNPREDICTABLE; if ((t2 == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; if ((ArchVersion() < 6) && wback && (m == n)) return false; break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); addr_t offset_addr; if (add) offset_addr = Rn + Rm; else offset_addr = Rn - Rm; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // MemA[address,4] = R[t]; uint32_t Rt = ReadCoreReg(t, &success); if (!success) return false; EmulateInstruction::Context context; if (t == 13) context.type = eContextPushRegisterOnStack; else context.type = eContextRegisterStore; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional offset_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, *data_reg); const uint32_t addr_byte_size = GetAddressByteSize(); if (!MemAWrite(context, address, Rt, addr_byte_size)) return false; // MemA[address+4,4] = R[t2]; uint32_t Rt2 = ReadCoreReg(t2, &success); if (!success) return false; data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2); context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, *data_reg); if (!MemAWrite(context, address + 4, Rt2, addr_byte_size)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress(offset_addr); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.319 VLDM // Vector Load Multiple loads multiple extension registers from consecutive // memory locations using an address from an ARM core register. bool EmulateInstructionARM::EmulateVLDM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then R[n] else R[n]-imm32; if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; for r = 0 to regs-1 if single_regs then S[d+r] = MemA[address,4]; address = address+4; else word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; // Combine the word-aligned words in the correct order for // current endianness. D[d+r] = if BigEndian() then word1:word2 else word2:word1; #endif bool success = false; if (ConditionPassed(opcode)) { bool single_regs; bool add; bool wback; uint32_t d; uint32_t n; uint32_t imm32; uint32_t regs; switch (encoding) { case eEncodingT1: case eEncodingA1: // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP; // if P == '1' && W == '0' then SEE VLDR; // if P == U && W == '1' then UNDEFINED; if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with // !), 101 (DB with !) // single_regs = FALSE; add = (U == '1'); wback = (W == '1'); single_regs = false; add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0) << 2; // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FLDMX'. regs = Bits32(opcode, 7, 0) / 2; // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then // UNPREDICTABLE; if (n == 15 && (wback || CurrentInstrSet() != eModeARM)) return false; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) return false; break; case eEncodingT2: case eEncodingA2: // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP; // if P == '1' && W == '0' then SEE VLDR; // if P == U && W == '1' then UNDEFINED; if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W // == '1'); d = // UInt(Vd:D); n = UInt(Rn); single_regs = true; add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); n = Bits32(opcode, 19, 16); // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8); imm32 = Bits32(opcode, 7, 0) << 2; regs = Bits32(opcode, 7, 0); // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then // UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) return false; // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || ((d + regs) > 32)) return false; break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = if add then R[n] else R[n]-imm32; addr_t address; if (add) address = Rn; else address = Rn - imm32; // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; EmulateInstruction::Context context; if (wback) { uint32_t value; if (add) value = Rn + imm32; else value = Rn - imm32; context.type = eContextAdjustBaseRegister; context.SetImmediateSigned(value - Rn); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; context.type = eContextRegisterLoad; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { if (single_regs) { // S[d+r] = MemA[address,4]; address = address+4; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d + r, data)) return false; address = address + 4; } else { // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = // address+8; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint32_t word1 = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; context.SetRegisterPlusOffset(*base_reg, (address + 4) - Rn); uint32_t word2 = MemARead(context, address + 4, addr_byte_size, 0, &success); if (!success) return false; address = address + 8; // // Combine the word-aligned words in the correct order for current // endianness. // D[d+r] = if BigEndian() then word1:word2 else word2:word1; uint64_t data; if (GetByteOrder() == eByteOrderBig) { data = word1; data = (data << 32) | word2; } else { data = word2; data = (data << 32) | word1; } if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d + r, data)) return false; } } } return true; } // A8.6.399 VSTM // Vector Store Multiple stores multiple extension registers to consecutive // memory locations using an address from an // ARM core register. bool EmulateInstructionARM::EmulateVSTM(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then R[n] else R[n]-imm32; if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; for r = 0 to regs-1 if single_regs then MemA[address,4] = S[d+r]; address = address+4; else // Store as two word-aligned words in the correct order for // current endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; address = address+8; #endif bool success = false; if (ConditionPassed(opcode)) { bool single_regs; bool add; bool wback; uint32_t d; uint32_t n; uint32_t imm32; uint32_t regs; switch (encoding) { case eEncodingT1: case eEncodingA1: // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH; // if P == '1' && W == '0' then SEE VSTR; // if P == U && W == '1' then UNDEFINED; if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with // !), 101 (DB with !) // single_regs = FALSE; add = (U == '1'); wback = (W == '1'); single_regs = false; add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); imm32 = Bits32(opcode, 7, 0) << 2; // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FSTMX'. regs = Bits32(opcode, 7, 0) / 2; // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then // UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) return false; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) return false; break; case eEncodingT2: case eEncodingA2: // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH; // if P == '1' && W == '0' then SEE VSTR; // if P == U && W == '1' then UNDEFINED; if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W // == '1'); d = // UInt(Vd:D); n = UInt(Rn); single_regs = true; add = BitIsSet(opcode, 23); wback = BitIsSet(opcode, 21); d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); n = Bits32(opcode, 19, 16); // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8); imm32 = Bits32(opcode, 7, 0) << 2; regs = Bits32(opcode, 7, 0); // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then // UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) return false; // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || ((d + regs) > 32)) return false; break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = if add then R[n] else R[n]-imm32; addr_t address; if (add) address = Rn; else address = Rn - imm32; EmulateInstruction::Context context; // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; if (wback) { uint32_t value; if (add) value = Rn + imm32; else value = Rn - imm32; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset(*base_reg, value - Rn); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; context.type = eContextRegisterStore; // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { if (single_regs) { // MemA[address,4] = S[d+r]; address = address+4; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d + r, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemAWrite(context, address, data, addr_byte_size)) return false; address = address + 4; } else { // // Store as two word-aligned words in the correct order for current // endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else // D[d+r]<31:0>; // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else // D[d+r]<63:32>; uint64_t data = ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d + r, 0, &success); if (!success) return false; std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r); if (GetByteOrder() == eByteOrderBig) { context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemAWrite(context, address, Bits64(data, 63, 32), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, (address + 4) - Rn); if (!MemAWrite(context, address + 4, Bits64(data, 31, 0), addr_byte_size)) return false; } else { context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, (address + 4) - Rn); if (!MemAWrite(context, address + 4, Bits64(data, 63, 32), addr_byte_size)) return false; } // address = address+8; address = address + 8; } } } return true; } // A8.6.320 // This instruction loads a single extension register from memory, using an // address from an ARM core register, with an optional offset. bool EmulateInstructionARM::EmulateVLDR(const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); base = if n == 15 then Align(PC,4) else R[n]; address = if add then (base + imm32) else (base - imm32); if single_reg then S[d] = MemA[address,4]; else word1 = MemA[address,4]; word2 = MemA[address+4,4]; // Combine the word-aligned words in the correct order for current // endianness. D[d] = if BigEndian() then word1:word2 else word2:word1; #endif bool success = false; if (ConditionPassed(opcode)) { bool single_reg; bool add; uint32_t imm32; uint32_t d; uint32_t n; switch (encoding) { case eEncodingT1: case eEncodingA1: // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', // 32); single_reg = false; add = BitIsSet(opcode, 23); imm32 = Bits32(opcode, 7, 0) << 2; // d = UInt(D:Vd); n = UInt(Rn); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); break; case eEncodingT2: case eEncodingA2: // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32); single_reg = true; add = BitIsSet(opcode, 23); imm32 = Bits32(opcode, 7, 0) << 2; // d = UInt(Vd:D); n = UInt(Rn); d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); n = Bits32(opcode, 19, 16); break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // base = if n == 15 then Align(PC,4) else R[n]; uint32_t base; if (n == 15) base = AlignPC(Rn); else base = Rn; // address = if add then (base + imm32) else (base - imm32); addr_t address; if (add) address = base + imm32; else address = base - imm32; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - base); if (single_reg) { // S[d] = MemA[address,4]; uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d, data)) return false; } else { // word1 = MemA[address,4]; word2 = MemA[address+4,4]; uint32_t word1 = MemARead(context, address, addr_byte_size, 0, &success); if (!success) return false; context.SetRegisterPlusOffset(*base_reg, (address + 4) - base); uint32_t word2 = MemARead(context, address + 4, addr_byte_size, 0, &success); if (!success) return false; // // Combine the word-aligned words in the correct order for current // endianness. // D[d] = if BigEndian() then word1:word2 else word2:word1; uint64_t data64; if (GetByteOrder() == eByteOrderBig) { data64 = word1; data64 = (data64 << 32) | word2; } else { data64 = word2; data64 = (data64 << 32) | word1; } if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d, data64)) return false; } } return true; } // A8.6.400 VSTR // This instruction stores a signle extension register to memory, using an // address from an ARM core register, with an optional offset. bool EmulateInstructionARM::EmulateVSTR(const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then (R[n] + imm32) else (R[n] - imm32); if single_reg then MemA[address,4] = S[d]; else // Store as two word-aligned words in the correct order for current // endianness. MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; #endif bool success = false; if (ConditionPassed(opcode)) { bool single_reg; bool add; uint32_t imm32; uint32_t d; uint32_t n; switch (encoding) { case eEncodingT1: case eEncodingA1: // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', // 32); single_reg = false; add = BitIsSet(opcode, 23); imm32 = Bits32(opcode, 7, 0) << 2; // d = UInt(D:Vd); n = UInt(Rn); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; if ((n == 15) && (CurrentInstrSet() != eModeARM)) return false; break; case eEncodingT2: case eEncodingA2: // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32); single_reg = true; add = BitIsSet(opcode, 23); imm32 = Bits32(opcode, 7, 0) << 2; // d = UInt(Vd:D); n = UInt(Rn); d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); n = Bits32(opcode, 19, 16); // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; if ((n == 15) && (CurrentInstrSet() != eModeARM)) return false; break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = if add then (R[n] + imm32) else (R[n] - imm32); addr_t address; if (add) address = Rn + imm32; else address = Rn - imm32; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, start_reg + d); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (single_reg) { // MemA[address,4] = S[d]; uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success); if (!success) return false; if (!MemAWrite(context, address, data, addr_byte_size)) return false; } else { // // Store as two word-aligned words in the correct order for current // endianness. // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; uint64_t data = ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success); if (!success) return false; if (GetByteOrder() == eByteOrderBig) { if (!MemAWrite(context, address, Bits64(data, 63, 32), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, (address + 4) - Rn); if (!MemAWrite(context, address + 4, Bits64(data, 31, 0), addr_byte_size)) return false; } else { if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, (address + 4) - Rn); if (!MemAWrite(context, address + 4, Bits64(data, 63, 32), addr_byte_size)) return false; } } } return true; } // A8.6.307 VLDI1 (multiple single elements) This instruction loads elements // from memory into one, two, three or four registers, without de-interleaving. // Every element of each register is loaded. bool EmulateInstructionARM::EmulateVLD1Multiple(const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); for r = 0 to regs-1 for e = 0 to elements-1 Elem[D[d+r],e,esize] = MemU[address,ebytes]; address = address + ebytes; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t regs; uint32_t alignment; uint32_t ebytes; uint32_t esize; uint32_t elements; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { // case type of // when '0111' // regs = 1; if align<1> == '1' then UNDEFINED; // when '1010' // regs = 2; if align == '11' then UNDEFINED; // when '0110' // regs = 3; if align<1> == '1' then UNDEFINED; // when '0010' // regs = 4; // otherwise // SEE 'Related encodings'; uint32_t type = Bits32(opcode, 11, 8); uint32_t align = Bits32(opcode, 5, 4); if (type == 7) // '0111' { regs = 1; if (BitIsSet(align, 1)) return false; } else if (type == 10) // '1010' { regs = 2; if (align == 3) return false; } else if (type == 6) // '0110' { regs = 3; if (BitIsSet(align, 1)) return false; } else if (type == 2) // '0010' { regs = 4; } else return false; // alignment = if align == '00' then 1 else 4 << UInt(align); if (align == 0) alignment = 1; else alignment = 4 << align; // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; ebytes = 1 << Bits32(opcode, 7, 6); esize = 8 * ebytes; elements = 8 / ebytes; // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 15); m = Bits32(opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); // if d+regs > 32 then UNPREDICTABLE; if ((d + regs) > 32) return false; } break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then // GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); if (wback) { uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = 8 * regs; uint32_t value = Rn + offset; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset(*base_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { // for e = 0 to elements-1 uint64_t assembled_data = 0; for (uint32_t e = 0; e < elements; ++e) { // Elem[D[d+r],e,esize] = MemU[address,ebytes]; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset(*base_reg, address - Rn); uint64_t data = MemURead(context, address, ebytes, 0, &success); if (!success) return false; assembled_data = (data << (e * esize)) | assembled_data; // New data goes to the left of existing data // address = address + ebytes; address = address + ebytes; } if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r, assembled_data)) return false; } } return true; } // A8.6.308 VLD1 (single element to one lane) // bool EmulateInstructionARM::EmulateVLD1Single(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); Elem[D[d],index,esize] = MemU[address,ebytes]; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t ebytes; uint32_t esize; uint32_t index; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t size = Bits32(opcode, 11, 10); uint32_t index_align = Bits32(opcode, 7, 4); // if size == '11' then SEE VLD1 (single element to all lanes); if (size == 3) return EmulateVLD1SingleAll(opcode, encoding); // case size of if (size == 0) // when '00' { // if index_align<0> != '0' then UNDEFINED; if (BitIsClear(index_align, 0)) return false; // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; ebytes = 1; esize = 8; index = Bits32(index_align, 3, 1); alignment = 1; } else if (size == 1) // when '01' { // if index_align<1> != '0' then UNDEFINED; if (BitIsClear(index_align, 1)) return false; // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); ebytes = 2; esize = 16; index = Bits32(index_align, 3, 2); // alignment = if index_align<0> == '0' then 1 else 2; if (BitIsClear(index_align, 0)) alignment = 1; else alignment = 2; } else if (size == 2) // when '10' { // if index_align<2> != '0' then UNDEFINED; if (BitIsClear(index_align, 2)) return false; // if index_align<1:0> != '00' && index_align<1:0> != '11' then // UNDEFINED; if ((Bits32(index_align, 1, 0) != 0) && (Bits32(index_align, 1, 0) != 3)) return false; // ebytes = 4; esize = 32; index = UInt(index_align<3>); ebytes = 4; esize = 32; index = Bit32(index_align, 3); // alignment = if index_align<1:0> == '00' then 1 else 4; if (Bits32(index_align, 1, 0) == 0) alignment = 1; else alignment = 4; } else { return false; } // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 // then UNPREDICTABLE; wback = (m != 15); register_index = ((m != 15) && (m != 13)); if (n == 15) return false; } break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then // GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; uint32_t value = Rn + offset; context.type = eContextAdjustBaseRegister; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*base_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } // Elem[D[d],index,esize] = MemU[address,ebytes]; uint32_t element = MemURead(context, address, esize, 0, &success); if (!success) return false; element = element << (index * esize); uint64_t reg_data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success); if (!success) return false; uint64_t all_ones = -1; uint64_t mask = all_ones << ((index + 1) * esize); // mask is all 1's to left of // where 'element' goes, & all 0's // at element & to the right of element. if (index > 0) mask = mask | Bits64(all_ones, (index * esize) - 1, 0); // add 1's to the right of where 'element' goes. // now mask should be 0's where element goes & 1's everywhere else. uint64_t masked_reg = reg_data & mask; // Take original reg value & zero out 'element' bits reg_data = masked_reg & element; // Put 'element' into those bits in reg_data. context.type = eContextRegisterLoad; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, reg_data)) return false; } return true; } // A8.6.391 VST1 (multiple single elements) Vector Store (multiple single // elements) stores elements to memory from one, two, three, or four registers, // without interleaving. Every element of each register is stored. bool EmulateInstructionARM::EmulateVST1Multiple(const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); for r = 0 to regs-1 for e = 0 to elements-1 MemU[address,ebytes] = Elem[D[d+r],e,esize]; address = address + ebytes; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t regs; uint32_t alignment; uint32_t ebytes; uint32_t esize; uint32_t elements; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t type = Bits32(opcode, 11, 8); uint32_t align = Bits32(opcode, 5, 4); // case type of if (type == 7) // when '0111' { // regs = 1; if align<1> == '1' then UNDEFINED; regs = 1; if (BitIsSet(align, 1)) return false; } else if (type == 10) // when '1010' { // regs = 2; if align == '11' then UNDEFINED; regs = 2; if (align == 3) return false; } else if (type == 6) // when '0110' { // regs = 3; if align<1> == '1' then UNDEFINED; regs = 3; if (BitIsSet(align, 1)) return false; } else if (type == 2) // when '0010' // regs = 4; regs = 4; else // otherwise // SEE 'Related encodings'; return false; // alignment = if align == '00' then 1 else 4 << UInt(align); if (align == 0) alignment = 1; else alignment = 4 << align; // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; ebytes = 1 << Bits32(opcode, 7, 6); esize = 8 * ebytes; elements = 8 / ebytes; // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; if ((d + regs) > 32) return false; if (n == 15) return false; } break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then // GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); if (wback) { uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = 8 * regs; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset(*base_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } context.type = eContextRegisterStore; // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d + r); uint64_t register_data = ReadRegisterUnsigned( eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success); if (!success) return false; // for e = 0 to elements-1 for (uint32_t e = 0; e < elements; ++e) { // MemU[address,ebytes] = Elem[D[d+r],e,esize]; uint64_t word = Bits64(register_data, ((e + 1) * esize) - 1, e * esize); context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemUWrite(context, address, word, ebytes)) return false; // address = address + ebytes; address = address + ebytes; } } } return true; } // A8.6.392 VST1 (single element from one lane) This instruction stores one // element to memory from one element of a register. bool EmulateInstructionARM::EmulateVST1Single(const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); MemU[address,ebytes] = Elem[D[d],index,esize]; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t ebytes; uint32_t esize; uint32_t index; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t size = Bits32(opcode, 11, 10); uint32_t index_align = Bits32(opcode, 7, 4); // if size == '11' then UNDEFINED; if (size == 3) return false; // case size of if (size == 0) // when '00' { // if index_align<0> != '0' then UNDEFINED; if (BitIsClear(index_align, 0)) return false; // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; ebytes = 1; esize = 8; index = Bits32(index_align, 3, 1); alignment = 1; } else if (size == 1) // when '01' { // if index_align<1> != '0' then UNDEFINED; if (BitIsClear(index_align, 1)) return false; // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); ebytes = 2; esize = 16; index = Bits32(index_align, 3, 2); // alignment = if index_align<0> == '0' then 1 else 2; if (BitIsClear(index_align, 0)) alignment = 1; else alignment = 2; } else if (size == 2) // when '10' { // if index_align<2> != '0' then UNDEFINED; if (BitIsClear(index_align, 2)) return false; // if index_align<1:0> != '00' && index_align<1:0> != '11' then // UNDEFINED; if ((Bits32(index_align, 1, 0) != 0) && (Bits32(index_align, 1, 0) != 3)) return false; // ebytes = 4; esize = 32; index = UInt(index_align<3>); ebytes = 4; esize = 32; index = Bit32(index_align, 3); // alignment = if index_align<1:0> == '00' then 1 else 4; if (Bits32(index_align, 1, 0) == 0) alignment = 1; else alignment = 4; } else { return false; } // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 // then UNPREDICTABLE; wback = (m != 15); register_index = ((m != 15) && (m != 13)); if (n == 15) return false; } break; default: return false; } std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then // GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset(*base_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } // MemU[address,ebytes] = Elem[D[d],index,esize]; uint64_t register_data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success); if (!success) return false; uint64_t word = Bits64(register_data, ((index + 1) * esize) - 1, index * esize); std::optional data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d); context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); if (!MemUWrite(context, address, word, ebytes)) return false; } return true; } // A8.6.309 VLD1 (single element to all lanes) This instruction loads one // element from memory into every element of one or two vectors. bool EmulateInstructionARM::EmulateVLD1SingleAll(const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); replicated_element = Replicate(MemU[address,ebytes], elements); for r = 0 to regs-1 D[d+r] = replicated_element; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t ebytes; uint32_t elements; uint32_t regs; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { // if size == '11' || (size == '00' && a == '1') then UNDEFINED; uint32_t size = Bits32(opcode, 7, 6); if ((size == 3) || ((size == 0) && BitIsSet(opcode, 4))) return false; // ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == '0' // then 1 else 2; ebytes = 1 << size; elements = 8 / ebytes; if (BitIsClear(opcode, 5)) regs = 1; else regs = 2; // alignment = if a == '0' then 1 else ebytes; if (BitIsClear(opcode, 4)) alignment = 1; else alignment = ebytes; // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); n = Bits32(opcode, 19, 16); m = Bits32(opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; if ((d + regs) > 32) return false; if (n == 15) return false; } break; default: return false; } uint32_t Rn = ReadCoreReg(n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then // GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg(m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; context.type = eContextAdjustBaseRegister; std::optional base_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); context.SetRegisterPlusOffset(*base_reg, offset); if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } // replicated_element = Replicate(MemU[address,ebytes], elements); context.type = eContextRegisterLoad; uint64_t word = MemURead(context, address, ebytes, 0, &success); if (!success) return false; uint64_t replicated_element = 0; uint32_t esize = ebytes * 8; for (uint32_t e = 0; e < elements; ++e) replicated_element = (replicated_element << esize) | Bits64(word, esize - 1, 0); // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { // D[d+r] = replicated_element; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r, replicated_element)) return false; } } return true; } // B6.2.13 SUBS PC, LR and related instructions The SUBS PC, LR, #"}, {0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push "}, // set r7 to point to a stack offset {0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #"}, {0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #"}, // copy the stack pointer to ip {0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp"}, {0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #"}, {0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #"}, // adjust the stack pointer {0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #"}, {0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s} , sp, {,}"}, // push one register // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH; {0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!"}, // vector push consecutive extension register(s) {0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 "}, {0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 "}, // Epilogue instructions {0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, {0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, {0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 "}, {0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 "}, // Supervisor Call (previously Software Interrupt) {0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, // Branch instructions // To resolve ambiguity, "blx