Added the start of opcode emulation for ARM instructions. This class is designed
to be fed 4 callbacks: read/write memory, and read/write registers. After this,
you can tell the object to read an instruction. This will cause the class to read
the PC, and read and instruction. Then you can emulate the instruction by calling
EvaluateInstruction. This will cause the class to figure out exactly what an opcode
does, and call the read/write mem/regs functions with actual values which allows one
to emulate an instruction without running a process, or it allows one to watch the
context information (the memory write is a pushing register 3 onto the stack at offset
12) so it can be used for generating call frame information. This way, in the future,
we will have one class that can be used to emulate instructions and generate our
unwind info from assembly.
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@123998 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/Utility/EmulateInstruction.cpp b/source/Plugins/Process/Utility/EmulateInstruction.cpp
new file mode 100644
index 0000000..5aeaae7
--- /dev/null
+++ b/source/Plugins/Process/Utility/EmulateInstruction.cpp
@@ -0,0 +1,98 @@
+//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "EmulateInstruction.h"
+
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/StreamString.h"
+using namespace lldb;
+using namespace lldb_private;
+
+
+EmulateInstruction::EmulateInstruction
+(
+ lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ void *baton,
+ ReadMemory read_mem_callback,
+ WriteMemory write_mem_callback,
+ ReadRegister read_reg_callback,
+ WriteRegister write_reg_callback
+) :
+ m_byte_order (lldb::eByteOrderHost),
+ m_addr_byte_size (sizeof (void *)),
+ m_baton (baton),
+ m_read_mem_callback (read_mem_callback),
+ m_write_mem_callback (write_mem_callback),
+ m_read_reg_callback (read_reg_callback),
+ m_write_reg_callback (write_reg_callback),
+ m_inst_pc (LLDB_INVALID_ADDRESS)
+{
+ ::bzero (&m_inst, sizeof (m_inst));
+}
+
+uint64_t
+EmulateInstruction::ReadRegisterUnsigned (uint32_t reg_kind, uint32_t reg_num, uint64_t fail_value, bool *success_ptr)
+{
+ uint64_t uval64 = 0;
+ bool success = m_read_reg_callback (m_baton, reg_kind, reg_num, uval64);
+ if (success_ptr)
+ *success_ptr = success;
+ if (!success)
+ uval64 = fail_value;
+ return uval64;
+}
+
+bool
+EmulateInstruction::WriteRegisterUnsigned (const Context &context, uint32_t reg_kind, uint32_t reg_num, uint64_t reg_value)
+{
+ return m_write_reg_callback (m_baton, context, reg_kind, reg_num, reg_value);
+}
+
+uint64_t
+EmulateInstruction::ReadMemoryUnsigned (const Context &context, lldb::addr_t addr, size_t byte_size, uint64_t fail_value, bool *success_ptr)
+{
+ uint64_t uval64 = 0;
+ bool success = false;
+ if (byte_size <= 8)
+ {
+ uint8_t buf[sizeof(uint64_t)];
+ size_t bytes_read = m_read_mem_callback (m_baton, context, addr, buf, byte_size);
+ if (bytes_read == byte_size)
+ {
+ uint32_t offset = 0;
+ DataExtractor data (buf, byte_size, m_byte_order, m_addr_byte_size);
+ uval64 = data.GetMaxU64 (&offset, byte_size);
+ success = true;
+ }
+ }
+
+ if (success_ptr)
+ *success_ptr = success;
+
+ if (!success)
+ uval64 = fail_value;
+ return uval64;
+}
+
+
+bool
+EmulateInstruction::WriteMemoryUnsigned (const Context &context,
+ lldb::addr_t addr,
+ uint64_t uval,
+ size_t uval_byte_size)
+{
+ StreamString strm(Stream::eBinary, GetAddressByteSize(), GetByteOrder());
+ strm.PutMaxHex64 (uval, uval_byte_size);
+
+ size_t bytes_written = m_write_mem_callback (m_baton, context, addr, strm.GetData(), uval_byte_size);
+ if (bytes_written == uval_byte_size)
+ return true;
+ return false;
+}
diff --git a/source/Plugins/Process/Utility/EmulateInstruction.h b/source/Plugins/Process/Utility/EmulateInstruction.h
new file mode 100644
index 0000000..59c4a34
--- /dev/null
+++ b/source/Plugins/Process/Utility/EmulateInstruction.h
@@ -0,0 +1,234 @@
+//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_EmulateInstruction_h_
+#define lldb_EmulateInstruction_h_
+
+#include "lldb/lldb-include.h"
+
+namespace lldb_private {
+
+class EmulateInstruction
+{
+public:
+ enum ContextType
+ {
+ eContextInvalid = 0,
+ eContextReadOpcode,
+ eContextImmediate,
+ eContextPushRegisterOnStack,
+ eContextAdjustStackPointer,
+ eContextRegisterPlusOffset,
+ };
+
+ struct Context
+ {
+ ContextType type;
+ lldb::addr_t arg0;
+ lldb::addr_t arg1;
+ lldb::addr_t arg2;
+ };
+
+ union Opcode
+ {
+ uint8_t inst8;
+ uint16_t inst16;
+ uint32_t inst32;
+ uint64_t inst64;
+ union inst
+ {
+ uint8_t bytes[16];
+ uint8_t length;
+ };
+ };
+
+ enum OpcodeType
+ {
+ eOpcode8,
+ eOpcode16,
+ eOpcode32,
+ eOpcode64,
+ eOpcodeBytes,
+ };
+
+ struct Instruction
+ {
+ OpcodeType opcode_type;
+ Opcode opcode;
+ };
+
+ typedef size_t (*ReadMemory) (void *baton,
+ const Context &context,
+ lldb::addr_t addr,
+ void *dst,
+ size_t length);
+
+ typedef size_t (*WriteMemory) (void *baton,
+ const Context &context,
+ lldb::addr_t addr,
+ const void *dst,
+ size_t length);
+
+ typedef bool (*ReadRegister) (void *baton,
+ uint32_t reg_kind,
+ uint32_t reg_num,
+ uint64_t ®_value);
+
+ typedef bool (*WriteRegister) (void *baton,
+ const Context &context,
+ uint32_t reg_kind,
+ uint32_t reg_num,
+ uint64_t reg_value);
+
+ EmulateInstruction (lldb::ByteOrder byte_order,
+ uint32_t addr_byte_size,
+ void *baton,
+ ReadMemory read_mem_callback,
+ WriteMemory write_mem_callback,
+ ReadRegister read_reg_callback,
+ WriteRegister write_reg_callback);
+
+ virtual ~EmulateInstruction()
+ {
+ }
+
+ virtual bool
+ ReadInstruction () = 0;
+
+ virtual bool
+ EvaluateInstruction () = 0;
+
+ // Create a mask that starts at bit zero and includes "bit"
+ static uint64_t
+ MaskUpToBit (const uint64_t bit)
+ {
+ return (1ull << (bit + 1ull)) - 1ull;
+ }
+
+ static bool
+ BitIsSet (const uint64_t value, const uint64_t bit)
+ {
+ return (value & (1ull << bit)) != 0;
+ }
+
+ static bool
+ BitIsClear (const uint64_t value, const uint64_t bit)
+ {
+ return (value & (1ull << bit)) == 0;
+ }
+
+ static int64_t
+ SignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit)
+ {
+ uint64_t result = UnsignedBits (value, msbit, lsbit);
+ if (BitIsSet(value, msbit))
+ {
+ // Sign extend
+ result |= ~MaskUpToBit (msbit - lsbit);
+ }
+ return result;
+ }
+
+ static uint64_t
+ UnsignedBits (const uint64_t value, const uint64_t msbit, const uint64_t lsbit)
+ {
+ uint64_t result = value >> lsbit;
+ result &= MaskUpToBit (msbit - lsbit);
+ return result;
+ }
+
+ uint64_t
+ ReadRegisterUnsigned (uint32_t reg_kind,
+ uint32_t reg_num,
+ uint64_t fail_value,
+ bool *success_ptr);
+
+ bool
+ WriteRegisterUnsigned (const Context &context,
+ uint32_t reg_kind,
+ uint32_t reg_num,
+ uint64_t reg_value);
+
+ uint64_t
+ ReadMemoryUnsigned (const Context &context,
+ lldb::addr_t addr,
+ size_t byte_size,
+ uint64_t fail_value,
+ bool *success_ptr);
+
+ bool
+ WriteMemoryUnsigned (const Context &context,
+ lldb::addr_t addr,
+ uint64_t uval,
+ size_t uval_byte_size);
+
+ static uint32_t
+ BitCount (uint64_t value)
+ {
+ uint32_t set_bit_count = 0;
+ while (value)
+ {
+ if (value & 1)
+ ++set_bit_count;
+ value >>= 1;
+ }
+ return set_bit_count;
+ }
+
+ uint32_t
+ GetAddressByteSize () const
+ {
+ return m_addr_byte_size;
+ }
+
+ lldb::ByteOrder
+ GetByteOrder () const
+ {
+ return m_byte_order;
+ }
+
+ uint64_t
+ OpcodeAsUnsigned (bool *success_ptr)
+ {
+ if (success_ptr)
+ *success_ptr = true;
+ switch (m_inst.opcode_type)
+ {
+ eOpcode8: return m_inst.opcode.inst8;
+ eOpcode16: return m_inst.opcode.inst16;
+ eOpcode32: return m_inst.opcode.inst32;
+ eOpcode64: return m_inst.opcode.inst64;
+ eOpcodeBytes:
+ break;
+ }
+ if (success_ptr)
+ *success_ptr = false;
+ return 0;
+ }
+
+protected:
+ lldb::ByteOrder m_byte_order;
+ uint32_t m_addr_byte_size;
+ void * m_baton;
+ ReadMemory m_read_mem_callback;
+ WriteMemory m_write_mem_callback;
+ ReadRegister m_read_reg_callback;
+ WriteRegister m_write_reg_callback;
+
+ lldb::addr_t m_inst_pc;
+ Instruction m_inst;
+ //------------------------------------------------------------------
+ // For EmulateInstruction only
+ //------------------------------------------------------------------
+ DISALLOW_COPY_AND_ASSIGN (EmulateInstruction);
+};
+
+} // namespace lldb_private
+
+#endif // lldb_EmulateInstruction_h_
diff --git a/source/Plugins/Process/Utility/EmulateInstructionARM.cpp b/source/Plugins/Process/Utility/EmulateInstructionARM.cpp
new file mode 100644
index 0000000..7055d5d
--- /dev/null
+++ b/source/Plugins/Process/Utility/EmulateInstructionARM.cpp
@@ -0,0 +1,299 @@
+//===-- 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 "EmulateInstructionARM.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ARM constants used during decoding
+#define REG_RD 0
+#define LDM_REGLIST 1
+#define PC_REG 15
+#define PC_REGLIST_BIT 0x8000
+
+// ARM conditions
+#define COND_EQ 0x0
+#define COND_NE 0x1
+#define COND_CS 0x2
+#define COND_HS 0x2
+#define COND_CC 0x3
+#define COND_LO 0x3
+#define COND_MI 0x4
+#define COND_PL 0x5
+#define COND_VS 0x6
+#define COND_VC 0x7
+#define COND_HI 0x8
+#define COND_LS 0x9
+#define COND_GE 0xA
+#define COND_LT 0xB
+#define COND_GT 0xC
+#define COND_LE 0xD
+#define COND_AL 0xE
+#define COND_UNCOND 0xF
+
+
+#define MASK_CPSR_MODE_MASK (0x0000001fu)
+#define MASK_CPSR_T (1u << 5)
+#define MASK_CPSR_F (1u << 6)
+#define MASK_CPSR_I (1u << 7)
+#define MASK_CPSR_A (1u << 8)
+#define MASK_CPSR_E (1u << 9)
+#define MASK_CPSR_GE_MASK (0x000f0000u)
+#define MASK_CPSR_J (1u << 24)
+#define MASK_CPSR_Q (1u << 27)
+#define MASK_CPSR_V (1u << 28)
+#define MASK_CPSR_C (1u << 29)
+#define MASK_CPSR_Z (1u << 30)
+#define MASK_CPSR_N (1u << 31)
+
+
+#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 << 8)
+#define ARMvAll (0xffffffffu)
+
+typedef bool (*EmulateCallback) (EmulateInstructionARM *emulator);
+
+
+typedef enum ARMEncoding
+{
+ eEncodingA1,
+ eEncodingA2,
+ eEncodingA3,
+ eEncodingA4,
+ eEncodingA5,
+ eEncodingT1,
+ eEncodingT2,
+ eEncodingT3,
+ eEncodingT4,
+ eEncodingT5,
+} ARMEncoding;
+
+
+typedef struct ARMOpcode
+{
+ uint32_t mask;
+ uint32_t value;
+ uint32_t variants;
+ ARMEncoding encoding;
+ const char *name;
+ EmulateCallback callback;
+};
+
+static bool
+EmulateARMPushEncodingA1 (EmulateInstructionARM *emulator)
+{
+#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 = emulator->OpcodeAsUnsigned (&success);
+ if (!success)
+ return false;
+
+ if (emulator->ConditionPassed())
+ {
+ const uint32_t addr_byte_size = emulator->GetAddressByteSize();
+ const addr_t sp = emulator->ReadRegisterUnsigned (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, 0, &success);
+ if (!success)
+ return false;
+ const uint32_t registers = EmulateInstruction::UnsignedBits (opcode, 15, 0);
+ addr_t sp_offset = addr_byte_size * EmulateInstruction::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 (EmulateInstruction::BitIsSet (registers, 1u << 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 = emulator->ReadRegisterUnsigned(eRegisterKindDWARF, context.arg1, 0, &success);
+ if (!success)
+ return false;
+ if (!emulator->WriteMemoryUnsigned (context, addr, reg_value, addr_byte_size))
+ return false;
+ addr += addr_byte_size;
+ }
+ }
+
+ if (EmulateInstruction::BitIsSet (registers, 1u << 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 = emulator->ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 0, &success);
+ if (!success)
+ return false;
+ if (!emulator->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 (!emulator->WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset))
+ return false;
+ }
+ return true;
+}
+
+static ARMOpcode g_arm_opcodes[] =
+{
+ { 0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, "PUSH<c> <registers>", EmulateARMPushEncodingA1 }
+};
+
+static const size_t k_num_arm_opcodes = sizeof(g_arm_opcodes)/sizeof(ARMOpcode);
+
+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::CurrentCond ()
+{
+ switch (m_inst_mode)
+ {
+ default:
+ case eModeInvalid:
+ break;
+
+ case eModeARM:
+ return UnsignedBits(m_inst.opcode.inst32, 31, 28);
+
+ case eModeThumb:
+ return 0x0000000Eu; // Return always for now, we need to handl IT instructions later
+ }
+ return UINT32_MAX; // Return invalid value
+}
+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;
+}
+
+
+bool
+EmulateInstructionARM::EvaluateInstruction ()
+{
+ return false;
+}
diff --git a/source/Plugins/Process/Utility/EmulateInstructionARM.h b/source/Plugins/Process/Utility/EmulateInstructionARM.h
new file mode 100644
index 0000000..5e592ae
--- /dev/null
+++ b/source/Plugins/Process/Utility/EmulateInstructionARM.h
@@ -0,0 +1,65 @@
+//===-- lldb_EmulateInstructionARM.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_EmulateInstructionARM_h_
+#define lldb_EmulateInstructionARM_h_
+
+#include "EmulateInstruction.h"
+#include "ARM_DWARF_Registers.h"
+
+namespace lldb_private {
+
+class EmulateInstructionARM : public EmulateInstruction
+{
+public:
+ enum Mode
+ {
+ eModeInvalid,
+ eModeARM,
+ eModeThumb
+ };
+
+ EmulateInstructionARM (void *baton,
+ ReadMemory read_mem_callback,
+ WriteMemory write_mem_callback,
+ ReadRegister read_reg_callback,
+ WriteRegister write_reg_callback) :
+ EmulateInstruction (lldb::eByteOrderLittle, // Byte order for ARM
+ 4, // Address size in byte
+ baton,
+ read_mem_callback,
+ write_mem_callback,
+ read_reg_callback,
+ write_reg_callback),
+ m_inst_mode (eModeInvalid)
+ {
+ }
+
+ virtual bool
+ ReadInstruction ();
+
+ virtual bool
+ EvaluateInstruction ();
+
+ bool
+ ConditionPassed ();
+
+ uint32_t
+ CurrentCond ();
+
+protected:
+
+ Mode m_inst_mode;
+ uint32_t m_inst_cpsr;
+
+};
+
+} // namespace lldb_private
+
+#endif // lldb_EmulateInstructionARM_h_