/* Common target dependent code for GDB on ARM systems.
- Copyright (C) 1988-2013 Free Software Foundation, Inc.
+ Copyright (C) 1988-2016 Free Software Foundation, Inc.
This file is part of GDB.
#include "frame.h"
#include "inferior.h"
+#include "infrun.h"
#include "gdbcmd.h"
#include "gdbcore.h"
-#include "gdb_string.h"
#include "dis-asm.h" /* For register styles. */
#include "regcache.h"
#include "reggroups.h"
#include "user-regs.h"
#include "observer.h"
+#include "arch/arm.h"
+#include "arch/arm-get-next-pcs.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
#include "coff/internal.h"
#include "elf/arm.h"
-#include "gdb_assert.h"
#include "vec.h"
#include "record.h"
struct regcache *regcache,
int regnum, const gdb_byte *buf);
-static int thumb_insn_size (unsigned short inst1);
+static CORE_ADDR
+ arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self);
+
+
+/* get_next_pcs operations. */
+static struct arm_get_next_pcs_ops arm_get_next_pcs_ops = {
+ arm_get_next_pcs_read_memory_unsigned_integer,
+ arm_get_next_pcs_syscall_next_pc,
+ arm_get_next_pcs_addr_bits_remove,
+ arm_get_next_pcs_is_thumb,
+ NULL,
+};
struct arm_prologue_cache
{
#define DISPLACED_STEPPING_ARCH_VERSION 5
-/* Addresses for calling Thumb functions have the bit 0 set.
- Here are some macros to test, set, or clear bit 0 of addresses. */
-#define IS_THUMB_ADDR(addr) ((addr) & 1)
-#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
-#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
-
/* Set to true if the 32-bit mode is in use. */
int arm_apcs_32 = 1;
return CPSR_T;
}
+/* Determine if the processor is currently executing in Thumb mode. */
+
+int
+arm_is_thumb (struct regcache *regcache)
+{
+ ULONGEST cpsr;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regcache));
+
+ cpsr = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM);
+
+ return (cpsr & t_bit) != 0;
+}
+
/* Determine if FRAME is executing in Thumb mode. */
int
0 };
unsigned int idx;
- data = objfile_data (sec->objfile, arm_objfile_data_key);
+ data = (struct arm_per_objfile *) objfile_data (sec->objfile,
+ arm_objfile_data_key);
if (data != NULL)
{
map = data->section_maps[sec->the_bfd_section->index];
int
arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
{
- struct minimal_symbol *sym;
+ struct bound_minimal_symbol sym;
char type;
struct displaced_step_closure* dsc
= get_displaced_step_closure_by_addr(memaddr);
/* Thumb functions have a "special" bit set in minimal symbols. */
sym = lookup_minimal_symbol_by_pc (memaddr);
- if (sym)
- return (MSYMBOL_IS_SPECIAL (sym));
+ if (sym.minsym)
+ return (MSYMBOL_IS_SPECIAL (sym.minsym));
/* If the user wants to override the fallback mode, let them. */
if (strcmp (arm_fallback_mode_string, "arm") == 0)
skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb)
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- struct minimal_symbol *msym;
+ struct bound_minimal_symbol msym;
msym = lookup_minimal_symbol_by_pc (pc);
- if (msym != NULL
- && SYMBOL_VALUE_ADDRESS (msym) == pc
- && SYMBOL_LINKAGE_NAME (msym) != NULL)
+ if (msym.minsym != NULL
+ && BMSYMBOL_VALUE_ADDRESS (msym) == pc
+ && MSYMBOL_LINKAGE_NAME (msym.minsym) != NULL)
{
- const char *name = SYMBOL_LINKAGE_NAME (msym);
+ const char *name = MSYMBOL_LINKAGE_NAME (msym.minsym);
/* The GNU linker's Thumb call stub to foo is named
__foo_from_thumb. */
/* On soft-float targets, __truncdfsf2 is called to convert promoted
arguments to their argument types in non-prototyped
functions. */
- if (strncmp (name, "__truncdfsf2", strlen ("__truncdfsf2")) == 0)
+ if (startswith (name, "__truncdfsf2"))
return 1;
- if (strncmp (name, "__aeabi_d2f", strlen ("__aeabi_d2f")) == 0)
+ if (startswith (name, "__aeabi_d2f"))
return 1;
/* Internal functions related to thread-local storage. */
- if (strncmp (name, "__tls_get_addr", strlen ("__tls_get_addr")) == 0)
+ if (startswith (name, "__tls_get_addr"))
return 1;
- if (strncmp (name, "__aeabi_read_tp", strlen ("__aeabi_read_tp")) == 0)
+ if (startswith (name, "__aeabi_read_tp"))
return 1;
}
else
return 0;
}
-/* Support routines for instruction parsing. */
-#define submask(x) ((1L << ((x) + 1)) - 1)
-#define bit(obj,st) (((obj) >> (st)) & 1)
-#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
-#define sbits(obj,st,fn) \
- ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
-#define BranchDest(addr,instr) \
- ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
-
/* Extract the immediate from instruction movw/movt of encoding T. INSN1 is
the first 16-bit of instruction, and INSN2 is the second 16-bit of
instruction. */
return (0x80 | (imm & 0x7f)) << (32 - count);
}
-/* Return 1 if the 16-bit Thumb instruction INST might change
- control flow, 0 otherwise. */
-
-static int
-thumb_instruction_changes_pc (unsigned short inst)
-{
- if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */
- return 1;
-
- if ((inst & 0xf000) == 0xd000) /* conditional branch */
- return 1;
-
- if ((inst & 0xf800) == 0xe000) /* unconditional branch */
- return 1;
-
- if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */
- return 1;
-
- if ((inst & 0xff87) == 0x4687) /* mov pc, REG */
- return 1;
-
- if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */
- return 1;
-
- return 0;
-}
-
-/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2
- might change control flow, 0 otherwise. */
+/* Return 1 if the 16-bit Thumb instruction INSN restores SP in
+ epilogue, 0 otherwise. */
static int
-thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
+thumb_instruction_restores_sp (unsigned short insn)
{
- if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
- {
- /* Branches and miscellaneous control instructions. */
-
- if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
- {
- /* B, BL, BLX. */
- return 1;
- }
- else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
- {
- /* SUBS PC, LR, #imm8. */
- return 1;
- }
- else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
- {
- /* Conditional branch. */
- return 1;
- }
-
- return 0;
- }
-
- if ((inst1 & 0xfe50) == 0xe810)
- {
- /* Load multiple or RFE. */
-
- if (bit (inst1, 7) && !bit (inst1, 8))
- {
- /* LDMIA or POP */
- if (bit (inst2, 15))
- return 1;
- }
- else if (!bit (inst1, 7) && bit (inst1, 8))
- {
- /* LDMDB */
- if (bit (inst2, 15))
- return 1;
- }
- else if (bit (inst1, 7) && bit (inst1, 8))
- {
- /* RFEIA */
- return 1;
- }
- else if (!bit (inst1, 7) && !bit (inst1, 8))
- {
- /* RFEDB */
- return 1;
- }
-
- return 0;
- }
-
- if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
- {
- /* MOV PC or MOVS PC. */
- return 1;
- }
-
- if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
- {
- /* LDR PC. */
- if (bits (inst1, 0, 3) == 15)
- return 1;
- if (bit (inst1, 7))
- return 1;
- if (bit (inst2, 11))
- return 1;
- if ((inst2 & 0x0fc0) == 0x0000)
- return 1;
-
- return 0;
- }
-
- if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
- {
- /* TBB. */
- return 1;
- }
-
- if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
- {
- /* TBH. */
- return 1;
- }
-
- return 0;
+ return (insn == 0x46bd /* mov sp, r7 */
+ || (insn & 0xff80) == 0xb000 /* add sp, imm */
+ || (insn & 0xfe00) == 0xbc00); /* pop <registers> */
}
/* Analyze a Thumb prologue, looking for a recognizable stack frame
pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]);
}
}
- else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR
- sub sp, #simm */
+ else if ((insn & 0xff80) == 0xb080) /* sub sp, #imm */
{
offset = (insn & 0x7f) << 2; /* get scaled offset */
- if (insn & 0x80) /* Check for SUB. */
- regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
- -offset);
- else
- regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
- offset);
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
+ -offset);
+ }
+ else if (thumb_instruction_restores_sp (insn))
+ {
+ /* Don't scan past the epilogue. */
+ break;
}
else if ((insn & 0xf800) == 0xa800) /* add Rd, sp, #imm */
regs[bits (insn, 8, 10)] = pv_add_constant (regs[ARM_SP_REGNUM],
unsigned int constant;
CORE_ADDR loc;
- offset = bits (insn, 0, 11);
+ offset = bits (inst2, 0, 11);
if (insn & 0x0080)
loc = start + 4 + offset;
else
unsigned int constant;
CORE_ADDR loc;
- offset = bits (insn, 0, 7) << 2;
+ offset = bits (inst2, 0, 7) << 2;
if (insn & 0x0080)
loc = start + 4 + offset;
else
{
*destreg = bits (insn1, 8, 10);
*offset = 2;
- address = bits (insn1, 0, 7);
+ address = (pc & 0xfffffffc) + 4 + (bits (insn1, 0, 7) << 2);
+ address = read_memory_unsigned_integer (address, 4,
+ byte_order_for_code);
}
else if ((insn1 & 0xfbf0) == 0xf240) /* movw Rd, #const */
{
unsigned int insn
= read_memory_unsigned_integer (pc, 4, byte_order_for_code);
- if ((insn & 0x0e5f0000) == 0x041f0000) /* ldr Rd, #immed */
+ if ((insn & 0x0e5f0000) == 0x041f0000) /* ldr Rd, [PC, #immed] */
{
- address = bits (insn, 0, 11);
+ address = bits (insn, 0, 11) + pc + 8;
+ address = read_memory_unsigned_integer (address, 4,
+ byte_order_for_code);
+
*destreg = bits (insn, 12, 15);
*offset = 4;
}
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned int basereg;
- struct minimal_symbol *stack_chk_guard;
+ struct bound_minimal_symbol stack_chk_guard;
int offset;
int is_thumb = arm_pc_is_thumb (gdbarch, pc);
CORE_ADDR addr;
return pc;
stack_chk_guard = lookup_minimal_symbol_by_pc (addr);
- /* If name of symbol doesn't start with '__stack_chk_guard', this
- instruction sequence is not for stack protector. If symbol is
- removed, we conservatively think this sequence is for stack protector. */
- if (stack_chk_guard
- && strncmp (SYMBOL_LINKAGE_NAME (stack_chk_guard), "__stack_chk_guard",
- strlen ("__stack_chk_guard")) != 0)
+ /* ADDR must correspond to a symbol whose name is __stack_chk_guard.
+ Otherwise, this sequence cannot be for stack protector. */
+ if (stack_chk_guard.minsym == NULL
+ || !startswith (MSYMBOL_LINKAGE_NAME (stack_chk_guard.minsym), "__stack_chk_guard"))
return pc;
if (is_thumb)
static CORE_ADDR
arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
{
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long inst;
- CORE_ADDR skip_pc;
CORE_ADDR func_addr, limit_pc;
/* See if we can determine the end of the prologue via the symbol table.
{
CORE_ADDR post_prologue_pc
= skip_prologue_using_sal (gdbarch, func_addr);
- struct symtab *s = find_pc_symtab (func_addr);
+ struct compunit_symtab *cust = find_pc_compunit_symtab (func_addr);
if (post_prologue_pc)
post_prologue_pc
will have producer information for most binaries; if it is
missing (e.g. for -gstabs), assuming the GNU tools. */
if (post_prologue_pc
- && (s == NULL
- || s->producer == NULL
- || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0
- || strncmp (s->producer, "clang ", sizeof ("clang ") - 1) == 0))
+ && (cust == NULL
+ || COMPUNIT_PRODUCER (cust) == NULL
+ || startswith (COMPUNIT_PRODUCER (cust), "GNU ")
+ || startswith (COMPUNIT_PRODUCER (cust), "clang ")))
return post_prologue_pc;
if (post_prologue_pc != 0)
/* Check if this is Thumb code. */
if (arm_pc_is_thumb (gdbarch, pc))
return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL);
-
- for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4)
- {
- inst = read_memory_unsigned_integer (skip_pc, 4, byte_order_for_code);
-
- /* "mov ip, sp" is no longer a required part of the prologue. */
- if (inst == 0xe1a0c00d) /* mov ip, sp */
- continue;
-
- if ((inst & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */
- continue;
-
- if ((inst & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */
- continue;
-
- /* Some prologues begin with "str lr, [sp, #-4]!". */
- if (inst == 0xe52de004) /* str lr, [sp, #-4]! */
- continue;
-
- if ((inst & 0xfffffff0) == 0xe92d0000) /* stmfd sp!,{a1,a2,a3,a4} */
- continue;
-
- if ((inst & 0xfffff800) == 0xe92dd800) /* stmfd sp!,{fp,ip,lr,pc} */
- continue;
-
- /* Any insns after this point may float into the code, if it makes
- for better instruction scheduling, so we skip them only if we
- find them, but still consider the function to be frame-ful. */
-
- /* We may have either one sfmfd instruction here, or several stfe
- insns, depending on the version of floating point code we
- support. */
- if ((inst & 0xffbf0fff) == 0xec2d0200) /* sfmfd fn, <cnt>, [sp]! */
- continue;
-
- if ((inst & 0xffff8fff) == 0xed6d0103) /* stfe fn, [sp, #-12]! */
- continue;
-
- if ((inst & 0xfffff000) == 0xe24cb000) /* sub fp, ip, #nn */
- continue;
-
- if ((inst & 0xfffff000) == 0xe24dd000) /* sub sp, sp, #nn */
- continue;
-
- if ((inst & 0xffffc000) == 0xe54b0000 /* strb r(0123),[r11,#-nn] */
- || (inst & 0xffffc0f0) == 0xe14b00b0 /* strh r(0123),[r11,#-nn] */
- || (inst & 0xffffc000) == 0xe50b0000) /* str r(0123),[r11,#-nn] */
- continue;
-
- if ((inst & 0xffffc000) == 0xe5cd0000 /* strb r(0123),[sp,#nn] */
- || (inst & 0xffffc0f0) == 0xe1cd00b0 /* strh r(0123),[sp,#nn] */
- || (inst & 0xffffc000) == 0xe58d0000) /* str r(0123),[sp,#nn] */
- continue;
-
- /* Un-recognized instruction; stop scanning. */
- break;
- }
-
- return skip_pc; /* End of prologue. */
+ else
+ return arm_analyze_prologue (gdbarch, pc, limit_pc, NULL);
}
/* *INDENT-OFF* */
thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
}
-/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
+/* Return 1 if the ARM instruction INSN restores SP in epilogue, 0
+ otherwise. */
static int
-arm_instruction_changes_pc (uint32_t this_instr)
+arm_instruction_restores_sp (unsigned int insn)
{
- if (bits (this_instr, 28, 31) == INST_NV)
- /* Unconditional instructions. */
- switch (bits (this_instr, 24, 27))
- {
- case 0xa:
- case 0xb:
- /* Branch with Link and change to Thumb. */
- return 1;
- case 0xc:
- case 0xd:
- case 0xe:
- /* Coprocessor register transfer. */
- if (bits (this_instr, 12, 15) == 15)
- error (_("Invalid update to pc in instruction"));
- return 0;
- default:
- return 0;
- }
- else
- switch (bits (this_instr, 25, 27))
- {
- case 0x0:
- if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
- {
- /* Multiplies and extra load/stores. */
- if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
- /* Neither multiplies nor extension load/stores are allowed
- to modify PC. */
- return 0;
-
- /* Otherwise, miscellaneous instructions. */
-
- /* BX <reg>, BXJ <reg>, BLX <reg> */
- if (bits (this_instr, 4, 27) == 0x12fff1
- || bits (this_instr, 4, 27) == 0x12fff2
- || bits (this_instr, 4, 27) == 0x12fff3)
- return 1;
-
- /* Other miscellaneous instructions are unpredictable if they
- modify PC. */
- return 0;
- }
- /* Data processing instruction. Fall through. */
-
- case 0x1:
- if (bits (this_instr, 12, 15) == 15)
- return 1;
- else
- return 0;
-
- case 0x2:
- case 0x3:
- /* Media instructions and architecturally undefined instructions. */
- if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
- return 0;
-
- /* Stores. */
- if (bit (this_instr, 20) == 0)
- return 0;
-
- /* Loads. */
- if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
- return 1;
- else
- return 0;
-
- case 0x4:
- /* Load/store multiple. */
- if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
- return 1;
- else
- return 0;
-
- case 0x5:
- /* Branch and branch with link. */
+ if (bits (insn, 28, 31) != INST_NV)
+ {
+ if ((insn & 0x0df0f000) == 0x0080d000
+ /* ADD SP (register or immediate). */
+ || (insn & 0x0df0f000) == 0x0040d000
+ /* SUB SP (register or immediate). */
+ || (insn & 0x0ffffff0) == 0x01a0d000
+ /* MOV SP. */
+ || (insn & 0x0fff0000) == 0x08bd0000
+ /* POP (LDMIA). */
+ || (insn & 0x0fff0000) == 0x049d0000)
+ /* POP of a single register. */
return 1;
+ }
- case 0x6:
- case 0x7:
- /* Coprocessor transfers or SWIs can not affect PC. */
- return 0;
-
- default:
- internal_error (__FILE__, __LINE__, _("bad value in switch"));
- }
+ return 0;
}
/* Analyze an ARM mode prologue starting at PROLOGUE_START and
CORE_ADDR prologue_start, CORE_ADDR prologue_end,
struct arm_prologue_cache *cache)
{
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
int regno;
CORE_ADDR offset, current_pc;
pv_t regs[ARM_FPS_REGNUM];
struct pv_area *stack;
struct cleanup *back_to;
- int framereg, framesize;
CORE_ADDR unrecognized_pc = 0;
/* Search the prologue looking for instructions that set up the
else if (arm_instruction_changes_pc (insn))
/* Don't scan past anything that might change control flow. */
break;
+ else if (arm_instruction_restores_sp (insn))
+ {
+ /* Don't scan past the epilogue. */
+ break;
+ }
else if ((insn & 0xfe500000) == 0xe8100000 /* ldm */
&& pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
/* Ignore block loads from the stack, potentially copying
continue;
else
{
- /* The optimizer might shove anything into the prologue,
- so we just skip what we don't recognize. */
+ /* The optimizer might shove anything into the prologue, if
+ we build up cache (cache != NULL) from scanning prologue,
+ we just skip what we don't recognize and scan further to
+ make cache as complete as possible. However, if we skip
+ prologue, we'll stop immediately on unrecognized
+ instruction. */
unrecognized_pc = current_pc;
- continue;
+ if (cache != NULL)
+ continue;
+ else
+ break;
}
}
if (unrecognized_pc == 0)
unrecognized_pc = current_pc;
- /* The frame size is just the distance from the frame register
- to the original stack pointer. */
- if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
- {
- /* Frame pointer is fp. */
- framereg = ARM_FP_REGNUM;
- framesize = -regs[ARM_FP_REGNUM].k;
- }
- else
- {
- /* Try the stack pointer... this is a bit desperate. */
- framereg = ARM_SP_REGNUM;
- framesize = -regs[ARM_SP_REGNUM].k;
- }
-
if (cache)
{
+ int framereg, framesize;
+
+ /* The frame size is just the distance from the frame register
+ to the original stack pointer. */
+ if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Frame pointer is fp. */
+ framereg = ARM_FP_REGNUM;
+ framesize = -regs[ARM_FP_REGNUM].k;
+ }
+ else
+ {
+ /* Try the stack pointer... this is a bit desperate. */
+ framereg = ARM_SP_REGNUM;
+ framesize = -regs[ARM_SP_REGNUM].k;
+ }
+
cache->framereg = framereg;
cache->framesize = framesize;
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- int regno;
- CORE_ADDR prologue_start, prologue_end, current_pc;
+ CORE_ADDR prologue_start, prologue_end;
CORE_ADDR prev_pc = get_frame_pc (this_frame);
CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
- pv_t regs[ARM_FPS_REGNUM];
- struct pv_area *stack;
- struct cleanup *back_to;
- CORE_ADDR offset;
/* Assume there is no frame until proven otherwise. */
cache->framereg = ARM_SP_REGNUM;
return cache;
}
+/* Implementation of the stop_reason hook for arm_prologue frames. */
+
+static enum unwind_stop_reason
+arm_prologue_unwind_stop_reason (struct frame_info *this_frame,
+ void **this_cache)
+{
+ struct arm_prologue_cache *cache;
+ CORE_ADDR pc;
+
+ if (*this_cache == NULL)
+ *this_cache = arm_make_prologue_cache (this_frame);
+ cache = (struct arm_prologue_cache *) *this_cache;
+
+ /* This is meant to halt the backtrace at "_start". */
+ pc = get_frame_pc (this_frame);
+ if (pc <= gdbarch_tdep (get_frame_arch (this_frame))->lowest_pc)
+ return UNWIND_OUTERMOST;
+
+ /* If we've hit a wall, stop. */
+ if (cache->prev_sp == 0)
+ return UNWIND_OUTERMOST;
+
+ return UNWIND_NO_REASON;
+}
+
/* Our frame ID for a normal frame is the current function's starting PC
and the caller's SP when we were called. */
if (*this_cache == NULL)
*this_cache = arm_make_prologue_cache (this_frame);
- cache = *this_cache;
-
- /* This is meant to halt the backtrace at "_start". */
- pc = get_frame_pc (this_frame);
- if (pc <= gdbarch_tdep (get_frame_arch (this_frame))->lowest_pc)
- return;
-
- /* If we've hit a wall, stop. */
- if (cache->prev_sp == 0)
- return;
+ cache = (struct arm_prologue_cache *) *this_cache;
/* Use function start address as part of the frame ID. If we cannot
identify the start address (due to missing symbol information),
fall back to just using the current PC. */
+ pc = get_frame_pc (this_frame);
func = get_frame_func (this_frame);
if (!func)
func = pc;
if (*this_cache == NULL)
*this_cache = arm_make_prologue_cache (this_frame);
- cache = *this_cache;
+ cache = (struct arm_prologue_cache *) *this_cache;
/* If we are asked to unwind the PC, then we need to return the LR
instead. The prologue may save PC, but it will point into this
struct frame_unwind arm_prologue_unwind = {
NORMAL_FRAME,
- default_frame_unwind_stop_reason,
+ arm_prologue_unwind_stop_reason,
arm_prologue_this_id,
arm_prologue_prev_register,
NULL,
static void
arm_exidx_data_free (struct objfile *objfile, void *arg)
{
- struct arm_exidx_data *data = arg;
+ struct arm_exidx_data *data = (struct arm_exidx_data *) arg;
unsigned int i;
for (i = 0; i < objfile->obfd->section_count; i++)
cleanups = make_cleanup (null_cleanup, NULL);
/* Read contents of exception table and index. */
- exidx = bfd_get_section_by_name (objfile->obfd, ".ARM.exidx");
+ exidx = bfd_get_section_by_name (objfile->obfd, ELF_STRING_ARM_unwind);
if (exidx)
{
exidx_vma = bfd_section_vma (objfile->obfd, exidx);
exidx_size = bfd_get_section_size (exidx);
- exidx_data = xmalloc (exidx_size);
+ exidx_data = (gdb_byte *) xmalloc (exidx_size);
make_cleanup (xfree, exidx_data);
if (!bfd_get_section_contents (objfile->obfd, exidx,
{
extab_vma = bfd_section_vma (objfile->obfd, extab);
extab_size = bfd_get_section_size (extab);
- extab_data = xmalloc (extab_size);
+ extab_data = (gdb_byte *) xmalloc (extab_size);
make_cleanup (xfree, extab_data);
if (!bfd_get_section_contents (objfile->obfd, extab,
extab section starting at ADDR. */
if (n_bytes || n_words)
{
- gdb_byte *p = entry = obstack_alloc (&objfile->objfile_obstack,
- n_bytes + n_words * 4 + 1);
+ gdb_byte *p = entry
+ = (gdb_byte *) obstack_alloc (&objfile->objfile_obstack,
+ n_bytes + n_words * 4 + 1);
while (n_bytes--)
*p++ = (gdb_byte) ((word >> (8 * n_bytes)) & 0xff);
struct arm_exidx_entry map_key = { memaddr - obj_section_addr (sec), 0 };
unsigned int idx;
- data = objfile_data (sec->objfile, arm_exidx_data_key);
+ data = ((struct arm_exidx_data *)
+ objfile_data (sec->objfile, arm_exidx_data_key));
if (data != NULL)
{
map = data->section_maps[sec->the_bfd_section->index];
arm_exidx_unwind_sniffer
};
+/* Recognize GCC's trampoline for thumb call-indirect. If we are in a
+ trampoline, return the target PC. Otherwise return 0.
+
+ void call0a (char c, short s, int i, long l) {}
+
+ int main (void)
+ {
+ (*pointer_to_call0a) (c, s, i, l);
+ }
+
+ Instead of calling a stub library function _call_via_xx (xx is
+ the register name), GCC may inline the trampoline in the object
+ file as below (register r2 has the address of call0a).
+
+ .global main
+ .type main, %function
+ ...
+ bl .L1
+ ...
+ .size main, .-main
+
+ .L1:
+ bx r2
+
+ The trampoline 'bx r2' doesn't belong to main. */
+
+static CORE_ADDR
+arm_skip_bx_reg (struct frame_info *frame, CORE_ADDR pc)
+{
+ /* The heuristics of recognizing such trampoline is that FRAME is
+ executing in Thumb mode and the instruction on PC is 'bx Rm'. */
+ if (arm_frame_is_thumb (frame))
+ {
+ gdb_byte buf[2];
+
+ if (target_read_memory (pc, buf, 2) == 0)
+ {
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ enum bfd_endian byte_order_for_code
+ = gdbarch_byte_order_for_code (gdbarch);
+ uint16_t insn
+ = extract_unsigned_integer (buf, 2, byte_order_for_code);
+
+ if ((insn & 0xff80) == 0x4700) /* bx <Rm> */
+ {
+ CORE_ADDR dest
+ = get_frame_register_unsigned (frame, bits (insn, 3, 6));
+
+ /* Clear the LSB so that gdb core sets step-resume
+ breakpoint at the right address. */
+ return UNMAKE_THUMB_ADDR (dest);
+ }
+ }
+ }
+
+ return 0;
+}
+
static struct arm_prologue_cache *
arm_make_stub_cache (struct frame_info *this_frame)
{
if (*this_cache == NULL)
*this_cache = arm_make_stub_cache (this_frame);
- cache = *this_cache;
+ cache = (struct arm_prologue_cache *) *this_cache;
*this_id = frame_id_build (cache->prev_sp, get_frame_pc (this_frame));
}
void **this_prologue_cache)
{
CORE_ADDR addr_in_block;
- char dummy[4];
+ gdb_byte dummy[4];
+ CORE_ADDR pc, start_addr;
+ const char *name;
addr_in_block = get_frame_address_in_block (this_frame);
- if (in_plt_section (addr_in_block, NULL)
+ pc = get_frame_pc (this_frame);
+ if (in_plt_section (addr_in_block)
/* We also use the stub winder if the target memory is unreadable
to avoid having the prologue unwinder trying to read it. */
- || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
+ || target_read_memory (pc, dummy, 4) != 0)
+ return 1;
+
+ if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0
+ && arm_skip_bx_reg (this_frame, pc) != 0)
return 1;
return 0;
if (*this_cache == NULL)
*this_cache = arm_m_exception_cache (this_frame);
- cache = *this_cache;
+ cache = (struct arm_prologue_cache *) *this_cache;
/* Our frame ID for a stub frame is the current SP and LR. */
*this_id = frame_id_build (cache->prev_sp,
void **this_cache,
int prev_regnum)
{
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct arm_prologue_cache *cache;
if (*this_cache == NULL)
*this_cache = arm_m_exception_cache (this_frame);
- cache = *this_cache;
+ cache = (struct arm_prologue_cache *) *this_cache;
/* The value was already reconstructed into PREV_SP. */
if (prev_regnum == ARM_SP_REGNUM)
if (*this_cache == NULL)
*this_cache = arm_make_prologue_cache (this_frame);
- cache = *this_cache;
+ cache = (struct arm_prologue_cache *) *this_cache;
return cache->prev_sp - cache->framesize;
}
}
}
-/* Return true if we are in the function's epilogue, i.e. after the
- instruction that destroyed the function's stack frame. */
+/* Implement the stack_frame_destroyed_p gdbarch method. */
static int
-thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+thumb_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned int insn, insn2;
found_return = 1;
else if (insn == 0x46f7) /* mov pc, lr */
found_return = 1;
- else if (insn == 0x46bd) /* mov sp, r7 */
- found_stack_adjust = 1;
- else if ((insn & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */
- found_stack_adjust = 1;
- else if ((insn & 0xfe00) == 0xbc00) /* pop <registers> */
- {
- found_stack_adjust = 1;
- if (insn & 0x0100) /* <registers> include PC. */
+ else if (thumb_instruction_restores_sp (insn))
+ {
+ if ((insn & 0xff00) == 0xbd00) /* pop <registers, PC> */
found_return = 1;
}
else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instruction */
if (insn == 0xe8bd) /* ldm.w sp!, <registers> */
{
- found_stack_adjust = 1;
if (insn2 & 0x8000) /* <registers> include PC. */
found_return = 1;
}
else if (insn == 0xf85d /* ldr.w <Rt>, [sp], #4 */
&& (insn2 & 0x0fff) == 0x0b04)
{
- found_stack_adjust = 1;
if ((insn2 & 0xf000) == 0xf000) /* <Rt> is PC. */
found_return = 1;
}
else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, <list> */
&& (insn2 & 0x0e00) == 0x0a00)
- found_stack_adjust = 1;
+ ;
else
break;
}
a 32-bit instruction. This is just a heuristic, so we do not worry
too much about false positives. */
- if (!found_stack_adjust)
- {
- if (pc - 4 < func_start)
- return 0;
- if (target_read_memory (pc - 4, buf, 4))
- return 0;
+ if (pc - 4 < func_start)
+ return 0;
+ if (target_read_memory (pc - 4, buf, 4))
+ return 0;
- insn = extract_unsigned_integer (buf, 2, byte_order_for_code);
- insn2 = extract_unsigned_integer (buf + 2, 2, byte_order_for_code);
-
- if (insn2 == 0x46bd) /* mov sp, r7 */
- found_stack_adjust = 1;
- else if ((insn2 & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */
- found_stack_adjust = 1;
- else if ((insn2 & 0xff00) == 0xbc00) /* pop <registers> without PC */
- found_stack_adjust = 1;
- else if (insn == 0xe8bd) /* ldm.w sp!, <registers> */
- found_stack_adjust = 1;
- else if (insn == 0xf85d /* ldr.w <Rt>, [sp], #4 */
- && (insn2 & 0x0fff) == 0x0b04)
- found_stack_adjust = 1;
- else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, <list> */
- && (insn2 & 0x0e00) == 0x0a00)
- found_stack_adjust = 1;
- }
+ insn = extract_unsigned_integer (buf, 2, byte_order_for_code);
+ insn2 = extract_unsigned_integer (buf + 2, 2, byte_order_for_code);
+
+ if (thumb_instruction_restores_sp (insn2))
+ found_stack_adjust = 1;
+ else if (insn == 0xe8bd) /* ldm.w sp!, <registers> */
+ found_stack_adjust = 1;
+ else if (insn == 0xf85d /* ldr.w <Rt>, [sp], #4 */
+ && (insn2 & 0x0fff) == 0x0b04)
+ found_stack_adjust = 1;
+ else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, <list> */
+ && (insn2 & 0x0e00) == 0x0a00)
+ found_stack_adjust = 1;
return found_stack_adjust;
}
-/* Return true if we are in the function's epilogue, i.e. after the
- instruction that destroyed the function's stack frame. */
-
static int
-arm_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+arm_stack_frame_destroyed_p_1 (struct gdbarch *gdbarch, CORE_ADDR pc)
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned int insn;
- int found_return, found_stack_adjust;
+ int found_return;
CORE_ADDR func_start, func_end;
- if (arm_pc_is_thumb (gdbarch, pc))
- return thumb_in_function_epilogue_p (gdbarch, pc);
-
if (!find_pc_partial_function (pc, NULL, &func_start, &func_end))
return 0;
if (pc < func_start + 4)
return 0;
- found_stack_adjust = 0;
insn = read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code);
- if (bits (insn, 28, 31) != INST_NV)
- {
- if ((insn & 0x0df0f000) == 0x0080d000)
- /* ADD SP (register or immediate). */
- found_stack_adjust = 1;
- else if ((insn & 0x0df0f000) == 0x0040d000)
- /* SUB SP (register or immediate). */
- found_stack_adjust = 1;
- else if ((insn & 0x0ffffff0) == 0x01a0d000)
- /* MOV SP. */
- found_stack_adjust = 1;
- else if ((insn & 0x0fff0000) == 0x08bd0000)
- /* POP (LDMIA). */
- found_stack_adjust = 1;
- else if ((insn & 0x0fff0000) == 0x049d0000)
- /* POP of a single register. */
- found_stack_adjust = 1;
- }
-
- if (found_stack_adjust)
+ if (arm_instruction_restores_sp (insn))
return 1;
return 0;
}
+/* Implement the stack_frame_destroyed_p gdbarch method. */
+
+static int
+arm_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ if (arm_pc_is_thumb (gdbarch, pc))
+ return thumb_stack_frame_destroyed_p (gdbarch, pc);
+ else
+ return arm_stack_frame_destroyed_p_1 (gdbarch, pc);
+}
/* When arguments must be pushed onto the stack, they go on in reverse
order. The code below implements a FILO (stack) to do this. */
{
int len;
struct stack_item *prev;
- void *data;
+ gdb_byte *data;
};
static struct stack_item *
-push_stack_item (struct stack_item *prev, const void *contents, int len)
+push_stack_item (struct stack_item *prev, const gdb_byte *contents, int len)
{
struct stack_item *si;
- si = xmalloc (sizeof (struct stack_item));
- si->data = xmalloc (len);
+ si = XNEW (struct stack_item);
+ si->data = (gdb_byte *) xmalloc (len);
si->len = len;
si->prev = prev;
memcpy (si->data, contents, len);
return TYPE_LENGTH (t);
case TYPE_CODE_ARRAY:
+ if (TYPE_VECTOR (t))
+ {
+ /* Use the natural alignment for vector types (the same for
+ scalar type), but the maximum alignment is 64-bit. */
+ if (TYPE_LENGTH (t) > 8)
+ return 8;
+ else
+ return TYPE_LENGTH (t);
+ }
+ else
+ return arm_type_align (TYPE_TARGET_TYPE (t));
case TYPE_CODE_COMPLEX:
- /* TODO: What about vector types? */
return arm_type_align (TYPE_TARGET_TYPE (t));
case TYPE_CODE_STRUCT:
classified from *BASE_TYPE, or two types differently classified
from each other, return -1, otherwise return the total number of
base-type elements found (possibly 0 in an empty structure or
- array). Vectors and complex types are not currently supported,
- matching the generic AAPCS support. */
+ array). Vector types are not currently supported, matching the
+ generic AAPCS support. */
static int
arm_vfp_cprc_sub_candidate (struct type *t,
}
break;
+ case TYPE_CODE_COMPLEX:
+ /* Arguments of complex T where T is one of the types float or
+ double get treated as if they are implemented as:
+
+ struct complexT
+ {
+ T real;
+ T imag;
+ };
+
+ */
+ switch (TYPE_LENGTH (t))
+ {
+ case 8:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_SINGLE;
+ else if (*base_type != VFP_CPRC_SINGLE)
+ return -1;
+ return 2;
+
+ case 16:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_DOUBLE;
+ else if (*base_type != VFP_CPRC_DOUBLE)
+ return -1;
+ return 2;
+
+ default:
+ return -1;
+ }
+ break;
+
case TYPE_CODE_ARRAY:
{
- int count;
- unsigned unitlen;
- count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t), base_type);
- if (count == -1)
- return -1;
- if (TYPE_LENGTH (t) == 0)
+ if (TYPE_VECTOR (t))
{
- gdb_assert (count == 0);
- return 0;
+ /* A 64-bit or 128-bit containerized vector type are VFP
+ CPRCs. */
+ switch (TYPE_LENGTH (t))
+ {
+ case 8:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_VEC64;
+ return 1;
+ case 16:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_VEC128;
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ else
+ {
+ int count;
+ unsigned unitlen;
+
+ count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t),
+ base_type);
+ if (count == -1)
+ return -1;
+ if (TYPE_LENGTH (t) == 0)
+ {
+ gdb_assert (count == 0);
+ return 0;
+ }
+ else if (count == 0)
+ return -1;
+ unitlen = arm_vfp_cprc_unit_length (*base_type);
+ gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0);
+ return TYPE_LENGTH (t) / unitlen;
}
- else if (count == 0)
- return -1;
- unitlen = arm_vfp_cprc_unit_length (*base_type);
- gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0);
- return TYPE_LENGTH (t) / unitlen;
}
break;
CORE_ADDR regval = extract_unsigned_integer (val, len, byte_order);
if (arm_pc_is_thumb (gdbarch, regval))
{
- bfd_byte *copy = alloca (len);
+ bfd_byte *copy = (bfd_byte *) alloca (len);
store_unsigned_integer (copy, len, byte_order,
MAKE_THUMB_ADDR (regval));
val = copy;
while (len > 0)
{
int partial_len = len < INT_REGISTER_SIZE ? len : INT_REGISTER_SIZE;
+ CORE_ADDR regval
+ = extract_unsigned_integer (val, partial_len, byte_order);
if (may_use_core_reg && argreg <= ARM_LAST_ARG_REGNUM)
{
/* The argument is being passed in a general purpose
register. */
- CORE_ADDR regval
- = extract_unsigned_integer (val, partial_len, byte_order);
if (byte_order == BFD_ENDIAN_BIG)
regval <<= (INT_REGISTER_SIZE - partial_len) * 8;
if (arm_debug)
}
else
{
+ gdb_byte buf[INT_REGISTER_SIZE];
+
+ memset (buf, 0, sizeof (buf));
+ store_unsigned_integer (buf, partial_len, byte_order, regval);
+
/* Push the arguments onto the stack. */
if (arm_debug)
fprintf_unfiltered (gdb_stdlog, "arg %d @ sp + %d\n",
argnum, nstack);
- si = push_stack_item (si, val, INT_REGISTER_SIZE);
+ si = push_stack_item (si, buf, INT_REGISTER_SIZE);
nstack += INT_REGISTER_SIZE;
}
}
static void
-print_fpu_flags (int flags)
+print_fpu_flags (struct ui_file *file, int flags)
{
if (flags & (1 << 0))
- fputs ("IVO ", stdout);
+ fputs_filtered ("IVO ", file);
if (flags & (1 << 1))
- fputs ("DVZ ", stdout);
+ fputs_filtered ("DVZ ", file);
if (flags & (1 << 2))
- fputs ("OFL ", stdout);
+ fputs_filtered ("OFL ", file);
if (flags & (1 << 3))
- fputs ("UFL ", stdout);
+ fputs_filtered ("UFL ", file);
if (flags & (1 << 4))
- fputs ("INX ", stdout);
- putchar ('\n');
+ fputs_filtered ("INX ", file);
+ fputc_filtered ('\n', file);
}
/* Print interesting information about the floating point processor
type = (status >> 24) & 127;
if (status & (1 << 31))
- printf (_("Hardware FPU type %d\n"), type);
+ fprintf_filtered (file, _("Hardware FPU type %d\n"), type);
else
- printf (_("Software FPU type %d\n"), type);
+ fprintf_filtered (file, _("Software FPU type %d\n"), type);
/* i18n: [floating point unit] mask */
- fputs (_("mask: "), stdout);
- print_fpu_flags (status >> 16);
+ fputs_filtered (_("mask: "), file);
+ print_fpu_flags (file, status >> 16);
/* i18n: [floating point unit] flags */
- fputs (_("flags: "), stdout);
- print_fpu_flags (status);
+ fputs_filtered (_("flags: "), file);
+ print_fpu_flags (file, status);
}
/* Construct the ARM extended floating point type. */
&d, dbl);
}
-static int
-condition_true (unsigned long cond, unsigned long status_reg)
-{
- if (cond == INST_AL || cond == INST_NV)
- return 1;
-
- switch (cond)
- {
- case INST_EQ:
- return ((status_reg & FLAG_Z) != 0);
- case INST_NE:
- return ((status_reg & FLAG_Z) == 0);
- case INST_CS:
- return ((status_reg & FLAG_C) != 0);
- case INST_CC:
- return ((status_reg & FLAG_C) == 0);
- case INST_MI:
- return ((status_reg & FLAG_N) != 0);
- case INST_PL:
- return ((status_reg & FLAG_N) == 0);
- case INST_VS:
- return ((status_reg & FLAG_V) != 0);
- case INST_VC:
- return ((status_reg & FLAG_V) == 0);
- case INST_HI:
- return ((status_reg & (FLAG_C | FLAG_Z)) == FLAG_C);
- case INST_LS:
- return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C);
- case INST_GE:
- return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0));
- case INST_LT:
- return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
- case INST_GT:
- return (((status_reg & FLAG_Z) == 0)
- && (((status_reg & FLAG_N) == 0)
- == ((status_reg & FLAG_V) == 0)));
- case INST_LE:
- return (((status_reg & FLAG_Z) != 0)
- || (((status_reg & FLAG_N) == 0)
- != ((status_reg & FLAG_V) == 0)));
- }
- return 1;
-}
+/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
+ of the appropriate mode (as encoded in the PC value), even if this
+ differs from what would be expected according to the symbol tables. */
-static unsigned long
-shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
- unsigned long pc_val, unsigned long status_reg)
+void
+arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
+ struct address_space *aspace,
+ CORE_ADDR pc)
{
- unsigned long res, shift;
- int rm = bits (inst, 0, 3);
- unsigned long shifttype = bits (inst, 5, 6);
+ struct cleanup *old_chain
+ = make_cleanup_restore_integer (&arm_override_mode);
- if (bit (inst, 4))
- {
- int rs = bits (inst, 8, 11);
- shift = (rs == 15 ? pc_val + 8
- : get_frame_register_unsigned (frame, rs)) & 0xFF;
- }
- else
- shift = bits (inst, 7, 11);
+ arm_override_mode = IS_THUMB_ADDR (pc);
+ pc = gdbarch_addr_bits_remove (gdbarch, pc);
- res = (rm == ARM_PC_REGNUM
- ? (pc_val + (bit (inst, 4) ? 12 : 8))
- : get_frame_register_unsigned (frame, rm));
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
- switch (shifttype)
- {
- case 0: /* LSL */
- res = shift >= 32 ? 0 : res << shift;
- break;
+ do_cleanups (old_chain);
+}
- case 1: /* LSR */
- res = shift >= 32 ? 0 : res >> shift;
- break;
+/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
+ the buffer to be NEW_LEN bytes ending at ENDADDR. Return
+ NULL if an error occurs. BUF is freed. */
- case 2: /* ASR */
- if (shift >= 32)
- shift = 31;
- res = ((res & 0x80000000L)
- ? ~((~res) >> shift) : res >> shift);
- break;
+static gdb_byte *
+extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
+ int old_len, int new_len)
+{
+ gdb_byte *new_buf;
+ int bytes_to_read = new_len - old_len;
- case 3: /* ROR/RRX */
- shift &= 31;
- if (shift == 0)
- res = (res >> 1) | (carry ? 0x80000000L : 0);
- else
- res = (res >> shift) | (res << (32 - shift));
- break;
+ new_buf = (gdb_byte *) xmalloc (new_len);
+ memcpy (new_buf + bytes_to_read, buf, old_len);
+ xfree (buf);
+ if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+ {
+ xfree (new_buf);
+ return NULL;
}
-
- return res & 0xffffffff;
+ return new_buf;
}
-/* Return number of 1-bits in VAL. */
+/* An IT block is at most the 2-byte IT instruction followed by
+ four 4-byte instructions. The furthest back we must search to
+ find an IT block that affects the current instruction is thus
+ 2 + 3 * 4 == 14 bytes. */
+#define MAX_IT_BLOCK_PREFIX 14
+
+/* Use a quick scan if there are more than this many bytes of
+ code. */
+#define IT_SCAN_THRESHOLD 32
-static int
-bitcount (unsigned long val)
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+ A breakpoint in an IT block may not be hit, depending on the
+ condition flags. */
+static CORE_ADDR
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
{
- int nbits;
- for (nbits = 0; val != 0; nbits++)
- val &= val - 1; /* Delete rightmost 1-bit in val. */
- return nbits;
-}
+ gdb_byte *buf;
+ char map_type;
+ CORE_ADDR boundary, func_start;
+ int buf_len;
+ enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+ int i, any, last_it, last_it_count;
-/* Return the size in bytes of the complete Thumb instruction whose
- first halfword is INST1. */
+ /* If we are using BKPT breakpoints, none of this is necessary. */
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+ return bpaddr;
-static int
-thumb_insn_size (unsigned short inst1)
-{
- if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
- return 4;
- else
- return 2;
-}
+ /* ARM mode does not have this problem. */
+ if (!arm_pc_is_thumb (gdbarch, bpaddr))
+ return bpaddr;
-static int
-thumb_advance_itstate (unsigned int itstate)
-{
- /* Preserve IT[7:5], the first three bits of the condition. Shift
- the upcoming condition flags left by one bit. */
- itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
+ /* We are setting a breakpoint in Thumb code that could potentially
+ contain an IT block. The first step is to find how much Thumb
+ code there is; we do not need to read outside of known Thumb
+ sequences. */
+ map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+ if (map_type == 0)
+ /* Thumb-2 code must have mapping symbols to have a chance. */
+ return bpaddr;
- /* If we have finished the IT block, clear the state. */
- if ((itstate & 0x0f) == 0)
- itstate = 0;
+ bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
- return itstate;
-}
+ if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+ && func_start > boundary)
+ boundary = func_start;
-/* Find the next PC after the current instruction executes. In some
- cases we can not statically determine the answer (see the IT state
- handling in this function); in that case, a breakpoint may be
- inserted in addition to the returned PC, which will be used to set
- another breakpoint by our caller. */
+ /* Search for a candidate IT instruction. We have to do some fancy
+ footwork to distinguish a real IT instruction from the second
+ half of a 32-bit instruction, but there is no need for that if
+ there's no candidate. */
+ buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+ if (buf_len == 0)
+ /* No room for an IT instruction. */
+ return bpaddr;
-static CORE_ADDR
-thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
- unsigned short inst1;
- CORE_ADDR nextpc = pc + 2; /* Default is next instruction. */
- unsigned long offset;
- ULONGEST status, itstate;
-
- nextpc = MAKE_THUMB_ADDR (nextpc);
- pc_val = MAKE_THUMB_ADDR (pc_val);
-
- inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
-
- /* Thumb-2 conditional execution support. There are eight bits in
- the CPSR which describe conditional execution state. Once
- reconstructed (they're in a funny order), the low five bits
- describe the low bit of the condition for each instruction and
- how many instructions remain. The high three bits describe the
- base condition. One of the low four bits will be set if an IT
- block is active. These bits read as zero on earlier
- processors. */
- status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
- itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
-
- /* If-Then handling. On GNU/Linux, where this routine is used, we
- use an undefined instruction as a breakpoint. Unlike BKPT, IT
- can disable execution of the undefined instruction. So we might
- miss the breakpoint if we set it on a skipped conditional
- instruction. Because conditional instructions can change the
- flags, affecting the execution of further instructions, we may
- need to set two breakpoints. */
-
- if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
+ buf = (gdb_byte *) xmalloc (buf_len);
+ if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+ return bpaddr;
+ any = 0;
+ for (i = 0; i < buf_len; i += 2)
{
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
{
- /* An IT instruction. Because this instruction does not
- modify the flags, we can accurately predict the next
- executed instruction. */
- itstate = inst1 & 0x00ff;
- pc += thumb_insn_size (inst1);
+ any = 1;
+ break;
+ }
+ }
- while (itstate != 0 && ! condition_true (itstate >> 4, status))
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
+ if (any == 0)
+ {
+ xfree (buf);
+ return bpaddr;
+ }
- return MAKE_THUMB_ADDR (pc);
- }
- else if (itstate != 0)
- {
- /* We are in a conditional block. Check the condition. */
- if (! condition_true (itstate >> 4, status))
- {
- /* Advance to the next executed instruction. */
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
+ /* OK, the code bytes before this instruction contain at least one
+ halfword which resembles an IT instruction. We know that it's
+ Thumb code, but there are still two possibilities. Either the
+ halfword really is an IT instruction, or it is the second half of
+ a 32-bit Thumb instruction. The only way we can tell is to
+ scan forwards from a known instruction boundary. */
+ if (bpaddr - boundary > IT_SCAN_THRESHOLD)
+ {
+ int definite;
- while (itstate != 0 && ! condition_true (itstate >> 4, status))
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
+ /* There's a lot of code before this instruction. Start with an
+ optimistic search; it's easy to recognize halfwords that can
+ not be the start of a 32-bit instruction, and use that to
+ lock on to the instruction boundaries. */
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len, IT_SCAN_THRESHOLD);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = IT_SCAN_THRESHOLD;
- return MAKE_THUMB_ADDR (pc);
- }
- else if ((itstate & 0x0f) == 0x08)
+ definite = 0;
+ for (i = 0; i < buf_len - sizeof (buf) && ! definite; i += 2)
+ {
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ if (thumb_insn_size (inst1) == 2)
{
- /* This is the last instruction of the conditional
- block, and it is executed. We can handle it normally
- because the following instruction is not conditional,
- and we must handle it normally because it is
- permitted to branch. Fall through. */
+ definite = 1;
+ break;
}
- else
- {
- int cond_negated;
-
- /* There are conditional instructions after this one.
- If this instruction modifies the flags, then we can
- not predict what the next executed instruction will
- be. Fortunately, this instruction is architecturally
- forbidden to branch; we know it will fall through.
- Start by skipping past it. */
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
-
- /* Set a breakpoint on the following instruction. */
- gdb_assert ((itstate & 0x0f) != 0);
- arm_insert_single_step_breakpoint (gdbarch, aspace,
- MAKE_THUMB_ADDR (pc));
- cond_negated = (itstate >> 4) & 1;
-
- /* Skip all following instructions with the same
- condition. If there is a later instruction in the IT
- block with the opposite condition, set the other
- breakpoint there. If not, then set a breakpoint on
- the instruction after the IT block. */
- do
- {
- inst1 = read_memory_unsigned_integer (pc, 2,
- byte_order_for_code);
- pc += thumb_insn_size (inst1);
- itstate = thumb_advance_itstate (itstate);
- }
- while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
+ }
- return MAKE_THUMB_ADDR (pc);
- }
+ /* At this point, if DEFINITE, BUF[I] is the first place we
+ are sure that we know the instruction boundaries, and it is far
+ enough from BPADDR that we could not miss an IT instruction
+ affecting BPADDR. If ! DEFINITE, give up - start from a
+ known boundary. */
+ if (! definite)
+ {
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len,
+ bpaddr - boundary);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = bpaddr - boundary;
+ i = 0;
}
}
- else if (itstate & 0x0f)
+ else
{
- /* We are in a conditional block. Check the condition. */
- int cond = itstate >> 4;
-
- if (! condition_true (cond, status))
- /* Advance to the next instruction. All the 32-bit
- instructions share a common prefix. */
- return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1));
-
- /* Otherwise, handle the instruction normally. */
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = bpaddr - boundary;
+ i = 0;
}
- if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
- {
- CORE_ADDR sp;
-
- /* Fetch the saved PC from the stack. It's stored above
- all of the other registers. */
- offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
- sp = get_frame_register_unsigned (frame, ARM_SP_REGNUM);
- nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order);
- }
- else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
+ /* Scan forwards. Find the last IT instruction before BPADDR. */
+ last_it = -1;
+ last_it_count = 0;
+ while (i < buf_len)
{
- unsigned long cond = bits (inst1, 8, 11);
- if (cond == 0x0f) /* 0x0f = SWI */
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ last_it_count--;
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
{
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
-
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
-
+ last_it = i;
+ if (inst1 & 0x0001)
+ last_it_count = 4;
+ else if (inst1 & 0x0002)
+ last_it_count = 3;
+ else if (inst1 & 0x0004)
+ last_it_count = 2;
+ else
+ last_it_count = 1;
}
- else if (cond != 0x0f && condition_true (cond, status))
- nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
+ i += thumb_insn_size (inst1);
}
- else if ((inst1 & 0xf800) == 0xe000) /* unconditional branch */
- {
- nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
- }
- else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */
- {
- unsigned short inst2;
- inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code);
-
- /* Default to the next instruction. */
- nextpc = pc + 4;
- nextpc = MAKE_THUMB_ADDR (nextpc);
-
- if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
- {
- /* Branches and miscellaneous control instructions. */
-
- if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
- {
- /* B, BL, BLX. */
- int j1, j2, imm1, imm2;
- imm1 = sbits (inst1, 0, 10);
- imm2 = bits (inst2, 0, 10);
- j1 = bit (inst2, 13);
- j2 = bit (inst2, 11);
-
- offset = ((imm1 << 12) + (imm2 << 1));
- offset ^= ((!j2) << 22) | ((!j1) << 23);
+ xfree (buf);
- nextpc = pc_val + offset;
- /* For BLX make sure to clear the low bits. */
- if (bit (inst2, 12) == 0)
- nextpc = nextpc & 0xfffffffc;
- }
- else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
- {
- /* SUBS PC, LR, #imm8. */
- nextpc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
- nextpc -= inst2 & 0x00ff;
- }
- else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
- {
- /* Conditional branch. */
- if (condition_true (bits (inst1, 6, 9), status))
- {
- int sign, j1, j2, imm1, imm2;
+ if (last_it == -1)
+ /* There wasn't really an IT instruction after all. */
+ return bpaddr;
- sign = sbits (inst1, 10, 10);
- imm1 = bits (inst1, 0, 5);
- imm2 = bits (inst2, 0, 10);
- j1 = bit (inst2, 13);
- j2 = bit (inst2, 11);
+ if (last_it_count < 1)
+ /* It was too far away. */
+ return bpaddr;
- offset = (sign << 20) + (j2 << 19) + (j1 << 18);
- offset += (imm1 << 12) + (imm2 << 1);
+ /* This really is a trouble spot. Move the breakpoint to the IT
+ instruction. */
+ return bpaddr - buf_len + last_it;
+}
- nextpc = pc_val + offset;
- }
- }
- }
- else if ((inst1 & 0xfe50) == 0xe810)
- {
- /* Load multiple or RFE. */
- int rn, offset, load_pc = 1;
+/* ARM displaced stepping support.
- rn = bits (inst1, 0, 3);
- if (bit (inst1, 7) && !bit (inst1, 8))
- {
- /* LDMIA or POP */
- if (!bit (inst2, 15))
- load_pc = 0;
- offset = bitcount (inst2) * 4 - 4;
- }
- else if (!bit (inst1, 7) && bit (inst1, 8))
- {
- /* LDMDB */
- if (!bit (inst2, 15))
- load_pc = 0;
- offset = -4;
- }
- else if (bit (inst1, 7) && bit (inst1, 8))
- {
- /* RFEIA */
- offset = 0;
- }
- else if (!bit (inst1, 7) && !bit (inst1, 8))
- {
- /* RFEDB */
- offset = -8;
- }
- else
- load_pc = 0;
+ Generally ARM displaced stepping works as follows:
- if (load_pc)
- {
- CORE_ADDR addr = get_frame_register_unsigned (frame, rn);
- nextpc = get_frame_memory_unsigned (frame, addr + offset, 4);
- }
- }
- else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
- {
- /* MOV PC or MOVS PC. */
- nextpc = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- nextpc = MAKE_THUMB_ADDR (nextpc);
- }
- else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
- {
- /* LDR PC. */
- CORE_ADDR base;
- int rn, load_pc = 1;
+ 1. When an instruction is to be single-stepped, it is first decoded by
+ arm_process_displaced_insn. Depending on the type of instruction, it is
+ then copied to a scratch location, possibly in a modified form. The
+ copy_* set of functions performs such modification, as necessary. A
+ breakpoint is placed after the modified instruction in the scratch space
+ to return control to GDB. Note in particular that instructions which
+ modify the PC will no longer do so after modification.
- rn = bits (inst1, 0, 3);
- base = get_frame_register_unsigned (frame, rn);
- if (rn == ARM_PC_REGNUM)
- {
- base = (base + 4) & ~(CORE_ADDR) 0x3;
- if (bit (inst1, 7))
- base += bits (inst2, 0, 11);
- else
- base -= bits (inst2, 0, 11);
- }
- else if (bit (inst1, 7))
- base += bits (inst2, 0, 11);
- else if (bit (inst2, 11))
- {
- if (bit (inst2, 10))
- {
- if (bit (inst2, 9))
- base += bits (inst2, 0, 7);
- else
- base -= bits (inst2, 0, 7);
- }
- }
- else if ((inst2 & 0x0fc0) == 0x0000)
- {
- int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
- base += get_frame_register_unsigned (frame, rm) << shift;
- }
- else
- /* Reserved. */
- load_pc = 0;
+ 2. The instruction is single-stepped, by setting the PC to the scratch
+ location address, and resuming. Control returns to GDB when the
+ breakpoint is hit.
- if (load_pc)
- nextpc = get_frame_memory_unsigned (frame, base, 4);
- }
- else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
- {
- /* TBB. */
- CORE_ADDR tbl_reg, table, offset, length;
+ 3. A cleanup function (cleanup_*) is called corresponding to the copy_*
+ function used for the current instruction. This function's job is to
+ put the CPU/memory state back to what it would have been if the
+ instruction had been executed unmodified in its original location. */
- tbl_reg = bits (inst1, 0, 3);
- if (tbl_reg == 0x0f)
- table = pc + 4; /* Regcache copy of PC isn't right yet. */
- else
- table = get_frame_register_unsigned (frame, tbl_reg);
+/* NOP instruction (mov r0, r0). */
+#define ARM_NOP 0xe1a00000
+#define THUMB_NOP 0x4600
- offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 1);
- nextpc = pc_val + length;
- }
- else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
- {
- /* TBH. */
- CORE_ADDR tbl_reg, table, offset, length;
+/* Helper for register reads for displaced stepping. In particular, this
+ returns the PC as it would be seen by the instruction at its original
+ location. */
- tbl_reg = bits (inst1, 0, 3);
- if (tbl_reg == 0x0f)
- table = pc + 4; /* Regcache copy of PC isn't right yet. */
- else
- table = get_frame_register_unsigned (frame, tbl_reg);
+ULONGEST
+displaced_read_reg (struct regcache *regs, struct displaced_step_closure *dsc,
+ int regno)
+{
+ ULONGEST ret;
+ CORE_ADDR from = dsc->insn_addr;
- offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3));
- length = 2 * get_frame_memory_unsigned (frame, table + offset, 2);
- nextpc = pc_val + length;
- }
- }
- else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */
- {
- if (bits (inst1, 3, 6) == 0x0f)
- nextpc = UNMAKE_THUMB_ADDR (pc_val);
- else
- nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
- }
- else if ((inst1 & 0xff87) == 0x4687) /* mov pc, REG */
+ if (regno == ARM_PC_REGNUM)
{
- if (bits (inst1, 3, 6) == 0x0f)
- nextpc = pc_val;
+ /* Compute pipeline offset:
+ - When executing an ARM instruction, PC reads as the address of the
+ current instruction plus 8.
+ - When executing a Thumb instruction, PC reads as the address of the
+ current instruction plus 4. */
+
+ if (!dsc->is_thumb)
+ from += 8;
else
- nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
+ from += 4;
- nextpc = MAKE_THUMB_ADDR (nextpc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: read pc value %.8lx\n",
+ (unsigned long) from);
+ return (ULONGEST) from;
}
- else if ((inst1 & 0xf500) == 0xb100)
+ else
{
- /* CBNZ or CBZ. */
- int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
- ULONGEST reg = get_frame_register_unsigned (frame, bits (inst1, 0, 2));
-
- if (bit (inst1, 11) && reg != 0)
- nextpc = pc_val + imm;
- else if (!bit (inst1, 11) && reg == 0)
- nextpc = pc_val + imm;
+ regcache_cooked_read_unsigned (regs, regno, &ret);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: read r%d value %.8lx\n",
+ regno, (unsigned long) ret);
+ return ret;
}
- return nextpc;
}
-/* Get the raw next address. PC is the current program counter, in
- FRAME, which is assumed to be executing in ARM mode.
-
- The value returned has the execution state of the next instruction
- encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is
- in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
- address. */
-
-static CORE_ADDR
-arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc)
+static int
+displaced_in_arm_mode (struct regcache *regs)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned long pc_val;
- unsigned long this_instr;
- unsigned long status;
- CORE_ADDR nextpc;
-
- pc_val = (unsigned long) pc;
- this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
-
- status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
- nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */
+ ULONGEST ps;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
- if (bits (this_instr, 28, 31) == INST_NV)
- switch (bits (this_instr, 24, 27))
- {
- case 0xa:
- case 0xb:
- {
- /* Branch with Link and change to Thumb. */
- nextpc = BranchDest (pc, this_instr);
- nextpc |= bit (this_instr, 24) << 1;
- nextpc = MAKE_THUMB_ADDR (nextpc);
- break;
- }
- case 0xc:
- case 0xd:
- case 0xe:
- /* Coprocessor register transfer. */
- if (bits (this_instr, 12, 15) == 15)
- error (_("Invalid update to pc in instruction"));
- break;
- }
- else if (condition_true (bits (this_instr, 28, 31), status))
- {
- switch (bits (this_instr, 24, 27))
- {
- case 0x0:
- case 0x1: /* data processing */
- case 0x2:
- case 0x3:
- {
- unsigned long operand1, operand2, result = 0;
- unsigned long rn;
- int c;
+ regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
- if (bits (this_instr, 12, 15) != 15)
- break;
+ return (ps & t_bit) == 0;
+}
- if (bits (this_instr, 22, 25) == 0
- && bits (this_instr, 4, 7) == 9) /* multiply */
- error (_("Invalid update to pc in instruction"));
+/* Write to the PC as from a branch instruction. */
- /* BX <reg>, BLX <reg> */
- if (bits (this_instr, 4, 27) == 0x12fff1
- || bits (this_instr, 4, 27) == 0x12fff3)
- {
- rn = bits (this_instr, 0, 3);
- nextpc = ((rn == ARM_PC_REGNUM)
- ? (pc_val + 8)
- : get_frame_register_unsigned (frame, rn));
+static void
+branch_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
+ ULONGEST val)
+{
+ if (!dsc->is_thumb)
+ /* Note: If bits 0/1 are set, this branch would be unpredictable for
+ architecture versions < 6. */
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
+ val & ~(ULONGEST) 0x3);
+ else
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
+ val & ~(ULONGEST) 0x1);
+}
- return nextpc;
- }
+/* Write to the PC as from a branch-exchange instruction. */
- /* Multiply into PC. */
- c = (status & FLAG_C) ? 1 : 0;
- rn = bits (this_instr, 16, 19);
- operand1 = ((rn == ARM_PC_REGNUM)
- ? (pc_val + 8)
- : get_frame_register_unsigned (frame, rn));
+static void
+bx_write_pc (struct regcache *regs, ULONGEST val)
+{
+ ULONGEST ps;
+ ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
- if (bit (this_instr, 25))
- {
- unsigned long immval = bits (this_instr, 0, 7);
- unsigned long rotate = 2 * bits (this_instr, 8, 11);
- operand2 = ((immval >> rotate) | (immval << (32 - rotate)))
- & 0xffffffff;
- }
- else /* operand 2 is a shifted register. */
- operand2 = shifted_reg_val (frame, this_instr, c,
- pc_val, status);
+ regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
- switch (bits (this_instr, 21, 24))
- {
- case 0x0: /*and */
- result = operand1 & operand2;
- break;
+ if ((val & 1) == 1)
+ {
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps | t_bit);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffe);
+ }
+ else if ((val & 2) == 0)
+ {
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val);
+ }
+ else
+ {
+ /* Unpredictable behaviour. Try to do something sensible (switch to ARM
+ mode, align dest to 4 bytes). */
+ warning (_("Single-stepping BX to non-word-aligned ARM instruction."));
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffc);
+ }
+}
- case 0x1: /*eor */
- result = operand1 ^ operand2;
- break;
+/* Write to the PC as if from a load instruction. */
- case 0x2: /*sub */
- result = operand1 - operand2;
- break;
+static void
+load_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
+ ULONGEST val)
+{
+ if (DISPLACED_STEPPING_ARCH_VERSION >= 5)
+ bx_write_pc (regs, val);
+ else
+ branch_write_pc (regs, dsc, val);
+}
- case 0x3: /*rsb */
- result = operand2 - operand1;
- break;
+/* Write to the PC as if from an ALU instruction. */
- case 0x4: /*add */
- result = operand1 + operand2;
- break;
+static void
+alu_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
+ ULONGEST val)
+{
+ if (DISPLACED_STEPPING_ARCH_VERSION >= 7 && !dsc->is_thumb)
+ bx_write_pc (regs, val);
+ else
+ branch_write_pc (regs, dsc, val);
+}
- case 0x5: /*adc */
- result = operand1 + operand2 + c;
- break;
+/* Helper for writing to registers for displaced stepping. Writing to the PC
+ has a varying effects depending on the instruction which does the write:
+ this is controlled by the WRITE_PC argument. */
- case 0x6: /*sbc */
- result = operand1 - operand2 + c;
- break;
+void
+displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc,
+ int regno, ULONGEST val, enum pc_write_style write_pc)
+{
+ if (regno == ARM_PC_REGNUM)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing pc %.8lx\n",
+ (unsigned long) val);
+ switch (write_pc)
+ {
+ case BRANCH_WRITE_PC:
+ branch_write_pc (regs, dsc, val);
+ break;
- case 0x7: /*rsc */
- result = operand2 - operand1 + c;
- break;
+ case BX_WRITE_PC:
+ bx_write_pc (regs, val);
+ break;
- case 0x8:
- case 0x9:
- case 0xa:
- case 0xb: /* tst, teq, cmp, cmn */
- result = (unsigned long) nextpc;
- break;
+ case LOAD_WRITE_PC:
+ load_write_pc (regs, dsc, val);
+ break;
- case 0xc: /*orr */
- result = operand1 | operand2;
- break;
+ case ALU_WRITE_PC:
+ alu_write_pc (regs, dsc, val);
+ break;
- case 0xd: /*mov */
- /* Always step into a function. */
- result = operand2;
- break;
+ case CANNOT_WRITE_PC:
+ warning (_("Instruction wrote to PC in an unexpected way when "
+ "single-stepping"));
+ break;
- case 0xe: /*bic */
- result = operand1 & ~operand2;
- break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid argument to displaced_write_reg"));
+ }
- case 0xf: /*mvn */
- result = ~operand2;
- break;
- }
-
- /* In 26-bit APCS the bottom two bits of the result are
- ignored, and we always end up in ARM state. */
- if (!arm_apcs_32)
- nextpc = arm_addr_bits_remove (gdbarch, result);
- else
- nextpc = result;
-
- break;
- }
-
- case 0x4:
- case 0x5: /* data transfer */
- case 0x6:
- case 0x7:
- if (bit (this_instr, 20))
- {
- /* load */
- if (bits (this_instr, 12, 15) == 15)
- {
- /* rd == pc */
- unsigned long rn;
- unsigned long base;
-
- if (bit (this_instr, 22))
- error (_("Invalid update to pc in instruction"));
-
- /* byte write to PC */
- rn = bits (this_instr, 16, 19);
- base = ((rn == ARM_PC_REGNUM)
- ? (pc_val + 8)
- : get_frame_register_unsigned (frame, rn));
-
- if (bit (this_instr, 24))
- {
- /* pre-indexed */
- int c = (status & FLAG_C) ? 1 : 0;
- unsigned long offset =
- (bit (this_instr, 25)
- ? shifted_reg_val (frame, this_instr, c, pc_val, status)
- : bits (this_instr, 0, 11));
-
- if (bit (this_instr, 23))
- base += offset;
- else
- base -= offset;
- }
- nextpc =
- (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) base,
- 4, byte_order);
- }
- }
- break;
+ dsc->wrote_to_pc = 1;
+ }
+ else
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing r%d value %.8lx\n",
+ regno, (unsigned long) val);
+ regcache_cooked_write_unsigned (regs, regno, val);
+ }
+}
- case 0x8:
- case 0x9: /* block transfer */
- if (bit (this_instr, 20))
- {
- /* LDM */
- if (bit (this_instr, 15))
- {
- /* loading pc */
- int offset = 0;
- unsigned long rn_val
- = get_frame_register_unsigned (frame,
- bits (this_instr, 16, 19));
+/* This function is used to concisely determine if an instruction INSN
+ references PC. Register fields of interest in INSN should have the
+ corresponding fields of BITMASK set to 0b1111. The function
+ returns return 1 if any of these fields in INSN reference the PC
+ (also 0b1111, r15), else it returns 0. */
- if (bit (this_instr, 23))
- {
- /* up */
- unsigned long reglist = bits (this_instr, 0, 14);
- offset = bitcount (reglist) * 4;
- if (bit (this_instr, 24)) /* pre */
- offset += 4;
- }
- else if (bit (this_instr, 24))
- offset = -4;
+static int
+insn_references_pc (uint32_t insn, uint32_t bitmask)
+{
+ uint32_t lowbit = 1;
- nextpc =
- (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR)
- (rn_val + offset),
- 4, byte_order);
- }
- }
- break;
+ while (bitmask != 0)
+ {
+ uint32_t mask;
- case 0xb: /* branch & link */
- case 0xa: /* branch */
- {
- nextpc = BranchDest (pc, this_instr);
- break;
- }
+ for (; lowbit && (bitmask & lowbit) == 0; lowbit <<= 1)
+ ;
- case 0xc:
- case 0xd:
- case 0xe: /* coproc ops */
- break;
- case 0xf: /* SWI */
- {
- struct gdbarch_tdep *tdep;
- tdep = gdbarch_tdep (gdbarch);
+ if (!lowbit)
+ break;
- if (tdep->syscall_next_pc != NULL)
- nextpc = tdep->syscall_next_pc (frame);
+ mask = lowbit * 0xf;
- }
- break;
+ if ((insn & mask) == mask)
+ return 1;
- default:
- fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n"));
- return (pc);
- }
+ bitmask &= ~mask;
}
- return nextpc;
+ return 0;
}
-/* Determine next PC after current instruction executes. Will call either
- arm_get_next_pc_raw or thumb_get_next_pc_raw. Error out if infinite
- loop is detected. */
+/* The simplest copy function. Many instructions have the same effect no
+ matter what address they are executed at: in those cases, use this. */
-CORE_ADDR
-arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+static int
+arm_copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
+ const char *iname, struct displaced_step_closure *dsc)
{
- CORE_ADDR nextpc;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, "
+ "opcode/class '%s' unmodified\n", (unsigned long) insn,
+ iname);
- if (arm_frame_is_thumb (frame))
- {
- nextpc = thumb_get_next_pc_raw (frame, pc);
- if (nextpc == MAKE_THUMB_ADDR (pc))
- error (_("Infinite loop detected"));
- }
- else
- {
- nextpc = arm_get_next_pc_raw (frame, pc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- }
+ dsc->modinsn[0] = insn;
- return nextpc;
+ return 0;
}
-/* Like insert_single_step_breakpoint, but make sure we use a breakpoint
- of the appropriate mode (as encoded in the PC value), even if this
- differs from what would be expected according to the symbol tables. */
-
-void
-arm_insert_single_step_breakpoint (struct gdbarch *gdbarch,
- struct address_space *aspace,
- CORE_ADDR pc)
+static int
+thumb_copy_unmodified_32bit (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, const char *iname,
+ struct displaced_step_closure *dsc)
{
- struct cleanup *old_chain
- = make_cleanup_restore_integer (&arm_override_mode);
-
- arm_override_mode = IS_THUMB_ADDR (pc);
- pc = gdbarch_addr_bits_remove (gdbarch, pc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x %.4x, "
+ "opcode/class '%s' unmodified\n", insn1, insn2,
+ iname);
- insert_single_step_breakpoint (gdbarch, aspace, pc);
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
- do_cleanups (old_chain);
+ return 0;
}
-/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D}
- instruction and ending with a STREX{,B,H,D} instruction. If such a sequence
- is found, attempt to step through it. A breakpoint is placed at the end of
- the sequence. */
-
+/* Copy 16-bit Thumb(Thumb and 16-bit Thumb-2) instruction without any
+ modification. */
static int
-thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
+thumb_copy_unmodified_16bit (struct gdbarch *gdbarch, uint16_t insn,
+ const char *iname,
+ struct displaced_step_closure *dsc)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- CORE_ADDR breaks[2] = {-1, -1};
- CORE_ADDR loc = pc;
- unsigned short insn1, insn2;
- int insn_count;
- int index;
- int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
- const int atomic_sequence_length = 16; /* Instruction sequence length. */
- ULONGEST status, itstate;
-
- /* We currently do not support atomic sequences within an IT block. */
- status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
- itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
- if (itstate & 0x0f)
- return 0;
-
- /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */
- insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
- if (thumb_insn_size (insn1) != 4)
- return 0;
-
- insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
- if (!((insn1 & 0xfff0) == 0xe850
- || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040)))
- return 0;
-
- /* Assume that no atomic sequence is longer than "atomic_sequence_length"
- instructions. */
- for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
- {
- insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
-
- if (thumb_insn_size (insn1) != 4)
- {
- /* Assume that there is at most one conditional branch in the
- atomic sequence. If a conditional branch is found, put a
- breakpoint in its destination address. */
- if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f)
- {
- if (last_breakpoint > 0)
- return 0; /* More than one conditional branch found,
- fallback to the standard code. */
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x, "
+ "opcode/class '%s' unmodified\n", insn,
+ iname);
- breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
- last_breakpoint++;
- }
+ dsc->modinsn[0] = insn;
- /* We do not support atomic sequences that use any *other*
- instructions but conditional branches to change the PC.
- Fall back to standard code to avoid losing control of
- execution. */
- else if (thumb_instruction_changes_pc (insn1))
- return 0;
- }
- else
- {
- insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code);
- loc += 2;
+ return 0;
+}
- /* Assume that there is at most one conditional branch in the
- atomic sequence. If a conditional branch is found, put a
- breakpoint in its destination address. */
- if ((insn1 & 0xf800) == 0xf000
- && (insn2 & 0xd000) == 0x8000
- && (insn1 & 0x0380) != 0x0380)
- {
- int sign, j1, j2, imm1, imm2;
- unsigned int offset;
+/* Preload instructions with immediate offset. */
- sign = sbits (insn1, 10, 10);
- imm1 = bits (insn1, 0, 5);
- imm2 = bits (insn2, 0, 10);
- j1 = bit (insn2, 13);
- j2 = bit (insn2, 11);
+static void
+cleanup_preload (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ if (!dsc->u.preload.immed)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+}
- offset = (sign << 20) + (j2 << 19) + (j1 << 18);
- offset += (imm1 << 12) + (imm2 << 1);
+static void
+install_preload (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, unsigned int rn)
+{
+ ULONGEST rn_val;
+ /* Preload instructions:
- if (last_breakpoint > 0)
- return 0; /* More than one conditional branch found,
- fallback to the standard code. */
+ {pli/pld} [rn, #+/-imm]
+ ->
+ {pli/pld} [r0, #+/-imm]. */
- breaks[1] = loc + offset;
- last_breakpoint++;
- }
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+ dsc->u.preload.immed = 1;
- /* We do not support atomic sequences that use any *other*
- instructions but conditional branches to change the PC.
- Fall back to standard code to avoid losing control of
- execution. */
- else if (thumb2_instruction_changes_pc (insn1, insn2))
- return 0;
+ dsc->cleanup = &cleanup_preload;
+}
- /* If we find a strex{,b,h,d}, we're done. */
- if ((insn1 & 0xfff0) == 0xe840
- || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
- break;
- }
- }
+static int
+arm_copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
- /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
- if (insn_count == atomic_sequence_length)
- return 0;
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return arm_copy_unmodified (gdbarch, insn, "preload", dsc);
- /* Insert a breakpoint right after the end of the atomic sequence. */
- breaks[0] = loc;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ (unsigned long) insn);
- /* Check for duplicated breakpoints. Check also for a breakpoint
- placed (branch instruction's destination) anywhere in sequence. */
- if (last_breakpoint
- && (breaks[1] == breaks[0]
- || (breaks[1] >= pc && breaks[1] < loc)))
- last_breakpoint = 0;
+ dsc->modinsn[0] = insn & 0xfff0ffff;
- /* Effectively inserts the breakpoints. */
- for (index = 0; index <= last_breakpoint; index++)
- arm_insert_single_step_breakpoint (gdbarch, aspace,
- MAKE_THUMB_ADDR (breaks[index]));
+ install_preload (gdbarch, regs, dsc, rn);
- return 1;
+ return 0;
}
static int
-arm_deal_with_atomic_sequence_raw (struct frame_info *frame)
+thumb2_copy_preload (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- CORE_ADDR pc = get_frame_pc (frame);
- CORE_ADDR breaks[2] = {-1, -1};
- CORE_ADDR loc = pc;
- unsigned int insn;
- int insn_count;
- int index;
- int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */
- const int atomic_sequence_length = 16; /* Instruction sequence length. */
-
- /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction.
- Note that we do not currently support conditionally executed atomic
- instructions. */
- insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code);
- loc += 4;
- if ((insn & 0xff9000f0) != 0xe1900090)
- return 0;
+ unsigned int rn = bits (insn1, 0, 3);
+ unsigned int u_bit = bit (insn1, 7);
+ int imm12 = bits (insn2, 0, 11);
+ ULONGEST pc_val;
- /* Assume that no atomic sequence is longer than "atomic_sequence_length"
- instructions. */
- for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count)
- {
- insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code);
- loc += 4;
+ if (rn != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "preload", dsc);
- /* Assume that there is at most one conditional branch in the atomic
- sequence. If a conditional branch is found, put a breakpoint in
- its destination address. */
- if (bits (insn, 24, 27) == 0xa)
- {
- if (last_breakpoint > 0)
- return 0; /* More than one conditional branch found, fallback
- to the standard single-step code. */
+ /* PC is only allowed to use in PLI (immediate,literal) Encoding T3, and
+ PLD (literal) Encoding T1. */
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying pld/pli pc (0x%x) %c imm12 %.4x\n",
+ (unsigned int) dsc->insn_addr, u_bit ? '+' : '-',
+ imm12);
- breaks[1] = BranchDest (loc - 4, insn);
- last_breakpoint++;
- }
+ if (!u_bit)
+ imm12 = -1 * imm12;
- /* We do not support atomic sequences that use any *other* instructions
- but conditional branches to change the PC. Fall back to standard
- code to avoid losing control of execution. */
- else if (arm_instruction_changes_pc (insn))
- return 0;
+ /* Rewrite instruction {pli/pld} PC imm12 into:
+ Prepare: tmp[0] <- r0, tmp[1] <- r1, r0 <- pc, r1 <- imm12
- /* If we find a strex{,b,h,d}, we're done. */
- if ((insn & 0xff9000f0) == 0xe1800090)
- break;
- }
+ {pli/pld} [r0, r1]
- /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
- if (insn_count == atomic_sequence_length)
- return 0;
+ Cleanup: r0 <- tmp[0], r1 <- tmp[1]. */
- /* Insert a breakpoint right after the end of the atomic sequence. */
- breaks[0] = loc;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
- /* Check for duplicated breakpoints. Check also for a breakpoint
- placed (branch instruction's destination) anywhere in sequence. */
- if (last_breakpoint
- && (breaks[1] == breaks[0]
- || (breaks[1] >= pc && breaks[1] < loc)))
- last_breakpoint = 0;
+ pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
- /* Effectively inserts the breakpoints. */
- for (index = 0; index <= last_breakpoint; index++)
- arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
+ displaced_write_reg (regs, dsc, 0, pc_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, imm12, CANNOT_WRITE_PC);
+ dsc->u.preload.immed = 0;
- return 1;
-}
+ /* {pli/pld} [r0, r1] */
+ dsc->modinsn[0] = insn1 & 0xfff0;
+ dsc->modinsn[1] = 0xf001;
+ dsc->numinsns = 2;
-int
-arm_deal_with_atomic_sequence (struct frame_info *frame)
-{
- if (arm_frame_is_thumb (frame))
- return thumb_deal_with_atomic_sequence_raw (frame);
- else
- return arm_deal_with_atomic_sequence_raw (frame);
+ dsc->cleanup = &cleanup_preload;
+ return 0;
}
-/* single_step() is called just before we want to resume the inferior,
- if we want to single-step it but there is no hardware or kernel
- single-step support. We find the target of the coming instruction
- and breakpoint it. */
+/* Preload instructions with register offset. */
-int
-arm_software_single_step (struct frame_info *frame)
+static void
+install_preload_reg(struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, unsigned int rn,
+ unsigned int rm)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc;
-
- if (arm_deal_with_atomic_sequence (frame))
- return 1;
+ ULONGEST rn_val, rm_val;
- next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
- arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+ /* Preload register-offset instructions:
- return 1;
-}
+ {pli/pld} [rn, rm {, shift}]
+ ->
+ {pli/pld} [r0, r1 {, shift}]. */
-/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
- the buffer to be NEW_LEN bytes ending at ENDADDR. Return
- NULL if an error occurs. BUF is freed. */
-
-static gdb_byte *
-extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
- int old_len, int new_len)
-{
- gdb_byte *new_buf;
- int bytes_to_read = new_len - old_len;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ rm_val = displaced_read_reg (regs, dsc, rm);
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rm_val, CANNOT_WRITE_PC);
+ dsc->u.preload.immed = 0;
- new_buf = xmalloc (new_len);
- memcpy (new_buf + bytes_to_read, buf, old_len);
- xfree (buf);
- if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
- {
- xfree (new_buf);
- return NULL;
- }
- return new_buf;
+ dsc->cleanup = &cleanup_preload;
}
-/* An IT block is at most the 2-byte IT instruction followed by
- four 4-byte instructions. The furthest back we must search to
- find an IT block that affects the current instruction is thus
- 2 + 3 * 4 == 14 bytes. */
-#define MAX_IT_BLOCK_PREFIX 14
-
-/* Use a quick scan if there are more than this many bytes of
- code. */
-#define IT_SCAN_THRESHOLD 32
-
-/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
- A breakpoint in an IT block may not be hit, depending on the
- condition flags. */
-static CORE_ADDR
-arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
+static int
+arm_copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- gdb_byte *buf;
- char map_type;
- CORE_ADDR boundary, func_start;
- int buf_len;
- enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
- int i, any, last_it, last_it_count;
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3);
- /* If we are using BKPT breakpoints, none of this is necessary. */
- if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
- return bpaddr;
- /* ARM mode does not have this problem. */
- if (!arm_pc_is_thumb (gdbarch, bpaddr))
- return bpaddr;
+ if (!insn_references_pc (insn, 0x000f000ful))
+ return arm_copy_unmodified (gdbarch, insn, "preload reg", dsc);
- /* We are setting a breakpoint in Thumb code that could potentially
- contain an IT block. The first step is to find how much Thumb
- code there is; we do not need to read outside of known Thumb
- sequences. */
- map_type = arm_find_mapping_symbol (bpaddr, &boundary);
- if (map_type == 0)
- /* Thumb-2 code must have mapping symbols to have a chance. */
- return bpaddr;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ (unsigned long) insn);
- bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
+ dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1;
- if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
- && func_start > boundary)
- boundary = func_start;
+ install_preload_reg (gdbarch, regs, dsc, rn, rm);
+ return 0;
+}
- /* Search for a candidate IT instruction. We have to do some fancy
- footwork to distinguish a real IT instruction from the second
- half of a 32-bit instruction, but there is no need for that if
- there's no candidate. */
- buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
- if (buf_len == 0)
- /* No room for an IT instruction. */
- return bpaddr;
+/* Copy/cleanup coprocessor load and store instructions. */
- buf = xmalloc (buf_len);
- if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
- return bpaddr;
- any = 0;
- for (i = 0; i < buf_len; i += 2)
- {
- unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
- if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
- {
- any = 1;
- break;
- }
- }
- if (any == 0)
- {
- xfree (buf);
- return bpaddr;
- }
+static void
+cleanup_copro_load_store (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST rn_val = displaced_read_reg (regs, dsc, 0);
- /* OK, the code bytes before this instruction contain at least one
- halfword which resembles an IT instruction. We know that it's
- Thumb code, but there are still two possibilities. Either the
- halfword really is an IT instruction, or it is the second half of
- a 32-bit Thumb instruction. The only way we can tell is to
- scan forwards from a known instruction boundary. */
- if (bpaddr - boundary > IT_SCAN_THRESHOLD)
- {
- int definite;
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
- /* There's a lot of code before this instruction. Start with an
- optimistic search; it's easy to recognize halfwords that can
- not be the start of a 32-bit instruction, and use that to
- lock on to the instruction boundaries. */
- buf = extend_buffer_earlier (buf, bpaddr, buf_len, IT_SCAN_THRESHOLD);
- if (buf == NULL)
- return bpaddr;
- buf_len = IT_SCAN_THRESHOLD;
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, LOAD_WRITE_PC);
+}
- definite = 0;
- for (i = 0; i < buf_len - sizeof (buf) && ! definite; i += 2)
- {
- unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
- if (thumb_insn_size (inst1) == 2)
- {
- definite = 1;
- break;
- }
- }
+static void
+install_copro_load_store (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ int writeback, unsigned int rn)
+{
+ ULONGEST rn_val;
- /* At this point, if DEFINITE, BUF[I] is the first place we
- are sure that we know the instruction boundaries, and it is far
- enough from BPADDR that we could not miss an IT instruction
- affecting BPADDR. If ! DEFINITE, give up - start from a
- known boundary. */
- if (! definite)
- {
- buf = extend_buffer_earlier (buf, bpaddr, buf_len,
- bpaddr - boundary);
- if (buf == NULL)
- return bpaddr;
- buf_len = bpaddr - boundary;
- i = 0;
- }
- }
- else
- {
- buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
- if (buf == NULL)
- return bpaddr;
- buf_len = bpaddr - boundary;
- i = 0;
- }
+ /* Coprocessor load/store instructions:
- /* Scan forwards. Find the last IT instruction before BPADDR. */
- last_it = -1;
- last_it_count = 0;
- while (i < buf_len)
- {
- unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
- last_it_count--;
- if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
- {
- last_it = i;
- if (inst1 & 0x0001)
- last_it_count = 4;
- else if (inst1 & 0x0002)
- last_it_count = 3;
- else if (inst1 & 0x0004)
- last_it_count = 2;
- else
- last_it_count = 1;
- }
- i += thumb_insn_size (inst1);
- }
+ {stc/stc2} [<Rn>, #+/-imm] (and other immediate addressing modes)
+ ->
+ {stc/stc2} [r0, #+/-imm].
- xfree (buf);
+ ldc/ldc2 are handled identically. */
- if (last_it == -1)
- /* There wasn't really an IT instruction after all. */
- return bpaddr;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ /* PC should be 4-byte aligned. */
+ rn_val = rn_val & 0xfffffffc;
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
- if (last_it_count < 1)
- /* It was too far away. */
- return bpaddr;
+ dsc->u.ldst.writeback = writeback;
+ dsc->u.ldst.rn = rn;
- /* This really is a trouble spot. Move the breakpoint to the IT
- instruction. */
- return bpaddr - buf_len + last_it;
+ dsc->cleanup = &cleanup_copro_load_store;
}
-/* ARM displaced stepping support.
-
- Generally ARM displaced stepping works as follows:
+static int
+arm_copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
- 1. When an instruction is to be single-stepped, it is first decoded by
- arm_process_displaced_insn (called from arm_displaced_step_copy_insn).
- Depending on the type of instruction, it is then copied to a scratch
- location, possibly in a modified form. The copy_* set of functions
- performs such modification, as necessary. A breakpoint is placed after
- the modified instruction in the scratch space to return control to GDB.
- Note in particular that instructions which modify the PC will no longer
- do so after modification.
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return arm_copy_unmodified (gdbarch, insn, "copro load/store", dsc);
- 2. The instruction is single-stepped, by setting the PC to the scratch
- location address, and resuming. Control returns to GDB when the
- breakpoint is hit.
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
+ "load/store insn %.8lx\n", (unsigned long) insn);
- 3. A cleanup function (cleanup_*) is called corresponding to the copy_*
- function used for the current instruction. This function's job is to
- put the CPU/memory state back to what it would have been if the
- instruction had been executed unmodified in its original location. */
+ dsc->modinsn[0] = insn & 0xfff0ffff;
-/* NOP instruction (mov r0, r0). */
-#define ARM_NOP 0xe1a00000
-#define THUMB_NOP 0x4600
+ install_copro_load_store (gdbarch, regs, dsc, bit (insn, 25), rn);
-/* Helper for register reads for displaced stepping. In particular, this
- returns the PC as it would be seen by the instruction at its original
- location. */
+ return 0;
+}
-ULONGEST
-displaced_read_reg (struct regcache *regs, struct displaced_step_closure *dsc,
- int regno)
+static int
+thumb2_copy_copro_load_store (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- ULONGEST ret;
- CORE_ADDR from = dsc->insn_addr;
-
- if (regno == ARM_PC_REGNUM)
- {
- /* Compute pipeline offset:
- - When executing an ARM instruction, PC reads as the address of the
- current instruction plus 8.
- - When executing a Thumb instruction, PC reads as the address of the
- current instruction plus 4. */
+ unsigned int rn = bits (insn1, 0, 3);
- if (!dsc->is_thumb)
- from += 8;
- else
- from += 4;
+ if (rn != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "copro load/store", dsc);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: read pc value %.8lx\n",
- (unsigned long) from);
- return (ULONGEST) from;
- }
- else
- {
- regcache_cooked_read_unsigned (regs, regno, &ret);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: read r%d value %.8lx\n",
- regno, (unsigned long) ret);
- return ret;
- }
-}
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
+ "load/store insn %.4x%.4x\n", insn1, insn2);
-static int
-displaced_in_arm_mode (struct regcache *regs)
-{
- ULONGEST ps;
- ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
+ dsc->modinsn[0] = insn1 & 0xfff0;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
- regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
+ /* This function is called for copying instruction LDC/LDC2/VLDR, which
+ doesn't support writeback, so pass 0. */
+ install_copro_load_store (gdbarch, regs, dsc, 0, rn);
- return (ps & t_bit) == 0;
+ return 0;
}
-/* Write to the PC as from a branch instruction. */
+/* Clean up branch instructions (actually perform the branch, by setting
+ PC). */
static void
-branch_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
- ULONGEST val)
+cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (!dsc->is_thumb)
- /* Note: If bits 0/1 are set, this branch would be unpredictable for
- architecture versions < 6. */
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
- val & ~(ULONGEST) 0x3);
- else
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
- val & ~(ULONGEST) 0x1);
-}
+ uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
+ int branch_taken = condition_true (dsc->u.branch.cond, status);
+ enum pc_write_style write_pc = dsc->u.branch.exchange
+ ? BX_WRITE_PC : BRANCH_WRITE_PC;
-/* Write to the PC as from a branch-exchange instruction. */
+ if (!branch_taken)
+ return;
-static void
-bx_write_pc (struct regcache *regs, ULONGEST val)
-{
- ULONGEST ps;
- ULONGEST t_bit = arm_psr_thumb_bit (get_regcache_arch (regs));
+ if (dsc->u.branch.link)
+ {
+ /* The value of LR should be the next insn of current one. In order
+ not to confuse logic hanlding later insn `bx lr', if current insn mode
+ is Thumb, the bit 0 of LR value should be set to 1. */
+ ULONGEST next_insn_addr = dsc->insn_addr + dsc->insn_size;
- regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
+ if (dsc->is_thumb)
+ next_insn_addr |= 0x1;
- if ((val & 1) == 1)
- {
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps | t_bit);
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffe);
- }
- else if ((val & 2) == 0)
- {
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val);
- }
- else
- {
- /* Unpredictable behaviour. Try to do something sensible (switch to ARM
- mode, align dest to 4 bytes). */
- warning (_("Single-stepping BX to non-word-aligned ARM instruction."));
- regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps & ~t_bit);
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffc);
+ displaced_write_reg (regs, dsc, ARM_LR_REGNUM, next_insn_addr,
+ CANNOT_WRITE_PC);
}
-}
-
-/* Write to the PC as if from a load instruction. */
-static void
-load_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
- ULONGEST val)
-{
- if (DISPLACED_STEPPING_ARCH_VERSION >= 5)
- bx_write_pc (regs, val);
- else
- branch_write_pc (regs, dsc, val);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->u.branch.dest, write_pc);
}
-/* Write to the PC as if from an ALU instruction. */
+/* Copy B/BL/BLX instructions with immediate destinations. */
static void
-alu_write_pc (struct regcache *regs, struct displaced_step_closure *dsc,
- ULONGEST val)
-{
- if (DISPLACED_STEPPING_ARCH_VERSION >= 7 && !dsc->is_thumb)
- bx_write_pc (regs, val);
- else
- branch_write_pc (regs, dsc, val);
-}
-
-/* Helper for writing to registers for displaced stepping. Writing to the PC
- has a varying effects depending on the instruction which does the write:
- this is controlled by the WRITE_PC argument. */
-
-void
-displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc,
- int regno, ULONGEST val, enum pc_write_style write_pc)
+install_b_bl_blx (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ unsigned int cond, int exchange, int link, long offset)
{
- if (regno == ARM_PC_REGNUM)
- {
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: writing pc %.8lx\n",
- (unsigned long) val);
- switch (write_pc)
- {
- case BRANCH_WRITE_PC:
- branch_write_pc (regs, dsc, val);
- break;
-
- case BX_WRITE_PC:
- bx_write_pc (regs, val);
- break;
+ /* Implement "BL<cond> <label>" as:
- case LOAD_WRITE_PC:
- load_write_pc (regs, dsc, val);
- break;
+ Preparation: cond <- instruction condition
+ Insn: mov r0, r0 (nop)
+ Cleanup: if (condition true) { r14 <- pc; pc <- label }.
- case ALU_WRITE_PC:
- alu_write_pc (regs, dsc, val);
- break;
+ B<cond> similar, but don't set r14 in cleanup. */
- case CANNOT_WRITE_PC:
- warning (_("Instruction wrote to PC in an unexpected way when "
- "single-stepping"));
- break;
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = link;
+ dsc->u.branch.exchange = exchange;
- default:
- internal_error (__FILE__, __LINE__,
- _("Invalid argument to displaced_write_reg"));
- }
+ dsc->u.branch.dest = dsc->insn_addr;
+ if (link && exchange)
+ /* For BLX, offset is computed from the Align (PC, 4). */
+ dsc->u.branch.dest = dsc->u.branch.dest & 0xfffffffc;
- dsc->wrote_to_pc = 1;
- }
+ if (dsc->is_thumb)
+ dsc->u.branch.dest += 4 + offset;
else
- {
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: writing r%d value %.8lx\n",
- regno, (unsigned long) val);
- regcache_cooked_write_unsigned (regs, regno, val);
- }
-}
-
-/* This function is used to concisely determine if an instruction INSN
- references PC. Register fields of interest in INSN should have the
- corresponding fields of BITMASK set to 0b1111. The function
- returns return 1 if any of these fields in INSN reference the PC
- (also 0b1111, r15), else it returns 0. */
+ dsc->u.branch.dest += 8 + offset;
+ dsc->cleanup = &cleanup_branch;
+}
static int
-insn_references_pc (uint32_t insn, uint32_t bitmask)
+arm_copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- uint32_t lowbit = 1;
-
- while (bitmask != 0)
- {
- uint32_t mask;
-
- for (; lowbit && (bitmask & lowbit) == 0; lowbit <<= 1)
- ;
-
- if (!lowbit)
- break;
+ unsigned int cond = bits (insn, 28, 31);
+ int exchange = (cond == 0xf);
+ int link = exchange || bit (insn, 24);
+ long offset;
- mask = lowbit * 0xf;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
+ "%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
+ (unsigned long) insn);
+ if (exchange)
+ /* For BLX, set bit 0 of the destination. The cleanup_branch function will
+ then arrange the switch into Thumb mode. */
+ offset = (bits (insn, 0, 23) << 2) | (bit (insn, 24) << 1) | 1;
+ else
+ offset = bits (insn, 0, 23) << 2;
- if ((insn & mask) == mask)
- return 1;
+ if (bit (offset, 25))
+ offset = offset | ~0x3ffffff;
- bitmask &= ~mask;
- }
+ dsc->modinsn[0] = ARM_NOP;
+ install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
return 0;
}
-/* The simplest copy function. Many instructions have the same effect no
- matter what address they are executed at: in those cases, use this. */
-
static int
-arm_copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
- const char *iname, struct displaced_step_closure *dsc)
+thumb2_copy_b_bl_blx (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, "
- "opcode/class '%s' unmodified\n", (unsigned long) insn,
- iname);
+ int link = bit (insn2, 14);
+ int exchange = link && !bit (insn2, 12);
+ int cond = INST_AL;
+ long offset = 0;
+ int j1 = bit (insn2, 13);
+ int j2 = bit (insn2, 11);
+ int s = sbits (insn1, 10, 10);
+ int i1 = !(j1 ^ bit (insn1, 10));
+ int i2 = !(j2 ^ bit (insn1, 10));
- dsc->modinsn[0] = insn;
-
- return 0;
-}
+ if (!link && !exchange) /* B */
+ {
+ offset = (bits (insn2, 0, 10) << 1);
+ if (bit (insn2, 12)) /* Encoding T4 */
+ {
+ offset |= (bits (insn1, 0, 9) << 12)
+ | (i2 << 22)
+ | (i1 << 23)
+ | (s << 24);
+ cond = INST_AL;
+ }
+ else /* Encoding T3 */
+ {
+ offset |= (bits (insn1, 0, 5) << 12)
+ | (j1 << 18)
+ | (j2 << 19)
+ | (s << 20);
+ cond = bits (insn1, 6, 9);
+ }
+ }
+ else
+ {
+ offset = (bits (insn1, 0, 9) << 12);
+ offset |= ((i2 << 22) | (i1 << 23) | (s << 24));
+ offset |= exchange ?
+ (bits (insn2, 1, 10) << 2) : (bits (insn2, 0, 10) << 1);
+ }
-static int
-thumb_copy_unmodified_32bit (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, const char *iname,
- struct displaced_step_closure *dsc)
-{
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x %.4x, "
- "opcode/class '%s' unmodified\n", insn1, insn2,
- iname);
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s insn "
+ "%.4x %.4x with offset %.8lx\n",
+ link ? (exchange) ? "blx" : "bl" : "b",
+ insn1, insn2, offset);
- dsc->modinsn[0] = insn1;
- dsc->modinsn[1] = insn2;
- dsc->numinsns = 2;
+ dsc->modinsn[0] = THUMB_NOP;
+ install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
return 0;
}
-/* Copy 16-bit Thumb(Thumb and 16-bit Thumb-2) instruction without any
- modification. */
+/* Copy B Thumb instructions. */
static int
-thumb_copy_unmodified_16bit (struct gdbarch *gdbarch, unsigned int insn,
- const char *iname,
- struct displaced_step_closure *dsc)
+thumb_copy_b (struct gdbarch *gdbarch, uint16_t insn,
+ struct displaced_step_closure *dsc)
{
+ unsigned int cond = 0;
+ int offset = 0;
+ unsigned short bit_12_15 = bits (insn, 12, 15);
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (bit_12_15 == 0xd)
+ {
+ /* offset = SignExtend (imm8:0, 32) */
+ offset = sbits ((insn << 1), 0, 8);
+ cond = bits (insn, 8, 11);
+ }
+ else if (bit_12_15 == 0xe) /* Encoding T2 */
+ {
+ offset = sbits ((insn << 1), 0, 11);
+ cond = INST_AL;
+ }
+
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x, "
- "opcode/class '%s' unmodified\n", insn,
- iname);
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying b immediate insn %.4x "
+ "with offset %d\n", insn, offset);
- dsc->modinsn[0] = insn;
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = 0;
+ dsc->u.branch.exchange = 0;
+ dsc->u.branch.dest = from + 4 + offset;
+
+ dsc->modinsn[0] = THUMB_NOP;
+
+ dsc->cleanup = &cleanup_branch;
return 0;
}
-/* Preload instructions with immediate offset. */
+/* Copy BX/BLX with register-specified destinations. */
static void
-cleanup_preload (struct gdbarch *gdbarch,
- struct regcache *regs, struct displaced_step_closure *dsc)
+install_bx_blx_reg (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, int link,
+ unsigned int cond, unsigned int rm)
{
- displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
- if (!dsc->u.preload.immed)
- displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
-}
+ /* Implement {BX,BLX}<cond> <reg>" as:
-static void
-install_preload (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, unsigned int rn)
-{
- ULONGEST rn_val;
- /* Preload instructions:
+ Preparation: cond <- instruction condition
+ Insn: mov r0, r0 (nop)
+ Cleanup: if (condition true) { r14 <- pc; pc <- dest; }.
- {pli/pld} [rn, #+/-imm]
- ->
- {pli/pld} [r0, #+/-imm]. */
+ Don't set r14 in cleanup for BX. */
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- rn_val = displaced_read_reg (regs, dsc, rn);
- displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
- dsc->u.preload.immed = 1;
+ dsc->u.branch.dest = displaced_read_reg (regs, dsc, rm);
- dsc->cleanup = &cleanup_preload;
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = link;
+
+ dsc->u.branch.exchange = 1;
+
+ dsc->cleanup = &cleanup_branch;
}
static int
-arm_copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- unsigned int rn = bits (insn, 16, 19);
-
- if (!insn_references_pc (insn, 0x000f0000ul))
- return arm_copy_unmodified (gdbarch, insn, "preload", dsc);
+ unsigned int cond = bits (insn, 28, 31);
+ /* BX: x12xxx1x
+ BLX: x12xxx3x. */
+ int link = bit (insn, 5);
+ unsigned int rm = bits (insn, 0, 3);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx",
(unsigned long) insn);
- dsc->modinsn[0] = insn & 0xfff0ffff;
-
- install_preload (gdbarch, regs, dsc, rn);
+ dsc->modinsn[0] = ARM_NOP;
+ install_bx_blx_reg (gdbarch, regs, dsc, link, cond, rm);
return 0;
}
static int
-thumb2_copy_preload (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
- struct regcache *regs, struct displaced_step_closure *dsc)
+thumb_copy_bx_blx_reg (struct gdbarch *gdbarch, uint16_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int rn = bits (insn1, 0, 3);
- unsigned int u_bit = bit (insn1, 7);
- int imm12 = bits (insn2, 0, 11);
- ULONGEST pc_val;
-
- if (rn != ARM_PC_REGNUM)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "preload", dsc);
+ int link = bit (insn, 7);
+ unsigned int rm = bits (insn, 3, 6);
- /* PC is only allowed to use in PLI (immediate,literal) Encoding T3, and
- PLD (literal) Encoding T1. */
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying pld/pli pc (0x%x) %c imm12 %.4x\n",
- (unsigned int) dsc->insn_addr, u_bit ? '+' : '-',
- imm12);
-
- if (!u_bit)
- imm12 = -1 * imm12;
-
- /* Rewrite instruction {pli/pld} PC imm12 into:
- Prepare: tmp[0] <- r0, tmp[1] <- r1, r0 <- pc, r1 <- imm12
-
- {pli/pld} [r0, r1]
-
- Cleanup: r0 <- tmp[0], r1 <- tmp[1]. */
-
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
-
- pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x",
+ (unsigned short) insn);
- displaced_write_reg (regs, dsc, 0, pc_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, imm12, CANNOT_WRITE_PC);
- dsc->u.preload.immed = 0;
+ dsc->modinsn[0] = THUMB_NOP;
- /* {pli/pld} [r0, r1] */
- dsc->modinsn[0] = insn1 & 0xfff0;
- dsc->modinsn[1] = 0xf001;
- dsc->numinsns = 2;
+ install_bx_blx_reg (gdbarch, regs, dsc, link, INST_AL, rm);
- dsc->cleanup = &cleanup_preload;
return 0;
}
-/* Preload instructions with register offset. */
+
+/* Copy/cleanup arithmetic/logic instruction with immediate RHS. */
static void
-install_preload_reg(struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, unsigned int rn,
- unsigned int rm)
+cleanup_alu_imm (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- ULONGEST rn_val, rm_val;
-
- /* Preload register-offset instructions:
-
- {pli/pld} [rn, rm {, shift}]
- ->
- {pli/pld} [r0, r1 {, shift}]. */
-
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
- rn_val = displaced_read_reg (regs, dsc, rn);
- rm_val = displaced_read_reg (regs, dsc, rm);
- displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, rm_val, CANNOT_WRITE_PC);
- dsc->u.preload.immed = 0;
-
- dsc->cleanup = &cleanup_preload;
+ ULONGEST rd_val = displaced_read_reg (regs, dsc, 0);
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
}
static int
-arm_copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_copy_alu_imm (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
unsigned int rn = bits (insn, 16, 19);
- unsigned int rm = bits (insn, 0, 3);
-
+ unsigned int rd = bits (insn, 12, 15);
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd);
+ ULONGEST rd_val, rn_val;
- if (!insn_references_pc (insn, 0x000f000ful))
- return arm_copy_unmodified (gdbarch, insn, "preload reg", dsc);
+ if (!insn_references_pc (insn, 0x000ff000ul))
+ return arm_copy_unmodified (gdbarch, insn, "ALU immediate", dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying immediate %s insn "
+ "%.8lx\n", is_mov ? "move" : "ALU",
(unsigned long) insn);
- dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1;
+ /* Instruction is of form:
- install_preload_reg (gdbarch, regs, dsc, rn, rm);
- return 0;
-}
+ <op><cond> rd, [rn,] #imm
-/* Copy/cleanup coprocessor load and store instructions. */
+ Rewrite as:
-static void
-cleanup_copro_load_store (struct gdbarch *gdbarch,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- ULONGEST rn_val = displaced_read_reg (regs, dsc, 0);
-
- displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
-
- if (dsc->u.ldst.writeback)
- displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, LOAD_WRITE_PC);
-}
-
-static void
-install_copro_load_store (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc,
- int writeback, unsigned int rn)
-{
- ULONGEST rn_val;
-
- /* Coprocessor load/store instructions:
-
- {stc/stc2} [<Rn>, #+/-imm] (and other immediate addressing modes)
- ->
- {stc/stc2} [r0, #+/-imm].
-
- ldc/ldc2 are handled identically. */
+ Preparation: tmp1, tmp2 <- r0, r1;
+ r0, r1 <- rd, rn
+ Insn: <op><cond> r0, r1, #imm
+ Cleanup: rd <- r0; r0 <- tmp1; r1 <- tmp2
+ */
dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
rn_val = displaced_read_reg (regs, dsc, rn);
- /* PC should be 4-byte aligned. */
- rn_val = rn_val & 0xfffffffc;
- displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+ rd_val = displaced_read_reg (regs, dsc, rd);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
- dsc->u.ldst.writeback = writeback;
- dsc->u.ldst.rn = rn;
+ if (is_mov)
+ dsc->modinsn[0] = insn & 0xfff00fff;
+ else
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x10000;
- dsc->cleanup = &cleanup_copro_load_store;
+ dsc->cleanup = &cleanup_alu_imm;
+
+ return 0;
}
static int
-arm_copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb2_copy_alu_imm (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int rn = bits (insn, 16, 19);
+ unsigned int op = bits (insn1, 5, 8);
+ unsigned int rn, rm, rd;
+ ULONGEST rd_val, rn_val;
- if (!insn_references_pc (insn, 0x000f0000ul))
- return arm_copy_unmodified (gdbarch, insn, "copro load/store", dsc);
+ rn = bits (insn1, 0, 3); /* Rn */
+ rm = bits (insn2, 0, 3); /* Rm */
+ rd = bits (insn2, 8, 11); /* Rd */
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
- "load/store insn %.8lx\n", (unsigned long) insn);
+ /* This routine is only called for instruction MOV. */
+ gdb_assert (op == 0x2 && rn == 0xf);
- dsc->modinsn[0] = insn & 0xfff0ffff;
+ if (rm != ARM_PC_REGNUM && rd != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "ALU imm", dsc);
- install_copro_load_store (gdbarch, regs, dsc, bit (insn, 25), rn);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x%.4x\n",
+ "ALU", insn1, insn2);
- return 0;
-}
+ /* Instruction is of form:
-static int
-thumb2_copy_copro_load_store (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int rn = bits (insn1, 0, 3);
+ <op><cond> rd, [rn,] #imm
- if (rn != ARM_PC_REGNUM)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "copro load/store", dsc);
+ Rewrite as:
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
- "load/store insn %.4x%.4x\n", insn1, insn2);
+ Preparation: tmp1, tmp2 <- r0, r1;
+ r0, r1 <- rd, rn
+ Insn: <op><cond> r0, r1, #imm
+ Cleanup: rd <- r0; r0 <- tmp1; r1 <- tmp2
+ */
- dsc->modinsn[0] = insn1 & 0xfff0;
- dsc->modinsn[1] = insn2;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ rd_val = displaced_read_reg (regs, dsc, rd);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
+
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = ((insn2 & 0xf0f0) | 0x1);
dsc->numinsns = 2;
- /* This function is called for copying instruction LDC/LDC2/VLDR, which
- doesn't support writeback, so pass 0. */
- install_copro_load_store (gdbarch, regs, dsc, 0, rn);
+ dsc->cleanup = &cleanup_alu_imm;
return 0;
}
-/* Clean up branch instructions (actually perform the branch, by setting
- PC). */
+/* Copy/cleanup arithmetic/logic insns with register RHS. */
static void
-cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
+cleanup_alu_reg (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
- int branch_taken = condition_true (dsc->u.branch.cond, status);
- enum pc_write_style write_pc = dsc->u.branch.exchange
- ? BX_WRITE_PC : BRANCH_WRITE_PC;
-
- if (!branch_taken)
- return;
-
- if (dsc->u.branch.link)
- {
- /* The value of LR should be the next insn of current one. In order
- not to confuse logic hanlding later insn `bx lr', if current insn mode
- is Thumb, the bit 0 of LR value should be set to 1. */
- ULONGEST next_insn_addr = dsc->insn_addr + dsc->insn_size;
+ ULONGEST rd_val;
+ int i;
- if (dsc->is_thumb)
- next_insn_addr |= 0x1;
+ rd_val = displaced_read_reg (regs, dsc, 0);
- displaced_write_reg (regs, dsc, ARM_LR_REGNUM, next_insn_addr,
- CANNOT_WRITE_PC);
- }
+ for (i = 0; i < 3; i++)
+ displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->u.branch.dest, write_pc);
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
}
-/* Copy B/BL/BLX instructions with immediate destinations. */
-
static void
-install_b_bl_blx (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc,
- unsigned int cond, int exchange, int link, long offset)
+install_alu_reg (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ unsigned int rd, unsigned int rn, unsigned int rm)
{
- /* Implement "BL<cond> <label>" as:
+ ULONGEST rd_val, rn_val, rm_val;
- Preparation: cond <- instruction condition
- Insn: mov r0, r0 (nop)
- Cleanup: if (condition true) { r14 <- pc; pc <- label }.
+ /* Instruction is of form:
- B<cond> similar, but don't set r14 in cleanup. */
+ <op><cond> rd, [rn,] rm [, <shift>]
- dsc->u.branch.cond = cond;
- dsc->u.branch.link = link;
- dsc->u.branch.exchange = exchange;
+ Rewrite as:
- dsc->u.branch.dest = dsc->insn_addr;
- if (link && exchange)
- /* For BLX, offset is computed from the Align (PC, 4). */
- dsc->u.branch.dest = dsc->u.branch.dest & 0xfffffffc;
+ Preparation: tmp1, tmp2, tmp3 <- r0, r1, r2;
+ r0, r1, r2 <- rd, rn, rm
+ Insn: <op><cond> r0, [r1,] r2 [, <shift>]
+ Cleanup: rd <- r0; r0, r1, r2 <- tmp1, tmp2, tmp3
+ */
- if (dsc->is_thumb)
- dsc->u.branch.dest += 4 + offset;
- else
- dsc->u.branch.dest += 8 + offset;
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
+ dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
+ rd_val = displaced_read_reg (regs, dsc, rd);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ rm_val = displaced_read_reg (regs, dsc, rm);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
- dsc->cleanup = &cleanup_branch;
+ dsc->cleanup = &cleanup_alu_reg;
}
+
static int
-arm_copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_copy_alu_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int cond = bits (insn, 28, 31);
- int exchange = (cond == 0xf);
- int link = exchange || bit (insn, 24);
- long offset;
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd);
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return arm_copy_unmodified (gdbarch, insn, "ALU reg", dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
- "%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
- (unsigned long) insn);
- if (exchange)
- /* For BLX, set bit 0 of the destination. The cleanup_branch function will
- then arrange the switch into Thumb mode. */
- offset = (bits (insn, 0, 23) << 2) | (bit (insn, 24) << 1) | 1;
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.8lx\n",
+ is_mov ? "move" : "ALU", (unsigned long) insn);
+
+ if (is_mov)
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x2;
else
- offset = bits (insn, 0, 23) << 2;
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x10002;
- if (bit (offset, 25))
- offset = offset | ~0x3ffffff;
+ install_alu_reg (gdbarch, regs, dsc, bits (insn, 12, 15), bits (insn, 16, 19),
+ bits (insn, 0, 3));
+ return 0;
+}
- dsc->modinsn[0] = ARM_NOP;
+static int
+thumb_copy_alu_reg (struct gdbarch *gdbarch, uint16_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned rm, rd;
+
+ rm = bits (insn, 3, 6);
+ rd = (bit (insn, 7) << 3) | bits (insn, 0, 2);
+
+ if (rd != ARM_PC_REGNUM && rm != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_16bit (gdbarch, insn, "ALU reg", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying ALU reg insn %.4x\n",
+ (unsigned short) insn);
+
+ dsc->modinsn[0] = ((insn & 0xff00) | 0x10);
+
+ install_alu_reg (gdbarch, regs, dsc, rd, rd, rm);
- install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
return 0;
}
-static int
-thumb2_copy_b_bl_blx (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
+/* Cleanup/copy arithmetic/logic insns with shifted register RHS. */
+
+static void
+cleanup_alu_shifted_reg (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- int link = bit (insn2, 14);
- int exchange = link && !bit (insn2, 12);
- int cond = INST_AL;
- long offset = 0;
- int j1 = bit (insn2, 13);
- int j2 = bit (insn2, 11);
- int s = sbits (insn1, 10, 10);
- int i1 = !(j1 ^ bit (insn1, 10));
- int i2 = !(j2 ^ bit (insn1, 10));
+ ULONGEST rd_val = displaced_read_reg (regs, dsc, 0);
+ int i;
- if (!link && !exchange) /* B */
- {
- offset = (bits (insn2, 0, 10) << 1);
- if (bit (insn2, 12)) /* Encoding T4 */
- {
- offset |= (bits (insn1, 0, 9) << 12)
- | (i2 << 22)
- | (i1 << 23)
- | (s << 24);
- cond = INST_AL;
- }
- else /* Encoding T3 */
- {
- offset |= (bits (insn1, 0, 5) << 12)
- | (j1 << 18)
- | (j2 << 19)
- | (s << 20);
- cond = bits (insn1, 6, 9);
- }
- }
- else
- {
- offset = (bits (insn1, 0, 9) << 12);
- offset |= ((i2 << 22) | (i1 << 23) | (s << 24));
- offset |= exchange ?
- (bits (insn2, 1, 10) << 2) : (bits (insn2, 0, 10) << 1);
- }
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying %s insn "
- "%.4x %.4x with offset %.8lx\n",
- link ? (exchange) ? "blx" : "bl" : "b",
- insn1, insn2, offset);
-
- dsc->modinsn[0] = THUMB_NOP;
-
- install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
- return 0;
-}
-
-/* Copy B Thumb instructions. */
-static int
-thumb_copy_b (struct gdbarch *gdbarch, unsigned short insn,
- struct displaced_step_closure *dsc)
-{
- unsigned int cond = 0;
- int offset = 0;
- unsigned short bit_12_15 = bits (insn, 12, 15);
- CORE_ADDR from = dsc->insn_addr;
-
- if (bit_12_15 == 0xd)
- {
- /* offset = SignExtend (imm8:0, 32) */
- offset = sbits ((insn << 1), 0, 8);
- cond = bits (insn, 8, 11);
- }
- else if (bit_12_15 == 0xe) /* Encoding T2 */
- {
- offset = sbits ((insn << 1), 0, 11);
- cond = INST_AL;
- }
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying b immediate insn %.4x "
- "with offset %d\n", insn, offset);
-
- dsc->u.branch.cond = cond;
- dsc->u.branch.link = 0;
- dsc->u.branch.exchange = 0;
- dsc->u.branch.dest = from + 4 + offset;
-
- dsc->modinsn[0] = THUMB_NOP;
-
- dsc->cleanup = &cleanup_branch;
+ for (i = 0; i < 4; i++)
+ displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
- return 0;
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
}
-/* Copy BX/BLX with register-specified destinations. */
-
static void
-install_bx_blx_reg (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, int link,
- unsigned int cond, unsigned int rm)
+install_alu_shifted_reg (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ unsigned int rd, unsigned int rn, unsigned int rm,
+ unsigned rs)
{
- /* Implement {BX,BLX}<cond> <reg>" as:
+ int i;
+ ULONGEST rd_val, rn_val, rm_val, rs_val;
- Preparation: cond <- instruction condition
- Insn: mov r0, r0 (nop)
- Cleanup: if (condition true) { r14 <- pc; pc <- dest; }.
+ /* Instruction is of form:
- Don't set r14 in cleanup for BX. */
+ <op><cond> rd, [rn,] rm, <shift> rs
- dsc->u.branch.dest = displaced_read_reg (regs, dsc, rm);
+ Rewrite as:
- dsc->u.branch.cond = cond;
- dsc->u.branch.link = link;
+ Preparation: tmp1, tmp2, tmp3, tmp4 <- r0, r1, r2, r3
+ r0, r1, r2, r3 <- rd, rn, rm, rs
+ Insn: <op><cond> r0, r1, r2, <shift> r3
+ Cleanup: tmp5 <- r0
+ r0, r1, r2, r3 <- tmp1, tmp2, tmp3, tmp4
+ rd <- tmp5
+ */
- dsc->u.branch.exchange = 1;
+ for (i = 0; i < 4; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
- dsc->cleanup = &cleanup_branch;
+ rd_val = displaced_read_reg (regs, dsc, rd);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ rm_val = displaced_read_reg (regs, dsc, rm);
+ rs_val = displaced_read_reg (regs, dsc, rs);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 3, rs_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
+ dsc->cleanup = &cleanup_alu_shifted_reg;
}
static int
-arm_copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_copy_alu_shifted_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int cond = bits (insn, 28, 31);
- /* BX: x12xxx1x
- BLX: x12xxx3x. */
- int link = bit (insn, 5);
- unsigned int rm = bits (insn, 0, 3);
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd);
+ unsigned int rd, rn, rm, rs;
+
+ if (!insn_references_pc (insn, 0x000fff0ful))
+ return arm_copy_unmodified (gdbarch, insn, "ALU shifted reg", dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx",
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying shifted reg %s insn "
+ "%.8lx\n", is_mov ? "move" : "ALU",
(unsigned long) insn);
- dsc->modinsn[0] = ARM_NOP;
+ rn = bits (insn, 16, 19);
+ rm = bits (insn, 0, 3);
+ rs = bits (insn, 8, 11);
+ rd = bits (insn, 12, 15);
+
+ if (is_mov)
+ dsc->modinsn[0] = (insn & 0xfff000f0) | 0x302;
+ else
+ dsc->modinsn[0] = (insn & 0xfff000f0) | 0x10302;
+
+ install_alu_shifted_reg (gdbarch, regs, dsc, rd, rn, rm, rs);
- install_bx_blx_reg (gdbarch, regs, dsc, link, cond, rm);
return 0;
}
-static int
-thumb_copy_bx_blx_reg (struct gdbarch *gdbarch, uint16_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- int link = bit (insn, 7);
- unsigned int rm = bits (insn, 3, 6);
+/* Clean up load instructions. */
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x",
- (unsigned short) insn);
+static void
+cleanup_load (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST rt_val, rt_val2 = 0, rn_val;
- dsc->modinsn[0] = THUMB_NOP;
+ rt_val = displaced_read_reg (regs, dsc, 0);
+ if (dsc->u.ldst.xfersize == 8)
+ rt_val2 = displaced_read_reg (regs, dsc, 1);
+ rn_val = displaced_read_reg (regs, dsc, 2);
- install_bx_blx_reg (gdbarch, regs, dsc, link, INST_AL, rm);
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ if (dsc->u.ldst.xfersize > 4)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.immed)
+ displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
- return 0;
+ /* Handle register writeback. */
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
+ /* Put result in right place. */
+ displaced_write_reg (regs, dsc, dsc->rd, rt_val, LOAD_WRITE_PC);
+ if (dsc->u.ldst.xfersize == 8)
+ displaced_write_reg (regs, dsc, dsc->rd + 1, rt_val2, LOAD_WRITE_PC);
}
-
-/* Copy/cleanup arithmetic/logic instruction with immediate RHS. */
+/* Clean up store instructions. */
static void
-cleanup_alu_imm (struct gdbarch *gdbarch,
- struct regcache *regs, struct displaced_step_closure *dsc)
+cleanup_store (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- ULONGEST rd_val = displaced_read_reg (regs, dsc, 0);
+ ULONGEST rn_val = displaced_read_reg (regs, dsc, 2);
+
displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
+ if (dsc->u.ldst.xfersize > 4)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.immed)
+ displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.restore_r4)
+ displaced_write_reg (regs, dsc, 4, dsc->tmp[4], CANNOT_WRITE_PC);
+
+ /* Writeback. */
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
}
+/* Copy "extra" load/store instructions. These are halfword/doubleword
+ transfers, which have a different encoding to byte/word transfers. */
+
static int
-arm_copy_alu_imm (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_copy_extra_ld_st (struct gdbarch *gdbarch, uint32_t insn, int unprivileged,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
+ unsigned int op1 = bits (insn, 20, 24);
+ unsigned int op2 = bits (insn, 5, 6);
+ unsigned int rt = bits (insn, 12, 15);
unsigned int rn = bits (insn, 16, 19);
- unsigned int rd = bits (insn, 12, 15);
- unsigned int op = bits (insn, 21, 24);
- int is_mov = (op == 0xd);
- ULONGEST rd_val, rn_val;
+ unsigned int rm = bits (insn, 0, 3);
+ char load[12] = {0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1};
+ char bytesize[12] = {2, 2, 2, 2, 8, 1, 8, 1, 8, 2, 8, 2};
+ int immed = (op1 & 0x4) != 0;
+ int opcode;
+ ULONGEST rt_val, rt_val2 = 0, rn_val, rm_val = 0;
- if (!insn_references_pc (insn, 0x000ff000ul))
- return arm_copy_unmodified (gdbarch, insn, "ALU immediate", dsc);
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return arm_copy_unmodified (gdbarch, insn, "extra load/store", dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying immediate %s insn "
- "%.8lx\n", is_mov ? "move" : "ALU",
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %sextra load/store "
+ "insn %.8lx\n", unprivileged ? "unprivileged " : "",
(unsigned long) insn);
- /* Instruction is of form:
-
- <op><cond> rd, [rn,] #imm
-
- Rewrite as:
+ opcode = ((op2 << 2) | (op1 & 0x1) | ((op1 & 0x4) >> 1)) - 4;
- Preparation: tmp1, tmp2 <- r0, r1;
- r0, r1 <- rd, rn
- Insn: <op><cond> r0, r1, #imm
- Cleanup: rd <- r0; r0 <- tmp1; r1 <- tmp2
- */
+ if (opcode < 0)
+ internal_error (__FILE__, __LINE__,
+ _("copy_extra_ld_st: instruction decode error"));
dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
- rn_val = displaced_read_reg (regs, dsc, rn);
- rd_val = displaced_read_reg (regs, dsc, rd);
- displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
- dsc->rd = rd;
+ dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
+ if (!immed)
+ dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
- if (is_mov)
- dsc->modinsn[0] = insn & 0xfff00fff;
- else
- dsc->modinsn[0] = (insn & 0xfff00fff) | 0x10000;
+ rt_val = displaced_read_reg (regs, dsc, rt);
+ if (bytesize[opcode] == 8)
+ rt_val2 = displaced_read_reg (regs, dsc, rt + 1);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ if (!immed)
+ rm_val = displaced_read_reg (regs, dsc, rm);
- dsc->cleanup = &cleanup_alu_imm;
+ displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
+ if (bytesize[opcode] == 8)
+ displaced_write_reg (regs, dsc, 1, rt_val2, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
+ if (!immed)
+ displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
- return 0;
-}
+ dsc->rd = rt;
+ dsc->u.ldst.xfersize = bytesize[opcode];
+ dsc->u.ldst.rn = rn;
+ dsc->u.ldst.immed = immed;
+ dsc->u.ldst.writeback = bit (insn, 24) == 0 || bit (insn, 21) != 0;
+ dsc->u.ldst.restore_r4 = 0;
-static int
-thumb2_copy_alu_imm (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int op = bits (insn1, 5, 8);
- unsigned int rn, rm, rd;
- ULONGEST rd_val, rn_val;
+ if (immed)
+ /* {ldr,str}<width><cond> rt, [rt2,] [rn, #imm]
+ ->
+ {ldr,str}<width><cond> r0, [r1,] [r2, #imm]. */
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
+ else
+ /* {ldr,str}<width><cond> rt, [rt2,] [rn, +/-rm]
+ ->
+ {ldr,str}<width><cond> r0, [r1,] [r2, +/-r3]. */
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
- rn = bits (insn1, 0, 3); /* Rn */
- rm = bits (insn2, 0, 3); /* Rm */
- rd = bits (insn2, 8, 11); /* Rd */
+ dsc->cleanup = load[opcode] ? &cleanup_load : &cleanup_store;
- /* This routine is only called for instruction MOV. */
- gdb_assert (op == 0x2 && rn == 0xf);
+ return 0;
+}
- if (rm != ARM_PC_REGNUM && rd != ARM_PC_REGNUM)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "ALU imm", dsc);
+/* Copy byte/half word/word loads and stores. */
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x%.4x\n",
- "ALU", insn1, insn2);
+static void
+install_load_store (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, int load,
+ int immed, int writeback, int size, int usermode,
+ int rt, int rm, int rn)
+{
+ ULONGEST rt_val, rn_val, rm_val = 0;
- /* Instruction is of form:
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
+ if (!immed)
+ dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
+ if (!load)
+ dsc->tmp[4] = displaced_read_reg (regs, dsc, 4);
- <op><cond> rd, [rn,] #imm
+ rt_val = displaced_read_reg (regs, dsc, rt);
+ rn_val = displaced_read_reg (regs, dsc, rn);
+ if (!immed)
+ rm_val = displaced_read_reg (regs, dsc, rm);
- Rewrite as:
+ displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
+ if (!immed)
+ displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
+ dsc->rd = rt;
+ dsc->u.ldst.xfersize = size;
+ dsc->u.ldst.rn = rn;
+ dsc->u.ldst.immed = immed;
+ dsc->u.ldst.writeback = writeback;
- Preparation: tmp1, tmp2 <- r0, r1;
- r0, r1 <- rd, rn
- Insn: <op><cond> r0, r1, #imm
- Cleanup: rd <- r0; r0 <- tmp1; r1 <- tmp2
- */
+ /* To write PC we can do:
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
- rn_val = displaced_read_reg (regs, dsc, rn);
- rd_val = displaced_read_reg (regs, dsc, rd);
- displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
- dsc->rd = rd;
+ Before this sequence of instructions:
+ r0 is the PC value got from displaced_read_reg, so r0 = from + 8;
+ r2 is the Rn value got from dispalced_read_reg.
- dsc->modinsn[0] = insn1;
- dsc->modinsn[1] = ((insn2 & 0xf0f0) | 0x1);
- dsc->numinsns = 2;
+ Insn1: push {pc} Write address of STR instruction + offset on stack
+ Insn2: pop {r4} Read it back from stack, r4 = addr(Insn1) + offset
+ Insn3: sub r4, r4, pc r4 = addr(Insn1) + offset - pc
+ = addr(Insn1) + offset - addr(Insn3) - 8
+ = offset - 16
+ Insn4: add r4, r4, #8 r4 = offset - 8
+ Insn5: add r0, r0, r4 r0 = from + 8 + offset - 8
+ = from + offset
+ Insn6: str r0, [r2, #imm] (or str r0, [r2, r3])
- dsc->cleanup = &cleanup_alu_imm;
+ Otherwise we don't know what value to write for PC, since the offset is
+ architecture-dependent (sometimes PC+8, sometimes PC+12). More details
+ of this can be found in Section "Saving from r15" in
+ http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204g/Cihbjifh.html */
- return 0;
+ dsc->cleanup = load ? &cleanup_load : &cleanup_store;
}
-/* Copy/cleanup arithmetic/logic insns with register RHS. */
-static void
-cleanup_alu_reg (struct gdbarch *gdbarch,
- struct regcache *regs, struct displaced_step_closure *dsc)
+static int
+thumb2_copy_load_literal (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc, int size)
{
- ULONGEST rd_val;
- int i;
-
- rd_val = displaced_read_reg (regs, dsc, 0);
+ unsigned int u_bit = bit (insn1, 7);
+ unsigned int rt = bits (insn2, 12, 15);
+ int imm12 = bits (insn2, 0, 11);
+ ULONGEST pc_val;
- for (i = 0; i < 3; i++)
- displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying ldr pc (0x%x) R%d %c imm12 %.4x\n",
+ (unsigned int) dsc->insn_addr, rt, u_bit ? '+' : '-',
+ imm12);
- displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
-}
+ if (!u_bit)
+ imm12 = -1 * imm12;
-static void
-install_alu_reg (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc,
- unsigned int rd, unsigned int rn, unsigned int rm)
-{
- ULONGEST rd_val, rn_val, rm_val;
+ /* Rewrite instruction LDR Rt imm12 into:
- /* Instruction is of form:
+ Prepare: tmp[0] <- r0, tmp[1] <- r2, tmp[2] <- r3, r2 <- pc, r3 <- imm12
- <op><cond> rd, [rn,] rm [, <shift>]
+ LDR R0, R2, R3,
- Rewrite as:
+ Cleanup: rt <- r0, r0 <- tmp[0], r2 <- tmp[1], r3 <- tmp[2]. */
- Preparation: tmp1, tmp2, tmp3 <- r0, r1, r2;
- r0, r1, r2 <- rd, rn, rm
- Insn: <op><cond> r0, r1, r2 [, <shift>]
- Cleanup: rd <- r0; r0, r1, r2 <- tmp1, tmp2, tmp3
- */
dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
- rd_val = displaced_read_reg (regs, dsc, rd);
- rn_val = displaced_read_reg (regs, dsc, rn);
- rm_val = displaced_read_reg (regs, dsc, rm);
- displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
- dsc->rd = rd;
+ dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
- dsc->cleanup = &cleanup_alu_reg;
-}
+ pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
-static int
-arm_copy_alu_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int op = bits (insn, 21, 24);
- int is_mov = (op == 0xd);
+ pc_val = pc_val & 0xfffffffc;
- if (!insn_references_pc (insn, 0x000ff00ful))
- return arm_copy_unmodified (gdbarch, insn, "ALU reg", dsc);
+ displaced_write_reg (regs, dsc, 2, pc_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 3, imm12, CANNOT_WRITE_PC);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.8lx\n",
- is_mov ? "move" : "ALU", (unsigned long) insn);
+ dsc->rd = rt;
- if (is_mov)
- dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x2;
- else
- dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x10002;
+ dsc->u.ldst.xfersize = size;
+ dsc->u.ldst.immed = 0;
+ dsc->u.ldst.writeback = 0;
+ dsc->u.ldst.restore_r4 = 0;
+
+ /* LDR R0, R2, R3 */
+ dsc->modinsn[0] = 0xf852;
+ dsc->modinsn[1] = 0x3;
+ dsc->numinsns = 2;
+
+ dsc->cleanup = &cleanup_load;
- install_alu_reg (gdbarch, regs, dsc, bits (insn, 12, 15), bits (insn, 16, 19),
- bits (insn, 0, 3));
return 0;
}
static int
-thumb_copy_alu_reg (struct gdbarch *gdbarch, uint16_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb2_copy_load_reg_imm (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ int writeback, int immed)
{
- unsigned rn, rm, rd;
-
- rd = bits (insn, 3, 6);
- rn = (bit (insn, 7) << 3) | bits (insn, 0, 2);
- rm = 2;
+ unsigned int rt = bits (insn2, 12, 15);
+ unsigned int rn = bits (insn1, 0, 3);
+ unsigned int rm = bits (insn2, 0, 3); /* Only valid if !immed. */
+ /* In LDR (register), there is also a register Rm, which is not allowed to
+ be PC, so we don't have to check it. */
- if (rd != ARM_PC_REGNUM && rn != ARM_PC_REGNUM)
- return thumb_copy_unmodified_16bit (gdbarch, insn, "ALU reg", dsc);
+ if (rt != ARM_PC_REGNUM && rn != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "load",
+ dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x\n",
- "ALU", (unsigned short) insn);
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying ldr r%d [r%d] insn %.4x%.4x\n",
+ rt, rn, insn1, insn2);
+
+ install_load_store (gdbarch, regs, dsc, 1, immed, writeback, 4,
+ 0, rt, rm, rn);
+
+ dsc->u.ldst.restore_r4 = 0;
- dsc->modinsn[0] = ((insn & 0xff00) | 0x08);
+ if (immed)
+ /* ldr[b]<cond> rt, [rn, #imm], etc.
+ ->
+ ldr[b]<cond> r0, [r2, #imm]. */
+ {
+ dsc->modinsn[0] = (insn1 & 0xfff0) | 0x2;
+ dsc->modinsn[1] = insn2 & 0x0fff;
+ }
+ else
+ /* ldr[b]<cond> rt, [rn, rm], etc.
+ ->
+ ldr[b]<cond> r0, [r2, r3]. */
+ {
+ dsc->modinsn[0] = (insn1 & 0xfff0) | 0x2;
+ dsc->modinsn[1] = (insn2 & 0x0ff0) | 0x3;
+ }
- install_alu_reg (gdbarch, regs, dsc, rd, rn, rm);
+ dsc->numinsns = 2;
return 0;
}
-/* Cleanup/copy arithmetic/logic insns with shifted register RHS. */
-static void
-cleanup_alu_shifted_reg (struct gdbarch *gdbarch,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- ULONGEST rd_val = displaced_read_reg (regs, dsc, 0);
- int i;
+static int
+arm_copy_ldr_str_ldrb_strb (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ int load, int size, int usermode)
+{
+ int immed = !bit (insn, 25);
+ int writeback = (bit (insn, 24) == 0 || bit (insn, 21) != 0);
+ unsigned int rt = bits (insn, 12, 15);
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3); /* Only valid if !immed. */
- for (i = 0; i < 4; i++)
- displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return arm_copy_unmodified (gdbarch, insn, "load/store", dsc);
- displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
-}
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying %s%s r%d [r%d] insn %.8lx\n",
+ load ? (size == 1 ? "ldrb" : "ldr")
+ : (size == 1 ? "strb" : "str"), usermode ? "t" : "",
+ rt, rn,
+ (unsigned long) insn);
-static void
-install_alu_shifted_reg (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc,
- unsigned int rd, unsigned int rn, unsigned int rm,
- unsigned rs)
-{
- int i;
- ULONGEST rd_val, rn_val, rm_val, rs_val;
+ install_load_store (gdbarch, regs, dsc, load, immed, writeback, size,
+ usermode, rt, rm, rn);
- /* Instruction is of form:
+ if (load || rt != ARM_PC_REGNUM)
+ {
+ dsc->u.ldst.restore_r4 = 0;
- <op><cond> rd, [rn,] rm, <shift> rs
+ if (immed)
+ /* {ldr,str}[b]<cond> rt, [rn, #imm], etc.
+ ->
+ {ldr,str}[b]<cond> r0, [r2, #imm]. */
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
+ else
+ /* {ldr,str}[b]<cond> rt, [rn, rm], etc.
+ ->
+ {ldr,str}[b]<cond> r0, [r2, r3]. */
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
+ }
+ else
+ {
+ /* We need to use r4 as scratch. Make sure it's restored afterwards. */
+ dsc->u.ldst.restore_r4 = 1;
+ dsc->modinsn[0] = 0xe92d8000; /* push {pc} */
+ dsc->modinsn[1] = 0xe8bd0010; /* pop {r4} */
+ dsc->modinsn[2] = 0xe044400f; /* sub r4, r4, pc. */
+ dsc->modinsn[3] = 0xe2844008; /* add r4, r4, #8. */
+ dsc->modinsn[4] = 0xe0800004; /* add r0, r0, r4. */
- Rewrite as:
+ /* As above. */
+ if (immed)
+ dsc->modinsn[5] = (insn & 0xfff00fff) | 0x20000;
+ else
+ dsc->modinsn[5] = (insn & 0xfff00ff0) | 0x20003;
- Preparation: tmp1, tmp2, tmp3, tmp4 <- r0, r1, r2, r3
- r0, r1, r2, r3 <- rd, rn, rm, rs
- Insn: <op><cond> r0, r1, r2, <shift> r3
- Cleanup: tmp5 <- r0
- r0, r1, r2, r3 <- tmp1, tmp2, tmp3, tmp4
- rd <- tmp5
- */
+ dsc->numinsns = 6;
+ }
- for (i = 0; i < 4; i++)
- dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
+ dsc->cleanup = load ? &cleanup_load : &cleanup_store;
- rd_val = displaced_read_reg (regs, dsc, rd);
- rn_val = displaced_read_reg (regs, dsc, rn);
- rm_val = displaced_read_reg (regs, dsc, rm);
- rs_val = displaced_read_reg (regs, dsc, rs);
- displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 3, rs_val, CANNOT_WRITE_PC);
- dsc->rd = rd;
- dsc->cleanup = &cleanup_alu_shifted_reg;
+ return 0;
}
-static int
-arm_copy_alu_shifted_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int op = bits (insn, 21, 24);
- int is_mov = (op == 0xd);
- unsigned int rd, rn, rm, rs;
+/* Cleanup LDM instructions with fully-populated register list. This is an
+ unfortunate corner case: it's impossible to implement correctly by modifying
+ the instruction. The issue is as follows: we have an instruction,
- if (!insn_references_pc (insn, 0x000fff0ful))
- return arm_copy_unmodified (gdbarch, insn, "ALU shifted reg", dsc);
+ ldm rN, {r0-r15}
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying shifted reg %s insn "
- "%.8lx\n", is_mov ? "move" : "ALU",
- (unsigned long) insn);
+ which we must rewrite to avoid loading PC. A possible solution would be to
+ do the load in two halves, something like (with suitable cleanup
+ afterwards):
- rn = bits (insn, 16, 19);
- rm = bits (insn, 0, 3);
- rs = bits (insn, 8, 11);
- rd = bits (insn, 12, 15);
+ mov r8, rN
+ ldm[id][ab] r8!, {r0-r7}
+ str r7, <temp>
+ ldm[id][ab] r8, {r7-r14}
+ <bkpt>
- if (is_mov)
- dsc->modinsn[0] = (insn & 0xfff000f0) | 0x302;
- else
- dsc->modinsn[0] = (insn & 0xfff000f0) | 0x10302;
+ but at present there's no suitable place for <temp>, since the scratch space
+ is overwritten before the cleanup routine is called. For now, we simply
+ emulate the instruction. */
- install_alu_shifted_reg (gdbarch, regs, dsc, rd, rn, rm, rs);
+static void
+cleanup_block_load_all (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ int inc = dsc->u.block.increment;
+ int bump_before = dsc->u.block.before ? (inc ? 4 : -4) : 0;
+ int bump_after = dsc->u.block.before ? 0 : (inc ? 4 : -4);
+ uint32_t regmask = dsc->u.block.regmask;
+ int regno = inc ? 0 : 15;
+ CORE_ADDR xfer_addr = dsc->u.block.xfer_addr;
+ int exception_return = dsc->u.block.load && dsc->u.block.user
+ && (regmask & 0x8000) != 0;
+ uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
+ int do_transfer = condition_true (dsc->u.block.cond, status);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- return 0;
-}
+ if (!do_transfer)
+ return;
-/* Clean up load instructions. */
+ /* If the instruction is ldm rN, {...pc}^, I don't think there's anything
+ sensible we can do here. Complain loudly. */
+ if (exception_return)
+ error (_("Cannot single-step exception return"));
-static void
-cleanup_load (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- ULONGEST rt_val, rt_val2 = 0, rn_val;
+ /* We don't handle any stores here for now. */
+ gdb_assert (dsc->u.block.load != 0);
- rt_val = displaced_read_reg (regs, dsc, 0);
- if (dsc->u.ldst.xfersize == 8)
- rt_val2 = displaced_read_reg (regs, dsc, 1);
- rn_val = displaced_read_reg (regs, dsc, 2);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: emulating block transfer: "
+ "%s %s %s\n", dsc->u.block.load ? "ldm" : "stm",
+ dsc->u.block.increment ? "inc" : "dec",
+ dsc->u.block.before ? "before" : "after");
- displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
- if (dsc->u.ldst.xfersize > 4)
- displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
- if (!dsc->u.ldst.immed)
- displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
+ while (regmask)
+ {
+ uint32_t memword;
- /* Handle register writeback. */
- if (dsc->u.ldst.writeback)
- displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
- /* Put result in right place. */
- displaced_write_reg (regs, dsc, dsc->rd, rt_val, LOAD_WRITE_PC);
- if (dsc->u.ldst.xfersize == 8)
- displaced_write_reg (regs, dsc, dsc->rd + 1, rt_val2, LOAD_WRITE_PC);
-}
+ if (inc)
+ while (regno <= ARM_PC_REGNUM && (regmask & (1 << regno)) == 0)
+ regno++;
+ else
+ while (regno >= 0 && (regmask & (1 << regno)) == 0)
+ regno--;
-/* Clean up store instructions. */
+ xfer_addr += bump_before;
-static void
-cleanup_store (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- ULONGEST rn_val = displaced_read_reg (regs, dsc, 2);
+ memword = read_memory_unsigned_integer (xfer_addr, 4, byte_order);
+ displaced_write_reg (regs, dsc, regno, memword, LOAD_WRITE_PC);
- displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
- if (dsc->u.ldst.xfersize > 4)
- displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
- if (!dsc->u.ldst.immed)
- displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
- if (!dsc->u.ldst.restore_r4)
- displaced_write_reg (regs, dsc, 4, dsc->tmp[4], CANNOT_WRITE_PC);
+ xfer_addr += bump_after;
- /* Writeback. */
- if (dsc->u.ldst.writeback)
- displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
+ regmask &= ~(1 << regno);
+ }
+
+ if (dsc->u.block.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.block.rn, xfer_addr,
+ CANNOT_WRITE_PC);
}
-/* Copy "extra" load/store instructions. These are halfword/doubleword
- transfers, which have a different encoding to byte/word transfers. */
+/* Clean up an STM which included the PC in the register list. */
-static int
-arm_copy_extra_ld_st (struct gdbarch *gdbarch, uint32_t insn, int unpriveleged,
- struct regcache *regs, struct displaced_step_closure *dsc)
+static void
+cleanup_block_store_pc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int op1 = bits (insn, 20, 24);
- unsigned int op2 = bits (insn, 5, 6);
- unsigned int rt = bits (insn, 12, 15);
- unsigned int rn = bits (insn, 16, 19);
- unsigned int rm = bits (insn, 0, 3);
- char load[12] = {0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1};
- char bytesize[12] = {2, 2, 2, 2, 8, 1, 8, 1, 8, 2, 8, 2};
- int immed = (op1 & 0x4) != 0;
- int opcode;
- ULONGEST rt_val, rt_val2 = 0, rn_val, rm_val = 0;
-
- if (!insn_references_pc (insn, 0x000ff00ful))
- return arm_copy_unmodified (gdbarch, insn, "extra load/store", dsc);
+ uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
+ int store_executed = condition_true (dsc->u.block.cond, status);
+ CORE_ADDR pc_stored_at, transferred_regs = bitcount (dsc->u.block.regmask);
+ CORE_ADDR stm_insn_addr;
+ uint32_t pc_val;
+ long offset;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying %sextra load/store "
- "insn %.8lx\n", unpriveleged ? "unpriveleged " : "",
- (unsigned long) insn);
+ /* If condition code fails, there's nothing else to do. */
+ if (!store_executed)
+ return;
- opcode = ((op2 << 2) | (op1 & 0x1) | ((op1 & 0x4) >> 1)) - 4;
+ if (dsc->u.block.increment)
+ {
+ pc_stored_at = dsc->u.block.xfer_addr + 4 * transferred_regs;
- if (opcode < 0)
- internal_error (__FILE__, __LINE__,
- _("copy_extra_ld_st: instruction decode error"));
-
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[1] = displaced_read_reg (regs, dsc, 1);
- dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
- if (!immed)
- dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
-
- rt_val = displaced_read_reg (regs, dsc, rt);
- if (bytesize[opcode] == 8)
- rt_val2 = displaced_read_reg (regs, dsc, rt + 1);
- rn_val = displaced_read_reg (regs, dsc, rn);
- if (!immed)
- rm_val = displaced_read_reg (regs, dsc, rm);
-
- displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
- if (bytesize[opcode] == 8)
- displaced_write_reg (regs, dsc, 1, rt_val2, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
- if (!immed)
- displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
+ if (dsc->u.block.before)
+ pc_stored_at += 4;
+ }
+ else
+ {
+ pc_stored_at = dsc->u.block.xfer_addr;
- dsc->rd = rt;
- dsc->u.ldst.xfersize = bytesize[opcode];
- dsc->u.ldst.rn = rn;
- dsc->u.ldst.immed = immed;
- dsc->u.ldst.writeback = bit (insn, 24) == 0 || bit (insn, 21) != 0;
- dsc->u.ldst.restore_r4 = 0;
+ if (dsc->u.block.before)
+ pc_stored_at -= 4;
+ }
- if (immed)
- /* {ldr,str}<width><cond> rt, [rt2,] [rn, #imm]
- ->
- {ldr,str}<width><cond> r0, [r1,] [r2, #imm]. */
- dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
- else
- /* {ldr,str}<width><cond> rt, [rt2,] [rn, +/-rm]
- ->
- {ldr,str}<width><cond> r0, [r1,] [r2, +/-r3]. */
- dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
+ pc_val = read_memory_unsigned_integer (pc_stored_at, 4, byte_order);
+ stm_insn_addr = dsc->scratch_base;
+ offset = pc_val - stm_insn_addr;
- dsc->cleanup = load[opcode] ? &cleanup_load : &cleanup_store;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: detected PC offset %.8lx for "
+ "STM instruction\n", offset);
- return 0;
+ /* Rewrite the stored PC to the proper value for the non-displaced original
+ instruction. */
+ write_memory_unsigned_integer (pc_stored_at, 4, byte_order,
+ dsc->insn_addr + offset);
}
-/* Copy byte/half word/word loads and stores. */
+/* Clean up an LDM which includes the PC in the register list. We clumped all
+ the registers in the transferred list into a contiguous range r0...rX (to
+ avoid loading PC directly and losing control of the debugged program), so we
+ must undo that here. */
static void
-install_load_store (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, int load,
- int immed, int writeback, int size, int usermode,
- int rt, int rm, int rn)
+cleanup_block_load_pc (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- ULONGEST rt_val, rn_val, rm_val = 0;
+ uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
+ int load_executed = condition_true (dsc->u.block.cond, status);
+ unsigned int mask = dsc->u.block.regmask, write_reg = ARM_PC_REGNUM;
+ unsigned int regs_loaded = bitcount (mask);
+ unsigned int num_to_shuffle = regs_loaded, clobbered;
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
- if (!immed)
- dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
- if (!load)
- dsc->tmp[4] = displaced_read_reg (regs, dsc, 4);
+ /* The method employed here will fail if the register list is fully populated
+ (we need to avoid loading PC directly). */
+ gdb_assert (num_to_shuffle < 16);
- rt_val = displaced_read_reg (regs, dsc, rt);
- rn_val = displaced_read_reg (regs, dsc, rn);
- if (!immed)
- rm_val = displaced_read_reg (regs, dsc, rm);
+ if (!load_executed)
+ return;
- displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
- if (!immed)
- displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
- dsc->rd = rt;
- dsc->u.ldst.xfersize = size;
- dsc->u.ldst.rn = rn;
- dsc->u.ldst.immed = immed;
- dsc->u.ldst.writeback = writeback;
+ clobbered = (1 << num_to_shuffle) - 1;
- /* To write PC we can do:
+ while (num_to_shuffle > 0)
+ {
+ if ((mask & (1 << write_reg)) != 0)
+ {
+ unsigned int read_reg = num_to_shuffle - 1;
- Before this sequence of instructions:
- r0 is the PC value got from displaced_read_reg, so r0 = from + 8;
- r2 is the Rn value got from dispalced_read_reg.
+ if (read_reg != write_reg)
+ {
+ ULONGEST rval = displaced_read_reg (regs, dsc, read_reg);
+ displaced_write_reg (regs, dsc, write_reg, rval, LOAD_WRITE_PC);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: move "
+ "loaded register r%d to r%d\n"), read_reg,
+ write_reg);
+ }
+ else if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: register "
+ "r%d already in the right place\n"),
+ write_reg);
- Insn1: push {pc} Write address of STR instruction + offset on stack
- Insn2: pop {r4} Read it back from stack, r4 = addr(Insn1) + offset
- Insn3: sub r4, r4, pc r4 = addr(Insn1) + offset - pc
- = addr(Insn1) + offset - addr(Insn3) - 8
- = offset - 16
- Insn4: add r4, r4, #8 r4 = offset - 8
- Insn5: add r0, r0, r4 r0 = from + 8 + offset - 8
- = from + offset
- Insn6: str r0, [r2, #imm] (or str r0, [r2, r3])
+ clobbered &= ~(1 << write_reg);
- Otherwise we don't know what value to write for PC, since the offset is
- architecture-dependent (sometimes PC+8, sometimes PC+12). More details
- of this can be found in Section "Saving from r15" in
- http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204g/Cihbjifh.html */
+ num_to_shuffle--;
+ }
- dsc->cleanup = load ? &cleanup_load : &cleanup_store;
+ write_reg--;
+ }
+
+ /* Restore any registers we scribbled over. */
+ for (write_reg = 0; clobbered != 0; write_reg++)
+ {
+ if ((clobbered & (1 << write_reg)) != 0)
+ {
+ displaced_write_reg (regs, dsc, write_reg, dsc->tmp[write_reg],
+ CANNOT_WRITE_PC);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: restored "
+ "clobbered register r%d\n"), write_reg);
+ clobbered &= ~(1 << write_reg);
+ }
+ }
+
+ /* Perform register writeback manually. */
+ if (dsc->u.block.writeback)
+ {
+ ULONGEST new_rn_val = dsc->u.block.xfer_addr;
+
+ if (dsc->u.block.increment)
+ new_rn_val += regs_loaded * 4;
+ else
+ new_rn_val -= regs_loaded * 4;
+
+ displaced_write_reg (regs, dsc, dsc->u.block.rn, new_rn_val,
+ CANNOT_WRITE_PC);
+ }
}
+/* Handle ldm/stm, apart from some tricky cases which are unlikely to occur
+ in user-level code (in particular exception return, ldm rn, {...pc}^). */
static int
-thumb2_copy_load_literal (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc, int size)
+arm_copy_block_xfer (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int u_bit = bit (insn1, 7);
- unsigned int rt = bits (insn2, 12, 15);
- int imm12 = bits (insn2, 0, 11);
- ULONGEST pc_val;
+ int load = bit (insn, 20);
+ int user = bit (insn, 22);
+ int increment = bit (insn, 23);
+ int before = bit (insn, 24);
+ int writeback = bit (insn, 21);
+ int rn = bits (insn, 16, 19);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying ldr pc (0x%x) R%d %c imm12 %.4x\n",
- (unsigned int) dsc->insn_addr, rt, u_bit ? '+' : '-',
- imm12);
+ /* Block transfers which don't mention PC can be run directly
+ out-of-line. */
+ if (rn != ARM_PC_REGNUM && (insn & 0x8000) == 0)
+ return arm_copy_unmodified (gdbarch, insn, "ldm/stm", dsc);
- if (!u_bit)
- imm12 = -1 * imm12;
+ if (rn == ARM_PC_REGNUM)
+ {
+ warning (_("displaced: Unpredictable LDM or STM with "
+ "base register r15"));
+ return arm_copy_unmodified (gdbarch, insn, "unpredictable ldm/stm", dsc);
+ }
- /* Rewrite instruction LDR Rt imm12 into:
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying block transfer insn "
+ "%.8lx\n", (unsigned long) insn);
- Prepare: tmp[0] <- r0, tmp[1] <- r2, tmp[2] <- r3, r2 <- pc, r3 <- imm12
+ dsc->u.block.xfer_addr = displaced_read_reg (regs, dsc, rn);
+ dsc->u.block.rn = rn;
- LDR R0, R2, R3,
+ dsc->u.block.load = load;
+ dsc->u.block.user = user;
+ dsc->u.block.increment = increment;
+ dsc->u.block.before = before;
+ dsc->u.block.writeback = writeback;
+ dsc->u.block.cond = bits (insn, 28, 31);
- Cleanup: rt <- r0, r0 <- tmp[0], r2 <- tmp[1], r3 <- tmp[2]. */
+ dsc->u.block.regmask = insn & 0xffff;
+ if (load)
+ {
+ if ((insn & 0xffff) == 0xffff)
+ {
+ /* LDM with a fully-populated register list. This case is
+ particularly tricky. Implement for now by fully emulating the
+ instruction (which might not behave perfectly in all cases, but
+ these instructions should be rare enough for that not to matter
+ too much). */
+ dsc->modinsn[0] = ARM_NOP;
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
- dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
+ dsc->cleanup = &cleanup_block_load_all;
+ }
+ else
+ {
+ /* LDM of a list of registers which includes PC. Implement by
+ rewriting the list of registers to be transferred into a
+ contiguous chunk r0...rX before doing the transfer, then shuffling
+ registers into the correct places in the cleanup routine. */
+ unsigned int regmask = insn & 0xffff;
+ unsigned int num_in_list = bitcount (regmask), new_regmask;
+ unsigned int i;
- pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+ for (i = 0; i < num_in_list; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
- pc_val = pc_val & 0xfffffffc;
+ /* Writeback makes things complicated. We need to avoid clobbering
+ the base register with one of the registers in our modified
+ register list, but just using a different register can't work in
+ all cases, e.g.:
- displaced_write_reg (regs, dsc, 2, pc_val, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 3, imm12, CANNOT_WRITE_PC);
+ ldm r14!, {r0-r13,pc}
- dsc->rd = rt;
-
- dsc->u.ldst.xfersize = size;
- dsc->u.ldst.immed = 0;
- dsc->u.ldst.writeback = 0;
- dsc->u.ldst.restore_r4 = 0;
-
- /* LDR R0, R2, R3 */
- dsc->modinsn[0] = 0xf852;
- dsc->modinsn[1] = 0x3;
- dsc->numinsns = 2;
+ which would need to be rewritten as:
- dsc->cleanup = &cleanup_load;
+ ldm rN!, {r0-r14}
- return 0;
-}
+ but that can't work, because there's no free register for N.
-static int
-thumb2_copy_load_reg_imm (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc,
- int writeback, int immed)
-{
- unsigned int rt = bits (insn2, 12, 15);
- unsigned int rn = bits (insn1, 0, 3);
- unsigned int rm = bits (insn2, 0, 3); /* Only valid if !immed. */
- /* In LDR (register), there is also a register Rm, which is not allowed to
- be PC, so we don't have to check it. */
+ Solve this by turning off the writeback bit, and emulating
+ writeback manually in the cleanup routine. */
- if (rt != ARM_PC_REGNUM && rn != ARM_PC_REGNUM)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "load",
- dsc);
+ if (writeback)
+ insn &= ~(1 << 21);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying ldr r%d [r%d] insn %.4x%.4x\n",
- rt, rn, insn1, insn2);
+ new_regmask = (1 << num_in_list) - 1;
- install_load_store (gdbarch, regs, dsc, 1, immed, writeback, 4,
- 0, rt, rm, rn);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM r%d%s, "
+ "{..., pc}: original reg list %.4x, modified "
+ "list %.4x\n"), rn, writeback ? "!" : "",
+ (int) insn & 0xffff, new_regmask);
- dsc->u.ldst.restore_r4 = 0;
+ dsc->modinsn[0] = (insn & ~0xffff) | (new_regmask & 0xffff);
- if (immed)
- /* ldr[b]<cond> rt, [rn, #imm], etc.
- ->
- ldr[b]<cond> r0, [r2, #imm]. */
- {
- dsc->modinsn[0] = (insn1 & 0xfff0) | 0x2;
- dsc->modinsn[1] = insn2 & 0x0fff;
+ dsc->cleanup = &cleanup_block_load_pc;
+ }
}
else
- /* ldr[b]<cond> rt, [rn, rm], etc.
- ->
- ldr[b]<cond> r0, [r2, r3]. */
{
- dsc->modinsn[0] = (insn1 & 0xfff0) | 0x2;
- dsc->modinsn[1] = (insn2 & 0x0ff0) | 0x3;
- }
+ /* STM of a list of registers which includes PC. Run the instruction
+ as-is, but out of line: this will store the wrong value for the PC,
+ so we must manually fix up the memory in the cleanup routine.
+ Doing things this way has the advantage that we can auto-detect
+ the offset of the PC write (which is architecture-dependent) in
+ the cleanup routine. */
+ dsc->modinsn[0] = insn;
- dsc->numinsns = 2;
+ dsc->cleanup = &cleanup_block_store_pc;
+ }
return 0;
}
-
static int
-arm_copy_ldr_str_ldrb_strb (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc,
- int load, int size, int usermode)
+thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- int immed = !bit (insn, 25);
- int writeback = (bit (insn, 24) == 0 || bit (insn, 21) != 0);
- unsigned int rt = bits (insn, 12, 15);
- unsigned int rn = bits (insn, 16, 19);
- unsigned int rm = bits (insn, 0, 3); /* Only valid if !immed. */
+ int rn = bits (insn1, 0, 3);
+ int load = bit (insn1, 4);
+ int writeback = bit (insn1, 5);
- if (!insn_references_pc (insn, 0x000ff00ful))
- return arm_copy_unmodified (gdbarch, insn, "load/store", dsc);
+ /* Block transfers which don't mention PC can be run directly
+ out-of-line. */
+ if (rn != ARM_PC_REGNUM && (insn2 & 0x8000) == 0)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "ldm/stm", dsc);
+
+ if (rn == ARM_PC_REGNUM)
+ {
+ warning (_("displaced: Unpredictable LDM or STM with "
+ "base register r15"));
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "unpredictable ldm/stm", dsc);
+ }
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying %s%s r%d [r%d] insn %.8lx\n",
- load ? (size == 1 ? "ldrb" : "ldr")
- : (size == 1 ? "strb" : "str"), usermode ? "t" : "",
- rt, rn,
- (unsigned long) insn);
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying block transfer insn "
+ "%.4x%.4x\n", insn1, insn2);
- install_load_store (gdbarch, regs, dsc, load, immed, writeback, size,
- usermode, rt, rm, rn);
+ /* Clear bit 13, since it should be always zero. */
+ dsc->u.block.regmask = (insn2 & 0xdfff);
+ dsc->u.block.rn = rn;
- if (load || rt != ARM_PC_REGNUM)
- {
- dsc->u.ldst.restore_r4 = 0;
+ dsc->u.block.load = load;
+ dsc->u.block.user = 0;
+ dsc->u.block.increment = bit (insn1, 7);
+ dsc->u.block.before = bit (insn1, 8);
+ dsc->u.block.writeback = writeback;
+ dsc->u.block.cond = INST_AL;
+ dsc->u.block.xfer_addr = displaced_read_reg (regs, dsc, rn);
- if (immed)
- /* {ldr,str}[b]<cond> rt, [rn, #imm], etc.
- ->
- {ldr,str}[b]<cond> r0, [r2, #imm]. */
- dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
+ if (load)
+ {
+ if (dsc->u.block.regmask == 0xffff)
+ {
+ /* This branch is impossible to happen. */
+ gdb_assert (0);
+ }
else
- /* {ldr,str}[b]<cond> rt, [rn, rm], etc.
- ->
- {ldr,str}[b]<cond> r0, [r2, r3]. */
- dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
+ {
+ unsigned int regmask = dsc->u.block.regmask;
+ unsigned int num_in_list = bitcount (regmask), new_regmask;
+ unsigned int i;
+
+ for (i = 0; i < num_in_list; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
+
+ if (writeback)
+ insn1 &= ~(1 << 5);
+
+ new_regmask = (1 << num_in_list) - 1;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM r%d%s, "
+ "{..., pc}: original reg list %.4x, modified "
+ "list %.4x\n"), rn, writeback ? "!" : "",
+ (int) dsc->u.block.regmask, new_regmask);
+
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = (new_regmask & 0xffff);
+ dsc->numinsns = 2;
+
+ dsc->cleanup = &cleanup_block_load_pc;
+ }
}
else
{
- /* We need to use r4 as scratch. Make sure it's restored afterwards. */
- dsc->u.ldst.restore_r4 = 1;
- dsc->modinsn[0] = 0xe92d8000; /* push {pc} */
- dsc->modinsn[1] = 0xe8bd0010; /* pop {r4} */
- dsc->modinsn[2] = 0xe044400f; /* sub r4, r4, pc. */
- dsc->modinsn[3] = 0xe2844008; /* add r4, r4, #8. */
- dsc->modinsn[4] = 0xe0800004; /* add r0, r0, r4. */
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
+ dsc->cleanup = &cleanup_block_store_pc;
+ }
+ return 0;
+}
- /* As above. */
- if (immed)
- dsc->modinsn[5] = (insn & 0xfff00fff) | 0x20000;
- else
- dsc->modinsn[5] = (insn & 0xfff00ff0) | 0x20003;
+/* Wrapper over read_memory_unsigned_integer for use in arm_get_next_pcs.
+ This is used to avoid a dependency on BFD's bfd_endian enum. */
- dsc->numinsns = 6;
- }
+ULONGEST
+arm_get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr, int len,
+ int byte_order)
+{
+ return read_memory_unsigned_integer (memaddr, len,
+ (enum bfd_endian) byte_order);
+}
- dsc->cleanup = load ? &cleanup_load : &cleanup_store;
+/* Wrapper over gdbarch_addr_bits_remove for use in arm_get_next_pcs. */
- return 0;
+CORE_ADDR
+arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+ CORE_ADDR val)
+{
+ return gdbarch_addr_bits_remove (get_regcache_arch (self->regcache), val);
}
-/* Cleanup LDM instructions with fully-populated register list. This is an
- unfortunate corner case: it's impossible to implement correctly by modifying
- the instruction. The issue is as follows: we have an instruction,
+/* Wrapper over syscall_next_pc for use in get_next_pcs. */
- ldm rN, {r0-r15}
+static CORE_ADDR
+arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
+{
+ return 0;
+}
- which we must rewrite to avoid loading PC. A possible solution would be to
- do the load in two halves, something like (with suitable cleanup
- afterwards):
+/* Wrapper over arm_is_thumb for use in arm_get_next_pcs. */
- mov r8, rN
- ldm[id][ab] r8!, {r0-r7}
- str r7, <temp>
- ldm[id][ab] r8, {r7-r14}
- <bkpt>
+int
+arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
+{
+ return arm_is_thumb (self->regcache);
+}
- but at present there's no suitable place for <temp>, since the scratch space
- is overwritten before the cleanup routine is called. For now, we simply
- emulate the instruction. */
+/* single_step() is called just before we want to resume the inferior,
+ if we want to single-step it but there is no hardware or kernel
+ single-step support. We find the target of the coming instructions
+ and breakpoint them. */
-static void
-cleanup_block_load_all (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
+int
+arm_software_single_step (struct frame_info *frame)
{
- int inc = dsc->u.block.increment;
- int bump_before = dsc->u.block.before ? (inc ? 4 : -4) : 0;
- int bump_after = dsc->u.block.before ? 0 : (inc ? 4 : -4);
- uint32_t regmask = dsc->u.block.regmask;
- int regno = inc ? 0 : 15;
- CORE_ADDR xfer_addr = dsc->u.block.xfer_addr;
- int exception_return = dsc->u.block.load && dsc->u.block.user
- && (regmask & 0x8000) != 0;
- uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
- int do_transfer = condition_true (dsc->u.block.cond, status);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct address_space *aspace = get_regcache_aspace (regcache);
+ struct arm_get_next_pcs next_pcs_ctx;
+ CORE_ADDR pc;
+ int i;
+ VEC (CORE_ADDR) *next_pcs = NULL;
+ struct cleanup *old_chain = make_cleanup (VEC_cleanup (CORE_ADDR), &next_pcs);
- if (!do_transfer)
- return;
+ arm_get_next_pcs_ctor (&next_pcs_ctx,
+ &arm_get_next_pcs_ops,
+ gdbarch_byte_order (gdbarch),
+ gdbarch_byte_order_for_code (gdbarch),
+ 0,
+ regcache);
- /* If the instruction is ldm rN, {...pc}^, I don't think there's anything
- sensible we can do here. Complain loudly. */
- if (exception_return)
- error (_("Cannot single-step exception return"));
+ next_pcs = arm_get_next_pcs (&next_pcs_ctx);
- /* We don't handle any stores here for now. */
- gdb_assert (dsc->u.block.load != 0);
+ for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
+ arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: emulating block transfer: "
- "%s %s %s\n", dsc->u.block.load ? "ldm" : "stm",
- dsc->u.block.increment ? "inc" : "dec",
- dsc->u.block.before ? "before" : "after");
+ do_cleanups (old_chain);
- while (regmask)
- {
- uint32_t memword;
+ return 1;
+}
- if (inc)
- while (regno <= ARM_PC_REGNUM && (regmask & (1 << regno)) == 0)
- regno++;
- else
- while (regno >= 0 && (regmask & (1 << regno)) == 0)
- regno--;
-
- xfer_addr += bump_before;
-
- memword = read_memory_unsigned_integer (xfer_addr, 4, byte_order);
- displaced_write_reg (regs, dsc, regno, memword, LOAD_WRITE_PC);
+/* Cleanup/copy SVC (SWI) instructions. These two functions are overridden
+ for Linux, where some SVC instructions must be treated specially. */
- xfer_addr += bump_after;
+static void
+cleanup_svc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ CORE_ADDR resume_addr = dsc->insn_addr + dsc->insn_size;
- regmask &= ~(1 << regno);
- }
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: cleanup for svc, resume at "
+ "%.8lx\n", (unsigned long) resume_addr);
- if (dsc->u.block.writeback)
- displaced_write_reg (regs, dsc, dsc->u.block.rn, xfer_addr,
- CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, resume_addr, BRANCH_WRITE_PC);
}
-/* Clean up an STM which included the PC in the register list. */
-static void
-cleanup_block_store_pc (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
- int store_executed = condition_true (dsc->u.block.cond, status);
- CORE_ADDR pc_stored_at, transferred_regs = bitcount (dsc->u.block.regmask);
- CORE_ADDR stm_insn_addr;
- uint32_t pc_val;
- long offset;
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+/* Common copy routine for svc instruciton. */
- /* If condition code fails, there's nothing else to do. */
- if (!store_executed)
- return;
+static int
+install_svc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ /* Preparation: none.
+ Insn: unmodified svc.
+ Cleanup: pc <- insn_addr + insn_size. */
- if (dsc->u.block.increment)
- {
- pc_stored_at = dsc->u.block.xfer_addr + 4 * transferred_regs;
+ /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
+ instruction. */
+ dsc->wrote_to_pc = 1;
- if (dsc->u.block.before)
- pc_stored_at += 4;
- }
+ /* Allow OS-specific code to override SVC handling. */
+ if (dsc->u.svc.copy_svc_os)
+ return dsc->u.svc.copy_svc_os (gdbarch, regs, dsc);
else
{
- pc_stored_at = dsc->u.block.xfer_addr;
-
- if (dsc->u.block.before)
- pc_stored_at -= 4;
+ dsc->cleanup = &cleanup_svc;
+ return 0;
}
+}
- pc_val = read_memory_unsigned_integer (pc_stored_at, 4, byte_order);
- stm_insn_addr = dsc->scratch_base;
- offset = pc_val - stm_insn_addr;
+static int
+arm_copy_svc (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: detected PC offset %.8lx for "
- "STM instruction\n", offset);
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
+ (unsigned long) insn);
- /* Rewrite the stored PC to the proper value for the non-displaced original
- instruction. */
- write_memory_unsigned_integer (pc_stored_at, 4, byte_order,
- dsc->insn_addr + offset);
-}
+ dsc->modinsn[0] = insn;
-/* Clean up an LDM which includes the PC in the register list. We clumped all
- the registers in the transferred list into a contiguous range r0...rX (to
- avoid loading PC directly and losing control of the debugged program), so we
- must undo that here. */
+ return install_svc (gdbarch, regs, dsc);
+}
-static void
-cleanup_block_load_pc (struct gdbarch *gdbarch,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+static int
+thumb_copy_svc (struct gdbarch *gdbarch, uint16_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM);
- int load_executed = condition_true (dsc->u.block.cond, status);
- unsigned int mask = dsc->u.block.regmask, write_reg = ARM_PC_REGNUM;
- unsigned int regs_loaded = bitcount (mask);
- unsigned int num_to_shuffle = regs_loaded, clobbered;
- /* The method employed here will fail if the register list is fully populated
- (we need to avoid loading PC directly). */
- gdb_assert (num_to_shuffle < 16);
-
- if (!load_executed)
- return;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.4x\n",
+ insn);
- clobbered = (1 << num_to_shuffle) - 1;
+ dsc->modinsn[0] = insn;
- while (num_to_shuffle > 0)
- {
- if ((mask & (1 << write_reg)) != 0)
- {
- unsigned int read_reg = num_to_shuffle - 1;
+ return install_svc (gdbarch, regs, dsc);
+}
- if (read_reg != write_reg)
- {
- ULONGEST rval = displaced_read_reg (regs, dsc, read_reg);
- displaced_write_reg (regs, dsc, write_reg, rval, LOAD_WRITE_PC);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: move "
- "loaded register r%d to r%d\n"), read_reg,
- write_reg);
- }
- else if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: register "
- "r%d already in the right place\n"),
- write_reg);
+/* Copy undefined instructions. */
- clobbered &= ~(1 << write_reg);
+static int
+arm_copy_undef (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
+{
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying undefined insn %.8lx\n",
+ (unsigned long) insn);
- num_to_shuffle--;
- }
+ dsc->modinsn[0] = insn;
- write_reg--;
- }
+ return 0;
+}
- /* Restore any registers we scribbled over. */
- for (write_reg = 0; clobbered != 0; write_reg++)
- {
- if ((clobbered & (1 << write_reg)) != 0)
- {
- displaced_write_reg (regs, dsc, write_reg, dsc->tmp[write_reg],
- CANNOT_WRITE_PC);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: restored "
- "clobbered register r%d\n"), write_reg);
- clobbered &= ~(1 << write_reg);
- }
- }
+static int
+thumb_32bit_copy_undef (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ struct displaced_step_closure *dsc)
+{
- /* Perform register writeback manually. */
- if (dsc->u.block.writeback)
- {
- ULONGEST new_rn_val = dsc->u.block.xfer_addr;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying undefined insn "
+ "%.4x %.4x\n", (unsigned short) insn1,
+ (unsigned short) insn2);
- if (dsc->u.block.increment)
- new_rn_val += regs_loaded * 4;
- else
- new_rn_val -= regs_loaded * 4;
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
- displaced_write_reg (regs, dsc, dsc->u.block.rn, new_rn_val,
- CANNOT_WRITE_PC);
- }
+ return 0;
}
-/* Handle ldm/stm, apart from some tricky cases which are unlikely to occur
- in user-level code (in particular exception return, ldm rn, {...pc}^). */
+/* Copy unpredictable instructions. */
static int
-arm_copy_block_xfer (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_copy_unpred (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
{
- int load = bit (insn, 20);
- int user = bit (insn, 22);
- int increment = bit (insn, 23);
- int before = bit (insn, 24);
- int writeback = bit (insn, 21);
- int rn = bits (insn, 16, 19);
-
- /* Block transfers which don't mention PC can be run directly
- out-of-line. */
- if (rn != ARM_PC_REGNUM && (insn & 0x8000) == 0)
- return arm_copy_unmodified (gdbarch, insn, "ldm/stm", dsc);
-
- if (rn == ARM_PC_REGNUM)
- {
- warning (_("displaced: Unpredictable LDM or STM with "
- "base register r15"));
- return arm_copy_unmodified (gdbarch, insn, "unpredictable ldm/stm", dsc);
- }
-
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying block transfer insn "
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying unpredictable insn "
"%.8lx\n", (unsigned long) insn);
- dsc->u.block.xfer_addr = displaced_read_reg (regs, dsc, rn);
- dsc->u.block.rn = rn;
+ dsc->modinsn[0] = insn;
- dsc->u.block.load = load;
- dsc->u.block.user = user;
- dsc->u.block.increment = increment;
- dsc->u.block.before = before;
- dsc->u.block.writeback = writeback;
- dsc->u.block.cond = bits (insn, 28, 31);
+ return 0;
+}
- dsc->u.block.regmask = insn & 0xffff;
+/* The decode_* functions are instruction decoding helpers. They mostly follow
+ the presentation in the ARM ARM. */
- if (load)
- {
- if ((insn & 0xffff) == 0xffff)
- {
- /* LDM with a fully-populated register list. This case is
- particularly tricky. Implement for now by fully emulating the
- instruction (which might not behave perfectly in all cases, but
- these instructions should be rare enough for that not to matter
- too much). */
- dsc->modinsn[0] = ARM_NOP;
+static int
+arm_decode_misc_memhint_neon (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int op1 = bits (insn, 20, 26), op2 = bits (insn, 4, 7);
+ unsigned int rn = bits (insn, 16, 19);
- dsc->cleanup = &cleanup_block_load_all;
- }
- else
- {
- /* LDM of a list of registers which includes PC. Implement by
- rewriting the list of registers to be transferred into a
- contiguous chunk r0...rX before doing the transfer, then shuffling
- registers into the correct places in the cleanup routine. */
- unsigned int regmask = insn & 0xffff;
- unsigned int num_in_list = bitcount (regmask), new_regmask, bit = 1;
- unsigned int to = 0, from = 0, i, new_rn;
+ if (op1 == 0x10 && (op2 & 0x2) == 0x0 && (rn & 0xe) == 0x0)
+ return arm_copy_unmodified (gdbarch, insn, "cps", dsc);
+ else if (op1 == 0x10 && op2 == 0x0 && (rn & 0xe) == 0x1)
+ return arm_copy_unmodified (gdbarch, insn, "setend", dsc);
+ else if ((op1 & 0x60) == 0x20)
+ return arm_copy_unmodified (gdbarch, insn, "neon dataproc", dsc);
+ else if ((op1 & 0x71) == 0x40)
+ return arm_copy_unmodified (gdbarch, insn, "neon elt/struct load/store",
+ dsc);
+ else if ((op1 & 0x77) == 0x41)
+ return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ else if ((op1 & 0x77) == 0x45)
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pli. */
+ else if ((op1 & 0x77) == 0x51)
+ {
+ if (rn != 0xf)
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ else
+ return arm_copy_unpred (gdbarch, insn, dsc);
+ }
+ else if ((op1 & 0x77) == 0x55)
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ else if (op1 == 0x57)
+ switch (op2)
+ {
+ case 0x1: return arm_copy_unmodified (gdbarch, insn, "clrex", dsc);
+ case 0x4: return arm_copy_unmodified (gdbarch, insn, "dsb", dsc);
+ case 0x5: return arm_copy_unmodified (gdbarch, insn, "dmb", dsc);
+ case 0x6: return arm_copy_unmodified (gdbarch, insn, "isb", dsc);
+ default: return arm_copy_unpred (gdbarch, insn, dsc);
+ }
+ else if ((op1 & 0x63) == 0x43)
+ return arm_copy_unpred (gdbarch, insn, dsc);
+ else if ((op2 & 0x1) == 0x0)
+ switch (op1 & ~0x80)
+ {
+ case 0x61:
+ return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ case 0x65:
+ return arm_copy_preload_reg (gdbarch, insn, regs, dsc); /* pli reg. */
+ case 0x71: case 0x75:
+ /* pld/pldw reg. */
+ return arm_copy_preload_reg (gdbarch, insn, regs, dsc);
+ case 0x63: case 0x67: case 0x73: case 0x77:
+ return arm_copy_unpred (gdbarch, insn, dsc);
+ default:
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
+ else
+ return arm_copy_undef (gdbarch, insn, dsc); /* Probably unreachable. */
+}
- for (i = 0; i < num_in_list; i++)
- dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
+static int
+arm_decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ if (bit (insn, 27) == 0)
+ return arm_decode_misc_memhint_neon (gdbarch, insn, regs, dsc);
+ /* Switch on bits: 0bxxxxx321xxx0xxxxxxxxxxxxxxxxxxxx. */
+ else switch (((insn & 0x7000000) >> 23) | ((insn & 0x100000) >> 20))
+ {
+ case 0x0: case 0x2:
+ return arm_copy_unmodified (gdbarch, insn, "srs", dsc);
- /* Writeback makes things complicated. We need to avoid clobbering
- the base register with one of the registers in our modified
- register list, but just using a different register can't work in
- all cases, e.g.:
+ case 0x1: case 0x3:
+ return arm_copy_unmodified (gdbarch, insn, "rfe", dsc);
- ldm r14!, {r0-r13,pc}
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
- which would need to be rewritten as:
+ case 0x8:
+ switch ((insn & 0xe00000) >> 21)
+ {
+ case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
+ /* stc/stc2. */
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- ldm rN!, {r0-r14}
+ case 0x2:
+ return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
- but that can't work, because there's no free register for N.
+ default:
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
- Solve this by turning off the writeback bit, and emulating
- writeback manually in the cleanup routine. */
+ case 0x9:
+ {
+ int rn_f = (bits (insn, 16, 19) == 0xf);
+ switch ((insn & 0xe00000) >> 21)
+ {
+ case 0x1: case 0x3:
+ /* ldc/ldc2 imm (undefined for rn == pc). */
+ return rn_f ? arm_copy_undef (gdbarch, insn, dsc)
+ : arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- if (writeback)
- insn &= ~(1 << 21);
+ case 0x2:
+ return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
- new_regmask = (1 << num_in_list) - 1;
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ /* ldc/ldc2 lit (undefined for rn != pc). */
+ return rn_f ? arm_copy_copro_load_store (gdbarch, insn, regs, dsc)
+ : arm_copy_undef (gdbarch, insn, dsc);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, _("displaced: LDM r%d%s, "
- "{..., pc}: original reg list %.4x, modified "
- "list %.4x\n"), rn, writeback ? "!" : "",
- (int) insn & 0xffff, new_regmask);
+ default:
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
+ }
- dsc->modinsn[0] = (insn & ~0xffff) | (new_regmask & 0xffff);
+ case 0xa:
+ return arm_copy_unmodified (gdbarch, insn, "stc/stc2", dsc);
- dsc->cleanup = &cleanup_block_load_pc;
- }
- }
- else
- {
- /* STM of a list of registers which includes PC. Run the instruction
- as-is, but out of line: this will store the wrong value for the PC,
- so we must manually fix up the memory in the cleanup routine.
- Doing things this way has the advantage that we can auto-detect
- the offset of the PC write (which is architecture-dependent) in
- the cleanup routine. */
- dsc->modinsn[0] = insn;
+ case 0xb:
+ if (bits (insn, 16, 19) == 0xf)
+ /* ldc/ldc2 lit. */
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
- dsc->cleanup = &cleanup_block_store_pc;
- }
+ case 0xc:
+ if (bit (insn, 4))
+ return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ else
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
- return 0;
+ case 0xd:
+ if (bit (insn, 4))
+ return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ else
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+
+ default:
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
}
+/* Decode miscellaneous instructions in dp/misc encoding space. */
+
static int
-thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- int rn = bits (insn1, 0, 3);
- int load = bit (insn1, 4);
- int writeback = bit (insn1, 5);
-
- /* Block transfers which don't mention PC can be run directly
- out-of-line. */
- if (rn != ARM_PC_REGNUM && (insn2 & 0x8000) == 0)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "ldm/stm", dsc);
+ unsigned int op2 = bits (insn, 4, 6);
+ unsigned int op = bits (insn, 21, 22);
- if (rn == ARM_PC_REGNUM)
+ switch (op2)
{
- warning (_("displaced: Unpredictable LDM or STM with "
- "base register r15"));
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "unpredictable ldm/stm", dsc);
- }
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying block transfer insn "
- "%.4x%.4x\n", insn1, insn2);
+ case 0x0:
+ return arm_copy_unmodified (gdbarch, insn, "mrs/msr", dsc);
- /* Clear bit 13, since it should be always zero. */
- dsc->u.block.regmask = (insn2 & 0xdfff);
- dsc->u.block.rn = rn;
+ case 0x1:
+ if (op == 0x1) /* bx. */
+ return arm_copy_bx_blx_reg (gdbarch, insn, regs, dsc);
+ else if (op == 0x3)
+ return arm_copy_unmodified (gdbarch, insn, "clz", dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
- dsc->u.block.load = load;
- dsc->u.block.user = 0;
- dsc->u.block.increment = bit (insn1, 7);
- dsc->u.block.before = bit (insn1, 8);
- dsc->u.block.writeback = writeback;
- dsc->u.block.cond = INST_AL;
- dsc->u.block.xfer_addr = displaced_read_reg (regs, dsc, rn);
+ case 0x2:
+ if (op == 0x1)
+ /* Not really supported. */
+ return arm_copy_unmodified (gdbarch, insn, "bxj", dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
- if (load)
- {
- if (dsc->u.block.regmask == 0xffff)
- {
- /* This branch is impossible to happen. */
- gdb_assert (0);
- }
+ case 0x3:
+ if (op == 0x1)
+ return arm_copy_bx_blx_reg (gdbarch, insn,
+ regs, dsc); /* blx register. */
else
- {
- unsigned int regmask = dsc->u.block.regmask;
- unsigned int num_in_list = bitcount (regmask), new_regmask, bit = 1;
- unsigned int to = 0, from = 0, i, new_rn;
+ return arm_copy_undef (gdbarch, insn, dsc);
- for (i = 0; i < num_in_list; i++)
- dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
+ case 0x5:
+ return arm_copy_unmodified (gdbarch, insn, "saturating add/sub", dsc);
- if (writeback)
- insn1 &= ~(1 << 5);
+ case 0x7:
+ if (op == 0x1)
+ return arm_copy_unmodified (gdbarch, insn, "bkpt", dsc);
+ else if (op == 0x3)
+ /* Not really supported. */
+ return arm_copy_unmodified (gdbarch, insn, "smc", dsc);
- new_regmask = (1 << num_in_list) - 1;
+ default:
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
+}
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, _("displaced: LDM r%d%s, "
- "{..., pc}: original reg list %.4x, modified "
- "list %.4x\n"), rn, writeback ? "!" : "",
- (int) dsc->u.block.regmask, new_regmask);
+static int
+arm_decode_dp_misc (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ if (bit (insn, 25))
+ switch (bits (insn, 20, 24))
+ {
+ case 0x10:
+ return arm_copy_unmodified (gdbarch, insn, "movw", dsc);
- dsc->modinsn[0] = insn1;
- dsc->modinsn[1] = (new_regmask & 0xffff);
- dsc->numinsns = 2;
+ case 0x14:
+ return arm_copy_unmodified (gdbarch, insn, "movt", dsc);
- dsc->cleanup = &cleanup_block_load_pc;
- }
- }
+ case 0x12: case 0x16:
+ return arm_copy_unmodified (gdbarch, insn, "msr imm", dsc);
+
+ default:
+ return arm_copy_alu_imm (gdbarch, insn, regs, dsc);
+ }
else
{
- dsc->modinsn[0] = insn1;
- dsc->modinsn[1] = insn2;
- dsc->numinsns = 2;
- dsc->cleanup = &cleanup_block_store_pc;
- }
- return 0;
-}
-
-/* Cleanup/copy SVC (SWI) instructions. These two functions are overridden
- for Linux, where some SVC instructions must be treated specially. */
-
-static void
-cleanup_svc (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- CORE_ADDR resume_addr = dsc->insn_addr + dsc->insn_size;
+ uint32_t op1 = bits (insn, 20, 24), op2 = bits (insn, 4, 7);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: cleanup for svc, resume at "
- "%.8lx\n", (unsigned long) resume_addr);
+ if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0)
+ return arm_copy_alu_reg (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
+ return arm_copy_alu_shifted_reg (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
+ return arm_decode_miscellaneous (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
+ return arm_copy_unmodified (gdbarch, insn, "halfword mul/mla", dsc);
+ else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
+ return arm_copy_unmodified (gdbarch, insn, "mul/mla", dsc);
+ else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
+ return arm_copy_unmodified (gdbarch, insn, "synch", dsc);
+ else if (op2 == 0xb || (op2 & 0xd) == 0xd)
+ /* 2nd arg means "unprivileged". */
+ return arm_copy_extra_ld_st (gdbarch, insn, (op1 & 0x12) == 0x02, regs,
+ dsc);
+ }
- displaced_write_reg (regs, dsc, ARM_PC_REGNUM, resume_addr, BRANCH_WRITE_PC);
+ /* Should be unreachable. */
+ return 1;
}
-
-/* Common copy routine for svc instruciton. */
-
static int
-install_svc (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_decode_ld_st_word_ubyte (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- /* Preparation: none.
- Insn: unmodified svc.
- Cleanup: pc <- insn_addr + insn_size. */
+ int a = bit (insn, 25), b = bit (insn, 4);
+ uint32_t op1 = bits (insn, 20, 24);
- /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
- instruction. */
- dsc->wrote_to_pc = 1;
+ if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
+ || (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 4, 0);
+ else if ((!a && (op1 & 0x17) == 0x02)
+ || (a && (op1 & 0x17) == 0x02 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 4, 1);
+ else if ((!a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03)
+ || (a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 4, 0);
+ else if ((!a && (op1 & 0x17) == 0x03)
+ || (a && (op1 & 0x17) == 0x03 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 4, 1);
+ else if ((!a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06)
+ || (a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 0);
+ else if ((!a && (op1 & 0x17) == 0x06)
+ || (a && (op1 & 0x17) == 0x06 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 1);
+ else if ((!a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07)
+ || (a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 0);
+ else if ((!a && (op1 & 0x17) == 0x07)
+ || (a && (op1 & 0x17) == 0x07 && !b))
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 1);
- /* Allow OS-specific code to override SVC handling. */
- if (dsc->u.svc.copy_svc_os)
- return dsc->u.svc.copy_svc_os (gdbarch, regs, dsc);
- else
- {
- dsc->cleanup = &cleanup_svc;
- return 0;
- }
+ /* Should be unreachable. */
+ return 1;
}
static int
-arm_copy_svc (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_decode_media (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
{
+ switch (bits (insn, 20, 24))
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ return arm_copy_unmodified (gdbarch, insn, "parallel add/sub signed", dsc);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
- (unsigned long) insn);
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ return arm_copy_unmodified (gdbarch, insn, "parallel add/sub unsigned", dsc);
- dsc->modinsn[0] = insn;
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return arm_copy_unmodified (gdbarch, insn,
+ "decode/pack/unpack/saturate/reverse", dsc);
- return install_svc (gdbarch, regs, dsc);
-}
+ case 0x18:
+ if (bits (insn, 5, 7) == 0) /* op2. */
+ {
+ if (bits (insn, 12, 15) == 0xf)
+ return arm_copy_unmodified (gdbarch, insn, "usad8", dsc);
+ else
+ return arm_copy_unmodified (gdbarch, insn, "usada8", dsc);
+ }
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
-static int
-thumb_copy_svc (struct gdbarch *gdbarch, uint16_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
-{
+ case 0x1a: case 0x1b:
+ if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
+ return arm_copy_unmodified (gdbarch, insn, "sbfx", dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.4x\n",
- insn);
+ case 0x1c: case 0x1d:
+ if (bits (insn, 5, 6) == 0x0) /* op2[1:0]. */
+ {
+ if (bits (insn, 0, 3) == 0xf)
+ return arm_copy_unmodified (gdbarch, insn, "bfc", dsc);
+ else
+ return arm_copy_unmodified (gdbarch, insn, "bfi", dsc);
+ }
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
- dsc->modinsn[0] = insn;
+ case 0x1e: case 0x1f:
+ if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
+ return arm_copy_unmodified (gdbarch, insn, "ubfx", dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc);
+ }
- return install_svc (gdbarch, regs, dsc);
+ /* Should be unreachable. */
+ return 1;
}
-/* Copy undefined instructions. */
-
static int
-arm_copy_undef (struct gdbarch *gdbarch, uint32_t insn,
- struct displaced_step_closure *dsc)
+arm_decode_b_bl_ldmstm (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying undefined insn %.8lx\n",
- (unsigned long) insn);
-
- dsc->modinsn[0] = insn;
-
- return 0;
+ if (bit (insn, 25))
+ return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
+ else
+ return arm_copy_block_xfer (gdbarch, insn, regs, dsc);
}
static int
-thumb_32bit_copy_undef (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
- struct displaced_step_closure *dsc)
+arm_decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
+ unsigned int opcode = bits (insn, 20, 24);
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying undefined insn "
- "%.4x %.4x\n", (unsigned short) insn1,
- (unsigned short) insn2);
+ switch (opcode)
+ {
+ case 0x04: case 0x05: /* VFP/Neon mrrc/mcrr. */
+ return arm_copy_unmodified (gdbarch, insn, "vfp/neon mrrc/mcrr", dsc);
- dsc->modinsn[0] = insn1;
- dsc->modinsn[1] = insn2;
- dsc->numinsns = 2;
+ case 0x08: case 0x0a: case 0x0c: case 0x0e:
+ case 0x12: case 0x16:
+ return arm_copy_unmodified (gdbarch, insn, "vfp/neon vstm/vpush", dsc);
- return 0;
+ case 0x09: case 0x0b: case 0x0d: case 0x0f:
+ case 0x13: case 0x17:
+ return arm_copy_unmodified (gdbarch, insn, "vfp/neon vldm/vpop", dsc);
+
+ case 0x10: case 0x14: case 0x18: case 0x1c: /* vstr. */
+ case 0x11: case 0x15: case 0x19: case 0x1d: /* vldr. */
+ /* Note: no writeback for these instructions. Bit 25 will always be
+ zero though (via caller), so the following works OK. */
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
+ }
+
+ /* Should be unreachable. */
+ return 1;
}
-/* Copy unpredictable instructions. */
+/* Decode shifted register instructions. */
static int
-arm_copy_unpred (struct gdbarch *gdbarch, uint32_t insn,
- struct displaced_step_closure *dsc)
+thumb2_decode_dp_shift_reg (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying unpredictable insn "
- "%.8lx\n", (unsigned long) insn);
+ /* PC is only allowed to be used in instruction MOV. */
- dsc->modinsn[0] = insn;
+ unsigned int op = bits (insn1, 5, 8);
+ unsigned int rn = bits (insn1, 0, 3);
- return 0;
+ if (op == 0x2 && rn == 0xf) /* MOV */
+ return thumb2_copy_alu_imm (gdbarch, insn1, insn2, regs, dsc);
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "dp (shift reg)", dsc);
}
-/* The decode_* functions are instruction decoding helpers. They mostly follow
- the presentation in the ARM ARM. */
+
+/* Decode extension register load/store. Exactly the same as
+ arm_decode_ext_reg_ld_st. */
static int
-arm_decode_misc_memhint_neon (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb2_decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int op1 = bits (insn, 20, 26), op2 = bits (insn, 4, 7);
- unsigned int rn = bits (insn, 16, 19);
+ unsigned int opcode = bits (insn1, 4, 8);
- if (op1 == 0x10 && (op2 & 0x2) == 0x0 && (rn & 0xe) == 0x0)
- return arm_copy_unmodified (gdbarch, insn, "cps", dsc);
- else if (op1 == 0x10 && op2 == 0x0 && (rn & 0xe) == 0x1)
- return arm_copy_unmodified (gdbarch, insn, "setend", dsc);
- else if ((op1 & 0x60) == 0x20)
- return arm_copy_unmodified (gdbarch, insn, "neon dataproc", dsc);
- else if ((op1 & 0x71) == 0x40)
- return arm_copy_unmodified (gdbarch, insn, "neon elt/struct load/store",
- dsc);
- else if ((op1 & 0x77) == 0x41)
- return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
- else if ((op1 & 0x77) == 0x45)
- return arm_copy_preload (gdbarch, insn, regs, dsc); /* pli. */
- else if ((op1 & 0x77) == 0x51)
+ switch (opcode)
{
- if (rn != 0xf)
- return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
- else
- return arm_copy_unpred (gdbarch, insn, dsc);
+ case 0x04: case 0x05:
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "vfp/neon vmov", dsc);
+
+ case 0x08: case 0x0c: /* 01x00 */
+ case 0x0a: case 0x0e: /* 01x10 */
+ case 0x12: case 0x16: /* 10x10 */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "vfp/neon vstm/vpush", dsc);
+
+ case 0x09: case 0x0d: /* 01x01 */
+ case 0x0b: case 0x0f: /* 01x11 */
+ case 0x13: case 0x17: /* 10x11 */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "vfp/neon vldm/vpop", dsc);
+
+ case 0x10: case 0x14: case 0x18: case 0x1c: /* vstr. */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "vstr", dsc);
+ case 0x11: case 0x15: case 0x19: case 0x1d: /* vldr. */
+ return thumb2_copy_copro_load_store (gdbarch, insn1, insn2, regs, dsc);
}
- else if ((op1 & 0x77) == 0x55)
- return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
- else if (op1 == 0x57)
- switch (op2)
- {
- case 0x1: return arm_copy_unmodified (gdbarch, insn, "clrex", dsc);
- case 0x4: return arm_copy_unmodified (gdbarch, insn, "dsb", dsc);
- case 0x5: return arm_copy_unmodified (gdbarch, insn, "dmb", dsc);
- case 0x6: return arm_copy_unmodified (gdbarch, insn, "isb", dsc);
- default: return arm_copy_unpred (gdbarch, insn, dsc);
- }
- else if ((op1 & 0x63) == 0x43)
- return arm_copy_unpred (gdbarch, insn, dsc);
- else if ((op2 & 0x1) == 0x0)
- switch (op1 & ~0x80)
- {
- case 0x61:
- return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
- case 0x65:
- return arm_copy_preload_reg (gdbarch, insn, regs, dsc); /* pli reg. */
- case 0x71: case 0x75:
- /* pld/pldw reg. */
- return arm_copy_preload_reg (gdbarch, insn, regs, dsc);
- case 0x63: case 0x67: case 0x73: case 0x77:
- return arm_copy_unpred (gdbarch, insn, dsc);
- default:
- return arm_copy_undef (gdbarch, insn, dsc);
- }
- else
- return arm_copy_undef (gdbarch, insn, dsc); /* Probably unreachable. */
+
+ /* Should be unreachable. */
+ return 1;
}
static int
-arm_decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
- if (bit (insn, 27) == 0)
- return arm_decode_misc_memhint_neon (gdbarch, insn, regs, dsc);
- /* Switch on bits: 0bxxxxx321xxx0xxxxxxxxxxxxxxxxxxxx. */
- else switch (((insn & 0x7000000) >> 23) | ((insn & 0x100000) >> 20))
- {
- case 0x0: case 0x2:
- return arm_copy_unmodified (gdbarch, insn, "srs", dsc);
+ unsigned int op1 = bits (insn, 20, 25);
+ int op = bit (insn, 4);
+ unsigned int coproc = bits (insn, 8, 11);
- case 0x1: case 0x3:
- return arm_copy_unmodified (gdbarch, insn, "rfe", dsc);
+ if ((op1 & 0x20) == 0x00 && (op1 & 0x3a) != 0x00 && (coproc & 0xe) == 0xa)
+ return arm_decode_ext_reg_ld_st (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x21) == 0x00 && (op1 & 0x3a) != 0x00
+ && (coproc & 0xe) != 0xa)
+ /* stc/stc2. */
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x21) == 0x01 && (op1 & 0x3a) != 0x00
+ && (coproc & 0xe) != 0xa)
+ /* ldc/ldc2 imm/lit. */
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x3e) == 0x00)
+ return arm_copy_undef (gdbarch, insn, dsc);
+ else if ((op1 & 0x3e) == 0x04 && (coproc & 0xe) == 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "neon 64bit xfer", dsc);
+ else if (op1 == 0x04 && (coproc & 0xe) != 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
+ else if (op1 == 0x05 && (coproc & 0xe) != 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+ else if ((op1 & 0x30) == 0x20 && !op)
+ {
+ if ((coproc & 0xe) == 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "vfp dataproc", dsc);
+ else
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ }
+ else if ((op1 & 0x30) == 0x20 && op)
+ return arm_copy_unmodified (gdbarch, insn, "neon 8/16/32 bit xfer", dsc);
+ else if ((op1 & 0x31) == 0x20 && op && (coproc & 0xe) != 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ else if ((op1 & 0x31) == 0x21 && op && (coproc & 0xe) != 0xa)
+ return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ else if ((op1 & 0x30) == 0x30)
+ return arm_copy_svc (gdbarch, insn, regs, dsc);
+ else
+ return arm_copy_undef (gdbarch, insn, dsc); /* Possibly unreachable. */
+}
- case 0x4: case 0x5: case 0x6: case 0x7:
- return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
+static int
+thumb2_decode_svc_copro (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int coproc = bits (insn2, 8, 11);
+ unsigned int bit_5_8 = bits (insn1, 5, 8);
+ unsigned int bit_9 = bit (insn1, 9);
+ unsigned int bit_4 = bit (insn1, 4);
- case 0x8:
- switch ((insn & 0xe00000) >> 21)
+ if (bit_9 == 0)
+ {
+ if (bit_5_8 == 2)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "neon 64bit xfer/mrrc/mrrc2/mcrr/mcrr2",
+ dsc);
+ else if (bit_5_8 == 0) /* UNDEFINED. */
+ return thumb_32bit_copy_undef (gdbarch, insn1, insn2, dsc);
+ else
{
- case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
- /* stc/stc2. */
- return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
-
- case 0x2:
- return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
-
- default:
- return arm_copy_undef (gdbarch, insn, dsc);
+ /*coproc is 101x. SIMD/VFP, ext registers load/store. */
+ if ((coproc & 0xe) == 0xa)
+ return thumb2_decode_ext_reg_ld_st (gdbarch, insn1, insn2, regs,
+ dsc);
+ else /* coproc is not 101x. */
+ {
+ if (bit_4 == 0) /* STC/STC2. */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "stc/stc2", dsc);
+ else /* LDC/LDC2 {literal, immeidate}. */
+ return thumb2_copy_copro_load_store (gdbarch, insn1, insn2,
+ regs, dsc);
+ }
}
+ }
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "coproc", dsc);
- case 0x9:
- {
- int rn_f = (bits (insn, 16, 19) == 0xf);
- switch ((insn & 0xe00000) >> 21)
- {
- case 0x1: case 0x3:
- /* ldc/ldc2 imm (undefined for rn == pc). */
- return rn_f ? arm_copy_undef (gdbarch, insn, dsc)
- : arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return 0;
+}
- case 0x2:
- return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+static void
+install_pc_relative (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, int rd)
+{
+ /* ADR Rd, #imm
- case 0x4: case 0x5: case 0x6: case 0x7:
- /* ldc/ldc2 lit (undefined for rn != pc). */
- return rn_f ? arm_copy_copro_load_store (gdbarch, insn, regs, dsc)
- : arm_copy_undef (gdbarch, insn, dsc);
+ Rewrite as:
- default:
- return arm_copy_undef (gdbarch, insn, dsc);
- }
- }
+ Preparation: Rd <- PC
+ Insn: ADD Rd, #imm
+ Cleanup: Null.
+ */
- case 0xa:
- return arm_copy_unmodified (gdbarch, insn, "stc/stc2", dsc);
+ /* Rd <- PC */
+ int val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+ displaced_write_reg (regs, dsc, rd, val, CANNOT_WRITE_PC);
+}
- case 0xb:
- if (bits (insn, 16, 19) == 0xf)
- /* ldc/ldc2 lit. */
- return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc);
+static int
+thumb_copy_pc_relative_16bit (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ int rd, unsigned int imm)
+{
- case 0xc:
- if (bit (insn, 4))
- return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
- else
- return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ /* Encoding T2: ADDS Rd, #imm */
+ dsc->modinsn[0] = (0x3000 | (rd << 8) | imm);
- case 0xd:
- if (bit (insn, 4))
- return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
- else
- return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ install_pc_relative (gdbarch, regs, dsc, rd);
- default:
- return arm_copy_undef (gdbarch, insn, dsc);
- }
+ return 0;
}
-/* Decode miscellaneous instructions in dp/misc encoding space. */
-
static int
-arm_decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb_decode_pc_relative_16bit (struct gdbarch *gdbarch, uint16_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int op2 = bits (insn, 4, 6);
- unsigned int op = bits (insn, 21, 22);
- unsigned int op1 = bits (insn, 16, 19);
+ unsigned int rd = bits (insn, 8, 10);
+ unsigned int imm8 = bits (insn, 0, 7);
- switch (op2)
- {
- case 0x0:
- return arm_copy_unmodified (gdbarch, insn, "mrs/msr", dsc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying thumb adr r%d, #%d insn %.4x\n",
+ rd, imm8, insn);
- case 0x1:
- if (op == 0x1) /* bx. */
- return arm_copy_bx_blx_reg (gdbarch, insn, regs, dsc);
- else if (op == 0x3)
- return arm_copy_unmodified (gdbarch, insn, "clz", dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc);
-
- case 0x2:
- if (op == 0x1)
- /* Not really supported. */
- return arm_copy_unmodified (gdbarch, insn, "bxj", dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc);
-
- case 0x3:
- if (op == 0x1)
- return arm_copy_bx_blx_reg (gdbarch, insn,
- regs, dsc); /* blx register. */
- else
- return arm_copy_undef (gdbarch, insn, dsc);
-
- case 0x5:
- return arm_copy_unmodified (gdbarch, insn, "saturating add/sub", dsc);
-
- case 0x7:
- if (op == 0x1)
- return arm_copy_unmodified (gdbarch, insn, "bkpt", dsc);
- else if (op == 0x3)
- /* Not really supported. */
- return arm_copy_unmodified (gdbarch, insn, "smc", dsc);
-
- default:
- return arm_copy_undef (gdbarch, insn, dsc);
- }
+ return thumb_copy_pc_relative_16bit (gdbarch, regs, dsc, rd, imm8);
}
static int
-arm_decode_dp_misc (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb_copy_pc_relative_32bit (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (bit (insn, 25))
- switch (bits (insn, 20, 24))
- {
- case 0x10:
- return arm_copy_unmodified (gdbarch, insn, "movw", dsc);
-
- case 0x14:
- return arm_copy_unmodified (gdbarch, insn, "movt", dsc);
+ unsigned int rd = bits (insn2, 8, 11);
+ /* Since immediate has the same encoding in ADR ADD and SUB, so we simply
+ extract raw immediate encoding rather than computing immediate. When
+ generating ADD or SUB instruction, we can simply perform OR operation to
+ set immediate into ADD. */
+ unsigned int imm_3_8 = insn2 & 0x70ff;
+ unsigned int imm_i = insn1 & 0x0400; /* Clear all bits except bit 10. */
- case 0x12: case 0x16:
- return arm_copy_unmodified (gdbarch, insn, "msr imm", dsc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying thumb adr r%d, #%d:%d insn %.4x%.4x\n",
+ rd, imm_i, imm_3_8, insn1, insn2);
- default:
- return arm_copy_alu_imm (gdbarch, insn, regs, dsc);
- }
- else
+ if (bit (insn1, 7)) /* Encoding T2 */
{
- uint32_t op1 = bits (insn, 20, 24), op2 = bits (insn, 4, 7);
-
- if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0)
- return arm_copy_alu_reg (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
- return arm_copy_alu_shifted_reg (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
- return arm_decode_miscellaneous (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
- return arm_copy_unmodified (gdbarch, insn, "halfword mul/mla", dsc);
- else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
- return arm_copy_unmodified (gdbarch, insn, "mul/mla", dsc);
- else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
- return arm_copy_unmodified (gdbarch, insn, "synch", dsc);
- else if (op2 == 0xb || (op2 & 0xd) == 0xd)
- /* 2nd arg means "unpriveleged". */
- return arm_copy_extra_ld_st (gdbarch, insn, (op1 & 0x12) == 0x02, regs,
- dsc);
+ /* Encoding T3: SUB Rd, Rd, #imm */
+ dsc->modinsn[0] = (0xf1a0 | rd | imm_i);
+ dsc->modinsn[1] = ((rd << 8) | imm_3_8);
+ }
+ else /* Encoding T3 */
+ {
+ /* Encoding T3: ADD Rd, Rd, #imm */
+ dsc->modinsn[0] = (0xf100 | rd | imm_i);
+ dsc->modinsn[1] = ((rd << 8) | imm_3_8);
}
+ dsc->numinsns = 2;
- /* Should be unreachable. */
- return 1;
+ install_pc_relative (gdbarch, regs, dsc, rd);
+
+ return 0;
}
static int
-arm_decode_ld_st_word_ubyte (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb_copy_16bit_ldr_literal (struct gdbarch *gdbarch, uint16_t insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- int a = bit (insn, 25), b = bit (insn, 4);
- uint32_t op1 = bits (insn, 20, 24);
- int rn_f = bits (insn, 16, 19) == 0xf;
+ unsigned int rt = bits (insn1, 8, 10);
+ unsigned int pc;
+ int imm8 = (bits (insn1, 0, 7) << 2);
- if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
- || (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 4, 0);
- else if ((!a && (op1 & 0x17) == 0x02)
- || (a && (op1 & 0x17) == 0x02 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 4, 1);
- else if ((!a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03)
- || (a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 4, 0);
- else if ((!a && (op1 & 0x17) == 0x03)
- || (a && (op1 & 0x17) == 0x03 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 4, 1);
- else if ((!a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06)
- || (a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 0);
- else if ((!a && (op1 & 0x17) == 0x06)
- || (a && (op1 & 0x17) == 0x06 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 1);
- else if ((!a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07)
- || (a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 0);
- else if ((!a && (op1 & 0x17) == 0x07)
- || (a && (op1 & 0x17) == 0x07 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 1);
+ /* LDR Rd, #imm8
- /* Should be unreachable. */
- return 1;
-}
+ Rwrite as:
-static int
-arm_decode_media (struct gdbarch *gdbarch, uint32_t insn,
- struct displaced_step_closure *dsc)
-{
- switch (bits (insn, 20, 24))
- {
- case 0x00: case 0x01: case 0x02: case 0x03:
- return arm_copy_unmodified (gdbarch, insn, "parallel add/sub signed", dsc);
+ Preparation: tmp0 <- R0, tmp2 <- R2, tmp3 <- R3, R2 <- PC, R3 <- #imm8;
- case 0x04: case 0x05: case 0x06: case 0x07:
- return arm_copy_unmodified (gdbarch, insn, "parallel add/sub unsigned", dsc);
+ Insn: LDR R0, [R2, R3];
+ Cleanup: R2 <- tmp2, R3 <- tmp3, Rd <- R0, R0 <- tmp0 */
- case 0x08: case 0x09: case 0x0a: case 0x0b:
- case 0x0c: case 0x0d: case 0x0e: case 0x0f:
- return arm_copy_unmodified (gdbarch, insn,
- "decode/pack/unpack/saturate/reverse", dsc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying thumb ldr r%d [pc #%d]\n"
+ , rt, imm8);
- case 0x18:
- if (bits (insn, 5, 7) == 0) /* op2. */
- {
- if (bits (insn, 12, 15) == 0xf)
- return arm_copy_unmodified (gdbarch, insn, "usad8", dsc);
- else
- return arm_copy_unmodified (gdbarch, insn, "usada8", dsc);
- }
- else
- return arm_copy_undef (gdbarch, insn, dsc);
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
+ dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
+ dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
+ pc = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+ /* The assembler calculates the required value of the offset from the
+ Align(PC,4) value of this instruction to the label. */
+ pc = pc & 0xfffffffc;
- case 0x1a: case 0x1b:
- if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
- return arm_copy_unmodified (gdbarch, insn, "sbfx", dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc);
+ displaced_write_reg (regs, dsc, 2, pc, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 3, imm8, CANNOT_WRITE_PC);
- case 0x1c: case 0x1d:
- if (bits (insn, 5, 6) == 0x0) /* op2[1:0]. */
- {
- if (bits (insn, 0, 3) == 0xf)
- return arm_copy_unmodified (gdbarch, insn, "bfc", dsc);
- else
- return arm_copy_unmodified (gdbarch, insn, "bfi", dsc);
- }
- else
- return arm_copy_undef (gdbarch, insn, dsc);
+ dsc->rd = rt;
+ dsc->u.ldst.xfersize = 4;
+ dsc->u.ldst.rn = 0;
+ dsc->u.ldst.immed = 0;
+ dsc->u.ldst.writeback = 0;
+ dsc->u.ldst.restore_r4 = 0;
- case 0x1e: case 0x1f:
- if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
- return arm_copy_unmodified (gdbarch, insn, "ubfx", dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc);
- }
+ dsc->modinsn[0] = 0x58d0; /* ldr r0, [r2, r3]*/
- /* Should be unreachable. */
- return 1;
-}
+ dsc->cleanup = &cleanup_load;
-static int
-arm_decode_b_bl_ldmstm (struct gdbarch *gdbarch, int32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- if (bit (insn, 25))
- return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
- else
- return arm_copy_block_xfer (gdbarch, insn, regs, dsc);
+ return 0;
}
+/* Copy Thumb cbnz/cbz insruction. */
+
static int
-arm_decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, uint16_t insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- unsigned int opcode = bits (insn, 20, 24);
+ int non_zero = bit (insn1, 11);
+ unsigned int imm5 = (bit (insn1, 9) << 6) | (bits (insn1, 3, 7) << 1);
+ CORE_ADDR from = dsc->insn_addr;
+ int rn = bits (insn1, 0, 2);
+ int rn_val = displaced_read_reg (regs, dsc, rn);
- switch (opcode)
+ dsc->u.branch.cond = (rn_val && non_zero) || (!rn_val && !non_zero);
+ /* CBNZ and CBZ do not affect the condition flags. If condition is true,
+ set it INST_AL, so cleanup_branch will know branch is taken, otherwise,
+ condition is false, let it be, cleanup_branch will do nothing. */
+ if (dsc->u.branch.cond)
{
- case 0x04: case 0x05: /* VFP/Neon mrrc/mcrr. */
- return arm_copy_unmodified (gdbarch, insn, "vfp/neon mrrc/mcrr", dsc);
+ dsc->u.branch.cond = INST_AL;
+ dsc->u.branch.dest = from + 4 + imm5;
+ }
+ else
+ dsc->u.branch.dest = from + 2;
- case 0x08: case 0x0a: case 0x0c: case 0x0e:
- case 0x12: case 0x16:
- return arm_copy_unmodified (gdbarch, insn, "vfp/neon vstm/vpush", dsc);
+ dsc->u.branch.link = 0;
+ dsc->u.branch.exchange = 0;
- case 0x09: case 0x0b: case 0x0d: case 0x0f:
- case 0x13: case 0x17:
- return arm_copy_unmodified (gdbarch, insn, "vfp/neon vldm/vpop", dsc);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s [r%d = 0x%x]"
+ " insn %.4x to %.8lx\n", non_zero ? "cbnz" : "cbz",
+ rn, rn_val, insn1, dsc->u.branch.dest);
- case 0x10: case 0x14: case 0x18: case 0x1c: /* vstr. */
- case 0x11: case 0x15: case 0x19: case 0x1d: /* vldr. */
- /* Note: no writeback for these instructions. Bit 25 will always be
- zero though (via caller), so the following works OK. */
- return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- }
+ dsc->modinsn[0] = THUMB_NOP;
- /* Should be unreachable. */
- return 1;
+ dsc->cleanup = &cleanup_branch;
+ return 0;
}
-/* Decode shifted register instructions. */
-
+/* Copy Table Branch Byte/Halfword */
static int
-thumb2_decode_dp_shift_reg (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
+thumb2_copy_table_branch (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- /* PC is only allowed to be used in instruction MOV. */
-
- unsigned int op = bits (insn1, 5, 8);
- unsigned int rn = bits (insn1, 0, 3);
-
- if (op == 0x2 && rn == 0xf) /* MOV */
- return thumb2_copy_alu_imm (gdbarch, insn1, insn2, regs, dsc);
- else
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "dp (shift reg)", dsc);
-}
-
-
-/* Decode extension register load/store. Exactly the same as
- arm_decode_ext_reg_ld_st. */
+ ULONGEST rn_val, rm_val;
+ int is_tbh = bit (insn2, 4);
+ CORE_ADDR halfwords = 0;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-static int
-thumb2_decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int opcode = bits (insn1, 4, 8);
+ rn_val = displaced_read_reg (regs, dsc, bits (insn1, 0, 3));
+ rm_val = displaced_read_reg (regs, dsc, bits (insn2, 0, 3));
- switch (opcode)
+ if (is_tbh)
{
- case 0x04: case 0x05:
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "vfp/neon vmov", dsc);
-
- case 0x08: case 0x0c: /* 01x00 */
- case 0x0a: case 0x0e: /* 01x10 */
- case 0x12: case 0x16: /* 10x10 */
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "vfp/neon vstm/vpush", dsc);
-
- case 0x09: case 0x0d: /* 01x01 */
- case 0x0b: case 0x0f: /* 01x11 */
- case 0x13: case 0x17: /* 10x11 */
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "vfp/neon vldm/vpop", dsc);
+ gdb_byte buf[2];
- case 0x10: case 0x14: case 0x18: case 0x1c: /* vstr. */
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "vstr", dsc);
- case 0x11: case 0x15: case 0x19: case 0x1d: /* vldr. */
- return thumb2_copy_copro_load_store (gdbarch, insn1, insn2, regs, dsc);
+ target_read_memory (rn_val + 2 * rm_val, buf, 2);
+ halfwords = extract_unsigned_integer (buf, 2, byte_order);
}
-
- /* Should be unreachable. */
- return 1;
-}
-
-static int
-arm_decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
- struct regcache *regs, struct displaced_step_closure *dsc)
-{
- unsigned int op1 = bits (insn, 20, 25);
- int op = bit (insn, 4);
- unsigned int coproc = bits (insn, 8, 11);
- unsigned int rn = bits (insn, 16, 19);
-
- if ((op1 & 0x20) == 0x00 && (op1 & 0x3a) != 0x00 && (coproc & 0xe) == 0xa)
- return arm_decode_ext_reg_ld_st (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x21) == 0x00 && (op1 & 0x3a) != 0x00
- && (coproc & 0xe) != 0xa)
- /* stc/stc2. */
- return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x21) == 0x01 && (op1 & 0x3a) != 0x00
- && (coproc & 0xe) != 0xa)
- /* ldc/ldc2 imm/lit. */
- return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x3e) == 0x00)
- return arm_copy_undef (gdbarch, insn, dsc);
- else if ((op1 & 0x3e) == 0x04 && (coproc & 0xe) == 0xa)
- return arm_copy_unmodified (gdbarch, insn, "neon 64bit xfer", dsc);
- else if (op1 == 0x04 && (coproc & 0xe) != 0xa)
- return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
- else if (op1 == 0x05 && (coproc & 0xe) != 0xa)
- return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
- else if ((op1 & 0x30) == 0x20 && !op)
+ else
{
- if ((coproc & 0xe) == 0xa)
- return arm_copy_unmodified (gdbarch, insn, "vfp dataproc", dsc);
- else
- return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ gdb_byte buf[1];
+
+ target_read_memory (rn_val + rm_val, buf, 1);
+ halfwords = extract_unsigned_integer (buf, 1, byte_order);
}
- else if ((op1 & 0x30) == 0x20 && op)
- return arm_copy_unmodified (gdbarch, insn, "neon 8/16/32 bit xfer", dsc);
- else if ((op1 & 0x31) == 0x20 && op && (coproc & 0xe) != 0xa)
- return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
- else if ((op1 & 0x31) == 0x21 && op && (coproc & 0xe) != 0xa)
- return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
- else if ((op1 & 0x30) == 0x30)
- return arm_copy_svc (gdbarch, insn, regs, dsc);
- else
- return arm_copy_undef (gdbarch, insn, dsc); /* Possibly unreachable. */
-}
-static int
-thumb2_decode_svc_copro (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int coproc = bits (insn2, 8, 11);
- unsigned int op1 = bits (insn1, 4, 9);
- unsigned int bit_5_8 = bits (insn1, 5, 8);
- unsigned int bit_9 = bit (insn1, 9);
- unsigned int bit_4 = bit (insn1, 4);
- unsigned int rn = bits (insn1, 0, 3);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: %s base 0x%x offset 0x%x"
+ " offset 0x%x\n", is_tbh ? "tbh" : "tbb",
+ (unsigned int) rn_val, (unsigned int) rm_val,
+ (unsigned int) halfwords);
- if (bit_9 == 0)
- {
- if (bit_5_8 == 2)
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "neon 64bit xfer/mrrc/mrrc2/mcrr/mcrr2",
- dsc);
- else if (bit_5_8 == 0) /* UNDEFINED. */
- return thumb_32bit_copy_undef (gdbarch, insn1, insn2, dsc);
- else
- {
- /*coproc is 101x. SIMD/VFP, ext registers load/store. */
- if ((coproc & 0xe) == 0xa)
- return thumb2_decode_ext_reg_ld_st (gdbarch, insn1, insn2, regs,
- dsc);
- else /* coproc is not 101x. */
- {
- if (bit_4 == 0) /* STC/STC2. */
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
- "stc/stc2", dsc);
- else /* LDC/LDC2 {literal, immeidate}. */
- return thumb2_copy_copro_load_store (gdbarch, insn1, insn2,
- regs, dsc);
- }
- }
- }
- else
- return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "coproc", dsc);
+ dsc->u.branch.cond = INST_AL;
+ dsc->u.branch.link = 0;
+ dsc->u.branch.exchange = 0;
+ dsc->u.branch.dest = dsc->insn_addr + 4 + 2 * halfwords;
+
+ dsc->cleanup = &cleanup_branch;
return 0;
}
static void
-install_pc_relative (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, int rd)
+cleanup_pop_pc_16bit_all (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- /* ADR Rd, #imm
+ /* PC <- r7 */
+ int val = displaced_read_reg (regs, dsc, 7);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, val, BX_WRITE_PC);
- Rewrite as:
+ /* r7 <- r8 */
+ val = displaced_read_reg (regs, dsc, 8);
+ displaced_write_reg (regs, dsc, 7, val, CANNOT_WRITE_PC);
- Preparation: Rd <- PC
- Insn: ADD Rd, #imm
- Cleanup: Null.
- */
+ /* r8 <- tmp[0] */
+ displaced_write_reg (regs, dsc, 8, dsc->tmp[0], CANNOT_WRITE_PC);
- /* Rd <- PC */
- int val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
- displaced_write_reg (regs, dsc, rd, val, CANNOT_WRITE_PC);
}
static int
-thumb_copy_pc_relative_16bit (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc,
- int rd, unsigned int imm)
+thumb_copy_pop_pc_16bit (struct gdbarch *gdbarch, uint16_t insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
+ dsc->u.block.regmask = insn1 & 0x00ff;
- /* Encoding T2: ADDS Rd, #imm */
- dsc->modinsn[0] = (0x3000 | (rd << 8) | imm);
-
- install_pc_relative (gdbarch, regs, dsc, rd);
+ /* Rewrite instruction: POP {rX, rY, ...,rZ, PC}
+ to :
- return 0;
-}
+ (1) register list is full, that is, r0-r7 are used.
+ Prepare: tmp[0] <- r8
-static int
-thumb_decode_pc_relative_16bit (struct gdbarch *gdbarch, uint16_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int rd = bits (insn, 8, 10);
- unsigned int imm8 = bits (insn, 0, 7);
+ POP {r0, r1, ...., r6, r7}; remove PC from reglist
+ MOV r8, r7; Move value of r7 to r8;
+ POP {r7}; Store PC value into r7.
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying thumb adr r%d, #%d insn %.4x\n",
- rd, imm8, insn);
+ Cleanup: PC <- r7, r7 <- r8, r8 <-tmp[0]
- return thumb_copy_pc_relative_16bit (gdbarch, regs, dsc, rd, imm8);
-}
+ (2) register list is not full, supposing there are N registers in
+ register list (except PC, 0 <= N <= 7).
+ Prepare: for each i, 0 - N, tmp[i] <- ri.
-static int
-thumb_copy_pc_relative_32bit (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int rd = bits (insn2, 8, 11);
- /* Since immediate has the same encoding in ADR ADD and SUB, so we simply
- extract raw immediate encoding rather than computing immediate. When
- generating ADD or SUB instruction, we can simply perform OR operation to
- set immediate into ADD. */
- unsigned int imm_3_8 = insn2 & 0x70ff;
- unsigned int imm_i = insn1 & 0x0400; /* Clear all bits except bit 10. */
+ POP {r0, r1, ...., rN};
+ Cleanup: Set registers in original reglist from r0 - rN. Restore r0 - rN
+ from tmp[] properly.
+ */
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
- "displaced: copying thumb adr r%d, #%d:%d insn %.4x%.4x\n",
- rd, imm_i, imm_3_8, insn1, insn2);
+ "displaced: copying thumb pop {%.8x, pc} insn %.4x\n",
+ dsc->u.block.regmask, insn1);
- if (bit (insn1, 7)) /* Encoding T2 */
- {
- /* Encoding T3: SUB Rd, Rd, #imm */
- dsc->modinsn[0] = (0xf1a0 | rd | imm_i);
- dsc->modinsn[1] = ((rd << 8) | imm_3_8);
- }
- else /* Encoding T3 */
- {
- /* Encoding T3: ADD Rd, Rd, #imm */
- dsc->modinsn[0] = (0xf100 | rd | imm_i);
- dsc->modinsn[1] = ((rd << 8) | imm_3_8);
- }
- dsc->numinsns = 2;
-
- install_pc_relative (gdbarch, regs, dsc, rd);
-
- return 0;
-}
-
-static int
-thumb_copy_16bit_ldr_literal (struct gdbarch *gdbarch, unsigned short insn1,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- unsigned int rt = bits (insn1, 8, 10);
- unsigned int pc;
- int imm8 = (bits (insn1, 0, 7) << 2);
- CORE_ADDR from = dsc->insn_addr;
-
- /* LDR Rd, #imm8
-
- Rwrite as:
-
- Preparation: tmp0 <- R0, tmp2 <- R2, tmp3 <- R3, R2 <- PC, R3 <- #imm8;
-
- Insn: LDR R0, [R2, R3];
- Cleanup: R2 <- tmp2, R3 <- tmp3, Rd <- R0, R0 <- tmp0 */
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying thumb ldr r%d [pc #%d]\n"
- , rt, imm8);
-
- dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
- dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
- dsc->tmp[3] = displaced_read_reg (regs, dsc, 3);
- pc = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
- /* The assembler calculates the required value of the offset from the
- Align(PC,4) value of this instruction to the label. */
- pc = pc & 0xfffffffc;
-
- displaced_write_reg (regs, dsc, 2, pc, CANNOT_WRITE_PC);
- displaced_write_reg (regs, dsc, 3, imm8, CANNOT_WRITE_PC);
-
- dsc->rd = rt;
- dsc->u.ldst.xfersize = 4;
- dsc->u.ldst.rn = 0;
- dsc->u.ldst.immed = 0;
- dsc->u.ldst.writeback = 0;
- dsc->u.ldst.restore_r4 = 0;
-
- dsc->modinsn[0] = 0x58d0; /* ldr r0, [r2, r3]*/
-
- dsc->cleanup = &cleanup_load;
-
- return 0;
-}
-
-/* Copy Thumb cbnz/cbz insruction. */
-
-static int
-thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, uint16_t insn1,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- int non_zero = bit (insn1, 11);
- unsigned int imm5 = (bit (insn1, 9) << 6) | (bits (insn1, 3, 7) << 1);
- CORE_ADDR from = dsc->insn_addr;
- int rn = bits (insn1, 0, 2);
- int rn_val = displaced_read_reg (regs, dsc, rn);
-
- dsc->u.branch.cond = (rn_val && non_zero) || (!rn_val && !non_zero);
- /* CBNZ and CBZ do not affect the condition flags. If condition is true,
- set it INST_AL, so cleanup_branch will know branch is taken, otherwise,
- condition is false, let it be, cleanup_branch will do nothing. */
- if (dsc->u.branch.cond)
- {
- dsc->u.branch.cond = INST_AL;
- dsc->u.branch.dest = from + 4 + imm5;
- }
- else
- dsc->u.branch.dest = from + 2;
-
- dsc->u.branch.link = 0;
- dsc->u.branch.exchange = 0;
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying %s [r%d = 0x%x]"
- " insn %.4x to %.8lx\n", non_zero ? "cbnz" : "cbz",
- rn, rn_val, insn1, dsc->u.branch.dest);
-
- dsc->modinsn[0] = THUMB_NOP;
-
- dsc->cleanup = &cleanup_branch;
- return 0;
-}
-
-/* Copy Table Branch Byte/Halfword */
-static int
-thumb2_copy_table_branch (struct gdbarch *gdbarch, uint16_t insn1,
- uint16_t insn2, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- ULONGEST rn_val, rm_val;
- int is_tbh = bit (insn2, 4);
- CORE_ADDR halfwords = 0;
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-
- rn_val = displaced_read_reg (regs, dsc, bits (insn1, 0, 3));
- rm_val = displaced_read_reg (regs, dsc, bits (insn2, 0, 3));
-
- if (is_tbh)
- {
- gdb_byte buf[2];
-
- target_read_memory (rn_val + 2 * rm_val, buf, 2);
- halfwords = extract_unsigned_integer (buf, 2, byte_order);
- }
- else
- {
- gdb_byte buf[1];
-
- target_read_memory (rn_val + rm_val, buf, 1);
- halfwords = extract_unsigned_integer (buf, 1, byte_order);
- }
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: %s base 0x%x offset 0x%x"
- " offset 0x%x\n", is_tbh ? "tbh" : "tbb",
- (unsigned int) rn_val, (unsigned int) rm_val,
- (unsigned int) halfwords);
-
- dsc->u.branch.cond = INST_AL;
- dsc->u.branch.link = 0;
- dsc->u.branch.exchange = 0;
- dsc->u.branch.dest = dsc->insn_addr + 4 + 2 * halfwords;
-
- dsc->cleanup = &cleanup_branch;
-
- return 0;
-}
-
-static void
-cleanup_pop_pc_16bit_all (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- /* PC <- r7 */
- int val = displaced_read_reg (regs, dsc, 7);
- displaced_write_reg (regs, dsc, ARM_PC_REGNUM, val, BX_WRITE_PC);
-
- /* r7 <- r8 */
- val = displaced_read_reg (regs, dsc, 8);
- displaced_write_reg (regs, dsc, 7, val, CANNOT_WRITE_PC);
-
- /* r8 <- tmp[0] */
- displaced_write_reg (regs, dsc, 8, dsc->tmp[0], CANNOT_WRITE_PC);
-
-}
-
-static int
-thumb_copy_pop_pc_16bit (struct gdbarch *gdbarch, unsigned short insn1,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
-{
- dsc->u.block.regmask = insn1 & 0x00ff;
-
- /* Rewrite instruction: POP {rX, rY, ...,rZ, PC}
- to :
-
- (1) register list is full, that is, r0-r7 are used.
- Prepare: tmp[0] <- r8
-
- POP {r0, r1, ...., r6, r7}; remove PC from reglist
- MOV r8, r7; Move value of r7 to r8;
- POP {r7}; Store PC value into r7.
-
- Cleanup: PC <- r7, r7 <- r8, r8 <-tmp[0]
-
- (2) register list is not full, supposing there are N registers in
- register list (except PC, 0 <= N <= 7).
- Prepare: for each i, 0 - N, tmp[i] <- ri.
-
- POP {r0, r1, ...., rN};
-
- Cleanup: Set registers in original reglist from r0 - rN. Restore r0 - rN
- from tmp[] properly.
- */
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog,
- "displaced: copying thumb pop {%.8x, pc} insn %.4x\n",
- dsc->u.block.regmask, insn1);
-
- if (dsc->u.block.regmask == 0xff)
+ if (dsc->u.block.regmask == 0xff)
{
dsc->tmp[0] = displaced_read_reg (regs, dsc, 8);
else
{
unsigned int num_in_list = bitcount (dsc->u.block.regmask);
- unsigned int new_regmask, bit = 1;
- unsigned int to = 0, from = 0, i, new_rn;
+ unsigned int i;
+ unsigned int new_regmask;
for (i = 0; i < num_in_list + 1; i++)
dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
int rt = bits (insn2, 12, 15);
int rn = bits (insn1, 0, 3);
int op1 = bits (insn1, 7, 8);
- int err = 0;
switch (bits (insn1, 5, 6))
{
static void
thumb_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
- CORE_ADDR to, struct regcache *regs,
+ struct regcache *regs,
struct displaced_step_closure *dsc)
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
dsc->wrote_to_pc = 0;
if (!displaced_in_arm_mode (regs))
- return thumb_process_displaced_insn (gdbarch, from, to, regs, dsc);
+ return thumb_process_displaced_insn (gdbarch, from, regs, dsc);
dsc->is_thumb = 0;
dsc->insn_size = 4;
break;
case 0xc: case 0xd: case 0xe: case 0xf:
- err = arm_decode_svc_copro (gdbarch, insn, to, regs, dsc);
+ err = arm_decode_svc_copro (gdbarch, insn, regs, dsc);
break;
}
unsigned int i, len, offset;
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
int size = dsc->is_thumb? 2 : 4;
- const unsigned char *bkp_insn;
+ const gdb_byte *bkp_insn;
offset = 0;
/* Poke modified instruction(s). */
paddress (gdbarch, from), paddress (gdbarch, to));
}
-/* Entry point for copying an instruction into scratch space for displaced
- stepping. */
-
-struct displaced_step_closure *
-arm_displaced_step_copy_insn (struct gdbarch *gdbarch,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
-{
- struct displaced_step_closure *dsc
- = xmalloc (sizeof (struct displaced_step_closure));
- arm_process_displaced_insn (gdbarch, from, to, regs, dsc);
- arm_displaced_init_closure (gdbarch, from, to, dsc);
-
- return dsc;
-}
-
/* Entry point for cleaning things up after a displaced instruction has been
single-stepped. */
static int
gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info)
{
- struct gdbarch *gdbarch = info->application_data;
+ struct gdbarch *gdbarch = (struct gdbarch *) info->application_data;
if (arm_pc_is_thumb (gdbarch, memaddr))
{
#define THUMB_LE_BREAKPOINT {0xbe,0xbe}
#define THUMB_BE_BREAKPOINT {0xbe,0xbe}
-static const char arm_default_arm_le_breakpoint[] = ARM_LE_BREAKPOINT;
-static const char arm_default_arm_be_breakpoint[] = ARM_BE_BREAKPOINT;
-static const char arm_default_thumb_le_breakpoint[] = THUMB_LE_BREAKPOINT;
-static const char arm_default_thumb_be_breakpoint[] = THUMB_BE_BREAKPOINT;
+static const gdb_byte arm_default_arm_le_breakpoint[] = ARM_LE_BREAKPOINT;
+static const gdb_byte arm_default_arm_be_breakpoint[] = ARM_BE_BREAKPOINT;
+static const gdb_byte arm_default_thumb_le_breakpoint[] = THUMB_LE_BREAKPOINT;
+static const gdb_byte arm_default_thumb_be_breakpoint[] = THUMB_BE_BREAKPOINT;
/* Determine the type and size of breakpoint to insert at PCPTR. Uses
the program counter value to determine whether a 16-bit or 32-bit
static int
arm_return_in_memory (struct gdbarch *gdbarch, struct type *type)
{
- int nRc;
enum type_code code;
- CHECK_TYPEDEF (type);
-
- /* In the ARM ABI, "integer" like aggregate types are returned in
- registers. For an aggregate type to be integer like, its size
- must be less than or equal to INT_REGISTER_SIZE and the
- offset of each addressable subfield must be zero. Note that bit
- fields are not addressable, and all addressable subfields of
- unions always start at offset zero.
+ type = check_typedef (type);
- This function is based on the behaviour of GCC 2.95.1.
- See: gcc/arm.c: arm_return_in_memory() for details.
-
- Note: All versions of GCC before GCC 2.95.2 do not set up the
- parameters correctly for a function returning the following
- structure: struct { float f;}; This should be returned in memory,
- not a register. Richard Earnshaw sent me a patch, but I do not
- know of any way to detect if a function like the above has been
- compiled with the correct calling convention. */
+ /* Simple, non-aggregate types (ie not including vectors and
+ complex) are always returned in a register (or registers). */
+ code = TYPE_CODE (type);
+ if (TYPE_CODE_STRUCT != code && TYPE_CODE_UNION != code
+ && TYPE_CODE_ARRAY != code && TYPE_CODE_COMPLEX != code)
+ return 0;
- /* All aggregate types that won't fit in a register must be returned
- in memory. */
- if (TYPE_LENGTH (type) > INT_REGISTER_SIZE)
+ if (TYPE_CODE_ARRAY == code && TYPE_VECTOR (type))
{
- return 1;
+ /* Vector values should be returned using ARM registers if they
+ are not over 16 bytes. */
+ return (TYPE_LENGTH (type) > 16);
}
- /* The AAPCS says all aggregates not larger than a word are returned
- in a register. */
if (gdbarch_tdep (gdbarch)->arm_abi != ARM_ABI_APCS)
- return 0;
-
- /* The only aggregate types that can be returned in a register are
- structs and unions. Arrays must be returned in memory. */
- code = TYPE_CODE (type);
- if ((TYPE_CODE_STRUCT != code) && (TYPE_CODE_UNION != code))
{
+ /* The AAPCS says all aggregates not larger than a word are returned
+ in a register. */
+ if (TYPE_LENGTH (type) <= INT_REGISTER_SIZE)
+ return 0;
+
return 1;
}
+ else
+ {
+ int nRc;
- /* Assume all other aggregate types can be returned in a register.
- Run a check for structures, unions and arrays. */
- nRc = 0;
+ /* All aggregate types that won't fit in a register must be returned
+ in memory. */
+ if (TYPE_LENGTH (type) > INT_REGISTER_SIZE)
+ return 1;
- if ((TYPE_CODE_STRUCT == code) || (TYPE_CODE_UNION == code))
- {
- int i;
- /* Need to check if this struct/union is "integer" like. For
- this to be true, its size must be less than or equal to
- INT_REGISTER_SIZE and the offset of each addressable
- subfield must be zero. Note that bit fields are not
- addressable, and unions always start at offset zero. If any
- of the subfields is a floating point type, the struct/union
- cannot be an integer type. */
-
- /* For each field in the object, check:
- 1) Is it FP? --> yes, nRc = 1;
- 2) Is it addressable (bitpos != 0) and
- not packed (bitsize == 0)?
- --> yes, nRc = 1
- */
-
- for (i = 0; i < TYPE_NFIELDS (type); i++)
- {
- enum type_code field_type_code;
- field_type_code = TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type,
- i)));
-
- /* Is it a floating point type field? */
- if (field_type_code == TYPE_CODE_FLT)
- {
- nRc = 1;
- break;
- }
+ /* In the ARM ABI, "integer" like aggregate types are returned in
+ registers. For an aggregate type to be integer like, its size
+ must be less than or equal to INT_REGISTER_SIZE and the
+ offset of each addressable subfield must be zero. Note that bit
+ fields are not addressable, and all addressable subfields of
+ unions always start at offset zero.
+
+ This function is based on the behaviour of GCC 2.95.1.
+ See: gcc/arm.c: arm_return_in_memory() for details.
- /* If bitpos != 0, then we have to care about it. */
- if (TYPE_FIELD_BITPOS (type, i) != 0)
+ Note: All versions of GCC before GCC 2.95.2 do not set up the
+ parameters correctly for a function returning the following
+ structure: struct { float f;}; This should be returned in memory,
+ not a register. Richard Earnshaw sent me a patch, but I do not
+ know of any way to detect if a function like the above has been
+ compiled with the correct calling convention. */
+
+ /* Assume all other aggregate types can be returned in a register.
+ Run a check for structures, unions and arrays. */
+ nRc = 0;
+
+ if ((TYPE_CODE_STRUCT == code) || (TYPE_CODE_UNION == code))
+ {
+ int i;
+ /* Need to check if this struct/union is "integer" like. For
+ this to be true, its size must be less than or equal to
+ INT_REGISTER_SIZE and the offset of each addressable
+ subfield must be zero. Note that bit fields are not
+ addressable, and unions always start at offset zero. If any
+ of the subfields is a floating point type, the struct/union
+ cannot be an integer type. */
+
+ /* For each field in the object, check:
+ 1) Is it FP? --> yes, nRc = 1;
+ 2) Is it addressable (bitpos != 0) and
+ not packed (bitsize == 0)?
+ --> yes, nRc = 1
+ */
+
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
{
- /* Bitfields are not addressable. If the field bitsize is
- zero, then the field is not packed. Hence it cannot be
- a bitfield or any other packed type. */
- if (TYPE_FIELD_BITSIZE (type, i) == 0)
+ enum type_code field_type_code;
+
+ field_type_code
+ = TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type,
+ i)));
+
+ /* Is it a floating point type field? */
+ if (field_type_code == TYPE_CODE_FLT)
{
nRc = 1;
break;
}
+
+ /* If bitpos != 0, then we have to care about it. */
+ if (TYPE_FIELD_BITPOS (type, i) != 0)
+ {
+ /* Bitfields are not addressable. If the field bitsize is
+ zero, then the field is not packed. Hence it cannot be
+ a bitfield or any other packed type. */
+ if (TYPE_FIELD_BITSIZE (type, i) == 0)
+ {
+ nRc = 1;
+ break;
+ }
+ }
}
}
- }
- return nRc;
+ return nRc;
+ }
}
/* Write into appropriate registers a function return value of type
|| arm_return_in_memory (gdbarch, valtype))
return RETURN_VALUE_STRUCT_CONVENTION;
}
-
- /* AAPCS returns complex types longer than a register in memory. */
- if (tdep->arm_abi != ARM_ABI_APCS
- && TYPE_CODE (valtype) == TYPE_CODE_COMPLEX
- && TYPE_LENGTH (valtype) > INT_REGISTER_SIZE)
- return RETURN_VALUE_STRUCT_CONVENTION;
+ else if (TYPE_CODE (valtype) == TYPE_CODE_COMPLEX)
+ {
+ if (arm_return_in_memory (gdbarch, valtype))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+ }
if (writebuf)
arm_store_return_value (valtype, regcache, writebuf);
/* Find the starting address and name of the function containing the PC. */
if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
- return 0;
+ {
+ /* Trampoline 'bx reg' doesn't belong to any functions. Do the
+ check here. */
+ start_addr = arm_skip_bx_reg (frame, pc);
+ if (start_addr != 0)
+ return start_addr;
+
+ return 0;
+ }
/* If PC is in a Thumb call or return stub, return the address of the
target PC, which is in a register. The thunk functions are called
_call_via_xx, where x is the register name. The possible names
are r0-r9, sl, fp, ip, sp, and lr. ARM RealView has similar
functions, named __ARM_call_via_r[0-7]. */
- if (strncmp (name, "_call_via_", 10) == 0
- || strncmp (name, "__ARM_call_via_", strlen ("__ARM_call_via_")) == 0)
+ if (startswith (name, "_call_via_")
+ || startswith (name, "__ARM_call_via_"))
{
/* Use the name suffix to determine which register contains the
target PC. */
namelen = strlen (name);
if (name[0] == '_' && name[1] == '_'
&& ((namelen > 2 + strlen ("_from_thumb")
- && strncmp (name + namelen - strlen ("_from_thumb"), "_from_thumb",
- strlen ("_from_thumb")) == 0)
+ && startswith (name + namelen - strlen ("_from_thumb"), "_from_thumb"))
|| (namelen > 2 + strlen ("_from_arm")
- && strncmp (name + namelen - strlen ("_from_arm"), "_from_arm",
- strlen ("_from_arm")) == 0)))
+ && startswith (name + namelen - strlen ("_from_arm"), "_from_arm"))))
{
char *target_name;
int target_len = namelen - 2;
- struct minimal_symbol *minsym;
+ struct bound_minimal_symbol minsym;
struct objfile *objfile;
struct obj_section *sec;
else
target_len -= strlen ("_from_arm");
- target_name = alloca (target_len + 1);
+ target_name = (char *) alloca (target_len + 1);
memcpy (target_name, name + 2, target_len);
target_name[target_len] = '\0';
sec = find_pc_section (pc);
objfile = (sec == NULL) ? NULL : sec->objfile;
minsym = lookup_minimal_symbol (target_name, NULL, objfile);
- if (minsym != NULL)
- return SYMBOL_VALUE_ADDRESS (minsym);
+ if (minsym.minsym != NULL)
+ return BMSYMBOL_VALUE_ADDRESS (minsym);
else
return 0;
}
set_fp_model_sfunc (char *args, int from_tty,
struct cmd_list_element *c)
{
- enum arm_float_model fp_model;
+ int fp_model;
for (fp_model = ARM_FLOAT_AUTO; fp_model != ARM_FLOAT_LAST; fp_model++)
if (strcmp (current_fp_model, fp_model_strings[fp_model]) == 0)
{
- arm_fp_model = fp_model;
+ arm_fp_model = (enum arm_float_model) fp_model;
break;
}
arm_set_abi (char *args, int from_tty,
struct cmd_list_element *c)
{
- enum arm_abi_kind arm_abi;
+ int arm_abi;
for (arm_abi = ARM_ABI_AUTO; arm_abi != ARM_ABI_LAST; arm_abi++)
if (strcmp (arm_abi_string, arm_abi_strings[arm_abi]) == 0)
{
- arm_abi_global = arm_abi;
+ arm_abi_global = (enum arm_abi_kind) arm_abi;
break;
}
arm_show_force_mode (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch ());
-
fprintf_filtered (file,
_("The current execution mode assumed "
"(even when symbols are available) is \"%s\".\n"),
static void
arm_objfile_data_free (struct objfile *objfile, void *arg)
{
- struct arm_per_objfile *data = arg;
+ struct arm_per_objfile *data = (struct arm_per_objfile *) arg;
unsigned int i;
for (i = 0; i < objfile->obfd->section_count; i++)
if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
return;
- data = objfile_data (objfile, arm_objfile_data_key);
+ data = (struct arm_per_objfile *) objfile_data (objfile,
+ arm_objfile_data_key);
if (data == NULL)
{
data = OBSTACK_ZALLOC (&objfile->objfile_obstack,
static struct value *
value_of_arm_user_reg (struct frame_info *frame, const void *baton)
{
- const int *reg_p = baton;
+ const int *reg_p = (const int *) baton;
return value_of_register (*reg_p, frame);
}
\f
enum arm_float_model fp_model = arm_fp_model;
struct tdesc_arch_data *tdesc_data = NULL;
int i, is_m = 0;
- int have_vfp_registers = 0, have_vfp_pseudos = 0, have_neon_pseudos = 0;
+ int vfp_register_count = 0, have_vfp_pseudos = 0, have_neon_pseudos = 0;
+ int have_wmmx_registers = 0;
int have_neon = 0;
int have_fpa_registers = 1;
const struct target_desc *tdesc = info.target_desc;
anyway, so assume APCS. */
arm_abi = ARM_ABI_APCS;
}
- else if (ei_osabi == ELFOSABI_NONE)
+ else if (ei_osabi == ELFOSABI_NONE || ei_osabi == ELFOSABI_GNU)
{
int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
int attr_arch, attr_profile;
OBJ_ATTR_PROC,
Tag_ABI_VFP_args))
{
- case 0:
+ case AEABI_VFP_args_base:
/* "The user intended FP parameter/result
passing to conform to AAPCS, base
variant". */
fp_model = ARM_FLOAT_SOFT_VFP;
break;
- case 1:
+ case AEABI_VFP_args_vfp:
/* "The user intended FP parameter/result
passing to conform to AAPCS, VFP
variant". */
fp_model = ARM_FLOAT_VFP;
break;
- case 2:
+ case AEABI_VFP_args_toolchain:
/* "The user intended FP parameter/result
passing to conform to tool chain-specific
conventions" - we don't know any such
conventions, so leave it as "auto". */
break;
+ case AEABI_VFP_args_compatible:
+ /* "Code is compatible with both the base
+ and VFP variants; the user did not permit
+ non-variadic functions to pass FP
+ parameters/results" - leave it as
+ "auto". */
+ break;
default:
/* Attribute value not mentioned in the
- October 2008 ABI, so leave it as
+ November 2012 ABI, so leave it as
"auto". */
break;
}
tdesc_data_cleanup (tdesc_data);
return NULL;
}
+
+ have_wmmx_registers = 1;
}
/* If we have a VFP unit, check whether the single precision registers
if (tdesc_unnumbered_register (feature, "s0") == 0)
have_vfp_pseudos = 1;
- have_vfp_registers = 1;
+ vfp_register_count = i;
/* If we have VFP, also check for NEON. The architecture allows
NEON without VFP (integer vector operations only), but GDB
return best_arch->gdbarch;
}
- tdep = xcalloc (1, sizeof (struct gdbarch_tdep));
+ tdep = XCNEW (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
/* Record additional information about the architecture we are defining.
tdep->fp_model = fp_model;
tdep->is_m = is_m;
tdep->have_fpa_registers = have_fpa_registers;
- tdep->have_vfp_registers = have_vfp_registers;
+ tdep->have_wmmx_registers = have_wmmx_registers;
+ gdb_assert (vfp_register_count == 0
+ || vfp_register_count == 16
+ || vfp_register_count == 32);
+ tdep->vfp_register_count = vfp_register_count;
tdep->have_vfp_pseudos = have_vfp_pseudos;
tdep->have_neon_pseudos = have_neon_pseudos;
tdep->have_neon = have_neon;
/* Advance PC across function entry code. */
set_gdbarch_skip_prologue (gdbarch, arm_skip_prologue);
- /* Detect whether PC is in function epilogue. */
- set_gdbarch_in_function_epilogue_p (gdbarch, arm_in_function_epilogue_p);
+ /* Detect whether PC is at a point where the stack has been destroyed. */
+ set_gdbarch_stack_frame_destroyed_p (gdbarch, arm_stack_frame_destroyed_p);
/* Skip trampolines. */
set_gdbarch_skip_trampoline_code (gdbarch, arm_skip_stub);
{
struct ui_file *stb;
long length;
- struct cmd_list_element *new_set, *new_show;
const char *setname;
const char *setdesc;
const char *const *regnames;
- int numregs, i, j;
+ int i;
static char *helptext;
char regdesc[1024], *rdptr = regdesc;
size_t rest = sizeof (regdesc);
/* Initialize the array that will be passed to
add_setshow_enum_cmd(). */
- valid_disassembly_styles
- = xmalloc ((num_disassembly_options + 1) * sizeof (char *));
+ valid_disassembly_styles = XNEWVEC (const char *,
+ num_disassembly_options + 1);
for (i = 0; i < num_disassembly_options; i++)
{
- numregs = get_arm_regnames (i, &setname, &setdesc, ®names);
+ get_arm_regnames (i, &setname, &setdesc, ®names);
valid_disassembly_styles[i] = setname;
length = snprintf (rdptr, rest, "%s - %s\n", setname, setdesc);
rdptr += length;
#define THUMB2_INSN_SIZE_BYTES 4
+/* Position of the bit within a 32-bit ARM instruction
+ that defines whether the instruction is a load or store. */
#define INSN_S_L_BIT_NUM 20
#define REG_ALLOC(REGS, LENGTH, RECORD_BUF) \
struct arm_mem_r
{
uint32_t len; /* Record length. */
- CORE_ADDR addr; /* Memory address. */
+ uint32_t addr; /* Memory address. */
};
/* ARM instruction record contains opcode of current insn
return 1;
}
+enum arm_record_result
+{
+ ARM_RECORD_SUCCESS = 0,
+ ARM_RECORD_FAILURE = 1
+};
+
typedef enum
{
ARM_RECORD_STRH=1,
uint32_t reg_src1 = 0, reg_src2 = 0;
uint32_t immed_high = 0, immed_low = 0,offset_8 = 0, tgt_mem_addr = 0;
- uint32_t opcode1 = 0;
arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
- opcode1 = bits (arm_insn_r->arm_insn, 20, 24);
-
if (14 == arm_insn_r->opcode || 10 == arm_insn_r->opcode)
{
uint32_t opcode1 = 0, opcode2 = 0, insn_op1 = 0;
uint32_t record_buf[8], record_buf_mem[8];
uint32_t reg_src1 = 0;
- uint32_t immed_high = 0, immed_low = 0,offset_8 = 0, tgt_mem_addr = 0;
struct regcache *reg_cache = arm_insn_r->regcache;
ULONGEST u_regval = 0;
{
/* SPSR is going to be changed. */
/* We need to get SPSR value, which is yet to be done. */
- printf_unfiltered (_("Process record does not support "
- "instruction 0x%0x at address %s.\n"),
- arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch,
- arm_insn_r->this_addr));
return -1;
}
}
arm_insn_r->reg_rec_count = 2;
/* Save SPSR also;how? */
- printf_unfiltered (_("Process record does not support "
- "instruction 0x%0x at address %s.\n"),
- arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
return -1;
}
else if(8 == bits (arm_insn_r->arm_insn, 4, 7)
{
/* SPSR is going to be changed. */
/* we need to get SPSR value, which is yet to be done */
- printf_unfiltered (_("Process record does not support "
- "instruction 0x%0x at address %s.\n"),
- arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch,
- arm_insn_r->this_addr));
return -1;
}
}
/* To be done for ARMv5 and later; as of now we return -1. */
if (-1 == ret)
- printf_unfiltered (_("Process record does not support instruction x%0x "
- "at address %s.\n"),arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
-
+ return ret;
REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
uint32_t record_buf[8], record_buf_mem[8];
ULONGEST u_regval[2] = {0};
- uint32_t reg_src1 = 0, reg_src2 = 0, reg_dest = 0;
- uint32_t immed_high = 0, immed_low = 0, offset_8 = 0, tgt_mem_addr = 0;
+ uint32_t reg_src1 = 0, reg_dest = 0;
uint32_t opcode1 = 0;
arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
{
/* SPSR is going to be changed. */
/* How to read SPSR value? */
- printf_unfiltered (_("Process record does not support instruction "
- "0x%0x at address %s.\n"),
- arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
return -1;
}
}
arm_insn_r->reg_rec_count = 2;
/* Save SPSR also; how? */
- printf_unfiltered (_("Process record does not support instruction "
- "0x%0x at address %s.\n"),arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch,
- arm_insn_r->this_addr));
return -1;
}
else if (11 == arm_insn_r->decode
return 0;
}
-/* Handling opcode 010 insns. */
+static int
+arm_record_media (insn_decode_record *arm_insn_r)
+{
+ uint32_t record_buf[8];
+
+ switch (bits (arm_insn_r->arm_insn, 22, 24))
+ {
+ case 0:
+ /* Parallel addition and subtraction, signed */
+ case 1:
+ /* Parallel addition and subtraction, unsigned */
+ case 2:
+ case 3:
+ /* Packing, unpacking, saturation and reversal */
+ {
+ int rd = bits (arm_insn_r->arm_insn, 12, 15);
+
+ record_buf[arm_insn_r->reg_rec_count++] = rd;
+ }
+ break;
+
+ case 4:
+ case 5:
+ /* Signed multiplies */
+ {
+ int rd = bits (arm_insn_r->arm_insn, 16, 19);
+ unsigned int op1 = bits (arm_insn_r->arm_insn, 20, 22);
+
+ record_buf[arm_insn_r->reg_rec_count++] = rd;
+ if (op1 == 0x0)
+ record_buf[arm_insn_r->reg_rec_count++] = ARM_PS_REGNUM;
+ else if (op1 == 0x4)
+ record_buf[arm_insn_r->reg_rec_count++]
+ = bits (arm_insn_r->arm_insn, 12, 15);
+ }
+ break;
+
+ case 6:
+ {
+ if (bit (arm_insn_r->arm_insn, 21)
+ && bits (arm_insn_r->arm_insn, 5, 6) == 0x2)
+ {
+ /* SBFX */
+ record_buf[arm_insn_r->reg_rec_count++]
+ = bits (arm_insn_r->arm_insn, 12, 15);
+ }
+ else if (bits (arm_insn_r->arm_insn, 20, 21) == 0x0
+ && bits (arm_insn_r->arm_insn, 5, 7) == 0x0)
+ {
+ /* USAD8 and USADA8 */
+ record_buf[arm_insn_r->reg_rec_count++]
+ = bits (arm_insn_r->arm_insn, 16, 19);
+ }
+ }
+ break;
+
+ case 7:
+ {
+ if (bits (arm_insn_r->arm_insn, 20, 21) == 0x3
+ && bits (arm_insn_r->arm_insn, 5, 7) == 0x7)
+ {
+ /* Permanently UNDEFINED */
+ return -1;
+ }
+ else
+ {
+ /* BFC, BFI and UBFX */
+ record_buf[arm_insn_r->reg_rec_count++]
+ = bits (arm_insn_r->arm_insn, 12, 15);
+ }
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+
+ return 0;
+}
+
+/* Handle ARM mode instructions with opcode 010. */
static int
arm_record_ld_st_imm_offset (insn_decode_record *arm_insn_r)
{
struct regcache *reg_cache = arm_insn_r->regcache;
- uint32_t reg_src1 = 0 , reg_dest = 0;
- uint32_t offset_12 = 0, tgt_mem_addr = 0;
+ uint32_t reg_base , reg_dest;
+ uint32_t offset_12, tgt_mem_addr;
uint32_t record_buf[8], record_buf_mem[8];
+ unsigned char wback;
+ ULONGEST u_regval;
- ULONGEST u_regval = 0;
+ /* Calculate wback. */
+ wback = (bit (arm_insn_r->arm_insn, 24) == 0)
+ || (bit (arm_insn_r->arm_insn, 21) == 1);
- arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
- arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
+ arm_insn_r->reg_rec_count = 0;
+ reg_base = bits (arm_insn_r->arm_insn, 16, 19);
if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
{
+ /* LDR (immediate), LDR (literal), LDRB (immediate), LDRB (literal), LDRBT
+ and LDRT. */
+
reg_dest = bits (arm_insn_r->arm_insn, 12, 15);
- /* LDR insn has a capability to do branching, if
- MOV LR, PC is precedded by LDR insn having Rn as R15
- in that case, it emulates branch and link insn, and hence we
- need to save CSPR and PC as well. */
- if (ARM_PC_REGNUM != reg_dest)
- {
- record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
- arm_insn_r->reg_rec_count = 1;
- }
- else
- {
- record_buf[0] = reg_dest;
- record_buf[1] = ARM_PS_REGNUM;
- arm_insn_r->reg_rec_count = 2;
- }
+ record_buf[arm_insn_r->reg_rec_count++] = reg_dest;
+
+ /* The LDR instruction is capable of doing branching. If MOV LR, PC
+ preceeds a LDR instruction having R15 as reg_base, it
+ emulates a branch and link instruction, and hence we need to save
+ CPSR and PC as well. */
+ if (ARM_PC_REGNUM == reg_dest)
+ record_buf[arm_insn_r->reg_rec_count++] = ARM_PS_REGNUM;
+
+ /* If wback is true, also save the base register, which is going to be
+ written to. */
+ if (wback)
+ record_buf[arm_insn_r->reg_rec_count++] = reg_base;
}
else
{
- /* Store, immediate offset, immediate pre-indexed,
- immediate post-indexed. */
- reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ /* STR (immediate), STRB (immediate), STRBT and STRT. */
+
offset_12 = bits (arm_insn_r->arm_insn, 0, 11);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
- /* U == 1 */
+ regcache_raw_read_unsigned (reg_cache, reg_base, &u_regval);
+
+ /* Handle bit U. */
if (bit (arm_insn_r->arm_insn, 23))
- {
- tgt_mem_addr = u_regval + offset_12;
- }
+ {
+ /* U == 1: Add the offset. */
+ tgt_mem_addr = (uint32_t) u_regval + offset_12;
+ }
else
- {
- tgt_mem_addr = u_regval - offset_12;
- }
+ {
+ /* U == 0: subtract the offset. */
+ tgt_mem_addr = (uint32_t) u_regval - offset_12;
+ }
+
+ /* Bit 22 tells us whether the store instruction writes 1 byte or 4
+ bytes. */
+ if (bit (arm_insn_r->arm_insn, 22))
+ {
+ /* STRB and STRBT: 1 byte. */
+ record_buf_mem[0] = 1;
+ }
+ else
+ {
+ /* STR and STRT: 4 bytes. */
+ record_buf_mem[0] = 4;
+ }
+
+ /* Handle bit P. */
+ if (bit (arm_insn_r->arm_insn, 24))
+ record_buf_mem[1] = tgt_mem_addr;
+ else
+ record_buf_mem[1] = (uint32_t) u_regval;
- switch (arm_insn_r->opcode)
- {
- /* STR. */
- case 8:
- case 12:
- /* STR. */
- case 9:
- case 13:
- /* STRT. */
- case 1:
- case 5:
- /* STR. */
- case 4:
- case 0:
- record_buf_mem[0] = 4;
- break;
-
- /* STRB. */
- case 10:
- case 14:
- /* STRB. */
- case 11:
- case 15:
- /* STRBT. */
- case 3:
- case 7:
- /* STRB. */
- case 2:
- case 6:
- record_buf_mem[0] = 1;
- break;
-
- default:
- gdb_assert_not_reached ("no decoding pattern found");
- break;
- }
- record_buf_mem[1] = tgt_mem_addr;
arm_insn_r->mem_rec_count = 1;
- if (9 == arm_insn_r->opcode || 11 == arm_insn_r->opcode
- || 13 == arm_insn_r->opcode || 15 == arm_insn_r->opcode
- || 0 == arm_insn_r->opcode || 2 == arm_insn_r->opcode
- || 4 == arm_insn_r->opcode || 6 == arm_insn_r->opcode
- || 1 == arm_insn_r->opcode || 3 == arm_insn_r->opcode
- || 5 == arm_insn_r->opcode || 7 == arm_insn_r->opcode
- )
- {
- /* We are handling pre-indexed mode; post-indexed mode;
- where Rn is going to be changed. */
- record_buf[0] = reg_src1;
- arm_insn_r->reg_rec_count = 1;
- }
+ /* If wback is true, also save the base register, which is going to be
+ written to. */
+ if (wback)
+ record_buf[arm_insn_r->reg_rec_count++] = reg_base;
}
REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
LONGEST s_word;
ULONGEST u_regval[2];
+ if (bit (arm_insn_r->arm_insn, 4))
+ return arm_record_media (arm_insn_r);
+
arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
}
}
- REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
+ return 0;
+}
+
+/* Handle ARM mode instructions with opcode 100. */
+
+static int
+arm_record_ld_st_multiple (insn_decode_record *arm_insn_r)
+{
+ struct regcache *reg_cache = arm_insn_r->regcache;
+ uint32_t register_count = 0, register_bits;
+ uint32_t reg_base, addr_mode;
+ uint32_t record_buf[24], record_buf_mem[48];
+ uint32_t wback;
+ ULONGEST u_regval;
+
+ /* Fetch the list of registers. */
+ register_bits = bits (arm_insn_r->arm_insn, 0, 15);
+ arm_insn_r->reg_rec_count = 0;
+
+ /* Fetch the base register that contains the address we are loading data
+ to. */
+ reg_base = bits (arm_insn_r->arm_insn, 16, 19);
+
+ /* Calculate wback. */
+ wback = (bit (arm_insn_r->arm_insn, 21) == 1);
+
+ if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* LDM/LDMIA/LDMFD, LDMDA/LDMFA, LDMDB and LDMIB. */
+
+ /* Find out which registers are going to be loaded from memory. */
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ record_buf[arm_insn_r->reg_rec_count++] = register_count;
+ register_bits = register_bits >> 1;
+ register_count++;
+ }
+
+
+ /* If wback is true, also save the base register, which is going to be
+ written to. */
+ if (wback)
+ record_buf[arm_insn_r->reg_rec_count++] = reg_base;
+
+ /* Save the CPSR register. */
+ record_buf[arm_insn_r->reg_rec_count++] = ARM_PS_REGNUM;
+ }
+ else
+ {
+ /* STM (STMIA, STMEA), STMDA (STMED), STMDB (STMFD) and STMIB (STMFA). */
+
+ addr_mode = bits (arm_insn_r->arm_insn, 23, 24);
+
+ regcache_raw_read_unsigned (reg_cache, reg_base, &u_regval);
+
+ /* Find out how many registers are going to be stored to memory. */
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ register_count++;
+ register_bits = register_bits >> 1;
+ }
+
+ switch (addr_mode)
+ {
+ /* STMDA (STMED): Decrement after. */
+ case 0:
+ record_buf_mem[1] = (uint32_t) u_regval
+ - register_count * INT_REGISTER_SIZE + 4;
+ break;
+ /* STM (STMIA, STMEA): Increment after. */
+ case 1:
+ record_buf_mem[1] = (uint32_t) u_regval;
+ break;
+ /* STMDB (STMFD): Decrement before. */
+ case 2:
+ record_buf_mem[1] = (uint32_t) u_regval
+ - register_count * INT_REGISTER_SIZE;
+ break;
+ /* STMIB (STMFA): Increment before. */
+ case 3:
+ record_buf_mem[1] = (uint32_t) u_regval + INT_REGISTER_SIZE;
+ break;
+ default:
+ gdb_assert_not_reached ("no decoding pattern found");
+ break;
+ }
+
+ record_buf_mem[0] = register_count * INT_REGISTER_SIZE;
+ arm_insn_r->mem_rec_count = 1;
+
+ /* If wback is true, also save the base register, which is going to be
+ written to. */
+ if (wback)
+ record_buf[arm_insn_r->reg_rec_count++] = reg_base;
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
+ return 0;
+}
+
+/* Handling opcode 101 insns. */
+
+static int
+arm_record_b_bl (insn_decode_record *arm_insn_r)
+{
+ uint32_t record_buf[8];
+
+ /* Handle B, BL, BLX(1) insns. */
+ /* B simply branches so we do nothing here. */
+ /* Note: BLX(1) doesnt fall here but instead it falls into
+ extension space. */
+ if (bit (arm_insn_r->arm_insn, 24))
+ {
+ record_buf[0] = ARM_LR_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+
+ return 0;
+}
+
+static int
+arm_record_unsupported_insn (insn_decode_record *arm_insn_r)
+{
+ printf_unfiltered (_("Process record does not support instruction "
+ "0x%0x at address %s.\n"),arm_insn_r->arm_insn,
+ paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
+
+ return -1;
+}
+
+/* Record handler for vector data transfer instructions. */
+
+static int
+arm_record_vdata_transfer_insn (insn_decode_record *arm_insn_r)
+{
+ uint32_t bits_a, bit_c, bit_l, reg_t, reg_v;
+ uint32_t record_buf[4];
+
+ const int num_regs = gdbarch_num_regs (arm_insn_r->gdbarch);
+ reg_t = bits (arm_insn_r->arm_insn, 12, 15);
+ reg_v = bits (arm_insn_r->arm_insn, 21, 23);
+ bits_a = bits (arm_insn_r->arm_insn, 21, 23);
+ bit_l = bit (arm_insn_r->arm_insn, 20);
+ bit_c = bit (arm_insn_r->arm_insn, 8);
+
+ /* Handle VMOV instruction. */
+ if (bit_l && bit_c)
+ {
+ record_buf[0] = reg_t;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (bit_l && !bit_c)
+ {
+ /* Handle VMOV instruction. */
+ if (bits_a == 0x00)
+ {
+ record_buf[0] = reg_t;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ /* Handle VMRS instruction. */
+ else if (bits_a == 0x07)
+ {
+ if (reg_t == 15)
+ reg_t = ARM_PS_REGNUM;
+
+ record_buf[0] = reg_t;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ else if (!bit_l && !bit_c)
+ {
+ /* Handle VMOV instruction. */
+ if (bits_a == 0x00)
+ {
+ record_buf[0] = ARM_D0_REGNUM + reg_v;
+
+ arm_insn_r->reg_rec_count = 1;
+ }
+ /* Handle VMSR instruction. */
+ else if (bits_a == 0x07)
+ {
+ record_buf[0] = ARM_FPSCR_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ else if (!bit_l && bit_c)
+ {
+ /* Handle VMOV instruction. */
+ if (!(bits_a & 0x04))
+ {
+ record_buf[0] = (reg_v | (bit (arm_insn_r->arm_insn, 7) << 4))
+ + ARM_D0_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ /* Handle VDUP instruction. */
+ else
+ {
+ if (bit (arm_insn_r->arm_insn, 21))
+ {
+ reg_v = reg_v | (bit (arm_insn_r->arm_insn, 7) << 4);
+ record_buf[0] = reg_v + ARM_D0_REGNUM;
+ record_buf[1] = reg_v + ARM_D0_REGNUM + 1;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else
+ {
+ reg_v = reg_v | (bit (arm_insn_r->arm_insn, 7) << 4);
+ record_buf[0] = reg_v + ARM_D0_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ return 0;
+}
+
+/* Record handler for extension register load/store instructions. */
+
+static int
+arm_record_exreg_ld_st_insn (insn_decode_record *arm_insn_r)
+{
+ uint32_t opcode, single_reg;
+ uint8_t op_vldm_vstm;
+ uint32_t record_buf[8], record_buf_mem[128];
+ ULONGEST u_regval = 0;
+
+ struct regcache *reg_cache = arm_insn_r->regcache;
+ const int num_regs = gdbarch_num_regs (arm_insn_r->gdbarch);
+
+ opcode = bits (arm_insn_r->arm_insn, 20, 24);
+ single_reg = !bit (arm_insn_r->arm_insn, 8);
+ op_vldm_vstm = opcode & 0x1b;
+
+ /* Handle VMOV instructions. */
+ if ((opcode & 0x1e) == 0x04)
+ {
+ if (bit (arm_insn_r->arm_insn, 20)) /* to_arm_registers bit 20? */
+ {
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = bits (arm_insn_r->arm_insn, 16, 19);
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else
+ {
+ uint8_t reg_m = bits (arm_insn_r->arm_insn, 0, 3);
+ uint8_t bit_m = bit (arm_insn_r->arm_insn, 5);
+
+ if (single_reg)
+ {
+ /* The first S register number m is REG_M:M (M is bit 5),
+ the corresponding D register number is REG_M:M / 2, which
+ is REG_M. */
+ record_buf[arm_insn_r->reg_rec_count++] = ARM_D0_REGNUM + reg_m;
+ /* The second S register number is REG_M:M + 1, the
+ corresponding D register number is (REG_M:M + 1) / 2.
+ IOW, if bit M is 1, the first and second S registers
+ are mapped to different D registers, otherwise, they are
+ in the same D register. */
+ if (bit_m)
+ {
+ record_buf[arm_insn_r->reg_rec_count++]
+ = ARM_D0_REGNUM + reg_m + 1;
+ }
+ }
+ else
+ {
+ record_buf[0] = ((bit_m << 4) + reg_m + ARM_D0_REGNUM);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ }
+ /* Handle VSTM and VPUSH instructions. */
+ else if (op_vldm_vstm == 0x08 || op_vldm_vstm == 0x0a
+ || op_vldm_vstm == 0x12)
+ {
+ uint32_t start_address, reg_rn, imm_off32, imm_off8, memory_count;
+ uint32_t memory_index = 0;
+
+ reg_rn = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval);
+ imm_off8 = bits (arm_insn_r->arm_insn, 0, 7);
+ imm_off32 = imm_off8 << 2;
+ memory_count = imm_off8;
+
+ if (bit (arm_insn_r->arm_insn, 23))
+ start_address = u_regval;
+ else
+ start_address = u_regval - imm_off32;
+
+ if (bit (arm_insn_r->arm_insn, 21))
+ {
+ record_buf[0] = reg_rn;
+ arm_insn_r->reg_rec_count = 1;
+ }
+
+ while (memory_count > 0)
+ {
+ if (single_reg)
+ {
+ record_buf_mem[memory_index] = 4;
+ record_buf_mem[memory_index + 1] = start_address;
+ start_address = start_address + 4;
+ memory_index = memory_index + 2;
+ }
+ else
+ {
+ record_buf_mem[memory_index] = 4;
+ record_buf_mem[memory_index + 1] = start_address;
+ record_buf_mem[memory_index + 2] = 4;
+ record_buf_mem[memory_index + 3] = start_address + 4;
+ start_address = start_address + 8;
+ memory_index = memory_index + 4;
+ }
+ memory_count--;
+ }
+ arm_insn_r->mem_rec_count = (memory_index >> 1);
+ }
+ /* Handle VLDM instructions. */
+ else if (op_vldm_vstm == 0x09 || op_vldm_vstm == 0x0b
+ || op_vldm_vstm == 0x13)
+ {
+ uint32_t reg_count, reg_vd;
+ uint32_t reg_index = 0;
+ uint32_t bit_d = bit (arm_insn_r->arm_insn, 22);
+
+ reg_vd = bits (arm_insn_r->arm_insn, 12, 15);
+ reg_count = bits (arm_insn_r->arm_insn, 0, 7);
+
+ /* REG_VD is the first D register number. If the instruction
+ loads memory to S registers (SINGLE_REG is TRUE), the register
+ number is (REG_VD << 1 | bit D), so the corresponding D
+ register number is (REG_VD << 1 | bit D) / 2 = REG_VD. */
+ if (!single_reg)
+ reg_vd = reg_vd | (bit_d << 4);
+
+ if (bit (arm_insn_r->arm_insn, 21) /* write back */)
+ record_buf[reg_index++] = bits (arm_insn_r->arm_insn, 16, 19);
+
+ /* If the instruction loads memory to D register, REG_COUNT should
+ be divided by 2, according to the ARM Architecture Reference
+ Manual. If the instruction loads memory to S register, divide by
+ 2 as well because two S registers are mapped to D register. */
+ reg_count = reg_count / 2;
+ if (single_reg && bit_d)
+ {
+ /* Increase the register count if S register list starts from
+ an odd number (bit d is one). */
+ reg_count++;
+ }
+
+ while (reg_count > 0)
+ {
+ record_buf[reg_index++] = ARM_D0_REGNUM + reg_vd + reg_count - 1;
+ reg_count--;
+ }
+ arm_insn_r->reg_rec_count = reg_index;
+ }
+ /* VSTR Vector store register. */
+ else if ((opcode & 0x13) == 0x10)
+ {
+ uint32_t start_address, reg_rn, imm_off32, imm_off8;
+ uint32_t memory_index = 0;
+
+ reg_rn = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval);
+ imm_off8 = bits (arm_insn_r->arm_insn, 0, 7);
+ imm_off32 = imm_off8 << 2;
+
+ if (bit (arm_insn_r->arm_insn, 23))
+ start_address = u_regval + imm_off32;
+ else
+ start_address = u_regval - imm_off32;
+
+ if (single_reg)
+ {
+ record_buf_mem[memory_index] = 4;
+ record_buf_mem[memory_index + 1] = start_address;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else
+ {
+ record_buf_mem[memory_index] = 4;
+ record_buf_mem[memory_index + 1] = start_address;
+ record_buf_mem[memory_index + 2] = 4;
+ record_buf_mem[memory_index + 3] = start_address + 4;
+ arm_insn_r->mem_rec_count = 2;
+ }
+ }
+ /* VLDR Vector load register. */
+ else if ((opcode & 0x13) == 0x11)
+ {
+ uint32_t reg_vd = bits (arm_insn_r->arm_insn, 12, 15);
+
+ if (!single_reg)
+ {
+ reg_vd = reg_vd | (bit (arm_insn_r->arm_insn, 22) << 4);
+ record_buf[0] = ARM_D0_REGNUM + reg_vd;
+ }
+ else
+ {
+ reg_vd = (reg_vd << 1) | bit (arm_insn_r->arm_insn, 22);
+ /* Record register D rather than pseudo register S. */
+ record_buf[0] = ARM_D0_REGNUM + reg_vd / 2;
+ }
+ arm_insn_r->reg_rec_count = 1;
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
+ return 0;
+}
+
+/* Record handler for arm/thumb mode VFP data processing instructions. */
+
+static int
+arm_record_vfp_data_proc_insn (insn_decode_record *arm_insn_r)
+{
+ uint32_t opc1, opc2, opc3, dp_op_sz, bit_d, reg_vd;
+ uint32_t record_buf[4];
+ enum insn_types {INSN_T0, INSN_T1, INSN_T2, INSN_T3, INSN_INV};
+ enum insn_types curr_insn_type = INSN_INV;
+
+ reg_vd = bits (arm_insn_r->arm_insn, 12, 15);
+ opc1 = bits (arm_insn_r->arm_insn, 20, 23);
+ opc2 = bits (arm_insn_r->arm_insn, 16, 19);
+ opc3 = bits (arm_insn_r->arm_insn, 6, 7);
+ dp_op_sz = bit (arm_insn_r->arm_insn, 8);
+ bit_d = bit (arm_insn_r->arm_insn, 22);
+ opc1 = opc1 & 0x04;
+
+ /* Handle VMLA, VMLS. */
+ if (opc1 == 0x00)
+ {
+ if (bit (arm_insn_r->arm_insn, 10))
+ {
+ if (bit (arm_insn_r->arm_insn, 6))
+ curr_insn_type = INSN_T0;
+ else
+ curr_insn_type = INSN_T1;
+ }
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VNMLA, VNMLS, VNMUL. */
+ else if (opc1 == 0x01)
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ /* Handle VMUL. */
+ else if (opc1 == 0x02 && !(opc3 & 0x01))
+ {
+ if (bit (arm_insn_r->arm_insn, 10))
+ {
+ if (bit (arm_insn_r->arm_insn, 6))
+ curr_insn_type = INSN_T0;
+ else
+ curr_insn_type = INSN_T1;
+ }
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VADD, VSUB. */
+ else if (opc1 == 0x03)
+ {
+ if (!bit (arm_insn_r->arm_insn, 9))
+ {
+ if (bit (arm_insn_r->arm_insn, 6))
+ curr_insn_type = INSN_T0;
+ else
+ curr_insn_type = INSN_T1;
+ }
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VDIV. */
+ else if (opc1 == 0x0b)
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ /* Handle all other vfp data processing instructions. */
+ else if (opc1 == 0x0b)
+ {
+ /* Handle VMOV. */
+ if (!(opc3 & 0x01) || (opc2 == 0x00 && opc3 == 0x01))
+ {
+ if (bit (arm_insn_r->arm_insn, 4))
+ {
+ if (bit (arm_insn_r->arm_insn, 6))
+ curr_insn_type = INSN_T0;
+ else
+ curr_insn_type = INSN_T1;
+ }
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VNEG and VABS. */
+ else if ((opc2 == 0x01 && opc3 == 0x01)
+ || (opc2 == 0x00 && opc3 == 0x03))
+ {
+ if (!bit (arm_insn_r->arm_insn, 11))
+ {
+ if (bit (arm_insn_r->arm_insn, 6))
+ curr_insn_type = INSN_T0;
+ else
+ curr_insn_type = INSN_T1;
+ }
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VSQRT. */
+ else if (opc2 == 0x01 && opc3 == 0x03)
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ /* Handle VCVT. */
+ else if (opc2 == 0x07 && opc3 == 0x03)
+ {
+ if (!dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ else if (opc3 & 0x01)
+ {
+ /* Handle VCVT. */
+ if ((opc2 == 0x08) || (opc2 & 0x0e) == 0x0c)
+ {
+ if (!bit (arm_insn_r->arm_insn, 18))
+ curr_insn_type = INSN_T2;
+ else
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ }
+ /* Handle VCVT. */
+ else if ((opc2 & 0x0e) == 0x0a || (opc2 & 0x0e) == 0x0e)
+ {
+ if (dp_op_sz)
+ curr_insn_type = INSN_T1;
+ else
+ curr_insn_type = INSN_T2;
+ }
+ /* Handle VCVTB, VCVTT. */
+ else if ((opc2 & 0x0e) == 0x02)
+ curr_insn_type = INSN_T2;
+ /* Handle VCMP, VCMPE. */
+ else if ((opc2 & 0x0e) == 0x04)
+ curr_insn_type = INSN_T3;
+ }
+ }
+
+ switch (curr_insn_type)
+ {
+ case INSN_T0:
+ reg_vd = reg_vd | (bit_d << 4);
+ record_buf[0] = reg_vd + ARM_D0_REGNUM;
+ record_buf[1] = reg_vd + ARM_D0_REGNUM + 1;
+ arm_insn_r->reg_rec_count = 2;
+ break;
+
+ case INSN_T1:
+ reg_vd = reg_vd | (bit_d << 4);
+ record_buf[0] = reg_vd + ARM_D0_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ break;
+
+ case INSN_T2:
+ reg_vd = (reg_vd << 1) | bit_d;
+ record_buf[0] = reg_vd + ARM_D0_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ break;
+
+ case INSN_T3:
+ record_buf[0] = ARM_FPSCR_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ break;
+
+ default:
+ gdb_assert_not_reached ("no decoding pattern found");
+ break;
+ }
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ return 0;
+}
+
+/* Handling opcode 110 insns. */
+
+static int
+arm_record_asimd_vfp_coproc (insn_decode_record *arm_insn_r)
+{
+ uint32_t op1, op1_ebit, coproc;
+
+ coproc = bits (arm_insn_r->arm_insn, 8, 11);
+ op1 = bits (arm_insn_r->arm_insn, 20, 25);
+ op1_ebit = bit (arm_insn_r->arm_insn, 20);
+
+ if ((coproc & 0x0e) == 0x0a)
+ {
+ /* Handle extension register ld/st instructions. */
+ if (!(op1 & 0x20))
+ return arm_record_exreg_ld_st_insn (arm_insn_r);
+
+ /* 64-bit transfers between arm core and extension registers. */
+ if ((op1 & 0x3e) == 0x04)
+ return arm_record_exreg_ld_st_insn (arm_insn_r);
+ }
+ else
+ {
+ /* Handle coprocessor ld/st instructions. */
+ if (!(op1 & 0x3a))
+ {
+ /* Store. */
+ if (!op1_ebit)
+ return arm_record_unsupported_insn (arm_insn_r);
+ else
+ /* Load. */
+ return arm_record_unsupported_insn (arm_insn_r);
+ }
+
+ /* Move to coprocessor from two arm core registers. */
+ if (op1 == 0x4)
+ return arm_record_unsupported_insn (arm_insn_r);
+
+ /* Move to two arm core registers from coprocessor. */
+ if (op1 == 0x5)
+ {
+ uint32_t reg_t[2];
+
+ reg_t[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ reg_t[1] = bits (arm_insn_r->arm_insn, 16, 19);
+ arm_insn_r->reg_rec_count = 2;
+
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, reg_t);
+ return 0;
+ }
+ }
+ return arm_record_unsupported_insn (arm_insn_r);
+}
+
+/* Handling opcode 111 insns. */
+
+static int
+arm_record_coproc_data_proc (insn_decode_record *arm_insn_r)
+{
+ uint32_t op, op1_sbit, op1_ebit, coproc;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (arm_insn_r->gdbarch);
+ struct regcache *reg_cache = arm_insn_r->regcache;
+
+ arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 24, 27);
+ coproc = bits (arm_insn_r->arm_insn, 8, 11);
+ op1_sbit = bit (arm_insn_r->arm_insn, 24);
+ op1_ebit = bit (arm_insn_r->arm_insn, 20);
+ op = bit (arm_insn_r->arm_insn, 4);
+
+ /* Handle arm SWI/SVC system call instructions. */
+ if (op1_sbit)
+ {
+ if (tdep->arm_syscall_record != NULL)
+ {
+ ULONGEST svc_operand, svc_number;
+
+ svc_operand = (0x00ffffff & arm_insn_r->arm_insn);
+
+ if (svc_operand) /* OABI. */
+ svc_number = svc_operand - 0x900000;
+ else /* EABI. */
+ regcache_raw_read_unsigned (reg_cache, 7, &svc_number);
+
+ return tdep->arm_syscall_record (reg_cache, svc_number);
+ }
+ else
+ {
+ printf_unfiltered (_("no syscall record support\n"));
+ return -1;
+ }
+ }
+
+ if ((coproc & 0x0e) == 0x0a)
+ {
+ /* VFP data-processing instructions. */
+ if (!op1_sbit && !op)
+ return arm_record_vfp_data_proc_insn (arm_insn_r);
+
+ /* Advanced SIMD, VFP instructions. */
+ if (!op1_sbit && op)
+ return arm_record_vdata_transfer_insn (arm_insn_r);
+ }
+ else
+ {
+ /* Coprocessor data operations. */
+ if (!op1_sbit && !op)
+ return arm_record_unsupported_insn (arm_insn_r);
+
+ /* Move to Coprocessor from ARM core register. */
+ if (!op1_sbit && !op1_ebit && op)
+ return arm_record_unsupported_insn (arm_insn_r);
+
+ /* Move to arm core register from coprocessor. */
+ if (!op1_sbit && op1_ebit && op)
+ {
+ uint32_t record_buf[1];
+
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ if (record_buf[0] == 15)
+ record_buf[0] = ARM_PS_REGNUM;
+
+ arm_insn_r->reg_rec_count = 1;
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count,
+ record_buf);
+ return 0;
+ }
+ }
+
+ return arm_record_unsupported_insn (arm_insn_r);
+}
+
+/* Handling opcode 000 insns. */
+
+static int
+thumb_record_shift_add_sub (insn_decode_record *thumb_insn_r)
+{
+ uint32_t record_buf[8];
+ uint32_t reg_src1 = 0;
+
+ reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
+
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+
+ return 0;
+}
+
+
+/* Handling opcode 001 insns. */
+
+static int
+thumb_record_add_sub_cmp_mov (insn_decode_record *thumb_insn_r)
+{
+ uint32_t record_buf[8];
+ uint32_t reg_src1 = 0;
+
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+
+ return 0;
+}
+
+/* Handling opcode 010 insns. */
+
+static int
+thumb_record_ld_st_reg_offset (insn_decode_record *thumb_insn_r)
+{
+ struct regcache *reg_cache = thumb_insn_r->regcache;
+ uint32_t record_buf[8], record_buf_mem[8];
+
+ uint32_t reg_src1 = 0, reg_src2 = 0;
+ uint32_t opcode1 = 0, opcode2 = 0, opcode3 = 0;
+
+ ULONGEST u_regval[2] = {0};
+
+ opcode1 = bits (thumb_insn_r->arm_insn, 10, 12);
+
+ if (bit (thumb_insn_r->arm_insn, 12))
+ {
+ /* Handle load/store register offset. */
+ opcode2 = bits (thumb_insn_r->arm_insn, 9, 10);
+ if (opcode2 >= 12 && opcode2 <= 15)
+ {
+ /* LDR(2), LDRB(2) , LDRH(2), LDRSB, LDRSH. */
+ reg_src1 = bits (thumb_insn_r->arm_insn,0, 2);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (opcode2 >= 8 && opcode2 <= 10)
+ {
+ /* STR(2), STRB(2), STRH(2) . */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
+ reg_src2 = bits (thumb_insn_r->arm_insn, 6, 8);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ if (8 == opcode2)
+ record_buf_mem[0] = 4; /* STR (2). */
+ else if (10 == opcode2)
+ record_buf_mem[0] = 1; /* STRB (2). */
+ else if (9 == opcode2)
+ record_buf_mem[0] = 2; /* STRH (2). */
+ record_buf_mem[1] = u_regval[0] + u_regval[1];
+ thumb_insn_r->mem_rec_count = 1;
+ }
+ }
+ else if (bit (thumb_insn_r->arm_insn, 11))
+ {
+ /* Handle load from literal pool. */
+ /* LDR(3). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (opcode1)
+ {
+ opcode2 = bits (thumb_insn_r->arm_insn, 8, 9);
+ opcode3 = bits (thumb_insn_r->arm_insn, 0, 2);
+ if ((3 == opcode2) && (!opcode3))
+ {
+ /* Branch with exchange. */
+ record_buf[0] = ARM_PS_REGNUM;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* Format 8; special data processing insns. */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = (bit (thumb_insn_r->arm_insn, 7) << 3
+ | bits (thumb_insn_r->arm_insn, 0, 2));
+ thumb_insn_r->reg_rec_count = 2;
+ }
+ }
+ else
+ {
+ /* Format 5; data processing insns. */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
+ if (bit (thumb_insn_r->arm_insn, 7))
+ {
+ reg_src1 = reg_src1 + 8;
+ }
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
+ }
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
+ record_buf_mem);
+
+ return 0;
+}
+
+/* Handling opcode 001 insns. */
+
+static int
+thumb_record_ld_st_imm_offset (insn_decode_record *thumb_insn_r)
+{
+ struct regcache *reg_cache = thumb_insn_r->regcache;
+ uint32_t record_buf[8], record_buf_mem[8];
+
+ uint32_t reg_src1 = 0;
+ uint32_t opcode = 0, immed_5 = 0;
+
+ ULONGEST u_regval = 0;
+
+ opcode = bits (thumb_insn_r->arm_insn, 11, 12);
+
+ if (opcode)
+ {
+ /* LDR(1). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* STR(1). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
+ immed_5 = bits (thumb_insn_r->arm_insn, 6, 10);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = u_regval + (immed_5 * 4);
+ thumb_insn_r->mem_rec_count = 1;
+ }
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
+ record_buf_mem);
+
+ return 0;
+}
+
+/* Handling opcode 100 insns. */
+
+static int
+thumb_record_ld_st_stack (insn_decode_record *thumb_insn_r)
+{
+ struct regcache *reg_cache = thumb_insn_r->regcache;
+ uint32_t record_buf[8], record_buf_mem[8];
+
+ uint32_t reg_src1 = 0;
+ uint32_t opcode = 0, immed_8 = 0, immed_5 = 0;
+
+ ULONGEST u_regval = 0;
+
+ opcode = bits (thumb_insn_r->arm_insn, 11, 12);
+
+ if (3 == opcode)
+ {
+ /* LDR(4). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (1 == opcode)
+ {
+ /* LDRH(1). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (2 == opcode)
+ {
+ /* STR(3). */
+ immed_8 = bits (thumb_insn_r->arm_insn, 0, 7);
+ regcache_raw_read_unsigned (reg_cache, ARM_SP_REGNUM, &u_regval);
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = u_regval + (immed_8 * 4);
+ thumb_insn_r->mem_rec_count = 1;
+ }
+ else if (0 == opcode)
+ {
+ /* STRH(1). */
+ immed_5 = bits (thumb_insn_r->arm_insn, 6, 10);
+ reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
+ record_buf_mem[0] = 2;
+ record_buf_mem[1] = u_regval + (immed_5 * 2);
+ thumb_insn_r->mem_rec_count = 1;
+ }
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
+ record_buf_mem);
+
return 0;
}
-/* Handling opcode 100 insns. */
+/* Handling opcode 101 insns. */
static int
-arm_record_ld_st_multiple (insn_decode_record *arm_insn_r)
+thumb_record_misc (insn_decode_record *thumb_insn_r)
{
- struct regcache *reg_cache = arm_insn_r->regcache;
+ struct regcache *reg_cache = thumb_insn_r->regcache;
- uint32_t register_list[16] = {0}, register_count = 0, register_bits = 0;
- uint32_t reg_src1 = 0, addr_mode = 0, no_of_regs = 0;
- uint32_t start_address = 0, index = 0;
+ uint32_t opcode = 0, opcode1 = 0, opcode2 = 0;
+ uint32_t register_bits = 0, register_count = 0;
+ uint32_t index = 0, start_address = 0;
uint32_t record_buf[24], record_buf_mem[48];
+ uint32_t reg_src1;
- ULONGEST u_regval[2] = {0};
+ ULONGEST u_regval = 0;
- /* This mode is exclusively for load and store multiple. */
- /* Handle incremenrt after/before and decrment after.before mode;
- Rn is changing depending on W bit, but as of now we store Rn too
- without optimization. */
+ opcode = bits (thumb_insn_r->arm_insn, 11, 12);
+ opcode1 = bits (thumb_insn_r->arm_insn, 8, 12);
+ opcode2 = bits (thumb_insn_r->arm_insn, 9, 12);
- if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ if (14 == opcode2)
{
- /* LDM (1,2,3) where LDM (3) changes CPSR too. */
-
- if (bit (arm_insn_r->arm_insn, 20) && !bit (arm_insn_r->arm_insn, 22))
- {
- register_bits = bits (arm_insn_r->arm_insn, 0, 15);
- no_of_regs = 15;
- }
- else
- {
- register_bits = bits (arm_insn_r->arm_insn, 0, 14);
- no_of_regs = 14;
- }
- /* Get Rn. */
- reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ /* POP. */
+ register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
while (register_bits)
{
if (register_bits & 0x00000001)
- register_list[register_count++] = 1;
+ record_buf[index++] = register_count;
register_bits = register_bits >> 1;
+ register_count++;
}
-
- /* Extra space for Base Register and CPSR; wihtout optimization. */
- record_buf[register_count] = reg_src1;
- record_buf[register_count + 1] = ARM_PS_REGNUM;
- arm_insn_r->reg_rec_count = register_count + 2;
-
- for (register_count = 0; register_count < no_of_regs; register_count++)
- {
- if (register_list[register_count])
- {
- /* Register_count gives total no of registers
- and dually working as reg number. */
- record_buf[index] = register_count;
- index++;
- }
- }
-
+ record_buf[index++] = ARM_PS_REGNUM;
+ record_buf[index++] = ARM_SP_REGNUM;
+ thumb_insn_r->reg_rec_count = index;
}
- else
+ else if (10 == opcode2)
{
- /* It handles both STM(1) and STM(2). */
- addr_mode = bits (arm_insn_r->arm_insn, 23, 24);
-
- register_bits = bits (arm_insn_r->arm_insn, 0, 15);
- /* Get Rn. */
- reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ /* PUSH. */
+ register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
+ regcache_raw_read_unsigned (reg_cache, ARM_SP_REGNUM, &u_regval);
while (register_bits)
{
if (register_bits & 0x00000001)
register_count++;
register_bits = register_bits >> 1;
}
-
- switch (addr_mode)
+ start_address = u_regval - \
+ (4 * (bit (thumb_insn_r->arm_insn, 8) + register_count));
+ thumb_insn_r->mem_rec_count = register_count;
+ while (register_count)
{
- /* Decrement after. */
- case 0:
- start_address = (u_regval[0]) - (register_count * 4) + 4;
- arm_insn_r->mem_rec_count = register_count;
- while (register_count)
- {
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
- }
- break;
-
- /* Increment after. */
- case 1:
- start_address = u_regval[0];
- arm_insn_r->mem_rec_count = register_count;
- while (register_count)
- {
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
- }
- break;
-
- /* Decrement before. */
- case 2:
-
- start_address = (u_regval[0]) - (register_count * 4);
- arm_insn_r->mem_rec_count = register_count;
- while (register_count)
- {
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
- }
- break;
-
- /* Increment before. */
- case 3:
- start_address = u_regval[0] + 4;
- arm_insn_r->mem_rec_count = register_count;
- while (register_count)
- {
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
- }
- break;
-
- default:
- gdb_assert_not_reached ("no decoding pattern found");
- break;
+ record_buf_mem[(register_count * 2) - 1] = start_address;
+ record_buf_mem[(register_count * 2) - 2] = 4;
+ start_address = start_address + 4;
+ register_count--;
}
-
- /* Base register also changes; based on condition and W bit. */
- /* We save it anyway without optimization. */
+ record_buf[0] = ARM_SP_REGNUM;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (0x1E == opcode1)
+ {
+ /* BKPT insn. */
+ /* Handle enhanced software breakpoint insn, BKPT. */
+ /* CPSR is changed to be executed in ARM state, disabling normal
+ interrupts, entering abort mode. */
+ /* According to high vector configuration PC is set. */
+ /* User hits breakpoint and type reverse, in that case, we need to go back with
+ previous CPSR and Program Counter. */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ thumb_insn_r->reg_rec_count = 2;
+ /* We need to save SPSR value, which is not yet done. */
+ printf_unfiltered (_("Process record does not support instruction "
+ "0x%0x at address %s.\n"),
+ thumb_insn_r->arm_insn,
+ paddress (thumb_insn_r->gdbarch,
+ thumb_insn_r->this_addr));
+ return -1;
+ }
+ else if ((0 == opcode) || (1 == opcode))
+ {
+ /* ADD(5), ADD(6). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
record_buf[0] = reg_src1;
- arm_insn_r->reg_rec_count = 1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (2 == opcode)
+ {
+ /* ADD(7), SUB(4). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ record_buf[0] = ARM_SP_REGNUM;
+ thumb_insn_r->reg_rec_count = 1;
}
- REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (arm_insn_r->arm_mems, arm_insn_r->mem_rec_count, record_buf_mem);
- return 0;
-}
-
-/* Handling opcode 101 insns. */
-
-static int
-arm_record_b_bl (insn_decode_record *arm_insn_r)
-{
- uint32_t record_buf[8];
-
- /* Handle B, BL, BLX(1) insns. */
- /* B simply branches so we do nothing here. */
- /* Note: BLX(1) doesnt fall here but instead it falls into
- extension space. */
- if (bit (arm_insn_r->arm_insn, 24))
- {
- record_buf[0] = ARM_LR_REGNUM;
- arm_insn_r->reg_rec_count = 1;
- }
-
- REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
+ record_buf_mem);
return 0;
}
/* Handling opcode 110 insns. */
static int
-arm_record_coproc (insn_decode_record *arm_insn_r)
+thumb_record_ldm_stm_swi (insn_decode_record *thumb_insn_r)
{
- printf_unfiltered (_("Process record does not support instruction "
- "0x%0x at address %s.\n"),arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
+ struct gdbarch_tdep *tdep = gdbarch_tdep (thumb_insn_r->gdbarch);
+ struct regcache *reg_cache = thumb_insn_r->regcache;
- return -1;
-}
+ uint32_t ret = 0; /* function return value: -1:record failure ; 0:success */
+ uint32_t reg_src1 = 0;
+ uint32_t opcode1 = 0, opcode2 = 0, register_bits = 0, register_count = 0;
+ uint32_t index = 0, start_address = 0;
+ uint32_t record_buf[24], record_buf_mem[48];
-/* Handling opcode 111 insns. */
+ ULONGEST u_regval = 0;
-static int
-arm_record_coproc_data_proc (insn_decode_record *arm_insn_r)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (arm_insn_r->gdbarch);
- struct regcache *reg_cache = arm_insn_r->regcache;
- uint32_t ret = 0; /* function return value: -1:record failure ; 0:success */
+ opcode1 = bits (thumb_insn_r->arm_insn, 8, 12);
+ opcode2 = bits (thumb_insn_r->arm_insn, 11, 12);
- /* Handle SWI insn; system call would be handled over here. */
+ if (1 == opcode2)
+ {
- arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 24, 27);
- if (15 == arm_insn_r->opcode)
- {
- /* Handle arm syscall insn. */
- if (tdep->arm_swi_record != NULL)
- {
- ret = tdep->arm_swi_record(reg_cache);
- }
- else
- {
- printf_unfiltered (_("no syscall record support\n"));
- ret = -1;
- }
- }
+ /* LDMIA. */
+ register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
+ /* Get Rn. */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ record_buf[index++] = register_count;
+ register_bits = register_bits >> 1;
+ register_count++;
+ }
+ record_buf[index++] = reg_src1;
+ thumb_insn_r->reg_rec_count = index;
+ }
+ else if (0 == opcode2)
+ {
+ /* It handles both STMIA. */
+ register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
+ /* Get Rn. */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ register_count++;
+ register_bits = register_bits >> 1;
+ }
+ start_address = u_regval;
+ thumb_insn_r->mem_rec_count = register_count;
+ while (register_count)
+ {
+ record_buf_mem[(register_count * 2) - 1] = start_address;
+ record_buf_mem[(register_count * 2) - 2] = 4;
+ start_address = start_address + 4;
+ register_count--;
+ }
+ }
+ else if (0x1F == opcode1)
+ {
+ /* Handle arm syscall insn. */
+ if (tdep->arm_syscall_record != NULL)
+ {
+ regcache_raw_read_unsigned (reg_cache, 7, &u_regval);
+ ret = tdep->arm_syscall_record (reg_cache, u_regval);
+ }
+ else
+ {
+ printf_unfiltered (_("no syscall record support\n"));
+ return -1;
+ }
+ }
+
+ /* B (1), conditional branch is automatically taken care in process_record,
+ as PC is saved there. */
+
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
+ record_buf_mem);
- printf_unfiltered (_("Process record does not support instruction "
- "0x%0x at address %s.\n"),arm_insn_r->arm_insn,
- paddress (arm_insn_r->gdbarch, arm_insn_r->this_addr));
return ret;
}
-/* Handling opcode 000 insns. */
+/* Handling opcode 111 insns. */
static int
-thumb_record_shift_add_sub (insn_decode_record *thumb_insn_r)
+thumb_record_branch (insn_decode_record *thumb_insn_r)
{
uint32_t record_buf[8];
- uint32_t reg_src1 = 0;
+ uint32_t bits_h = 0;
- reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
+ bits_h = bits (thumb_insn_r->arm_insn, 11, 12);
- record_buf[0] = ARM_PS_REGNUM;
- record_buf[1] = reg_src1;
- thumb_insn_r->reg_rec_count = 2;
+ if (2 == bits_h || 3 == bits_h)
+ {
+ /* BL */
+ record_buf[0] = ARM_LR_REGNUM;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (1 == bits_h)
+ {
+ /* BLX(1). */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ thumb_insn_r->reg_rec_count = 2;
+ }
+
+ /* B(2) is automatically taken care in process_record, as PC is
+ saved there. */
REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- return 0;
+ return 0;
}
-
-/* Handling opcode 001 insns. */
+/* Handler for thumb2 load/store multiple instructions. */
static int
-thumb_record_add_sub_cmp_mov (insn_decode_record *thumb_insn_r)
+thumb2_record_ld_st_multiple (insn_decode_record *thumb2_insn_r)
{
- uint32_t record_buf[8];
- uint32_t reg_src1 = 0;
+ struct regcache *reg_cache = thumb2_insn_r->regcache;
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ uint32_t reg_rn, op;
+ uint32_t register_bits = 0, register_count = 0;
+ uint32_t index = 0, start_address = 0;
+ uint32_t record_buf[24], record_buf_mem[48];
+
+ ULONGEST u_regval = 0;
+
+ reg_rn = bits (thumb2_insn_r->arm_insn, 16, 19);
+ op = bits (thumb2_insn_r->arm_insn, 23, 24);
+
+ if (0 == op || 3 == op)
+ {
+ if (bit (thumb2_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* Handle RFE instruction. */
+ record_buf[0] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* Handle SRS instruction after reading banked SP. */
+ return arm_record_unsupported_insn (thumb2_insn_r);
+ }
+ }
+ else if (1 == op || 2 == op)
+ {
+ if (bit (thumb2_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* Handle LDM/LDMIA/LDMFD and LDMDB/LDMEA instructions. */
+ register_bits = bits (thumb2_insn_r->arm_insn, 0, 15);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ record_buf[index++] = register_count;
+
+ register_count++;
+ register_bits = register_bits >> 1;
+ }
+ record_buf[index++] = reg_rn;
+ record_buf[index++] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = index;
+ }
+ else
+ {
+ /* Handle STM/STMIA/STMEA and STMDB/STMFD. */
+ register_bits = bits (thumb2_insn_r->arm_insn, 0, 15);
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ register_count++;
+
+ register_bits = register_bits >> 1;
+ }
- record_buf[0] = ARM_PS_REGNUM;
- record_buf[1] = reg_src1;
- thumb_insn_r->reg_rec_count = 2;
+ if (1 == op)
+ {
+ /* Start address calculation for LDMDB/LDMEA. */
+ start_address = u_regval;
+ }
+ else if (2 == op)
+ {
+ /* Start address calculation for LDMDB/LDMEA. */
+ start_address = u_regval - register_count * 4;
+ }
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
+ thumb2_insn_r->mem_rec_count = register_count;
+ while (register_count)
+ {
+ record_buf_mem[register_count * 2 - 1] = start_address;
+ record_buf_mem[register_count * 2 - 2] = 4;
+ start_address = start_address + 4;
+ register_count--;
+ }
+ record_buf[0] = reg_rn;
+ record_buf[1] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 2;
+ }
+ }
- return 0;
+ MEM_ALLOC (thumb2_insn_r->arm_mems, thumb2_insn_r->mem_rec_count,
+ record_buf_mem);
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
}
-/* Handling opcode 010 insns. */
+/* Handler for thumb2 load/store (dual/exclusive) and table branch
+ instructions. */
static int
-thumb_record_ld_st_reg_offset (insn_decode_record *thumb_insn_r)
+thumb2_record_ld_st_dual_ex_tbb (insn_decode_record *thumb2_insn_r)
{
- struct regcache *reg_cache = thumb_insn_r->regcache;
- uint32_t record_buf[8], record_buf_mem[8];
+ struct regcache *reg_cache = thumb2_insn_r->regcache;
- uint32_t reg_src1 = 0, reg_src2 = 0;
- uint32_t opcode1 = 0, opcode2 = 0, opcode3 = 0;
+ uint32_t reg_rd, reg_rn, offset_imm;
+ uint32_t reg_dest1, reg_dest2;
+ uint32_t address, offset_addr;
+ uint32_t record_buf[8], record_buf_mem[8];
+ uint32_t op1, op2, op3;
- ULONGEST u_regval[2] = {0};
+ ULONGEST u_regval[2];
- opcode1 = bits (thumb_insn_r->arm_insn, 10, 12);
+ op1 = bits (thumb2_insn_r->arm_insn, 23, 24);
+ op2 = bits (thumb2_insn_r->arm_insn, 20, 21);
+ op3 = bits (thumb2_insn_r->arm_insn, 4, 7);
- if (bit (thumb_insn_r->arm_insn, 12))
+ if (bit (thumb2_insn_r->arm_insn, INSN_S_L_BIT_NUM))
{
- /* Handle load/store register offset. */
- opcode2 = bits (thumb_insn_r->arm_insn, 9, 10);
- if (opcode2 >= 12 && opcode2 <= 15)
+ if(!(1 == op1 && 1 == op2 && (0 == op3 || 1 == op3)))
{
- /* LDR(2), LDRB(2) , LDRH(2), LDRSB, LDRSH. */
- reg_src1 = bits (thumb_insn_r->arm_insn,0, 2);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
+ reg_dest1 = bits (thumb2_insn_r->arm_insn, 12, 15);
+ record_buf[0] = reg_dest1;
+ record_buf[1] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 2;
}
- else if (opcode2 >= 8 && opcode2 <= 10)
+
+ if (3 == op2 || (op1 & 2) || (1 == op1 && 1 == op2 && 7 == op3))
{
- /* STR(2), STRB(2), STRH(2) . */
- reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
- reg_src2 = bits (thumb_insn_r->arm_insn, 6, 8);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
- regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
- if (8 == opcode2)
- record_buf_mem[0] = 4; /* STR (2). */
- else if (10 == opcode2)
- record_buf_mem[0] = 1; /* STRB (2). */
- else if (9 == opcode2)
- record_buf_mem[0] = 2; /* STRH (2). */
- record_buf_mem[1] = u_regval[0] + u_regval[1];
- thumb_insn_r->mem_rec_count = 1;
+ reg_dest2 = bits (thumb2_insn_r->arm_insn, 8, 11);
+ record_buf[2] = reg_dest2;
+ thumb2_insn_r->reg_rec_count = 3;
}
}
- else if (bit (thumb_insn_r->arm_insn, 11))
- {
- /* Handle load from literal pool. */
- /* LDR(3). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
- }
- else if (opcode1)
+ else
{
- opcode2 = bits (thumb_insn_r->arm_insn, 8, 9);
- opcode3 = bits (thumb_insn_r->arm_insn, 0, 2);
- if ((3 == opcode2) && (!opcode3))
+ reg_rn = bits (thumb2_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval[0]);
+
+ if (0 == op1 && 0 == op2)
{
- /* Branch with exchange. */
- record_buf[0] = ARM_PS_REGNUM;
- thumb_insn_r->reg_rec_count = 1;
+ /* Handle STREX. */
+ offset_imm = bits (thumb2_insn_r->arm_insn, 0, 7);
+ address = u_regval[0] + (offset_imm * 4);
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = address;
+ thumb2_insn_r->mem_rec_count = 1;
+ reg_rd = bits (thumb2_insn_r->arm_insn, 0, 3);
+ record_buf[0] = reg_rd;
+ thumb2_insn_r->reg_rec_count = 1;
}
- else
+ else if (1 == op1 && 0 == op2)
{
- /* Format 8; special data processing insns. */
- reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
- record_buf[0] = ARM_PS_REGNUM;
- record_buf[1] = reg_src1;
- thumb_insn_r->reg_rec_count = 2;
+ reg_rd = bits (thumb2_insn_r->arm_insn, 0, 3);
+ record_buf[0] = reg_rd;
+ thumb2_insn_r->reg_rec_count = 1;
+ address = u_regval[0];
+ record_buf_mem[1] = address;
+
+ if (4 == op3)
+ {
+ /* Handle STREXB. */
+ record_buf_mem[0] = 1;
+ thumb2_insn_r->mem_rec_count = 1;
+ }
+ else if (5 == op3)
+ {
+ /* Handle STREXH. */
+ record_buf_mem[0] = 2 ;
+ thumb2_insn_r->mem_rec_count = 1;
+ }
+ else if (7 == op3)
+ {
+ /* Handle STREXD. */
+ address = u_regval[0];
+ record_buf_mem[0] = 4;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = address + 4;
+ thumb2_insn_r->mem_rec_count = 2;
+ }
}
- }
- else
- {
- /* Format 5; data processing insns. */
- reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
- if (bit (thumb_insn_r->arm_insn, 7))
+ else
{
- reg_src1 = reg_src1 + 8;
+ offset_imm = bits (thumb2_insn_r->arm_insn, 0, 7);
+
+ if (bit (thumb2_insn_r->arm_insn, 24))
+ {
+ if (bit (thumb2_insn_r->arm_insn, 23))
+ offset_addr = u_regval[0] + (offset_imm * 4);
+ else
+ offset_addr = u_regval[0] - (offset_imm * 4);
+
+ address = offset_addr;
+ }
+ else
+ address = u_regval[0];
+
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = address;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = address + 4;
+ thumb2_insn_r->mem_rec_count = 2;
+ record_buf[0] = reg_rn;
+ thumb2_insn_r->reg_rec_count = 1;
}
- record_buf[0] = ARM_PS_REGNUM;
- record_buf[1] = reg_src1;
- thumb_insn_r->reg_rec_count = 2;
}
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
- record_buf_mem);
-
- return 0;
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ MEM_ALLOC (thumb2_insn_r->arm_mems, thumb2_insn_r->mem_rec_count,
+ record_buf_mem);
+ return ARM_RECORD_SUCCESS;
}
-/* Handling opcode 001 insns. */
+/* Handler for thumb2 data processing (shift register and modified immediate)
+ instructions. */
static int
-thumb_record_ld_st_imm_offset (insn_decode_record *thumb_insn_r)
+thumb2_record_data_proc_sreg_mimm (insn_decode_record *thumb2_insn_r)
{
- struct regcache *reg_cache = thumb_insn_r->regcache;
- uint32_t record_buf[8], record_buf_mem[8];
-
- uint32_t reg_src1 = 0;
- uint32_t opcode = 0, immed_5 = 0;
-
- ULONGEST u_regval = 0;
+ uint32_t reg_rd, op;
+ uint32_t record_buf[8];
- opcode = bits (thumb_insn_r->arm_insn, 11, 12);
+ op = bits (thumb2_insn_r->arm_insn, 21, 24);
+ reg_rd = bits (thumb2_insn_r->arm_insn, 8, 11);
- if (opcode)
+ if ((0 == op || 4 == op || 8 == op || 13 == op) && 15 == reg_rd)
{
- /* LDR(1). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
+ record_buf[0] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 1;
}
else
{
- /* STR(1). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
- immed_5 = bits (thumb_insn_r->arm_insn, 6, 10);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
- record_buf_mem[0] = 4;
- record_buf_mem[1] = u_regval + (immed_5 * 4);
- thumb_insn_r->mem_rec_count = 1;
+ record_buf[0] = reg_rd;
+ record_buf[1] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 2;
}
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
- record_buf_mem);
-
- return 0;
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
}
-/* Handling opcode 100 insns. */
+/* Generic handler for thumb2 instructions which effect destination and PS
+ registers. */
static int
-thumb_record_ld_st_stack (insn_decode_record *thumb_insn_r)
+thumb2_record_ps_dest_generic (insn_decode_record *thumb2_insn_r)
{
- struct regcache *reg_cache = thumb_insn_r->regcache;
- uint32_t record_buf[8], record_buf_mem[8];
-
- uint32_t reg_src1 = 0;
- uint32_t opcode = 0, immed_8 = 0, immed_5 = 0;
-
- ULONGEST u_regval = 0;
-
- opcode = bits (thumb_insn_r->arm_insn, 11, 12);
+ uint32_t reg_rd;
+ uint32_t record_buf[8];
- if (3 == opcode)
- {
- /* LDR(4). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
- }
- else if (1 == opcode)
- {
- /* LDRH(1). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
- }
- else if (2 == opcode)
- {
- /* STR(3). */
- immed_8 = bits (thumb_insn_r->arm_insn, 0, 7);
- regcache_raw_read_unsigned (reg_cache, ARM_SP_REGNUM, &u_regval);
- record_buf_mem[0] = 4;
- record_buf_mem[1] = u_regval + (immed_8 * 4);
- thumb_insn_r->mem_rec_count = 1;
- }
- else if (0 == opcode)
- {
- /* STRH(1). */
- immed_5 = bits (thumb_insn_r->arm_insn, 6, 10);
- reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
- record_buf_mem[0] = 2;
- record_buf_mem[1] = u_regval + (immed_5 * 2);
- thumb_insn_r->mem_rec_count = 1;
- }
+ reg_rd = bits (thumb2_insn_r->arm_insn, 8, 11);
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
- record_buf_mem);
+ record_buf[0] = reg_rd;
+ record_buf[1] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 2;
- return 0;
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
}
-/* Handling opcode 101 insns. */
+/* Handler for thumb2 branch and miscellaneous control instructions. */
static int
-thumb_record_misc (insn_decode_record *thumb_insn_r)
+thumb2_record_branch_misc_cntrl (insn_decode_record *thumb2_insn_r)
{
- struct regcache *reg_cache = thumb_insn_r->regcache;
-
- uint32_t opcode = 0, opcode1 = 0, opcode2 = 0;
- uint32_t register_bits = 0, register_count = 0;
- uint32_t register_list[8] = {0}, index = 0, start_address = 0;
- uint32_t record_buf[24], record_buf_mem[48];
- uint32_t reg_src1;
-
- ULONGEST u_regval = 0;
+ uint32_t op, op1, op2;
+ uint32_t record_buf[8];
- opcode = bits (thumb_insn_r->arm_insn, 11, 12);
- opcode1 = bits (thumb_insn_r->arm_insn, 8, 12);
- opcode2 = bits (thumb_insn_r->arm_insn, 9, 12);
+ op = bits (thumb2_insn_r->arm_insn, 20, 26);
+ op1 = bits (thumb2_insn_r->arm_insn, 12, 14);
+ op2 = bits (thumb2_insn_r->arm_insn, 8, 11);
- if (14 == opcode2)
- {
- /* POP. */
- register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
- while (register_bits)
- {
- if (register_bits & 0x00000001)
- register_list[register_count++] = 1;
- register_bits = register_bits >> 1;
- }
- record_buf[register_count] = ARM_PS_REGNUM;
- record_buf[register_count + 1] = ARM_SP_REGNUM;
- thumb_insn_r->reg_rec_count = register_count + 2;
- for (register_count = 0; register_count < 8; register_count++)
- {
- if (register_list[register_count])
- {
- record_buf[index] = register_count;
- index++;
- }
- }
- }
- else if (10 == opcode2)
+ /* Handle MSR insn. */
+ if (!(op1 & 0x2) && 0x38 == op)
{
- /* PUSH. */
- register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
- regcache_raw_read_unsigned (reg_cache, ARM_PC_REGNUM, &u_regval);
- while (register_bits)
+ if (!(op2 & 0x3))
{
- if (register_bits & 0x00000001)
- register_count++;
- register_bits = register_bits >> 1;
+ /* CPSR is going to be changed. */
+ record_buf[0] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 1;
}
- start_address = u_regval - \
- (4 * (bit (thumb_insn_r->arm_insn, 8) + register_count));
- thumb_insn_r->mem_rec_count = register_count;
- while (register_count)
+ else
{
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
+ arm_record_unsupported_insn(thumb2_insn_r);
+ return -1;
}
- record_buf[0] = ARM_SP_REGNUM;
- thumb_insn_r->reg_rec_count = 1;
}
- else if (0x1E == opcode1)
+ else if (4 == (op1 & 0x5) || 5 == (op1 & 0x5))
{
- /* BKPT insn. */
- /* Handle enhanced software breakpoint insn, BKPT. */
- /* CPSR is changed to be executed in ARM state, disabling normal
- interrupts, entering abort mode. */
- /* According to high vector configuration PC is set. */
- /* User hits breakpoint and type reverse, in that case, we need to go back with
- previous CPSR and Program Counter. */
+ /* BLX. */
record_buf[0] = ARM_PS_REGNUM;
record_buf[1] = ARM_LR_REGNUM;
- thumb_insn_r->reg_rec_count = 2;
- /* We need to save SPSR value, which is not yet done. */
- printf_unfiltered (_("Process record does not support instruction "
- "0x%0x at address %s.\n"),
- thumb_insn_r->arm_insn,
- paddress (thumb_insn_r->gdbarch,
- thumb_insn_r->this_addr));
- return -1;
+ thumb2_insn_r->reg_rec_count = 2;
+ }
+
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
+}
+
+/* Handler for thumb2 store single data item instructions. */
+
+static int
+thumb2_record_str_single_data (insn_decode_record *thumb2_insn_r)
+{
+ struct regcache *reg_cache = thumb2_insn_r->regcache;
+
+ uint32_t reg_rn, reg_rm, offset_imm, shift_imm;
+ uint32_t address, offset_addr;
+ uint32_t record_buf[8], record_buf_mem[8];
+ uint32_t op1, op2;
+
+ ULONGEST u_regval[2];
+
+ op1 = bits (thumb2_insn_r->arm_insn, 21, 23);
+ op2 = bits (thumb2_insn_r->arm_insn, 6, 11);
+ reg_rn = bits (thumb2_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval[0]);
+
+ if (bit (thumb2_insn_r->arm_insn, 23))
+ {
+ /* T2 encoding. */
+ offset_imm = bits (thumb2_insn_r->arm_insn, 0, 11);
+ offset_addr = u_regval[0] + offset_imm;
+ address = offset_addr;
}
- else if ((0 == opcode) || (1 == opcode))
+ else
{
- /* ADD(5), ADD(6). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- record_buf[0] = reg_src1;
- thumb_insn_r->reg_rec_count = 1;
+ /* T3 encoding. */
+ if ((0 == op1 || 1 == op1 || 2 == op1) && !(op2 & 0x20))
+ {
+ /* Handle STRB (register). */
+ reg_rm = bits (thumb2_insn_r->arm_insn, 0, 3);
+ regcache_raw_read_unsigned (reg_cache, reg_rm, &u_regval[1]);
+ shift_imm = bits (thumb2_insn_r->arm_insn, 4, 5);
+ offset_addr = u_regval[1] << shift_imm;
+ address = u_regval[0] + offset_addr;
+ }
+ else
+ {
+ offset_imm = bits (thumb2_insn_r->arm_insn, 0, 7);
+ if (bit (thumb2_insn_r->arm_insn, 10))
+ {
+ if (bit (thumb2_insn_r->arm_insn, 9))
+ offset_addr = u_regval[0] + offset_imm;
+ else
+ offset_addr = u_regval[0] - offset_imm;
+
+ address = offset_addr;
+ }
+ else
+ address = u_regval[0];
+ }
}
- else if (2 == opcode)
+
+ switch (op1)
{
- /* ADD(7), SUB(4). */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- record_buf[0] = ARM_SP_REGNUM;
- thumb_insn_r->reg_rec_count = 1;
+ /* Store byte instructions. */
+ case 4:
+ case 0:
+ record_buf_mem[0] = 1;
+ break;
+ /* Store half word instructions. */
+ case 1:
+ case 5:
+ record_buf_mem[0] = 2;
+ break;
+ /* Store word instructions. */
+ case 2:
+ case 6:
+ record_buf_mem[0] = 4;
+ break;
+
+ default:
+ gdb_assert_not_reached ("no decoding pattern found");
+ break;
}
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
- record_buf_mem);
+ record_buf_mem[1] = address;
+ thumb2_insn_r->mem_rec_count = 1;
+ record_buf[0] = reg_rn;
+ thumb2_insn_r->reg_rec_count = 1;
- return 0;
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ MEM_ALLOC (thumb2_insn_r->arm_mems, thumb2_insn_r->mem_rec_count,
+ record_buf_mem);
+ return ARM_RECORD_SUCCESS;
}
-/* Handling opcode 110 insns. */
+/* Handler for thumb2 load memory hints instructions. */
static int
-thumb_record_ldm_stm_swi (insn_decode_record *thumb_insn_r)
+thumb2_record_ld_mem_hints (insn_decode_record *thumb2_insn_r)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (thumb_insn_r->gdbarch);
- struct regcache *reg_cache = thumb_insn_r->regcache;
+ uint32_t record_buf[8];
+ uint32_t reg_rt, reg_rn;
- uint32_t ret = 0; /* function return value: -1:record failure ; 0:success */
- uint32_t reg_src1 = 0;
- uint32_t opcode1 = 0, opcode2 = 0, register_bits = 0, register_count = 0;
- uint32_t register_list[8] = {0}, index = 0, start_address = 0;
- uint32_t record_buf[24], record_buf_mem[48];
+ reg_rt = bits (thumb2_insn_r->arm_insn, 12, 15);
+ reg_rn = bits (thumb2_insn_r->arm_insn, 16, 19);
- ULONGEST u_regval = 0;
+ if (ARM_PC_REGNUM != reg_rt)
+ {
+ record_buf[0] = reg_rt;
+ record_buf[1] = reg_rn;
+ record_buf[2] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 3;
- opcode1 = bits (thumb_insn_r->arm_insn, 8, 12);
- opcode2 = bits (thumb_insn_r->arm_insn, 11, 12);
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
+ }
- if (1 == opcode2)
+ return ARM_RECORD_FAILURE;
+}
+
+/* Handler for thumb2 load word instructions. */
+
+static int
+thumb2_record_ld_word (insn_decode_record *thumb2_insn_r)
+{
+ uint32_t record_buf[8];
+
+ record_buf[0] = bits (thumb2_insn_r->arm_insn, 12, 15);
+ record_buf[1] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 2;
+
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
+}
+
+/* Handler for thumb2 long multiply, long multiply accumulate, and
+ divide instructions. */
+
+static int
+thumb2_record_lmul_lmla_div (insn_decode_record *thumb2_insn_r)
+{
+ uint32_t opcode1 = 0, opcode2 = 0;
+ uint32_t record_buf[8];
+
+ opcode1 = bits (thumb2_insn_r->arm_insn, 20, 22);
+ opcode2 = bits (thumb2_insn_r->arm_insn, 4, 7);
+
+ if (0 == opcode1 || 2 == opcode1 || (opcode1 >= 4 && opcode1 <= 6))
+ {
+ /* Handle SMULL, UMULL, SMULAL. */
+ /* Handle SMLAL(S), SMULL(S), UMLAL(S), UMULL(S). */
+ record_buf[0] = bits (thumb2_insn_r->arm_insn, 16, 19);
+ record_buf[1] = bits (thumb2_insn_r->arm_insn, 12, 15);
+ record_buf[2] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 3;
+ }
+ else if (1 == opcode1 || 3 == opcode2)
{
+ /* Handle SDIV and UDIV. */
+ record_buf[0] = bits (thumb2_insn_r->arm_insn, 16, 19);
+ record_buf[1] = bits (thumb2_insn_r->arm_insn, 12, 15);
+ record_buf[2] = ARM_PS_REGNUM;
+ thumb2_insn_r->reg_rec_count = 3;
+ }
+ else
+ return ARM_RECORD_FAILURE;
- /* LDMIA. */
- register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
- /* Get Rn. */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- while (register_bits)
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ return ARM_RECORD_SUCCESS;
+}
+
+/* Record handler for thumb32 coprocessor instructions. */
+
+static int
+thumb2_record_coproc_insn (insn_decode_record *thumb2_insn_r)
+{
+ if (bit (thumb2_insn_r->arm_insn, 25))
+ return arm_record_coproc_data_proc (thumb2_insn_r);
+ else
+ return arm_record_asimd_vfp_coproc (thumb2_insn_r);
+}
+
+/* Record handler for advance SIMD structure load/store instructions. */
+
+static int
+thumb2_record_asimd_struct_ld_st (insn_decode_record *thumb2_insn_r)
+{
+ struct regcache *reg_cache = thumb2_insn_r->regcache;
+ uint32_t l_bit, a_bit, b_bits;
+ uint32_t record_buf[128], record_buf_mem[128];
+ uint32_t reg_rn, reg_vd, address, f_elem;
+ uint32_t index_r = 0, index_e = 0, bf_regs = 0, index_m = 0, loop_t = 0;
+ uint8_t f_ebytes;
+
+ l_bit = bit (thumb2_insn_r->arm_insn, 21);
+ a_bit = bit (thumb2_insn_r->arm_insn, 23);
+ b_bits = bits (thumb2_insn_r->arm_insn, 8, 11);
+ reg_rn = bits (thumb2_insn_r->arm_insn, 16, 19);
+ reg_vd = bits (thumb2_insn_r->arm_insn, 12, 15);
+ reg_vd = (bit (thumb2_insn_r->arm_insn, 22) << 4) | reg_vd;
+ f_ebytes = (1 << bits (thumb2_insn_r->arm_insn, 6, 7));
+ f_elem = 8 / f_ebytes;
+
+ if (!l_bit)
+ {
+ ULONGEST u_regval = 0;
+ regcache_raw_read_unsigned (reg_cache, reg_rn, &u_regval);
+ address = u_regval;
+
+ if (!a_bit)
{
- if (register_bits & 0x00000001)
- register_list[register_count++] = 1;
- register_bits = register_bits >> 1;
+ /* Handle VST1. */
+ if (b_bits == 0x02 || b_bits == 0x0a || (b_bits & 0x0e) == 0x06)
+ {
+ if (b_bits == 0x07)
+ bf_regs = 1;
+ else if (b_bits == 0x0a)
+ bf_regs = 2;
+ else if (b_bits == 0x06)
+ bf_regs = 3;
+ else if (b_bits == 0x02)
+ bf_regs = 4;
+ else
+ bf_regs = 0;
+
+ for (index_r = 0; index_r < bf_regs; index_r++)
+ {
+ for (index_e = 0; index_e < f_elem; index_e++)
+ {
+ record_buf_mem[index_m++] = f_ebytes;
+ record_buf_mem[index_m++] = address;
+ address = address + f_ebytes;
+ thumb2_insn_r->mem_rec_count += 1;
+ }
+ }
+ }
+ /* Handle VST2. */
+ else if (b_bits == 0x03 || (b_bits & 0x0e) == 0x08)
+ {
+ if (b_bits == 0x09 || b_bits == 0x08)
+ bf_regs = 1;
+ else if (b_bits == 0x03)
+ bf_regs = 2;
+ else
+ bf_regs = 0;
+
+ for (index_r = 0; index_r < bf_regs; index_r++)
+ for (index_e = 0; index_e < f_elem; index_e++)
+ {
+ for (loop_t = 0; loop_t < 2; loop_t++)
+ {
+ record_buf_mem[index_m++] = f_ebytes;
+ record_buf_mem[index_m++] = address + (loop_t * f_ebytes);
+ thumb2_insn_r->mem_rec_count += 1;
+ }
+ address = address + (2 * f_ebytes);
+ }
+ }
+ /* Handle VST3. */
+ else if ((b_bits & 0x0e) == 0x04)
+ {
+ for (index_e = 0; index_e < f_elem; index_e++)
+ {
+ for (loop_t = 0; loop_t < 3; loop_t++)
+ {
+ record_buf_mem[index_m++] = f_ebytes;
+ record_buf_mem[index_m++] = address + (loop_t * f_ebytes);
+ thumb2_insn_r->mem_rec_count += 1;
+ }
+ address = address + (3 * f_ebytes);
+ }
+ }
+ /* Handle VST4. */
+ else if (!(b_bits & 0x0e))
+ {
+ for (index_e = 0; index_e < f_elem; index_e++)
+ {
+ for (loop_t = 0; loop_t < 4; loop_t++)
+ {
+ record_buf_mem[index_m++] = f_ebytes;
+ record_buf_mem[index_m++] = address + (loop_t * f_ebytes);
+ thumb2_insn_r->mem_rec_count += 1;
+ }
+ address = address + (4 * f_ebytes);
+ }
+ }
}
- record_buf[register_count] = reg_src1;
- thumb_insn_r->reg_rec_count = register_count + 1;
- for (register_count = 0; register_count < 8; register_count++)
+ else
{
- if (register_list[register_count])
+ uint8_t bft_size = bits (thumb2_insn_r->arm_insn, 10, 11);
+
+ if (bft_size == 0x00)
+ f_ebytes = 1;
+ else if (bft_size == 0x01)
+ f_ebytes = 2;
+ else if (bft_size == 0x02)
+ f_ebytes = 4;
+ else
+ f_ebytes = 0;
+
+ /* Handle VST1. */
+ if (!(b_bits & 0x0b) || b_bits == 0x08)
+ thumb2_insn_r->mem_rec_count = 1;
+ /* Handle VST2. */
+ else if ((b_bits & 0x0b) == 0x01 || b_bits == 0x09)
+ thumb2_insn_r->mem_rec_count = 2;
+ /* Handle VST3. */
+ else if ((b_bits & 0x0b) == 0x02 || b_bits == 0x0a)
+ thumb2_insn_r->mem_rec_count = 3;
+ /* Handle VST4. */
+ else if ((b_bits & 0x0b) == 0x03 || b_bits == 0x0b)
+ thumb2_insn_r->mem_rec_count = 4;
+
+ for (index_m = 0; index_m < thumb2_insn_r->mem_rec_count; index_m++)
{
- record_buf[index] = register_count;
- index++;
+ record_buf_mem[index_m] = f_ebytes;
+ record_buf_mem[index_m] = address + (index_m * f_ebytes);
}
}
}
- else if (0 == opcode2)
+ else
{
- /* It handles both STMIA. */
- register_bits = bits (thumb_insn_r->arm_insn, 0, 7);
- /* Get Rn. */
- reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
- while (register_bits)
+ if (!a_bit)
{
- if (register_bits & 0x00000001)
- register_count++;
- register_bits = register_bits >> 1;
+ /* Handle VLD1. */
+ if (b_bits == 0x02 || b_bits == 0x0a || (b_bits & 0x0e) == 0x06)
+ thumb2_insn_r->reg_rec_count = 1;
+ /* Handle VLD2. */
+ else if (b_bits == 0x03 || (b_bits & 0x0e) == 0x08)
+ thumb2_insn_r->reg_rec_count = 2;
+ /* Handle VLD3. */
+ else if ((b_bits & 0x0e) == 0x04)
+ thumb2_insn_r->reg_rec_count = 3;
+ /* Handle VLD4. */
+ else if (!(b_bits & 0x0e))
+ thumb2_insn_r->reg_rec_count = 4;
}
- start_address = u_regval;
- thumb_insn_r->mem_rec_count = register_count;
- while (register_count)
+ else
{
- record_buf_mem[(register_count * 2) - 1] = start_address;
- record_buf_mem[(register_count * 2) - 2] = 4;
- start_address = start_address + 4;
- register_count--;
+ /* Handle VLD1. */
+ if (!(b_bits & 0x0b) || b_bits == 0x08 || b_bits == 0x0c)
+ thumb2_insn_r->reg_rec_count = 1;
+ /* Handle VLD2. */
+ else if ((b_bits & 0x0b) == 0x01 || b_bits == 0x09 || b_bits == 0x0d)
+ thumb2_insn_r->reg_rec_count = 2;
+ /* Handle VLD3. */
+ else if ((b_bits & 0x0b) == 0x02 || b_bits == 0x0a || b_bits == 0x0e)
+ thumb2_insn_r->reg_rec_count = 3;
+ /* Handle VLD4. */
+ else if ((b_bits & 0x0b) == 0x03 || b_bits == 0x0b || b_bits == 0x0f)
+ thumb2_insn_r->reg_rec_count = 4;
+
+ for (index_r = 0; index_r < thumb2_insn_r->reg_rec_count; index_r++)
+ record_buf[index_r] = reg_vd + ARM_D0_REGNUM + index_r;
}
}
- else if (0x1F == opcode1)
+
+ if (bits (thumb2_insn_r->arm_insn, 0, 3) != 15)
{
- /* Handle arm syscall insn. */
- if (tdep->arm_swi_record != NULL)
- {
- ret = tdep->arm_swi_record(reg_cache);
- }
- else
- {
- printf_unfiltered (_("no syscall record support\n"));
- return -1;
- }
+ record_buf[index_r] = reg_rn;
+ thumb2_insn_r->reg_rec_count += 1;
}
- /* B (1), conditional branch is automatically taken care in process_record,
- as PC is saved there. */
-
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- MEM_ALLOC (thumb_insn_r->arm_mems, thumb_insn_r->mem_rec_count,
- record_buf_mem);
-
- return ret;
+ REG_ALLOC (thumb2_insn_r->arm_regs, thumb2_insn_r->reg_rec_count,
+ record_buf);
+ MEM_ALLOC (thumb2_insn_r->arm_mems, thumb2_insn_r->mem_rec_count,
+ record_buf_mem);
+ return 0;
}
-/* Handling opcode 111 insns. */
+/* Decodes thumb2 instruction type and invokes its record handler. */
-static int
-thumb_record_branch (insn_decode_record *thumb_insn_r)
+static unsigned int
+thumb2_record_decode_insn_handler (insn_decode_record *thumb2_insn_r)
{
- uint32_t record_buf[8];
- uint32_t bits_h = 0;
+ uint32_t op, op1, op2;
- bits_h = bits (thumb_insn_r->arm_insn, 11, 12);
+ op = bit (thumb2_insn_r->arm_insn, 15);
+ op1 = bits (thumb2_insn_r->arm_insn, 27, 28);
+ op2 = bits (thumb2_insn_r->arm_insn, 20, 26);
- if (2 == bits_h || 3 == bits_h)
+ if (op1 == 0x01)
{
- /* BL */
- record_buf[0] = ARM_LR_REGNUM;
- thumb_insn_r->reg_rec_count = 1;
+ if (!(op2 & 0x64 ))
+ {
+ /* Load/store multiple instruction. */
+ return thumb2_record_ld_st_multiple (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x64) ^ 0x04))
+ {
+ /* Load/store (dual/exclusive) and table branch instruction. */
+ return thumb2_record_ld_st_dual_ex_tbb (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x20) ^ 0x20))
+ {
+ /* Data-processing (shifted register). */
+ return thumb2_record_data_proc_sreg_mimm (thumb2_insn_r);
+ }
+ else if (op2 & 0x40)
+ {
+ /* Co-processor instructions. */
+ return thumb2_record_coproc_insn (thumb2_insn_r);
+ }
}
- else if (1 == bits_h)
+ else if (op1 == 0x02)
{
- /* BLX(1). */
- record_buf[0] = ARM_PS_REGNUM;
- record_buf[1] = ARM_LR_REGNUM;
- thumb_insn_r->reg_rec_count = 2;
+ if (op)
+ {
+ /* Branches and miscellaneous control instructions. */
+ return thumb2_record_branch_misc_cntrl (thumb2_insn_r);
+ }
+ else if (op2 & 0x20)
+ {
+ /* Data-processing (plain binary immediate) instruction. */
+ return thumb2_record_ps_dest_generic (thumb2_insn_r);
+ }
+ else
+ {
+ /* Data-processing (modified immediate). */
+ return thumb2_record_data_proc_sreg_mimm (thumb2_insn_r);
+ }
}
+ else if (op1 == 0x03)
+ {
+ if (!(op2 & 0x71 ))
+ {
+ /* Store single data item. */
+ return thumb2_record_str_single_data (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x71) ^ 0x10))
+ {
+ /* Advanced SIMD or structure load/store instructions. */
+ return thumb2_record_asimd_struct_ld_st (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x67) ^ 0x01))
+ {
+ /* Load byte, memory hints instruction. */
+ return thumb2_record_ld_mem_hints (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x67) ^ 0x03))
+ {
+ /* Load halfword, memory hints instruction. */
+ return thumb2_record_ld_mem_hints (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x67) ^ 0x05))
+ {
+ /* Load word instruction. */
+ return thumb2_record_ld_word (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x70) ^ 0x20))
+ {
+ /* Data-processing (register) instruction. */
+ return thumb2_record_ps_dest_generic (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x78) ^ 0x30))
+ {
+ /* Multiply, multiply accumulate, abs diff instruction. */
+ return thumb2_record_ps_dest_generic (thumb2_insn_r);
+ }
+ else if (!((op2 & 0x78) ^ 0x38))
+ {
+ /* Long multiply, long multiply accumulate, and divide. */
+ return thumb2_record_lmul_lmla_div (thumb2_insn_r);
+ }
+ else if (op2 & 0x40)
+ {
+ /* Co-processor instructions. */
+ return thumb2_record_coproc_insn (thumb2_insn_r);
+ }
+ }
- /* B(2) is automatically taken care in process_record, as PC is
- saved there. */
-
- REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
-
- return 0;
+ return -1;
}
-
/* Extracts arm/thumb/thumb2 insn depending on the size, and returns 0 on success
and positive val on fauilure. */
return 1;
insn_record->arm_insn = (uint32_t) extract_unsigned_integer (&buf[0],
insn_size,
- gdbarch_byte_order (insn_record->gdbarch));
+ gdbarch_byte_order_for_code (insn_record->gdbarch));
return 0;
}
static int
decode_insn (insn_decode_record *arm_record, record_type_t record_type,
- uint32_t insn_size)
+ uint32_t insn_size)
{
- /* (Starting from numerical 0); bits 25, 26, 27 decodes type of arm instruction. */
- static const sti_arm_hdl_fp_t const arm_handle_insn[8] =
+ /* (Starting from numerical 0); bits 25, 26, 27 decodes type of arm
+ instruction. */
+ static const sti_arm_hdl_fp_t arm_handle_insn[8] =
{
arm_record_data_proc_misc_ld_str, /* 000. */
arm_record_data_proc_imm, /* 001. */
arm_record_ld_st_reg_offset, /* 011. */
arm_record_ld_st_multiple, /* 100. */
arm_record_b_bl, /* 101. */
- arm_record_coproc, /* 110. */
+ arm_record_asimd_vfp_coproc, /* 110. */
arm_record_coproc_data_proc /* 111. */
};
- /* (Starting from numerical 0); bits 13,14,15 decodes type of thumb instruction. */
- static const sti_arm_hdl_fp_t const thumb_handle_insn[8] =
+ /* (Starting from numerical 0); bits 13,14,15 decodes type of thumb
+ instruction. */
+ static const sti_arm_hdl_fp_t thumb_handle_insn[8] =
{ \
thumb_record_shift_add_sub, /* 000. */
thumb_record_add_sub_cmp_mov, /* 001. */
if (extract_arm_insn (arm_record, insn_size))
{
if (record_debug)
- {
- printf_unfiltered (_("Process record: error reading memory at "
- "addr %s len = %d.\n"),
- paddress (arm_record->gdbarch, arm_record->this_addr), insn_size);
- }
+ {
+ printf_unfiltered (_("Process record: error reading memory at "
+ "addr %s len = %d.\n"),
+ paddress (arm_record->gdbarch,
+ arm_record->this_addr), insn_size);
+ }
return -1;
}
else if (ARM_RECORD == record_type)
{
arm_record->cond = bits (arm_record->arm_insn, 28, 31);
insn_id = bits (arm_record->arm_insn, 25, 27);
- ret = arm_record_extension_space (arm_record);
- /* If this insn has fallen into extension space
- then we need not decode it anymore. */
- if (ret != -1 && !INSN_RECORDED(arm_record))
- {
- ret = arm_handle_insn[insn_id] (arm_record);
- }
+
+ if (arm_record->cond == 0xf)
+ ret = arm_record_extension_space (arm_record);
+ else
+ {
+ /* If this insn has fallen into extension space
+ then we need not decode it anymore. */
+ ret = arm_handle_insn[insn_id] (arm_record);
+ }
+ if (ret != ARM_RECORD_SUCCESS)
+ {
+ arm_record_unsupported_insn (arm_record);
+ ret = -1;
+ }
}
else if (THUMB_RECORD == record_type)
{
arm_record->cond = -1;
insn_id = bits (arm_record->arm_insn, 13, 15);
ret = thumb_handle_insn[insn_id] (arm_record);
+ if (ret != ARM_RECORD_SUCCESS)
+ {
+ arm_record_unsupported_insn (arm_record);
+ ret = -1;
+ }
}
else if (THUMB2_RECORD == record_type)
{
- printf_unfiltered (_("Process record doesnt support thumb32 instruction "
- "0x%0x at address %s.\n"),arm_record->arm_insn,
- paddress (arm_record->gdbarch,
- arm_record->this_addr));
- ret = -1;
+ /* As thumb does not have condition codes, we set negative. */
+ arm_record->cond = -1;
+
+ /* Swap first half of 32bit thumb instruction with second half. */
+ arm_record->arm_insn
+ = (arm_record->arm_insn >> 16) | (arm_record->arm_insn << 16);
+
+ ret = thumb2_record_decode_insn_handler (arm_record);
+
+ if (ret != ARM_RECORD_SUCCESS)
+ {
+ arm_record_unsupported_insn (arm_record);
+ ret = -1;
+ }
}
else
{
}
-/* Parse the current instruction and record the values of the registers and
+/* Parse the current instruction and record the values of the registers and
memory that will be changed in current instruction to record_arch_list".
Return -1 if something is wrong. */
int
-arm_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
- CORE_ADDR insn_addr)
+arm_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR insn_addr)
{
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
uint32_t no_of_rec = 0;
uint32_t ret = 0; /* return value: -1:record failure ; 0:success */
ULONGEST t_bit = 0, insn_id = 0;
if (record_debug > 1)
{
fprintf_unfiltered (gdb_stdlog, "Process record: arm_process_record "
- "addr = %s\n",
+ "addr = %s\n",
paddress (gdbarch, arm_record.this_addr));
}
if (extract_arm_insn (&arm_record, 2))
{
if (record_debug)
- {
- printf_unfiltered (_("Process record: error reading memory at "
- "addr %s len = %d.\n"),
- paddress (arm_record.gdbarch,
- arm_record.this_addr), 2);
- }
+ {
+ printf_unfiltered (_("Process record: error reading memory at "
+ "addr %s len = %d.\n"),
+ paddress (arm_record.gdbarch,
+ arm_record.this_addr), 2);
+ }
return -1;
}
insn_id = bits (arm_record.arm_insn, 11, 15);
/* is it thumb2 insn? */
if ((0x1D == insn_id) || (0x1E == insn_id) || (0x1F == insn_id))
- {
- ret = decode_insn (&arm_record, THUMB2_RECORD,
- THUMB2_INSN_SIZE_BYTES);
- }
+ {
+ ret = decode_insn (&arm_record, THUMB2_RECORD,
+ THUMB2_INSN_SIZE_BYTES);
+ }
else
- {
- /* We are decoding thumb insn. */
- ret = decode_insn (&arm_record, THUMB_RECORD, THUMB_INSN_SIZE_BYTES);
- }
+ {
+ /* We are decoding thumb insn. */
+ ret = decode_insn (&arm_record, THUMB_RECORD, THUMB_INSN_SIZE_BYTES);
+ }
}
if (0 == ret)
/* Record registers. */
record_full_arch_list_add_reg (arm_record.regcache, ARM_PC_REGNUM);
if (arm_record.arm_regs)
- {
- for (no_of_rec = 0; no_of_rec < arm_record.reg_rec_count; no_of_rec++)
- {
- if (record_full_arch_list_add_reg
+ {
+ for (no_of_rec = 0; no_of_rec < arm_record.reg_rec_count; no_of_rec++)
+ {
+ if (record_full_arch_list_add_reg
(arm_record.regcache , arm_record.arm_regs[no_of_rec]))
- ret = -1;
- }
- }
+ ret = -1;
+ }
+ }
/* Record memories. */
if (arm_record.arm_mems)
- {
- for (no_of_rec = 0; no_of_rec < arm_record.mem_rec_count; no_of_rec++)
- {
- if (record_full_arch_list_add_mem
- ((CORE_ADDR)arm_record.arm_mems[no_of_rec].addr,
+ {
+ for (no_of_rec = 0; no_of_rec < arm_record.mem_rec_count; no_of_rec++)
+ {
+ if (record_full_arch_list_add_mem
+ ((CORE_ADDR)arm_record.arm_mems[no_of_rec].addr,
arm_record.arm_mems[no_of_rec].len))
- ret = -1;
- }
- }
+ ret = -1;
+ }
+ }
if (record_full_arch_list_add_end ())
- ret = -1;
+ ret = -1;
}
return ret;
}
-