blob: 0513042102376828cdf58dfba8f8e4cc2b528fa8 [file] [log] [blame]
//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdlib.h>
#include "EmulateInstructionARM.h"
#include "lldb/Core/ConstString.h"
#include "Plugins/Process/Utility/ARMDefines.h"
#include "Plugins/Process/Utility/ARMUtils.h"
#include "Utility/ARM_DWARF_Registers.h"
#include "llvm/Support/MathExtras.h" // for SignExtend32 template function
// and CountTrailingZeros_32 function
using namespace lldb;
using namespace lldb_private;
static inline uint32_t Align(uint32_t val, uint32_t alignment)
{
return alignment * (val / alignment);
}
//----------------------------------------------------------------------
//
// ITSession implementation
//
//----------------------------------------------------------------------
// A8.6.50
// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition.
static unsigned short CountITSize(unsigned ITMask) {
// First count the trailing zeros of the IT mask.
unsigned TZ = llvm::CountTrailingZeros_32(ITMask);
if (TZ > 3)
{
printf("Encoding error: IT Mask '0000'\n");
return 0;
}
return (4 - TZ);
}
// Init ITState. Note that at least one bit is always 1 in mask.
bool ITSession::InitIT(unsigned short 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)
{
printf("Encoding error: IT FirstCond '1111'\n");
return false;
}
if (FirstCond == 0xE && ITCounter != 1)
{
printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n");
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 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 ARMv8 (1u << 9)
#define ARMvAll (0xffffffffu)
#define ARMV4T_ABOVE (ARMv4T|ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV5_ABOVE (ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV6T2_ABOVE (ARMv6T2|ARMv7|ARMv8)
//----------------------------------------------------------------------
//
// EmulateInstructionARM implementation
//
//----------------------------------------------------------------------
void
EmulateInstructionARM::Initialize ()
{
}
void
EmulateInstructionARM::Terminate ()
{
}
// Write "bits (32) UNKNOWN" to memory address "address". Helper function for many ARM instructions.
bool
EmulateInstructionARM::WriteBits32UnknownToMemory (addr_t address)
{
EmulateInstruction::Context context = { EmulateInstruction::eContextWriteMemoryRandomBits,
address,
0,
0 };
uint32_t random_data = rand ();
const uint32_t addr_byte_size = GetAddressByteSize();
if (!WriteMemoryUnsigned (context, address, random_data, addr_byte_size))
return false;
return true;
}
// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM instructions.
bool
EmulateInstructionARM::WriteBits32Unknown (int n)
{
EmulateInstruction::Context context = { EmulateInstruction::eContextWriteRegisterRandomBits,
eRegisterKindDWARF,
dwarf_r0 + n,
0 };
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;
}
// 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 (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if (ConditionPassed())
{
EncodingSpecificOperations();
NullCheckIfThumbEE(13);
address = SP - 4*BitCount(registers);
for (i = 0 to 14)
{
if (registers<i> == ’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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t addr_byte_size = GetAddressByteSize();
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = { EmulateInstruction::eContextPushRegisterOnStack, eRegisterKindDWARF, 0, 0 };
for (i=0; i<15; ++i)
{
if (BitIsSet (registers, i))
{
context.arg1 = dwarf_r0 + i; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
uint32_t reg_value = ReadRegisterUnsigned(eRegisterKindDWARF, context.arg1, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, addr, reg_value, addr_byte_size))
return false;
addr += addr_byte_size;
}
}
if (BitIsSet (registers, 15))
{
context.arg1 = dwarf_pc; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, addr, pc + 8, addr_byte_size))
return false;
}
context.type = EmulateInstruction::eContextAdjustStackPointer;
context.arg0 = eRegisterKindGeneric;
context.arg1 = LLDB_REGNUM_GENERIC_SP;
context.arg2 = -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 (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if (ConditionPassed())
{
EncodingSpecificOperations(); NullCheckIfThumbEE(13);
address = SP;
for i = 0 to 14
if registers<i> == ‘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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t addr_byte_size = GetAddressByteSize();
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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;
break;
case eEncodingT3:
Rt = Bits32(opcode, 15, 12);
// if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE;
if (Rt == dwarf_sp)
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 (Bit32(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 = { EmulateInstruction::eContextPopRegisterOffStack, eRegisterKindDWARF, 0, 0 };
for (i=0; i<15; ++i)
{
if (BitIsSet (registers, i))
{
context.arg1 = dwarf_r0 + i; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
data = ReadMemoryUnsigned(context, addr, 4, 0, &success);
if (!success)
return false;
if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, context.arg1, data))
return false;
addr += addr_byte_size;
}
}
if (BitIsSet (registers, 15))
{
context.arg1 = dwarf_pc; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
data = ReadMemoryUnsigned(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.arg0 = eRegisterKindGeneric;
context.arg1 = LLDB_REGNUM_GENERIC_SP;
context.arg2 = 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::EmulateAddRdSPImmediate (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
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 (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;
//const uint32_t opcode = OpcodeAsUnsigned (&success);
//if (!success)
// return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
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 (ARMEncoding encoding)
{
return EmulateMovRdRm (encoding);
}
// Move from register to register.
// MOV (register)
bool
EmulateInstructionARM::EmulateMovRdRm (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
uint32_t Rm; // the source register
uint32_t Rd; // the destination register
bool setflags;
switch (encoding) {
case eEncodingT1:
Rm = Bits32(opcode, 6, 3);
Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 1);
setflags = false;
break;
case eEncodingT2:
Rm = Bits32(opcode, 5, 3);
Rd = Bits32(opcode, 2, 1);
setflags = true;
break;
default:
return false;
}
uint32_t reg_value = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
// The context specifies that Rm is to be moved into Rd.
EmulateInstruction::Context context = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + Rm,
0 };
if (Rd == 15)
{
if (!ALUWritePC (context, reg_value))
return false;
}
else
{
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, reg_value))
return false;
if (setflags)
{
m_new_inst_cpsr = m_inst_cpsr;
SetBit32(m_new_inst_cpsr, CPSR_N, Bit32(reg_value, CPSR_N));
SetBit32(m_new_inst_cpsr, CPSR_Z, reg_value == 0 ? 1 : 0);
if (m_new_inst_cpsr != m_inst_cpsr)
{
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
return false;
}
}
}
}
return true;
}
// PC relative immediate load into register, possibly followed by ADD (SP plus register).
// LDR (literal)
bool
EmulateInstructionARM::EmulateLDRRtPCRelative (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
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
if (!success)
return false;
// PC relative immediate load context
EmulateInstruction::Context context = {EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_PC,
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;
base = Align(pc + 4, 4);
context.arg2 = 4 + imm32;
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
&& m_it_session.InITBlock()
&& !m_it_session.LastInITBlock())
return false;
base = Align(pc + 4, 4);
context.arg2 = 4 + imm32;
break;
default:
return false;
}
if (add)
address = base + imm32;
else
address = base - imm32;
data = ReadMemoryUnsigned(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;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data))
return false;
}
return true;
}
// An add operation to adjust the SP.
// ADD (SP plus immediate)
bool
EmulateInstructionARM::EmulateAddSPImmediate (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &success);
if (!success)
return false;
uint32_t imm32; // the immediate operand
switch (encoding) {
case eEncodingT2:
imm32 = ThumbImmScaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
break;
default:
return false;
}
addr_t sp_offset = imm32;
addr_t addr = sp + sp_offset; // the adjusted stack pointer value
EmulateInstruction::Context context = { EmulateInstruction::eContextAdjustStackPointer,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
sp_offset };
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr))
return false;
}
return true;
}
// An add operation to adjust the SP.
// ADD (SP plus register)
bool
EmulateInstructionARM::EmulateAddSPRm (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value
EmulateInstruction::Context context = { EmulateInstruction::eContextAdjustStackPointer,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
reg_value };
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 (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 = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
EmulateInstruction::Context context = { EmulateInstruction::eContextRelativeBranchImmediate, 0, 0, 0};
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &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 + 4) | 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 + 4 + imm32;
context.arg1 = 4 + imm32; // signed offset
context.arg2 = eModeThumb; // target instruction set
if (m_it_session.InITBlock() && !m_it_session.LastInITBlock())
return false;
break;
}
case eEncodingT2:
{
lr = (pc + 4) | 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, 4) + imm32;
context.arg1 = 4 + imm32; // signed offset
context.arg2 = eModeARM; // target instruction set
if (m_it_session.InITBlock() && !m_it_session.LastInITBlock())
return false;
break;
}
case eEncodingA1:
lr = pc + 4; // return address
imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
target = Align(pc + 8, 4) + imm32;
context.arg1 = 8 + imm32; // signed offset
context.arg2 = eModeARM; // target instruction set
break;
case eEncodingA2:
lr = pc + 4; // return address
imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | Bits32(opcode, 24, 24) << 1);
target = pc + 8 + imm32;
context.arg1 = 8 + imm32; // signed offset
context.arg2 = eModeThumb; // target instruction set
break;
default:
return false;
}
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr))
return false;
if (!BranchWritePC(context, target))
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 (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
EmulateInstruction::Context context = { EmulateInstruction::eContextAbsoluteBranchRegister, 0, 0, 0};
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &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 (m_it_session.InITBlock() && !m_it_session.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 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
context.arg0 = eRegisterKindDWARF;
context.arg1 = dwarf_r0 + Rm;
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.
// BX
bool
EmulateInstructionARM::EmulateBXRm (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if (ConditionPassed())
{
EncodingSpecificOperations();
BXWritePC(R[m]);
}
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
EmulateInstruction::Context context = { EmulateInstruction::eContextAbsoluteBranchRegister, 0, 0, 0};
uint32_t Rm; // the register with the target address
switch (encoding) {
case eEncodingT1:
Rm = Bits32(opcode, 6, 3);
if (m_it_session.InITBlock() && !m_it_session.LastInITBlock())
return false;
break;
case eEncodingA1:
Rm = Bits32(opcode, 3, 0);
break;
default:
return false;
}
addr_t target = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
context.arg0 = eRegisterKindDWARF;
context.arg1 = dwarf_r0 + Rm;
if (!BXWritePC(context, target))
return false;
}
return true;
}
// Set r7 to point to some ip offset.
// SUB (immediate)
bool
EmulateInstructionARM::EmulateSubR7IPImmediate (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t ip = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r12, 0, &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 = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r12,
-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::EmulateSubIPSPImmediate (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
-sp_offset };
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r12, addr))
return false;
}
return true;
}
// A sub operation to adjust the SP -- allocate space for local storage.
bool
EmulateInstructionARM::EmulateSubSPImmdiate (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &success);
if (!success)
return false;
uint32_t imm32;
switch (encoding) {
case eEncodingT1:
imm32 = ThumbImmScaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
case eEncodingT2:
imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8)
break;
case eEncodingT3:
imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32)
break;
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 = { EmulateInstruction::eContextAdjustStackPointer,
eRegisterKindGeneric,
LLDB_REGNUM_GENERIC_SP,
-sp_offset };
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr))
return false;
}
return true;
}
// A store operation to the stack that also updates the SP.
bool
EmulateInstructionARM::EmulateSTRRtSP (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t addr_byte_size = GetAddressByteSize();
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &success);
if (!success)
return false;
uint32_t Rt; // the source register
uint32_t imm12;
switch (encoding) {
case eEncodingA1:
Rt = Bits32(opcode, 15, 12);
imm12 = Bits32(opcode, 11, 0);
break;
default:
return false;
}
addr_t sp_offset = imm12;
addr_t addr = sp - sp_offset;
EmulateInstruction::Context context = { EmulateInstruction::eContextPushRegisterOnStack, eRegisterKindDWARF, 0, 0 };
if (Rt != 15)
{
context.arg1 = dwarf_r0 + Rt; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
uint32_t reg_value = ReadRegisterUnsigned(eRegisterKindDWARF, context.arg1, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, addr, reg_value, addr_byte_size))
return false;
}
else
{
context.arg1 = dwarf_pc; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, addr, pc + 8, addr_byte_size))
return false;
}
context.type = EmulateInstruction::eContextAdjustStackPointer;
context.arg0 = eRegisterKindGeneric;
context.arg1 = LLDB_REGNUM_GENERIC_SP;
context.arg2 = -sp_offset;
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
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 (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t addr_byte_size = GetAddressByteSize();
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 = { EmulateInstruction::eContextPushRegisterOnStack, eRegisterKindDWARF, 0, 0 };
for (i=d; i<regs; ++i)
{
context.arg1 = start_reg + i; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
// uint64_t to accommodate 64-bit registers.
uint64_t reg_value = ReadRegisterUnsigned(eRegisterKindDWARF, context.arg1, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, addr, reg_value, reg_byte_size))
return false;
addr += reg_byte_size;
}
context.type = EmulateInstruction::eContextAdjustStackPointer;
context.arg0 = eRegisterKindGeneric;
context.arg1 = LLDB_REGNUM_GENERIC_SP;
context.arg2 = -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 (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t addr_byte_size = GetAddressByteSize();
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &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 accomodate 64-bit registers.
EmulateInstruction::Context context = { EmulateInstruction::eContextPopRegisterOffStack, eRegisterKindDWARF, 0, 0 };
for (i=d; i<regs; ++i)
{
context.arg1 = start_reg + i; // arg1 in the context is the DWARF register number
context.arg2 = addr - sp; // arg2 in the context is the stack pointer offset
data = ReadMemoryUnsigned(context, addr, reg_byte_size, 0, &success);
if (!success)
return false;
if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, context.arg1, data))
return false;
addr += reg_byte_size;
}
context.type = EmulateInstruction::eContextAdjustStackPointer;
context.arg0 = eRegisterKindGeneric;
context.arg1 = LLDB_REGNUM_GENERIC_SP;
context.arg2 = sp_offset;
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset))
return false;
}
return true;
}
// SVC (previously SWI)
bool
EmulateInstructionARM::EmulateSVC (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if (ConditionPassed())
{
EncodingSpecificOperations();
CallSupervisor();
}
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &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 = { EmulateInstruction::eContextSupervisorCall, mode, imm32, 0};
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 (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
EncodingSpecificOperations();
ITSTATE.IT<7:0> = firstcond:mask;
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
m_it_session.InitIT(Bits32(opcode, 7, 0));
return true;
}
// Branch causes a branch to a target address.
bool
EmulateInstructionARM::EmulateB (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if (ConditionPassed())
{
EncodingSpecificOperations();
BranchWritePC(PC + imm32);
}
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
EmulateInstruction::Context context = { EmulateInstruction::eContextRelativeBranchImmediate, 0, 0, 0};
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &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 + 4 + imm32;
context.arg1 = 4 + imm32; // signed offset
context.arg2 = eModeThumb; // target instruction set
break;
case eEncodingT2:
imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0));
target = pc + 4 + imm32;
context.arg1 = 4 + imm32; // signed offset
context.arg2 = eModeThumb; // target instruction set
break;
case eEncodingT3:
// The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
{
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 + 4 + imm32;
context.arg1 = eModeThumb; // target instruction set
context.arg2 = 4 + imm32; // signed offset
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 + 4 + imm32;
context.arg1 = eModeThumb; // target instruction set
context.arg2 = 4 + imm32; // signed offset
break;
}
case eEncodingA1:
imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2);
target = pc + 8 + imm32;
context.arg1 = eModeARM; // target instruction set
context.arg2 = 8 + imm32; // signed offset
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 (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
EncodingSpecificOperations();
if nonzero ^ IsZero(R[n]) then
BranchWritePC(PC + imm32);
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
// Read the register value from the operand register Rn.
uint32_t reg_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Bits32(opcode, 2, 0), 0, &success);
if (!success)
return false;
EmulateInstruction::Context context = { EmulateInstruction::eContextRelativeBranchImmediate, 0, 0, 0};
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &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 + 4 + imm32;
context.arg1 = 4 + imm32; // signed offset
context.arg2 = eModeThumb; // target instruction set
break;
default:
return false;
}
if (nonzero ^ (reg_val == 0))
if (!BranchWritePC(context, target))
return false;
return true;
}
// ADD <Rdn>, <Rm>
// where <Rdn> the destination register is also the first operand register
// and <Rm> is the second operand register.
bool
EmulateInstructionARM::EmulateAddRdnRm (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
uint32_t Rd, Rn, Rm;
//bool setflags = false;
switch (encoding)
{
case eEncodingT2:
// setflags = FALSE
Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
Rm = Bits32(opcode, 6, 3);
if (Rn == 15 && Rm == 15)
return false;
break;
default:
return false;
}
int32_t result, val1, val2;
// Read the first operand.
if (Rn == 15)
val1 = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
else
val1 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + Rn, 0, &success);
if (!success)
return false;
// Read the second operand.
if (Rm == 15)
val2 = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
else
val2 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
result = val1 + val2;
EmulateInstruction::Context context = { EmulateInstruction::eContextImmediate,
result,
0,
0 };
if (Rd == 15)
{
if (!ALUWritePC (context, result))
return false;
}
else
{
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, result))
return false;
}
}
return true;
}
// CMP (immediate)
bool
EmulateInstructionARM::EmulateCmpRnImm (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return 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;
default:
return false;
}
// Read the register value from the operand register Rn.
uint32_t reg_val = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rn, 0, &success);
if (!success)
return false;
EmulateInstruction::Context context = { EmulateInstruction::eContextImmediate, 0, 0, 0};
AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1);
m_new_inst_cpsr = m_inst_cpsr;
SetBit32(m_new_inst_cpsr, CPSR_N, Bit32(res.result, CPSR_N));
SetBit32(m_new_inst_cpsr, CPSR_Z, res.result == 0 ? 1 : 0);
SetBit32(m_new_inst_cpsr, CPSR_C, res.carry_out);
SetBit32(m_new_inst_cpsr, CPSR_V, res.overflow);
if (m_new_inst_cpsr != m_inst_cpsr)
{
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
return false;
}
return true;
}
// CMP (register)
bool
EmulateInstructionARM::EmulateCmpRnRm (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
uint32_t Rn; // the first operand
uint32_t Rm; // the second operand
switch (encoding) {
case eEncodingT1:
Rn = Bits32(opcode, 2, 0);
Rm = Bits32(opcode, 5, 3);
break;
case eEncodingT2:
Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0);
Rm = Bits32(opcode, 6, 3);
if (Rn < 8 && Rm < 8)
return false;
if (Rn == 15 || Rm == 15)
return false;
break;
default:
return false;
}
// Read the register value from register Rn.
uint32_t reg_val1 = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rn, 0, &success);
if (!success)
return false;
// Read the register value from register Rm.
// The register value is not being shifted since we don't handle ARM for now.
uint32_t reg_val2 = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rm, 0, &success);
if (!success)
return false;
EmulateInstruction::Context context = { EmulateInstruction::eContextImmediate, 0, 0, 0};
AddWithCarryResult res = AddWithCarry(reg_val1, reg_val2, 1);
m_new_inst_cpsr = m_inst_cpsr;
SetBit32(m_new_inst_cpsr, CPSR_N, Bit32(res.result, CPSR_N));
SetBit32(m_new_inst_cpsr, CPSR_Z, res.result == 0 ? 1 : 0);
SetBit32(m_new_inst_cpsr, CPSR_C, res.carry_out);
SetBit32(m_new_inst_cpsr, CPSR_V, res.overflow);
if (m_new_inst_cpsr != m_inst_cpsr)
{
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
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 (ARMEncoding encoding)
{
#if 0
// ARM pseudo code...
if ConditionPassed()
EncodingSpecificOperations(); NullCheckIfThumbEE (n);
address = R[n];
for i = 0 to 14
if registers<i> == '1' then
R[i] = MemA[address, 4]; address = address + 4;
if registers<15> == '1' then
LoadWritePC (MemA[address, 4]);
if wback && registers<n> == '0' then R[n] = R[n] + 4 * BitCount (registers);
if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
uint32_t n;
uint32_t registers = 0;
bool wback;
const uint32_t addr_byte_size = GetAddressByteSize();
switch (encoding)
{
case eEncodingT1:
n = Bits32 (opcode, 10, 8);
registers = Bits32 (opcode, 7, 0);
wback = BitIsClear (registers, n);
// if BitCount(registers) < 1 then UNPREDICTABLE;
if (BitCount(registers) < 1)
return false;
break;
case eEncodingT2:
n = Bits32 (opcode, 19, 16);
registers = Bits32 (opcode, 15, 0);
wback = BitIsSet (opcode, 21);
if ((n == 15)
|| (BitCount (registers) < 2)
|| (BitIsSet (opcode, 14) && BitIsSet (opcode, 15)))
return false;
if (BitIsSet (registers, 15)
&& m_it_session.InITBlock()
&& !m_it_session.LastInITBlock())
return false;
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 = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + n,
offset };
for (int i = 0; i < 14; ++i)
{
if (BitIsSet (registers, i))
{
context.type = EmulateInstruction::eContextRegisterPlusOffset;
context.arg2 = offset;
if (wback && (n == 13)) // Pop Instruction
context.type = EmulateInstruction::eContextPopRegisterOffStack;
// R[i] = MemA [address, 4]; address = address + 4;
uint32_t data = ReadMemoryUnsigned (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.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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.arg2 = 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 registers.
// The consecutive memorty locations end at this address and the address just below the lowest of those locations
// can optionally be written back tot he base registers.
bool
EmulateInstructionARM::EmulateLDMDA (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<i> == ’1’ then
R[i] = MemA[address,4]; address = address + 4;
if registers<15> == ’1’ then
LoadWritePC(MemA[address,4]);
if wback && registers<n> == ’0’ then R[n] = R[n] - 4*BitCount(registers);
if wback && registers<n> == ’1’ then R[n] = bits(32) UNKNOWN;
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
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 address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
address = address - (addr_byte_size * BitCount (registers)) + addr_byte_size;
EmulateInstruction::Context context = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + n,
offset };
// for i = 0 to 14
for (int i = 0; i < 14; ++i)
{
// if registers<i> == ’1’ then
if (BitIsSet (registers, i))
{
// R[i] = MemA[address,4]; address = address + 4;
context.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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<n> == ’0’ then R[n] = R[n] - 4*BitCount(registers);
if (wback && BitIsClear (registers, n))
{
addr_t addr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
offset = (addr_byte_size * BitCount (registers)) * -1;
context.type = EmulateInstruction::eContextAdjustBaseRegister;
context.arg2 = offset;
addr = addr + offset;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr))
return false;
}
// if wback && registers<n> == ’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 lcoations 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 (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<i> == ’1’ then
R[i] = MemA[address,4]; address = address + 4;
if registers<15> == ’1’ then
LoadWritePC(MemA[address,4]);
if wback && registers<n> == ’0’ then R[n] = R[n] - 4*BitCount(registers);
if wback && registers<n> == ’1’ then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
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);
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)
&& m_it_session.InITBlock()
&& !m_it_session.LastInITBlock())
return false;
// if wback && registers<n> == ’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 address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
address = address - (addr_byte_size * BitCount (registers));
EmulateInstruction::Context context = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + n,
offset };
for (int i = 0; i < 14; ++i)
{
if (BitIsSet (registers, i))
{
// R[i] = MemA[address,4]; address = address + 4;
context.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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<n> == ’0’ then R[n] = R[n] - 4*BitCount(registers);
if (wback && BitIsClear (registers, n))
{
addr_t addr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
offset = (addr_byte_size * BitCount (registers)) * -1;
context.type = EmulateInstruction::eContextAdjustBaseRegister;
context.arg2 = offset;
addr = addr + offset;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr))
return false;
}
// if wback && registers<n> == ’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 (ARMEncoding encoding)
{
#if 0
if ConditionPassed() then
EncodingSpecificOperations();
address = R[n] + 4;
for i = 0 to 14
if registers<i> == ’1’ then
R[i] = MemA[address,4]; address = address + 4;
if registers<15> == ’1’ then
LoadWritePC(MemA[address,4]);
if wback && registers<n> == ’0’ then R[n] = R[n] + 4*BitCount(registers);
if wback && registers<n> == ’1’ then R[n] = bits(32) UNKNOWN;
#endif
bool success = false;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
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 address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
address = address + addr_byte_size;
EmulateInstruction::Context context = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + n,
offset };
for (int i = 0; i < 14; ++i)
{
if (BitIsSet (registers, i))
{
// R[i] = MemA[address,4]; address = address + 4;
context.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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.arg2 = offset;
uint32_t data = ReadMemoryUnsigned (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<n> == ’0’ then R[n] = R[n] + 4*BitCount(registers);
if (wback && BitIsClear (registers, n))
{
addr_t addr = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success);
if (!success)
return false;
offset = addr_byte_size * BitCount (registers);
context.type = EmulateInstruction::eContextAdjustBaseRegister;
context.arg2 = offset;
addr = addr + offset;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr))
return false;
}
// if wback && registers<n> == ’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 (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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed())
{
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, 5, 3);
Rn = Bits32(opcode, 2, 0);
imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32);
// index = TRUE; add = TRUE; wback = FALSE
add = true;
index = true;
wback = false;
break;
default:
return false;
}
uint32_t base = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + Rn, 0, &success);
if (!success)
return false;
if (add)
offset_addr = base + imm32;
else
offset_addr = base - imm32;
address = (index ? offset_addr : base);
if (wback)
{
EmulateInstruction::Context ctx = { EmulateInstruction::eContextRegisterPlusOffset,
eRegisterKindDWARF,
dwarf_r0 + Rn,
(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 = {EmulateInstruction::eContextImmediate,
0,
0,
0};
// Read memory from the address.
data = ReadMemoryUnsigned(context, address, 4, 0, &success);
if (!success)
return false;
context.arg0 = data;
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
return false;
}
return true;
}
// STM stores multiple registers to consecutive memory locations using an address from a base register. The
// consecutive memory locations start at this address, and teh address just above the last of those locations can
// optionally be written back to the base register.
bool
EmulateInstructionARM::EmulateSTM (ARMEncoding encoding)
{
#if 0
if ConditionPassed() then
EncodingSpecificOperations(); NullCheckIfThumbEE(n);
address = R[n];
for i = 0 to 14
if registers<i> == ’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;
const uint32_t opcode = OpcodeAsUnsigned (&success);
if (!success)
return false;
if (ConditionPassed ())
{
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);
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);
wback = BitIsSet (opcode, 21);
// if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE;
if ((n == 15) || (BitCount (registers) < 2))
return false;
// if wback && registers<n> == ’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 = { EmulateInstruction::eContextRegisterStore,
eRegisterKindDWARF,
dwarf_r0 + n,
offset };
// for i = 0 to 14
for (int i = 0; i < 14; ++i)
{
int lowest_set_bit = 14;
// if registers<i> == ’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;
context.arg1 = dwarf_r0 + i;
context.arg2 = address + offset;
if (!WriteMemoryUnsigned (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))
{
const addr_t sp = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &success);
if (!success)
return false;
context.arg1 = dwarf_pc; // arg1 in the context is the DWARF register number
context.arg2 = address + offset - sp; // arg2 in the context is the stack pointer offset
const uint32_t pc = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
if (!success)
return false;
if (!WriteMemoryUnsigned (context, address + offset, pc + 8, 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.arg1 = dwarf_r0 + n;
context.arg2 = offset;
addr_t data = address + offset;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data))
return false;
}
}
return true;
}
EmulateInstructionARM::ARMOpcode*
EmulateInstructionARM::GetARMOpcodeForInstruction (const uint32_t opcode)
{
static ARMOpcode
g_arm_opcodes[] =
{
//----------------------------------------------------------------------
// Prologue instructions
//----------------------------------------------------------------------
// push register(s)
{ 0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulatePush, "push <registers>" },
{ 0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, eSize32, &EmulateInstructionARM::EmulatePush, "push <register>" },
// set r7 to point to a stack offset
{ 0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateAddRdSPImmediate, "add r7, sp, #<const>" },
{ 0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSubR7IPImmediate, "sub r7, ip, #<const>"},
// copy the stack pointer to ip
{ 0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateMovRdSP, "mov ip, sp" },
{ 0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateAddRdSPImmediate, "add ip, sp, #<const>" },
{ 0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSubIPSPImmediate, "sub ip, sp, #<const>"},
// adjust the stack pointer
{ 0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSubSPImmdiate, "sub sp, sp, #<const>"},
// push one register
// if Rn == '1101' && imm12 == '000000000100' then SEE PUSH;
{ 0x0fff0000, 0x052d0000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!" },
// vector push consecutive extension register(s)
{ 0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
{ 0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
//----------------------------------------------------------------------
// Epilogue instructions
//----------------------------------------------------------------------
{ 0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulatePop, "pop <registers>"},
{ 0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, eSize32, &EmulateInstructionARM::EmulatePop, "pop <register>"},
{ 0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
{ 0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
//----------------------------------------------------------------------
// Supervisor Call (previously Software Interrupt)
//----------------------------------------------------------------------
{ 0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"},
//----------------------------------------------------------------------
// Branch instructions
//----------------------------------------------------------------------
{ 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSVC, "b #imm24"},
// To resolve ambiguity, "blx <label>" should come before "bl <label>".
{ 0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
{ 0x0f000000, 0x0b000000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
{ 0x0ffffff0, 0x012fff30, ARMV5_ABOVE, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
// for example, "bx lr"
{ 0x0ffffff0, 0x012fff10, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
//----------------------------------------------------------------------
// Load instructions
//----------------------------------------------------------------------
{ 0x0fd00000, 0x08900000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>" },
{ 0x0fd00000, 0x08100000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateLDMDA, "ldmda<c> <Rn>{!} <registers>" },
{ 0x0fd00000, 0x09100000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>" },
{ 0x0fd00000, 0x09900000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateLDMIB, "ldmib<c> <Rn<{!} <registers>" },
//----------------------------------------------------------------------
// Store instructions
//----------------------------------------------------------------------
{ 0x0fd00000, 0x08800000, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>" }
};
static const size_t k_num_arm_opcodes = sizeof(g_arm_opcodes)/sizeof(ARMOpcode);
for (size_t i=0; i<k_num_arm_opcodes; ++i)
{
if ((g_arm_opcodes[i].mask & opcode) == g_arm_opcodes[i].value)
return &g_arm_opcodes[i];
}
return NULL;
}
EmulateInstructionARM::ARMOpcode*
EmulateInstructionARM::GetThumbOpcodeForInstruction (const uint32_t opcode)
{
static ARMOpcode
g_thumb_opcodes[] =
{
//----------------------------------------------------------------------
// Prologue instructions
//----------------------------------------------------------------------
// push register(s)
{ 0xfffffe00, 0x0000b400, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulatePush, "push <registers>" },
{ 0xffff0000, 0xe92d0000, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulatePush, "push.w <registers>" },
{ 0xffff0fff, 0xf84d0d04, ARMV6T2_ABOVE, eEncodingT3, eSize32, &EmulateInstructionARM::EmulatePush, "push.w <register>" },
// set r7 to point to a stack offset
{ 0xffffff00, 0x0000af00, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateAddRdSPImmediate, "add r7, sp, #imm" },
// copy the stack pointer to r7
{ 0xffffffff, 0x0000466f, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateMovRdSP, "mov r7, sp" },
// move from high register to low register (comes after "mov r7, sp" to resolve ambiguity)
{ 0xffffffc0, 0x00004640, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateMovLowHigh, "mov r0-r7, r8-r15" },
// PC-relative load into register (see also EmulateAddSPRm)
{ 0xfffff800, 0x00004800, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr <Rt>, [PC, #imm]"},
// adjust the stack pointer
{ 0xffffff87, 0x00004485, ARMvAll, eEncodingT2, eSize16, &EmulateInstructionARM::EmulateAddSPRm, "add sp, <Rm>"},
{ 0xffffff80, 0x0000b080, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateSubSPImmdiate, "add sp, sp, #imm"},
{ 0xfbef8f00, 0xf1ad0d00, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateSubSPImmdiate, "sub.w sp, sp, #<const>"},
{ 0xfbff8f00, 0xf2ad0d00, ARMV6T2_ABOVE, eEncodingT3, eSize32, &EmulateInstructionARM::EmulateSubSPImmdiate, "subw sp, sp, #imm12"},
// vector push consecutive extension register(s)
{ 0xffbf0f00, 0xed2d0b00, ARMV6T2_ABOVE, eEncodingT1, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"},
{ 0xffbf0f00, 0xed2d0a00, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"},
//----------------------------------------------------------------------
// Epilogue instructions
//----------------------------------------------------------------------
{ 0xffffff80, 0x0000b000, ARMvAll, eEncodingT2, eSize16, &EmulateInstructionARM::EmulateAddSPImmediate, "add sp, #imm"},
{ 0xfffffe00, 0x0000bc00, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulatePop, "pop <registers>"},
{ 0xffff0000, 0xe8bd0000, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulatePop, "pop.w <registers>" },
{ 0xffff0fff, 0xf85d0d04, ARMV6T2_ABOVE, eEncodingT3, eSize32, &EmulateInstructionARM::EmulatePop, "pop.w <register>" },
{ 0xffbf0f00, 0xecbd0b00, ARMV6T2_ABOVE, eEncodingT1, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"},
{ 0xffbf0f00, 0xecbd0a00, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"},
//----------------------------------------------------------------------
// Supervisor Call (previously Software Interrupt)
//----------------------------------------------------------------------
{ 0xffffff00, 0x0000df00, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateSVC, "svc #imm8"},
//----------------------------------------------------------------------
// If Then makes up to four following instructions conditional.
//----------------------------------------------------------------------
{ 0xffffff00, 0x0000bf00, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateIT, "it{<x>{<y>{<z>}}} <firstcond>"},
//----------------------------------------------------------------------
// Branch instructions
//----------------------------------------------------------------------
// To resolve ambiguity, "b<c> #imm8" should come after "svc #imm8".
{ 0xfffff000, 0x0000d000, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateB, "b<c> #imm8 (outside IT)"},
{ 0xffff8000, 0x0000e000, ARMvAll, eEncodingT2, eSize16, &EmulateInstructionARM::EmulateB, "b #imm11 (outside or last in IT)"},
{ 0xf800d000, 0xf0008000, ARMV6T2_ABOVE, eEncodingT3, eSize32, &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside IT)"},
{ 0xf800d000, 0xf0009000, ARMV6T2_ABOVE, eEncodingT4, eSize32, &EmulateInstructionARM::EmulateB, "b.w #imm8 (outside or last in IT)"},
// J1 == J2 == 1
{ 0xf800f800, 0xf000f800, ARMV4T_ABOVE, eEncodingT1, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"},
// J1 == J2 == 1
{ 0xf800e800, 0xf000e800, ARMV5_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
{ 0xffffff87, 0x00004780, ARMV5_ABOVE, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"},
// for example, "bx lr"
{ 0xffffff87, 0x00004700, ARMvAll, eEncodingA1, eSize32, &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"},
// compare and branch
{ 0xfffff500, 0x0000b100, ARMV6T2_ABOVE, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateCB, "cb{n}z <Rn>, <label>"},
//----------------------------------------------------------------------
// Data-processing instructions
//----------------------------------------------------------------------
// Make sure "add sp, <Rm>" comes before this instruction, so there's no ambiguity decoding the two.
{ 0xffffff00, 0x00004400, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateAddRdnRm, "add <Rdn>, <Rm>"},
// move from high register to high register
{ 0xffffff00, 0x00004600, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateMovRdRm, "mov<c> <Rd>, <Rm>"},
// move from low register to low register
{ 0xffffffc0, 0x00000000, ARMvAll, eEncodingT2, eSize16, &EmulateInstructionARM::EmulateMovRdRm, "movs <Rd>, <Rm>"},
// compare a register with immediate
{ 0xfffff800, 0x00002800, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateCmpRnImm, "cmp<c> <Rn>, #imm8"},
// compare Rn with Rm (Rn and Rm both from r0-r7)
{ 0xffffffc0, 0x00004280, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateCmpRnRm, "cmp<c> <Rn>, <Rm>"},
// compare Rn with Rm (Rn and Rm not both from r0-r7)
{ 0xffffff00, 0x00004500, ARMvAll, eEncodingT2, eSize16, &EmulateInstructionARM::EmulateCmpRnRm, "cmp<c> <Rn>, <Rm>"},
//----------------------------------------------------------------------
// Load instructions
//----------------------------------------------------------------------
{ 0xfffff800, 0x0000c800, ARMV4T_ABOVE, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>" },
{ 0xffd02000, 0xe8900000, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateLDM, "ldm<c>.w <Rn>{!} <registers>" },
{ 0xffd00000, 0xe9100000, ARMV6T2_ABOVE, eEncodingT1, eSize32, &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>" },
{ 0xfffff800, 0x00006800, ARMvAll, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#imm}]"},
// Thumb2 PC-relative load into register
{ 0xff7f0000, 0xf85f0000, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr<c>.w <Rt>, [PC, +/-#imm}]"},
//----------------------------------------------------------------------
// Store instructions
//----------------------------------------------------------------------
{ 0xfffff800, 0x0000c000, ARMV4T_ABOVE, eEncodingT1, eSize16, &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>" },
{ 0xffd00000, 0xe8800000, ARMV6T2_ABOVE, eEncodingT2, eSize32, &EmulateInstructionARM::EmulateSTM, "stm<c>.w <Rn>{!} <registers>" }
};
const size_t k_num_thumb_opcodes = sizeof(g_thumb_opcodes)/sizeof(ARMOpcode);
for (size_t i=0; i<k_num_thumb_opcodes; ++i)
{
if ((g_thumb_opcodes[i].mask & opcode) == g_thumb_opcodes[i].value)
return &g_thumb_opcodes[i];
}
return NULL;
}
bool
EmulateInstructionARM::SetTargetTriple (const ConstString &triple)
{
m_arm_isa = 0;
const char *triple_cstr = triple.GetCString();
if (triple_cstr)
{
const char *dash = ::strchr (triple_cstr, '-');
if (dash)
{
std::string arch (triple_cstr, dash);
const char *arch_cstr = arch.c_str();
if (strcasecmp(arch_cstr, "armv4t") == 0)
m_arm_isa = ARMv4T;
else if (strcasecmp(arch_cstr, "armv4") == 0)
m_arm_isa = ARMv4;
else if (strcasecmp(arch_cstr, "armv5tej") == 0)
m_arm_isa = ARMv5TEJ;
else if (strcasecmp(arch_cstr, "armv5te") == 0)
m_arm_isa = ARMv5TE;
else if (strcasecmp(arch_cstr, "armv5t") == 0)
m_arm_isa = ARMv5T;
else if (strcasecmp(arch_cstr, "armv6k") == 0)
m_arm_isa = ARMv6K;
else if (strcasecmp(arch_cstr, "armv6") == 0)
m_arm_isa = ARMv6;
else if (strcasecmp(arch_cstr, "armv6t2") == 0)
m_arm_isa = ARMv6T2;
else if (strcasecmp(arch_cstr, "armv7") == 0)
m_arm_isa = ARMv7;
else if (strcasecmp(arch_cstr, "armv8") == 0)
m_arm_isa = ARMv8;
}
}
return m_arm_isa != 0;
}
bool
EmulateInstructionARM::ReadInstruction ()
{
bool success = false;
m_inst_cpsr = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, 0, &success);
if (success)
{
addr_t pc = ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_ADDRESS, &success);
if (success)
{
Context read_inst_context = {eContextReadOpcode, 0, 0};
if (m_inst_cpsr & MASK_CPSR_T)
{
m_inst_mode = eModeThumb;
uint32_t thumb_opcode = ReadMemoryUnsigned(read_inst_context, pc, 2, 0, &success);
if (success)
{
if ((m_inst.opcode.inst16 & 0xe000) != 0xe000 || ((m_inst.opcode.inst16 & 0x1800u) == 0))
{
m_inst.opcode_type = eOpcode16;
m_inst.opcode.inst16 = thumb_opcode;
}
else
{
m_inst.opcode_type = eOpcode32;
m_inst.opcode.inst32 = (thumb_opcode << 16) | ReadMemoryUnsigned(read_inst_context, pc + 2, 2, 0, &success);
}
}
}
else
{
m_inst_mode = eModeARM;
m_inst.opcode_type = eOpcode32;
m_inst.opcode.inst32 = ReadMemoryUnsigned(read_inst_context, pc, 4, 0, &success);
}
}
}
if (!success)
{
m_inst_mode = eModeInvalid;
m_inst_pc = LLDB_INVALID_ADDRESS;
}
return success;
}
uint32_t
EmulateInstructionARM::ArchVersion ()
{
return m_arm_isa;
}
bool
EmulateInstructionARM::ConditionPassed ()
{
if (m_inst_cpsr == 0)
return false;
const uint32_t cond = CurrentCond ();
if (cond == UINT32_MAX)
return false;
bool result = false;
switch (UnsignedBits(cond, 3, 1))
{
case 0: result = (m_inst_cpsr & MASK_CPSR_Z) != 0; break;
case 1: result = (m_inst_cpsr & MASK_CPSR_C) != 0; break;
case 2: result = (m_inst_cpsr & MASK_CPSR_N) != 0; break;
case 3: result = (m_inst_cpsr & MASK_CPSR_V) != 0; break;
case 4: result = ((m_inst_cpsr & MASK_CPSR_C) != 0) && ((m_inst_cpsr & MASK_CPSR_Z) == 0); break;
case 5:
{
bool n = (m_inst_cpsr & MASK_CPSR_N);
bool v = (m_inst_cpsr & MASK_CPSR_V);
result = n == v;
}
break;
case 6:
{
bool n = (m_inst_cpsr & MASK_CPSR_N);
bool v = (m_inst_cpsr & MASK_CPSR_V);
result = n == v && ((m_inst_cpsr & MASK_CPSR_Z) == 0);
}
break;
case 7:
result = true;
break;
}
if (cond & 1)
result = !result;
return result;
}
uint32_t
EmulateInstructionARM::CurrentCond ()
{
switch (m_inst_mode)
{
default:
case eModeInvalid:
break;
case eModeARM:
return UnsignedBits(m_inst.opcode.inst32, 31, 28);
case eModeThumb:
// For T1 and T3 encodings of the Branch instruction, it returns the 4-bit
// 'cond' field of the encoding.
if (m_inst.opcode_type == eOpcode16 &&
Bits32(m_inst.opcode.inst16, 15, 12) == 0x0d &&
Bits32(m_inst.opcode.inst16, 11, 7) != 0x0f)
{
return Bits32(m_inst.opcode.inst16, 11, 7);
}
else if (m_inst.opcode_type == eOpcode32 &&
Bits32(m_inst.opcode.inst32, 31, 27) == 0x1e &&
Bits32(m_inst.opcode.inst32, 15, 14) == 0x02 &&
Bits32(m_inst.opcode.inst32, 12, 12) == 0x00 &&
Bits32(m_inst.opcode.inst32, 25, 22) <= 0x0d)
{
return Bits32(m_inst.opcode.inst32, 25, 22);
}
return m_it_session.GetCond();
}
return UINT32_MAX; // Return invalid value
}
bool
EmulateInstructionARM::BranchWritePC (const Context &context, uint32_t addr)
{
addr_t target;
// Check the current instruction set.
if (CurrentInstrSet() == eModeARM)
target = addr & 0xfffffffc;
else
target = addr & 0xfffffffe;
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target))
return false;
return true;
}
// As a side effect, BXWritePC sets context.arg2 to eModeARM or eModeThumb by inspecting addr.
bool
EmulateInstructionARM::BXWritePC (Context &context, uint32_t addr)
{
addr_t target;
// If the CPSR is changed due to switching between ARM and Thumb ISETSTATE,
// we want to record it and issue a WriteRegister callback so the clients
// can track the mode changes accordingly.
bool cpsr_changed = false;
if (BitIsSet(addr, 0))
{
if (CurrentInstrSet() != eModeThumb)
{
SelectInstrSet(eModeThumb);
cpsr_changed = true;
}
target = addr & 0xfffffffe;
context.arg2 = eModeThumb;
}
else if (BitIsClear(addr, 1))
{
if (CurrentInstrSet() != eModeARM)
{
SelectInstrSet(eModeARM);
cpsr_changed = true;
}
target = addr & 0xfffffffc;
context.arg2 = eModeARM;
}
else
return false; // address<1:0> == '10' => UNPREDICTABLE
if (cpsr_changed)
{
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr))
return false;
}
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target))
return false;
return true;
}
// Dispatches to either BXWritePC or BranchWritePC based on architecture versions.
bool
EmulateInstructionARM::LoadWritePC (Context &context, uint32_t addr)
{
if (ArchVersion() >= ARMv5T)
return BXWritePC(context, addr);
else
return BranchWritePC((const Context)context, addr);
}
// Dispatches to either BXWritePC or BranchWritePC based on architecture versions and current instruction set.
bool
EmulateInstructionARM::ALUWritePC (Context &context, uint32_t addr)
{
if (ArchVersion() >= ARMv7 && CurrentInstrSet() == eModeARM)
return BXWritePC(context, addr);
else
return BranchWritePC((const Context)context, addr);
}
EmulateInstructionARM::Mode
EmulateInstructionARM::CurrentInstrSet ()
{
return m_inst_mode;
}
// Set the 'T' bit of our CPSR. The m_inst_mode gets updated when the next
// ReadInstruction() is performed. This function has a side effect of updating
// the m_new_inst_cpsr member variable if necessary.
bool
EmulateInstructionARM::SelectInstrSet (Mode arm_or_thumb)
{
m_new_inst_cpsr = m_inst_cpsr;
switch (arm_or_thumb)
{
default:
return false;
eModeARM:
// Clear the T bit.
m_new_inst_cpsr &= ~MASK_CPSR_T;
break;
eModeThumb:
// Set the T bit.
m_new_inst_cpsr |= MASK_CPSR_T;
break;
}
return true;
}
// This function returns TRUE if the processor currently provides support for
// unaligned memory accesses, or FALSE otherwise. This is always TRUE in ARMv7,
// controllable by the SCTLR.U bit in ARMv6, and always FALSE before ARMv6.
bool
EmulateInstructionARM::UnalignedSupport()
{
return (ArchVersion() >= ARMv7);
}
// The main addition and subtraction instructions can produce status information
// about both unsigned carry and signed overflow conditions. This status
// information can be used to synthesize multi-word additions and subtractions.
EmulateInstructionARM::AddWithCarryResult
EmulateInstructionARM::AddWithCarry (uint32_t x, uint32_t y, uint8_t carry_in)
{
uint32_t result;
uint8_t carry_out;
uint8_t overflow;
uint64_t unsigned_sum = x + y + carry_in;
int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in;
result = UnsignedBits(unsigned_sum, 31, 0);
carry_out = (result == unsigned_sum ? 0 : 1);
overflow = ((int32_t)result == signed_sum ? 0 : 1);
AddWithCarryResult res = { result, carry_out, overflow };
return res;
}
bool
EmulateInstructionARM::EvaluateInstruction ()
{
// Advance the ITSTATE bits to their values for the next instruction.
if (m_inst_mode == eModeThumb && m_it_session.InITBlock())
m_it_session.ITAdvance();
return false;
}