/* Common target dependent code for GDB on ARM systems.
- Copyright (C) 1988, 1989, 1991, 1992, 1993, 1995, 1996, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 1988-2014 Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#include "defs.h"
+
#include <ctype.h> /* XXX for isupper (). */
-#include "defs.h"
#include "frame.h"
#include "inferior.h"
#include "gdbcmd.h"
#include "gdbcore.h"
-#include "gdb_string.h"
+#include <string.h>
#include "dis-asm.h" /* For register styles. */
#include "regcache.h"
#include "reggroups.h"
#include "dwarf2-frame.h"
#include "gdbtypes.h"
#include "prologue-value.h"
+#include "remote.h"
#include "target-descriptions.h"
#include "user-regs.h"
#include "observer.h"
#include "gdb_assert.h"
#include "vec.h"
+#include "record.h"
+#include "record-full.h"
+
#include "features/arm-with-m.c"
+#include "features/arm-with-m-fpa-layout.c"
+#include "features/arm-with-m-vfp-d16.c"
+#include "features/arm-with-iwmmxt.c"
+#include "features/arm-with-vfpv2.c"
+#include "features/arm-with-vfpv3.c"
+#include "features/arm-with-neon.c"
static int arm_debug;
/* The type of floating-point to use. Keep this in sync with enum
arm_float_model, and the help string in _initialize_arm_tdep. */
-static const char *fp_model_strings[] =
+static const char *const fp_model_strings[] =
{
"auto",
"softfpa",
static const char *current_fp_model = "auto";
/* The ABI to use. Keep this in sync with arm_abi_kind. */
-static const char *arm_abi_strings[] =
+static const char *const arm_abi_strings[] =
{
"auto",
"APCS",
static const char *arm_abi_string = "auto";
/* The execution mode to assume. */
-static const char *arm_mode_strings[] =
+static const char *const arm_mode_strings[] =
{
"auto",
"arm",
struct regcache *regcache,
int regnum, const gdb_byte *buf);
+static int thumb_insn_size (unsigned short inst1);
+
struct arm_prologue_cache
{
/* The stack pointer at the time this frame was created; i.e. the
int
arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr)
{
- struct obj_section *sec;
- 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)
static CORE_ADDR
arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val)
{
+ /* On M-profile devices, do not strip the low bit from EXC_RETURN
+ (the magic exception return address). */
+ if (gdbarch_tdep (gdbarch)->is_m
+ && (val & 0xfffffff0) == 0xfffffff0)
+ return val;
+
if (arm_apcs_32)
return UNMAKE_THUMB_ADDR (val);
else
return (val & 0x03fffffc);
}
-/* When reading symbols, we need to zap the low bit of the address,
- which may be set to 1 for Thumb functions. */
-static CORE_ADDR
-arm_smash_text_address (struct gdbarch *gdbarch, CORE_ADDR val)
-{
- return val & ~1;
-}
-
/* Return 1 if PC is the start of a compiler helper function which
can be safely ignored during prologue skipping. IS_THUMB is true
if the function is known to be a Thumb function due to the way it
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
+ && SYMBOL_VALUE_ADDRESS (msym.minsym) == pc
+ && SYMBOL_LINKAGE_NAME (msym.minsym) != NULL)
{
- const char *name = SYMBOL_LINKAGE_NAME (msym);
+ const char *name = SYMBOL_LINKAGE_NAME (msym.minsym);
/* The GNU linker's Thumb call stub to foo is named
__foo_from_thumb. */
#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)))
+ ((CORE_ADDR) (((unsigned 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
constant = read_memory_unsigned_integer (loc, 4, byte_order);
regs[bits (insn, 8, 10)] = pv_constant (constant);
}
- else if ((insn & 0xe000) == 0xe000)
+ else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instructions. */
{
unsigned short inst2;
cache->framereg = THUMB_FP_REGNUM;
cache->framesize = -regs[THUMB_FP_REGNUM].k;
}
- else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
+ else
{
/* Try the stack pointer... this is a bit desperate. */
cache->framereg = ARM_SP_REGNUM;
cache->framesize = -regs[ARM_SP_REGNUM].k;
}
- else
- {
- /* We're just out of luck. We don't know where the frame is. */
- cache->framereg = -1;
- cache->framesize = 0;
- }
for (i = 0; i < 16; i++)
if (pv_area_find_reg (stack, gdbarch, i, &offset))
arm_skip_stack_protector(CORE_ADDR pc, struct gdbarch *gdbarch)
{
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- unsigned int address, basereg;
- struct minimal_symbol *stack_chk_guard;
+ unsigned int basereg;
+ struct bound_minimal_symbol stack_chk_guard;
int offset;
int is_thumb = arm_pc_is_thumb (gdbarch, pc);
CORE_ADDR 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",
+ if (stack_chk_guard.minsym
+ && strncmp (SYMBOL_LINKAGE_NAME (stack_chk_guard.minsym),
+ "__stack_chk_guard",
strlen ("__stack_chk_guard")) != 0)
return pc;
unsigned long inst;
CORE_ADDR skip_pc;
CORE_ADDR func_addr, limit_pc;
- struct symtab_and_line sal;
/* See if we can determine the end of the prologue via the symbol table.
If so, then return either PC, or the PC after the prologue, whichever
if (post_prologue_pc
&& (s == NULL
|| s->producer == NULL
- || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0))
+ || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0
+ || strncmp (s->producer, "clang ", sizeof ("clang ") - 1) == 0))
return post_prologue_pc;
if (post_prologue_pc != 0)
{
CORE_ADDR prologue_start;
CORE_ADDR prologue_end;
- CORE_ADDR current_pc;
if (find_pc_partial_function (block_addr, NULL, &prologue_start,
&prologue_end))
framereg = ARM_FP_REGNUM;
framesize = -regs[ARM_FP_REGNUM].k;
}
- else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
+ else
{
/* Try the stack pointer... this is a bit desperate. */
framereg = ARM_SP_REGNUM;
framesize = -regs[ARM_SP_REGNUM].k;
}
- else
- {
- /* We're just out of luck. We don't know where the frame is. */
- framereg = -1;
- framesize = 0;
- }
if (cache)
{
static void
arm_exidx_new_objfile (struct objfile *objfile)
{
- struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+ struct cleanup *cleanups;
struct arm_exidx_data *data;
asection *exidx, *extab;
bfd_vma exidx_vma = 0, extab_vma = 0;
/* If we've already touched this file, do nothing. */
if (!objfile || objfile_data (objfile, arm_exidx_data_key) != NULL)
return;
+ cleanups = make_cleanup (null_cleanup, NULL);
/* Read contents of exception table and index. */
exidx = bfd_get_section_by_name (objfile->obfd, ".ARM.exidx");
void **this_prologue_cache)
{
CORE_ADDR addr_in_block;
- char dummy[4];
+ gdb_byte dummy[4];
addr_in_block = get_frame_address_in_block (this_frame);
- if (in_plt_section (addr_in_block, NULL)
+ 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)
arm_stub_unwind_sniffer
};
+/* Put here the code to store, into CACHE->saved_regs, the addresses
+ of the saved registers of frame described by THIS_FRAME. CACHE is
+ returned. */
+
+static struct arm_prologue_cache *
+arm_m_exception_cache (struct frame_info *this_frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct arm_prologue_cache *cache;
+ CORE_ADDR unwound_sp;
+ LONGEST xpsr;
+
+ cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
+ cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+
+ unwound_sp = get_frame_register_unsigned (this_frame,
+ ARM_SP_REGNUM);
+
+ /* The hardware saves eight 32-bit words, comprising xPSR,
+ ReturnAddress, LR (R14), R12, R3, R2, R1, R0. See details in
+ "B1.5.6 Exception entry behavior" in
+ "ARMv7-M Architecture Reference Manual". */
+ cache->saved_regs[0].addr = unwound_sp;
+ cache->saved_regs[1].addr = unwound_sp + 4;
+ cache->saved_regs[2].addr = unwound_sp + 8;
+ cache->saved_regs[3].addr = unwound_sp + 12;
+ cache->saved_regs[12].addr = unwound_sp + 16;
+ cache->saved_regs[14].addr = unwound_sp + 20;
+ cache->saved_regs[15].addr = unwound_sp + 24;
+ cache->saved_regs[ARM_PS_REGNUM].addr = unwound_sp + 28;
+
+ /* If bit 9 of the saved xPSR is set, then there is a four-byte
+ aligner between the top of the 32-byte stack frame and the
+ previous context's stack pointer. */
+ cache->prev_sp = unwound_sp + 32;
+ if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr)
+ && (xpsr & (1 << 9)) != 0)
+ cache->prev_sp += 4;
+
+ return cache;
+}
+
+/* Implementation of function hook 'this_id' in
+ 'struct frame_uwnind'. */
+
+static void
+arm_m_exception_this_id (struct frame_info *this_frame,
+ void **this_cache,
+ struct frame_id *this_id)
+{
+ struct arm_prologue_cache *cache;
+
+ if (*this_cache == NULL)
+ *this_cache = arm_m_exception_cache (this_frame);
+ cache = *this_cache;
+
+ /* Our frame ID for a stub frame is the current SP and LR. */
+ *this_id = frame_id_build (cache->prev_sp,
+ get_frame_pc (this_frame));
+}
+
+/* Implementation of function hook 'prev_register' in
+ 'struct frame_uwnind'. */
+
+static struct value *
+arm_m_exception_prev_register (struct frame_info *this_frame,
+ 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;
+
+ /* The value was already reconstructed into PREV_SP. */
+ if (prev_regnum == ARM_SP_REGNUM)
+ return frame_unwind_got_constant (this_frame, prev_regnum,
+ cache->prev_sp);
+
+ return trad_frame_get_prev_register (this_frame, cache->saved_regs,
+ prev_regnum);
+}
+
+/* Implementation of function hook 'sniffer' in
+ 'struct frame_uwnind'. */
+
+static int
+arm_m_exception_unwind_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_prologue_cache)
+{
+ CORE_ADDR this_pc = get_frame_pc (this_frame);
+
+ /* No need to check is_m; this sniffer is only registered for
+ M-profile architectures. */
+
+ /* Exception frames return to one of these magic PCs. Other values
+ are not defined as of v7-M. See details in "B1.5.8 Exception
+ return behavior" in "ARMv7-M Architecture Reference Manual". */
+ if (this_pc == 0xfffffff1 || this_pc == 0xfffffff9
+ || this_pc == 0xfffffffd)
+ return 1;
+
+ return 0;
+}
+
+/* Frame unwinder for M-profile exceptions. */
+
+struct frame_unwind arm_m_exception_unwind =
+{
+ SIGTRAMP_FRAME,
+ default_frame_unwind_stop_reason,
+ arm_m_exception_this_id,
+ arm_m_exception_prev_register,
+ NULL,
+ arm_m_exception_unwind_sniffer
+};
+
static CORE_ADDR
arm_normal_frame_base (struct frame_info *this_frame, void **this_cache)
{
if (insn & 0x0100) /* <registers> include PC. */
found_return = 1;
}
- else if ((insn & 0xe000) == 0xe000) /* 32-bit Thumb-2 instruction */
+ else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instruction */
{
if (target_read_memory (scan_pc, buf, 2))
break;
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)
case TYPE_CODE_FLT:
case TYPE_CODE_SET:
case TYPE_CODE_RANGE:
- case TYPE_CODE_BITSTRING:
case TYPE_CODE_REF:
case TYPE_CODE_CHAR:
case TYPE_CODE_BOOL:
val + i * unit_length);
else
{
- sprintf (name_buf, "%c%d", reg_char, reg_scaled + i);
+ xsnprintf (name_buf, sizeof (name_buf), "%c%d",
+ reg_char, reg_scaled + i);
regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
strlen (name_buf));
regcache_cooked_write (regcache, regnum,
}
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. */
{
char name_buf[4];
- sprintf (name_buf, "s%d", reg - 64);
+ xsnprintf (name_buf, sizeof (name_buf), "s%d", reg - 64);
return user_reg_map_name_to_regnum (gdbarch, name_buf,
strlen (name_buf));
}
{
char name_buf[4];
- sprintf (name_buf, "d%d", reg - 256);
+ xsnprintf (name_buf, sizeof (name_buf), "d%d", reg - 256);
return user_reg_map_name_to_regnum (gdbarch, name_buf,
strlen (name_buf));
}
int cond = itstate >> 4;
if (! condition_true (cond, status))
- {
- /* Advance to the next instruction. All the 32-bit
- instructions share a common prefix. */
- if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
- return MAKE_THUMB_ADDR (pc + 4);
- else
- return MAKE_THUMB_ADDR (pc + 2);
- }
+ /* 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. */
}
{
nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
}
- else if ((inst1 & 0xe000) == 0xe000) /* 32-bit instruction */
+ 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);
else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */
{
if (bits (inst1, 3, 6) == 0x0f)
- nextpc = pc_val;
+ nextpc = UNMAKE_THUMB_ADDR (pc_val);
else
nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
}
else
base -= offset;
}
- nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base,
- 4, byte_order);
+ nextpc =
+ (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) base,
+ 4, byte_order);
}
}
break;
{
/* loading pc */
int offset = 0;
+ unsigned long rn_val
+ = get_frame_register_unsigned (frame,
+ bits (this_instr, 16, 19));
if (bit (this_instr, 23))
{
else if (bit (this_instr, 24))
offset = -4;
- {
- unsigned long rn_val =
- get_frame_register_unsigned (frame,
- bits (this_instr, 16, 19));
- nextpc =
- (CORE_ADDR) read_memory_integer ((CORE_ADDR) (rn_val
- + offset),
- 4, byte_order);
- }
+ nextpc =
+ (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR)
+ (rn_val + offset),
+ 4, byte_order);
}
}
break;
CORE_ADDR nextpc;
if (arm_frame_is_thumb (frame))
- {
- nextpc = thumb_get_next_pc_raw (frame, pc);
- if (nextpc == MAKE_THUMB_ADDR (pc))
- error (_("Infinite loop detected"));
- }
+ nextpc = thumb_get_next_pc_raw (frame, pc);
else
- {
- nextpc = arm_get_next_pc_raw (frame, pc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- }
+ nextpc = arm_get_next_pc_raw (frame, pc);
return nextpc;
}
do_cleanups (old_chain);
}
+/* 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. */
+
+static int
+thumb_deal_with_atomic_sequence_raw (struct frame_info *frame)
+{
+ 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. */
+
+ breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1);
+ last_breakpoint++;
+ }
+
+ /* 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;
+
+ /* 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;
+
+ sign = sbits (insn1, 10, 10);
+ imm1 = bits (insn1, 0, 5);
+ imm2 = bits (insn2, 0, 10);
+ j1 = bit (insn2, 13);
+ j2 = bit (insn2, 11);
+
+ offset = (sign << 20) + (j2 << 19) + (j1 << 18);
+ offset += (imm1 << 12) + (imm2 << 1);
+
+ if (last_breakpoint > 0)
+ return 0; /* More than one conditional branch found,
+ fallback to the standard code. */
+
+ breaks[1] = loc + offset;
+ last_breakpoint++;
+ }
+
+ /* 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;
+
+ /* If we find a strex{,b,h,d}, we're done. */
+ if ((insn1 & 0xfff0) == 0xe840
+ || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040))
+ break;
+ }
+ }
+
+ /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
+ if (insn_count == atomic_sequence_length)
+ return 0;
+
+ /* Insert a breakpoint right after the end of the atomic sequence. */
+ breaks[0] = loc;
+
+ /* 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;
+
+ /* Effectively inserts the breakpoints. */
+ for (index = 0; index <= last_breakpoint; index++)
+ arm_insert_single_step_breakpoint (gdbarch, aspace,
+ MAKE_THUMB_ADDR (breaks[index]));
+
+ return 1;
+}
+
+static int
+arm_deal_with_atomic_sequence_raw (struct frame_info *frame)
+{
+ 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;
+
+ /* 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;
+
+ /* 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. */
+
+ breaks[1] = BranchDest (loc - 4, insn);
+ last_breakpoint++;
+ }
+
+ /* 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;
+
+ /* If we find a strex{,b,h,d}, we're done. */
+ if ((insn & 0xff9000f0) == 0xe1800090)
+ break;
+ }
+
+ /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */
+ if (insn_count == atomic_sequence_length)
+ return 0;
+
+ /* Insert a breakpoint right after the end of the atomic sequence. */
+ breaks[0] = loc;
+
+ /* 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;
+
+ /* Effectively inserts the breakpoints. */
+ for (index = 0; index <= last_breakpoint; index++)
+ arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
+
+ return 1;
+}
+
+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);
+}
+
/* 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
{
struct gdbarch *gdbarch = get_frame_arch (frame);
struct address_space *aspace = get_frame_address_space (frame);
- CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ CORE_ADDR next_pc;
+
+ if (arm_deal_with_atomic_sequence (frame))
+ return 1;
+ next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc);
return 1;
extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
int old_len, int new_len)
{
- gdb_byte *new_buf, *middle;
+ gdb_byte *new_buf;
int bytes_to_read = new_len - old_len;
new_buf = xmalloc (new_len);
gdb_byte *buf;
char map_type;
CORE_ADDR boundary, func_start;
- int buf_len, buf2_len;
+ int buf_len;
enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
int i, any, last_it, last_it_count;
/* NOP instruction (mov r0, r0). */
#define ARM_NOP 0xe1a00000
+#define THUMB_NOP 0x4600
/* Helper for register reads for displaced stepping. In particular, this
returns the PC as it would be seen by the instruction at its original
matter what address they are executed at: in those cases, use this. */
static int
-copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
- const char *iname, struct displaced_step_closure *dsc)
+arm_copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
+ const char *iname, struct displaced_step_closure *dsc)
{
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, "
return 0;
}
+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);
+
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
+
+ return 0;
+}
+
+/* Copy 16-bit Thumb(Thumb and 16-bit Thumb-2) instruction without any
+ modification. */
+static int
+thumb_copy_unmodified_16bit (struct gdbarch *gdbarch, unsigned int insn,
+ const char *iname,
+ struct displaced_step_closure *dsc)
+{
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x, "
+ "opcode/class '%s' unmodified\n", insn,
+ iname);
+
+ dsc->modinsn[0] = insn;
+
+ return 0;
+}
+
/* Preload instructions with immediate offset. */
static void
displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
}
-static int
-copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+static void
+install_preload (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, unsigned int rn)
{
- unsigned int rn = bits (insn, 16, 19);
ULONGEST rn_val;
-
- if (!insn_references_pc (insn, 0x000f0000ul))
- return copy_unmodified (gdbarch, insn, "preload", dsc);
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
- (unsigned long) insn);
-
/* Preload instructions:
{pli/pld} [rn, #+/-imm]
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->modinsn[0] = insn & 0xfff0ffff;
-
dsc->cleanup = &cleanup_preload;
-
- return 0;
}
-/* Preload instructions with register offset. */
-
static int
-copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
+arm_copy_preload (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);
- ULONGEST rn_val, rm_val;
- if (!insn_references_pc (insn, 0x000f000ful))
- return copy_unmodified (gdbarch, insn, "preload reg", dsc);
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return arm_copy_unmodified (gdbarch, insn, "preload", dsc);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
(unsigned long) insn);
- /* Preload register-offset instructions:
+ dsc->modinsn[0] = insn & 0xfff0ffff;
- {pli/pld} [rn, rm {, shift}]
- ->
- {pli/pld} [r0, r1 {, shift}]. */
+ install_preload (gdbarch, regs, dsc, rn);
+
+ return 0;
+}
+
+static int
+thumb2_copy_preload (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ 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);
+
+ /* 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);
- 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);
+ pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+
+ 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] = (insn & 0xfff0fff0) | 0x1;
+ /* {pli/pld} [r0, r1] */
+ dsc->modinsn[0] = insn1 & 0xfff0;
+ dsc->modinsn[1] = 0xf001;
+ dsc->numinsns = 2;
dsc->cleanup = &cleanup_preload;
-
return 0;
}
-/* Copy/cleanup coprocessor load and store instructions. */
+/* Preload instructions with register offset. */
static void
-cleanup_copro_load_store (struct gdbarch *gdbarch,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+install_preload_reg(struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, unsigned int rn,
+ unsigned int rm)
{
- ULONGEST rn_val = displaced_read_reg (regs, dsc, 0);
+ ULONGEST rn_val, rm_val;
- displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ /* Preload register-offset instructions:
- if (dsc->u.ldst.writeback)
- displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, LOAD_WRITE_PC);
-}
+ {pli/pld} [rn, rm {, shift}]
+ ->
+ {pli/pld} [r0, r1 {, shift}]. */
-static int
-copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+ 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;
+}
+
+static int
+arm_copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
unsigned int rn = bits (insn, 16, 19);
- ULONGEST rn_val;
+ unsigned int rm = bits (insn, 0, 3);
- if (!insn_references_pc (insn, 0x000f0000ul))
- return copy_unmodified (gdbarch, insn, "copro load/store", dsc);
+
+ if (!insn_references_pc (insn, 0x000f000ful))
+ return arm_copy_unmodified (gdbarch, insn, "preload reg", dsc);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
- "load/store insn %.8lx\n", (unsigned long) insn);
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ (unsigned long) insn);
+
+ dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1;
+
+ install_preload_reg (gdbarch, regs, dsc, rn, rm);
+ return 0;
+}
+
+/* Copy/cleanup coprocessor load and store instructions. */
+
+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:
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);
- dsc->u.ldst.writeback = bit (insn, 25);
+ dsc->u.ldst.writeback = writeback;
dsc->u.ldst.rn = rn;
+ dsc->cleanup = &cleanup_copro_load_store;
+}
+
+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);
+
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return arm_copy_unmodified (gdbarch, insn, "copro load/store", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
+ "load/store insn %.8lx\n", (unsigned long) insn);
+
dsc->modinsn[0] = insn & 0xfff0ffff;
- dsc->cleanup = &cleanup_copro_load_store;
+ install_copro_load_store (gdbarch, regs, dsc, bit (insn, 25), rn);
+
+ return 0;
+}
+
+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);
+
+ 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: copying coprocessor "
+ "load/store insn %.4x%.4x\n", insn1, insn2);
+
+ dsc->modinsn[0] = insn1 & 0xfff0;
+ dsc->modinsn[1] = insn2;
+ 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);
return 0;
}
/* 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)
+{
+ /* Implement "BL<cond> <label>" as:
+
+ Preparation: cond <- instruction condition
+ Insn: mov r0, r0 (nop)
+ Cleanup: if (condition true) { r14 <- pc; pc <- label }.
+
+ B<cond> similar, but don't set r14 in cleanup. */
+
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = link;
+ dsc->u.branch.exchange = exchange;
+
+ 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;
+
+ if (dsc->is_thumb)
+ dsc->u.branch.dest += 4 + offset;
+ else
+ dsc->u.branch.dest += 8 + offset;
+
+ dsc->cleanup = &cleanup_branch;
+}
static int
-copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_copy_b_bl_blx (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);
- CORE_ADDR from = dsc->insn_addr;
long offset;
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
"%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
(unsigned long) insn);
-
- /* Implement "BL<cond> <label>" as:
-
- Preparation: cond <- instruction condition
- Insn: mov r0, r0 (nop)
- Cleanup: if (condition true) { r14 <- pc; pc <- label }.
-
- B<cond> similar, but don't set r14 in cleanup. */
-
if (exchange)
/* For BLX, set bit 0 of the destination. The cleanup_branch function will
then arrange the switch into Thumb mode. */
if (bit (offset, 25))
offset = offset | ~0x3ffffff;
- dsc->u.branch.cond = cond;
- dsc->u.branch.link = link;
- dsc->u.branch.exchange = exchange;
- dsc->u.branch.dest = from + 8 + offset;
-
dsc->modinsn[0] = ARM_NOP;
- dsc->cleanup = &cleanup_branch;
-
+ install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
return 0;
}
-/* Copy BX/BLX with register-specified destinations. */
+static int
+thumb2_copy_b_bl_blx (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, 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));
+
+ 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
-copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+thumb_copy_b (struct gdbarch *gdbarch, unsigned short insn,
+ 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 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 %s register insn "
- "%.8lx\n", (link) ? "blx" : "bx",
- (unsigned long) insn);
+ 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;
+
+ return 0;
+}
+/* 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)
+{
/* Implement {BX,BLX}<cond> <reg>" as:
Preparation: cond <- instruction condition
dsc->u.branch.cond = cond;
dsc->u.branch.link = link;
+
dsc->u.branch.exchange = 1;
+ dsc->cleanup = &cleanup_branch;
+}
+
+static int
+arm_copy_bx_blx_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);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx",
+ (unsigned long) insn);
+
dsc->modinsn[0] = ARM_NOP;
- dsc->cleanup = &cleanup_branch;
+ 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);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x",
+ (unsigned short) insn);
+
+ dsc->modinsn[0] = THUMB_NOP;
+
+ install_bx_blx_reg (gdbarch, regs, dsc, link, INST_AL, rm);
return 0;
}
+
/* Copy/cleanup arithmetic/logic instruction with immediate RHS. */
static void
}
static int
-copy_alu_imm (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 rd = bits (insn, 12, 15);
ULONGEST rd_val, rn_val;
if (!insn_references_pc (insn, 0x000ff000ul))
- return copy_unmodified (gdbarch, insn, "ALU immediate", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "ALU immediate", dsc);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying immediate %s insn "
return 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;
+
+ rn = bits (insn1, 0, 3); /* Rn */
+ rm = bits (insn2, 0, 3); /* Rm */
+ rd = bits (insn2, 8, 11); /* Rd */
+
+ /* This routine is only called for instruction MOV. */
+ gdb_assert (op == 0x2 && rn == 0xf);
+
+ if (rm != ARM_PC_REGNUM && rd != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "ALU imm", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x%.4x\n",
+ "ALU", insn1, insn2);
+
+ /* Instruction is of form:
+
+ <op><cond> rd, [rn,] #imm
+
+ Rewrite as:
+
+ 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);
+ 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;
+
+ dsc->cleanup = &cleanup_alu_imm;
+
+ return 0;
+}
+
/* Copy/cleanup arithmetic/logic insns with register RHS. */
static void
displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
}
-static int
-copy_alu_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+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)
{
- 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, rm_val;
- if (!insn_references_pc (insn, 0x000ff00ful))
- return copy_unmodified (gdbarch, insn, "ALU reg", dsc);
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.8lx\n",
- is_mov ? "move" : "ALU", (unsigned long) insn);
-
/* Instruction is of form:
<op><cond> rd, [rn,] rm [, <shift>]
displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
dsc->rd = rd;
+ dsc->cleanup = &cleanup_alu_reg;
+}
+
+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);
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return arm_copy_unmodified (gdbarch, insn, "ALU reg", dsc);
+
+ if (debug_displaced)
+ 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
dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x10002;
- dsc->cleanup = &cleanup_alu_reg;
-
+ 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)
+{
+ unsigned rn, rm, rd;
+
+ rd = bits (insn, 3, 6);
+ rn = (bit (insn, 7) << 3) | bits (insn, 0, 2);
+ rm = 2;
+
+ if (rd != ARM_PC_REGNUM && rn != ARM_PC_REGNUM)
+ return thumb_copy_unmodified_16bit (gdbarch, insn, "ALU reg", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.4x\n",
+ "ALU", (unsigned short) insn);
+
+ dsc->modinsn[0] = ((insn & 0xff00) | 0x08);
+
+ install_alu_reg (gdbarch, regs, dsc, rd, rn, rm);
+
return 0;
}
displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
}
-static int
-copy_alu_shifted_reg (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+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)
{
- unsigned int rn = bits (insn, 16, 19);
- unsigned int rm = bits (insn, 0, 3);
- unsigned int rd = bits (insn, 12, 15);
- unsigned int rs = bits (insn, 8, 11);
- unsigned int op = bits (insn, 21, 24);
- int is_mov = (op == 0xd), i;
+ int i;
ULONGEST rd_val, rn_val, rm_val, rs_val;
- if (!insn_references_pc (insn, 0x000fff0ful))
- return copy_unmodified (gdbarch, insn, "ALU shifted reg", dsc);
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying shifted reg %s insn "
- "%.8lx\n", is_mov ? "move" : "ALU",
- (unsigned long) insn);
-
/* Instruction is of form:
<op><cond> rd, [rn,] rm, <shift> rs
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_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;
+
+ 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 shifted reg %s insn "
+ "%.8lx\n", is_mov ? "move" : "ALU",
+ (unsigned long) insn);
+
+ 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;
- dsc->cleanup = &cleanup_alu_shifted_reg;
+ install_alu_shifted_reg (gdbarch, regs, dsc, rd, rn, rm, rs);
return 0;
}
transfers, which have a different encoding to byte/word transfers. */
static int
-copy_extra_ld_st (struct gdbarch *gdbarch, uint32_t insn, int unpriveleged,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_copy_extra_ld_st (struct gdbarch *gdbarch, uint32_t insn, int unpriveleged,
+ struct regcache *regs, struct displaced_step_closure *dsc)
{
unsigned int op1 = bits (insn, 20, 24);
unsigned int op2 = bits (insn, 5, 6);
ULONGEST rt_val, rt_val2 = 0, rn_val, rm_val = 0;
if (!insn_references_pc (insn, 0x000ff00ful))
- return copy_unmodified (gdbarch, insn, "extra load/store", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "extra load/store", dsc);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying %sextra load/store "
return 0;
}
-/* Copy byte/word loads and stores. */
+/* Copy byte/half word/word loads and stores. */
-static int
-copy_ldr_str_ldrb_strb (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc, int load, int byte,
- int usermode)
+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)
{
- int immed = !bit (insn, 25);
- 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. */
ULONGEST rt_val, rn_val, rm_val = 0;
- if (!insn_references_pc (insn, 0x000ff00ful))
- return copy_unmodified (gdbarch, insn, "load/store", dsc);
-
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying %s%s insn %.8lx\n",
- load ? (byte ? "ldrb" : "ldr")
- : (byte ? "strb" : "str"), usermode ? "t" : "",
- (unsigned long) insn);
-
dsc->tmp[0] = displaced_read_reg (regs, dsc, 0);
dsc->tmp[2] = displaced_read_reg (regs, dsc, 2);
if (!immed)
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 = byte ? 1 : 4;
+ dsc->u.ldst.xfersize = size;
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.writeback = writeback;
/* To write PC we can do:
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 */
+ dsc->cleanup = load ? &cleanup_load : &cleanup_store;
+}
+
+
+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)
+{
+ unsigned int u_bit = bit (insn1, 7);
+ unsigned int rt = bits (insn2, 12, 15);
+ int imm12 = bits (insn2, 0, 11);
+ ULONGEST pc_val;
+
+ 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);
+
+ if (!u_bit)
+ imm12 = -1 * imm12;
+
+ /* Rewrite instruction LDR Rt imm12 into:
+
+ Prepare: tmp[0] <- r0, tmp[1] <- r2, tmp[2] <- r3, r2 <- pc, r3 <- imm12
+
+ LDR R0, R2, R3,
+
+ Cleanup: rt <- r0, r0 <- tmp[0], r2 <- tmp[1], r3 <- tmp[2]. */
+
+
+ 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_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+
+ pc_val = pc_val & 0xfffffffc;
+
+ displaced_write_reg (regs, dsc, 2, pc_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 3, imm12, CANNOT_WRITE_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;
+
+ dsc->cleanup = &cleanup_load;
+
+ return 0;
+}
+
+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. */
+
+ 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 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;
+
+ 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;
+ }
+
+ dsc->numinsns = 2;
+
+ 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)
+{
+ 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. */
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return arm_copy_unmodified (gdbarch, insn, "load/store", 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);
+
+ install_load_store (gdbarch, regs, dsc, load, immed, writeback, size,
+ usermode, rt, rm, rn);
+
if (load || rt != ARM_PC_REGNUM)
{
dsc->u.ldst.restore_r4 = 0;
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), i;
+ 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;
in user-level code (in particular exception return, ldm rn, {...pc}^). */
static int
-copy_block_xfer (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_copy_block_xfer (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
int load = bit (insn, 20);
int user = bit (insn, 22);
/* Block transfers which don't mention PC can be run directly
out-of-line. */
if (rn != ARM_PC_REGNUM && (insn & 0x8000) == 0)
- return copy_unmodified (gdbarch, insn, "ldm/stm", dsc);
+ 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 copy_unmodified (gdbarch, insn, "unpredictable ldm/stm", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "unpredictable ldm/stm", dsc);
}
if (debug_displaced)
return 0;
}
+static int
+thumb2_copy_block_xfer (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ 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);
+
+ 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 block transfer insn "
+ "%.4x%.4x\n", insn1, insn2);
+
+ /* Clear bit 13, since it should be always zero. */
+ dsc->u.block.regmask = (insn2 & 0xdfff);
+ dsc->u.block.rn = rn;
+
+ 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 (load)
+ {
+ if (dsc->u.block.regmask == 0xffff)
+ {
+ /* This branch is impossible to happen. */
+ gdb_assert (0);
+ }
+ 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;
+
+ 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
+ {
+ 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. */
displaced_write_reg (regs, dsc, ARM_PC_REGNUM, resume_addr, BRANCH_WRITE_PC);
}
-static int
-copy_svc (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
-{
- if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
- (unsigned long) insn);
+/* Common copy routine for svc instruciton. */
+static int
+install_svc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
/* Preparation: none.
Insn: unmodified svc.
- Cleanup: pc <- insn_addr + 4. */
-
- dsc->modinsn[0] = insn;
+ Cleanup: pc <- insn_addr + insn_size. */
/* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
instruction. */
dsc->cleanup = &cleanup_svc;
return 0;
}
+}
+
+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: copying svc insn %.8lx\n",
+ (unsigned long) insn);
+
+ dsc->modinsn[0] = insn;
+
+ return install_svc (gdbarch, regs, dsc);
+}
+
+static int
+thumb_copy_svc (struct gdbarch *gdbarch, uint16_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.4x\n",
+ insn);
+
+ dsc->modinsn[0] = insn;
+ return install_svc (gdbarch, regs, dsc);
}
/* Copy undefined instructions. */
static int
-copy_undef (struct gdbarch *gdbarch, uint32_t insn,
- struct displaced_step_closure *dsc)
+arm_copy_undef (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
{
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
return 0;
}
+static int
+thumb_32bit_copy_undef (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2,
+ struct displaced_step_closure *dsc)
+{
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying undefined insn "
+ "%.4x %.4x\n", (unsigned short) insn1,
+ (unsigned short) insn2);
+
+ dsc->modinsn[0] = insn1;
+ dsc->modinsn[1] = insn2;
+ dsc->numinsns = 2;
+
+ return 0;
+}
+
/* Copy unpredictable instructions. */
static int
-copy_unpred (struct gdbarch *gdbarch, uint32_t insn,
- struct displaced_step_closure *dsc)
+arm_copy_unpred (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
{
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: copying unpredictable insn "
the presentation in the ARM ARM. */
static int
-decode_misc_memhint_neon (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+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);
if (op1 == 0x10 && (op2 & 0x2) == 0x0 && (rn & 0xe) == 0x0)
- return copy_unmodified (gdbarch, insn, "cps", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "cps", dsc);
else if (op1 == 0x10 && op2 == 0x0 && (rn & 0xe) == 0x1)
- return copy_unmodified (gdbarch, insn, "setend", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "setend", dsc);
else if ((op1 & 0x60) == 0x20)
- return copy_unmodified (gdbarch, insn, "neon dataproc", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "neon dataproc", dsc);
else if ((op1 & 0x71) == 0x40)
- return copy_unmodified (gdbarch, insn, "neon elt/struct load/store", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "neon elt/struct load/store",
+ dsc);
else if ((op1 & 0x77) == 0x41)
- return copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
else if ((op1 & 0x77) == 0x45)
- return copy_preload (gdbarch, insn, regs, dsc); /* pli. */
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pli. */
else if ((op1 & 0x77) == 0x51)
{
if (rn != 0xf)
- return copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
else
- return copy_unpred (gdbarch, insn, dsc);
+ return arm_copy_unpred (gdbarch, insn, dsc);
}
else if ((op1 & 0x77) == 0x55)
- return copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ return arm_copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
else if (op1 == 0x57)
switch (op2)
{
- case 0x1: return copy_unmodified (gdbarch, insn, "clrex", dsc);
- case 0x4: return copy_unmodified (gdbarch, insn, "dsb", dsc);
- case 0x5: return copy_unmodified (gdbarch, insn, "dmb", dsc);
- case 0x6: return copy_unmodified (gdbarch, insn, "isb", dsc);
- default: return copy_unpred (gdbarch, insn, dsc);
+ 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 copy_unpred (gdbarch, insn, dsc);
+ return arm_copy_unpred (gdbarch, insn, dsc);
else if ((op2 & 0x1) == 0x0)
switch (op1 & ~0x80)
{
case 0x61:
- return copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
case 0x65:
- return copy_preload_reg (gdbarch, insn, regs, dsc); /* pli reg. */
+ return arm_copy_preload_reg (gdbarch, insn, regs, dsc); /* pli reg. */
case 0x71: case 0x75:
/* pld/pldw reg. */
- return copy_preload_reg (gdbarch, insn, regs, dsc);
+ return arm_copy_preload_reg (gdbarch, insn, regs, dsc);
case 0x63: case 0x67: case 0x73: case 0x77:
- return copy_unpred (gdbarch, insn, dsc);
+ return arm_copy_unpred (gdbarch, insn, dsc);
default:
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
else
- return copy_undef (gdbarch, insn, dsc); /* Probably unreachable. */
+ return arm_copy_undef (gdbarch, insn, dsc); /* Probably unreachable. */
}
static int
-decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- struct displaced_step_closure *dsc)
+arm_decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
if (bit (insn, 27) == 0)
- return decode_misc_memhint_neon (gdbarch, insn, regs, dsc);
+ 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 copy_unmodified (gdbarch, insn, "srs", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "srs", dsc);
case 0x1: case 0x3:
- return copy_unmodified (gdbarch, insn, "rfe", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "rfe", dsc);
case 0x4: case 0x5: case 0x6: case 0x7:
- return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+ return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
case 0x8:
switch ((insn & 0xe00000) >> 21)
{
case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
/* stc/stc2. */
- return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
case 0x2:
- return copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
default:
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
case 0x9:
{
case 0x1: case 0x3:
/* ldc/ldc2 imm (undefined for rn == pc). */
- return rn_f ? copy_undef (gdbarch, insn, dsc)
- : copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return rn_f ? arm_copy_undef (gdbarch, insn, dsc)
+ : arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
case 0x2:
- return copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
case 0x4: case 0x5: case 0x6: case 0x7:
/* ldc/ldc2 lit (undefined for rn != pc). */
- return rn_f ? copy_copro_load_store (gdbarch, insn, regs, dsc)
- : copy_undef (gdbarch, insn, dsc);
+ return rn_f ? arm_copy_copro_load_store (gdbarch, insn, regs, dsc)
+ : arm_copy_undef (gdbarch, insn, dsc);
default:
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
}
case 0xa:
- return copy_unmodified (gdbarch, insn, "stc/stc2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "stc/stc2", dsc);
case 0xb:
if (bits (insn, 16, 19) == 0xf)
/* ldc/ldc2 lit. */
- return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0xc:
if (bit (insn, 4))
- return copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
else
- return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
case 0xd:
if (bit (insn, 4))
- return copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
else
- return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
default:
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
}
/* Decode miscellaneous instructions in dp/misc encoding space. */
static int
-decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
- 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)
{
unsigned int op2 = bits (insn, 4, 6);
unsigned int op = bits (insn, 21, 22);
switch (op2)
{
case 0x0:
- return copy_unmodified (gdbarch, insn, "mrs/msr", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mrs/msr", dsc);
case 0x1:
if (op == 0x1) /* bx. */
- return copy_bx_blx_reg (gdbarch, insn, regs, dsc);
+ return arm_copy_bx_blx_reg (gdbarch, insn, regs, dsc);
else if (op == 0x3)
- return copy_unmodified (gdbarch, insn, "clz", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "clz", dsc);
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x2:
if (op == 0x1)
/* Not really supported. */
- return copy_unmodified (gdbarch, insn, "bxj", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "bxj", dsc);
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x3:
if (op == 0x1)
- return copy_bx_blx_reg (gdbarch, insn,
+ return arm_copy_bx_blx_reg (gdbarch, insn,
regs, dsc); /* blx register. */
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x5:
- return copy_unmodified (gdbarch, insn, "saturating add/sub", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "saturating add/sub", dsc);
case 0x7:
if (op == 0x1)
- return copy_unmodified (gdbarch, insn, "bkpt", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "bkpt", dsc);
else if (op == 0x3)
/* Not really supported. */
- return copy_unmodified (gdbarch, insn, "smc", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "smc", dsc);
default:
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
}
static int
-decode_dp_misc (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
- struct displaced_step_closure *dsc)
+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 copy_unmodified (gdbarch, insn, "movw", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "movw", dsc);
case 0x14:
- return copy_unmodified (gdbarch, insn, "movt", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "movt", dsc);
case 0x12: case 0x16:
- return copy_unmodified (gdbarch, insn, "msr imm", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "msr imm", dsc);
default:
- return copy_alu_imm (gdbarch, insn, regs, dsc);
+ return arm_copy_alu_imm (gdbarch, insn, regs, dsc);
}
else
{
uint32_t op1 = bits (insn, 20, 24), op2 = bits (insn, 4, 7);
if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0)
- return copy_alu_reg (gdbarch, insn, regs, dsc);
+ return arm_copy_alu_reg (gdbarch, insn, regs, dsc);
else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
- return copy_alu_shifted_reg (gdbarch, insn, regs, dsc);
+ return arm_copy_alu_shifted_reg (gdbarch, insn, regs, dsc);
else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
- return decode_miscellaneous (gdbarch, insn, regs, dsc);
+ return arm_decode_miscellaneous (gdbarch, insn, regs, dsc);
else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
- return copy_unmodified (gdbarch, insn, "halfword mul/mla", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "halfword mul/mla", dsc);
else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
- return copy_unmodified (gdbarch, insn, "mul/mla", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mul/mla", dsc);
else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
- return copy_unmodified (gdbarch, insn, "synch", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "synch", dsc);
else if (op2 == 0xb || (op2 & 0xd) == 0xd)
/* 2nd arg means "unpriveleged". */
- return copy_extra_ld_st (gdbarch, insn, (op1 & 0x12) == 0x02, regs,
- dsc);
+ return arm_copy_extra_ld_st (gdbarch, insn, (op1 & 0x12) == 0x02, regs,
+ dsc);
}
/* Should be unreachable. */
}
static int
-decode_ld_st_word_ubyte (struct gdbarch *gdbarch, uint32_t insn,
- 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)
{
int a = bit (insn, 25), b = bit (insn, 4);
uint32_t op1 = bits (insn, 20, 24);
if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
|| (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
- return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 0, 0);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 0, 1);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 0, 0);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 0, 1);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 0);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 1);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 0);
+ 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 copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 1);
+ return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 1);
/* Should be unreachable. */
return 1;
}
static int
-decode_media (struct gdbarch *gdbarch, uint32_t insn,
- 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 copy_unmodified (gdbarch, insn, "parallel add/sub signed", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "parallel add/sub signed", dsc);
case 0x04: case 0x05: case 0x06: case 0x07:
- return copy_unmodified (gdbarch, insn, "parallel add/sub unsigned", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "parallel add/sub unsigned", dsc);
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
- return copy_unmodified (gdbarch, insn,
+ return arm_copy_unmodified (gdbarch, insn,
"decode/pack/unpack/saturate/reverse", dsc);
case 0x18:
if (bits (insn, 5, 7) == 0) /* op2. */
{
if (bits (insn, 12, 15) == 0xf)
- return copy_unmodified (gdbarch, insn, "usad8", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "usad8", dsc);
else
- return copy_unmodified (gdbarch, insn, "usada8", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "usada8", dsc);
}
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x1a: case 0x1b:
if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
- return copy_unmodified (gdbarch, insn, "sbfx", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "sbfx", dsc);
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x1c: case 0x1d:
if (bits (insn, 5, 6) == 0x0) /* op2[1:0]. */
{
if (bits (insn, 0, 3) == 0xf)
- return copy_unmodified (gdbarch, insn, "bfc", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "bfc", dsc);
else
- return copy_unmodified (gdbarch, insn, "bfi", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "bfi", dsc);
}
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
case 0x1e: case 0x1f:
if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
- return copy_unmodified (gdbarch, insn, "ubfx", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "ubfx", dsc);
else
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
}
/* Should be unreachable. */
}
static int
-decode_b_bl_ldmstm (struct gdbarch *gdbarch, int32_t insn,
- struct regcache *regs, struct displaced_step_closure *dsc)
+arm_decode_b_bl_ldmstm (struct gdbarch *gdbarch, int32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
if (bit (insn, 25))
- return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+ return arm_copy_b_bl_blx (gdbarch, insn, regs, dsc);
else
- return copy_block_xfer (gdbarch, insn, regs, dsc);
+ return arm_copy_block_xfer (gdbarch, insn, regs, dsc);
}
static int
-decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint32_t insn,
- struct regcache *regs,
- 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);
switch (opcode)
{
case 0x04: case 0x05: /* VFP/Neon mrrc/mcrr. */
- return copy_unmodified (gdbarch, insn, "vfp/neon mrrc/mcrr", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "vfp/neon mrrc/mcrr", dsc);
case 0x08: case 0x0a: case 0x0c: case 0x0e:
case 0x12: case 0x16:
- return copy_unmodified (gdbarch, insn, "vfp/neon vstm/vpush", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "vfp/neon vstm/vpush", dsc);
case 0x09: case 0x0b: case 0x0d: case 0x0f:
case 0x13: case 0x17:
- return copy_unmodified (gdbarch, insn, "vfp/neon vldm/vpop", dsc);
+ 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 copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
}
/* Should be unreachable. */
return 1;
}
+/* Decode shifted register instructions. */
+
static int
-decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
- struct regcache *regs, 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)
{
- 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);
+ /* PC is only allowed to be used in instruction MOV. */
- if ((op1 & 0x20) == 0x00 && (op1 & 0x3a) != 0x00 && (coproc & 0xe) == 0xa)
- return decode_ext_reg_ld_st (gdbarch, insn, regs, dsc);
- else if ((op1 & 0x21) == 0x00 && (op1 & 0x3a) != 0x00
- && (coproc & 0xe) != 0xa)
+ 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. */
+
+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);
+
+ switch (opcode)
+ {
+ 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);
+ }
+
+ /* 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 copy_copro_load_store (gdbarch, insn, regs, dsc);
+ 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 copy_copro_load_store (gdbarch, insn, regs, dsc);
+ return arm_copy_copro_load_store (gdbarch, insn, regs, dsc);
else if ((op1 & 0x3e) == 0x00)
- return copy_undef (gdbarch, insn, dsc);
+ return arm_copy_undef (gdbarch, insn, dsc);
else if ((op1 & 0x3e) == 0x04 && (coproc & 0xe) == 0xa)
- return copy_unmodified (gdbarch, insn, "neon 64bit xfer", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "neon 64bit xfer", dsc);
else if (op1 == 0x04 && (coproc & 0xe) != 0xa)
- return copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
else if (op1 == 0x05 && (coproc & 0xe) != 0xa)
- return copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
else if ((op1 & 0x30) == 0x20 && !op)
{
if ((coproc & 0xe) == 0xa)
- return copy_unmodified (gdbarch, insn, "vfp dataproc", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "vfp dataproc", dsc);
else
- return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
}
else if ((op1 & 0x30) == 0x20 && op)
- return copy_unmodified (gdbarch, insn, "neon 8/16/32 bit xfer", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "neon 8/16/32 bit xfer", dsc);
else if ((op1 & 0x31) == 0x20 && op && (coproc & 0xe) != 0xa)
- return copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
else if ((op1 & 0x31) == 0x21 && op && (coproc & 0xe) != 0xa)
- return copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ return arm_copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
else if ((op1 & 0x30) == 0x30)
- return copy_svc (gdbarch, insn, regs, dsc);
+ return arm_copy_svc (gdbarch, insn, regs, dsc);
else
- return copy_undef (gdbarch, insn, dsc); /* Possibly unreachable. */
+ 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 (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);
+
+ return 0;
}
static void
-thumb_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
- CORE_ADDR to, struct regcache *regs,
- struct displaced_step_closure *dsc)
+install_pc_relative (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, int rd)
{
- error (_("Displaced stepping is only supported in ARM mode"));
+ /* ADR Rd, #imm
+
+ Rewrite as:
+
+ Preparation: Rd <- PC
+ Insn: ADD Rd, #imm
+ Cleanup: Null.
+ */
+
+ /* Rd <- PC */
+ int val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM);
+ displaced_write_reg (regs, dsc, rd, val, CANNOT_WRITE_PC);
}
-void
-arm_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
- CORE_ADDR to, struct regcache *regs,
- struct displaced_step_closure *dsc)
+static int
+thumb_copy_pc_relative_16bit (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc,
+ int rd, unsigned int imm)
{
- int err = 0;
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- uint32_t insn;
- /* Most displaced instructions use a 1-instruction scratch space, so set this
- here and override below if/when necessary. */
- dsc->numinsns = 1;
- dsc->insn_addr = from;
- dsc->scratch_base = to;
- dsc->cleanup = NULL;
- dsc->wrote_to_pc = 0;
+ /* Encoding T2: ADDS Rd, #imm */
+ dsc->modinsn[0] = (0x3000 | (rd << 8) | imm);
- if (!displaced_in_arm_mode (regs))
- return thumb_process_displaced_insn (gdbarch, from, to, regs, dsc);
+ install_pc_relative (gdbarch, regs, dsc, rd);
+
+ return 0;
+}
+
+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);
- dsc->is_thumb = 0;
- dsc->insn_size = 4;
- insn = read_memory_unsigned_integer (from, 4, byte_order_for_code);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: stepping insn %.8lx "
- "at %.8lx\n", (unsigned long) insn,
- (unsigned long) from);
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying thumb adr r%d, #%d insn %.4x\n",
+ rd, imm8, insn);
- if ((insn & 0xf0000000) == 0xf0000000)
- err = decode_unconditional (gdbarch, insn, regs, dsc);
- else switch (((insn & 0x10) >> 4) | ((insn & 0xe000000) >> 24))
+ return thumb_copy_pc_relative_16bit (gdbarch, regs, dsc, rd, imm8);
+}
+
+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. */
+
+ 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);
+
+ if (bit (insn1, 7)) /* Encoding T2 */
{
- case 0x0: case 0x1: case 0x2: case 0x3:
- err = decode_dp_misc (gdbarch, insn, regs, dsc);
- break;
+ /* 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;
- case 0x4: case 0x5: case 0x6:
- err = decode_ld_st_word_ubyte (gdbarch, insn, regs, dsc);
- break;
+ install_pc_relative (gdbarch, regs, dsc, rd);
- case 0x7:
- err = decode_media (gdbarch, insn, dsc);
- break;
+ return 0;
+}
- case 0x8: case 0x9: case 0xa: case 0xb:
- err = decode_b_bl_ldmstm (gdbarch, insn, regs, dsc);
- break;
+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;
- case 0xc: case 0xd: case 0xe: case 0xf:
- err = decode_svc_copro (gdbarch, insn, to, regs, dsc);
- break;
- }
+ /* LDR Rd, #imm8
- if (err)
- internal_error (__FILE__, __LINE__,
- _("arm_process_displaced_insn: Instruction decode error"));
+ 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;
}
-/* Actually set up the scratch space for a displaced instruction. */
+/* Copy Thumb cbnz/cbz insruction. */
-void
-arm_displaced_init_closure (struct gdbarch *gdbarch, CORE_ADDR from,
- CORE_ADDR to, struct displaced_step_closure *dsc)
+static int
+thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, uint16_t insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- 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;
+ 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);
- offset = 0;
- /* Poke modified instruction(s). */
- for (i = 0; i < dsc->numinsns; i++)
+ 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)
{
- if (debug_displaced)
- {
- fprintf_unfiltered (gdb_stdlog, "displaced: writing insn ");
- if (size == 4)
- fprintf_unfiltered (gdb_stdlog, "%.8lx",
- dsc->modinsn[i]);
- else if (size == 2)
- fprintf_unfiltered (gdb_stdlog, "%.4x",
- (unsigned short)dsc->modinsn[i]);
+ dsc->u.branch.cond = INST_AL;
+ dsc->u.branch.dest = from + 4 + imm5;
+ }
+ else
+ dsc->u.branch.dest = from + 2;
- fprintf_unfiltered (gdb_stdlog, " at %.8lx\n",
- (unsigned long) to + offset);
+ dsc->u.branch.link = 0;
+ dsc->u.branch.exchange = 0;
- }
- write_memory_unsigned_integer (to + offset, size,
- byte_order_for_code,
- dsc->modinsn[i]);
- offset += size;
- }
+ 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);
- /* Choose the correct breakpoint instruction. */
- if (dsc->is_thumb)
+ 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)
{
- bkp_insn = tdep->thumb_breakpoint;
- len = tdep->thumb_breakpoint_size;
+ gdb_byte buf[2];
+
+ target_read_memory (rn_val + 2 * rm_val, buf, 2);
+ halfwords = extract_unsigned_integer (buf, 2, byte_order);
}
else
{
- bkp_insn = tdep->arm_breakpoint;
- len = tdep->arm_breakpoint_size;
- }
+ gdb_byte buf[1];
- /* Put breakpoint afterwards. */
- write_memory (to + offset, bkp_insn, len);
+ 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: copy %s->%s: ",
- paddress (gdbarch, from), paddress (gdbarch, to));
-}
+ 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);
-/* Entry point for copying an instruction into scratch space for displaced
- stepping. */
+ 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;
-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);
+ dsc->cleanup = &cleanup_branch;
- return dsc;
+ return 0;
}
-/* Entry point for cleaning things up after a displaced instruction has been
- single-stepped. */
-
-void
-arm_displaced_step_fixup (struct gdbarch *gdbarch,
- struct displaced_step_closure *dsc,
- CORE_ADDR from, CORE_ADDR to,
- struct regcache *regs)
+static void
+cleanup_pop_pc_16bit_all (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- if (dsc->cleanup)
- dsc->cleanup (gdbarch, regs, dsc);
+ /* PC <- r7 */
+ int val = displaced_read_reg (regs, dsc, 7);
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, val, BX_WRITE_PC);
- if (!dsc->wrote_to_pc)
- regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
- dsc->insn_addr + dsc->insn_size);
+ /* 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);
-#include "bfd-in2.h"
-#include "libcoff.h"
+}
static int
-gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info)
+thumb_copy_pop_pc_16bit (struct gdbarch *gdbarch, unsigned short insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- struct gdbarch *gdbarch = info->application_data;
+ dsc->u.block.regmask = insn1 & 0x00ff;
- if (arm_pc_is_thumb (gdbarch, memaddr))
- {
- static asymbol *asym;
- static combined_entry_type ce;
- static struct coff_symbol_struct csym;
- static struct bfd fake_bfd;
- static bfd_target fake_target;
+ /* Rewrite instruction: POP {rX, rY, ...,rZ, PC}
+ to :
- if (csym.native == NULL)
- {
- /* Create a fake symbol vector containing a Thumb symbol.
- This is solely so that the code in print_insn_little_arm()
- and print_insn_big_arm() in opcodes/arm-dis.c will detect
- the presence of a Thumb symbol and switch to decoding
- Thumb instructions. */
+ (1) register list is full, that is, r0-r7 are used.
+ Prepare: tmp[0] <- r8
- fake_target.flavour = bfd_target_coff_flavour;
- fake_bfd.xvec = &fake_target;
- ce.u.syment.n_sclass = C_THUMBEXTFUNC;
- csym.native = &ce;
- csym.symbol.the_bfd = &fake_bfd;
- csym.symbol.name = "fake";
- asym = (asymbol *) & csym;
- }
+ 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.
- memaddr = UNMAKE_THUMB_ADDR (memaddr);
- info->symbols = &asym;
- }
- else
- info->symbols = NULL;
-
- if (info->endian == BFD_ENDIAN_BIG)
- return print_insn_big_arm (memaddr, info);
- else
- return print_insn_little_arm (memaddr, info);
-}
-
-/* The following define instruction sequences that will cause ARM
- cpu's to take an undefined instruction trap. These are used to
- signal a breakpoint to GDB.
-
- The newer ARMv4T cpu's are capable of operating in ARM or Thumb
- modes. A different instruction is required for each mode. The ARM
- cpu's can also be big or little endian. Thus four different
- instructions are needed to support all cases.
-
- Note: ARMv4 defines several new instructions that will take the
- undefined instruction trap. ARM7TDMI is nominally ARMv4T, but does
- not in fact add the new instructions. The new undefined
- instructions in ARMv4 are all instructions that had no defined
- behaviour in earlier chips. There is no guarantee that they will
- raise an exception, but may be treated as NOP's. In practice, it
- may only safe to rely on instructions matching:
-
- 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
- 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- C C C C 0 1 1 x x x x x x x x x x x x x x x x x x x x 1 x x x x
-
- Even this may only true if the condition predicate is true. The
- following use a condition predicate of ALWAYS so it is always TRUE.
-
- There are other ways of forcing a breakpoint. GNU/Linux, RISC iX,
- and NetBSD all use a software interrupt rather than an undefined
- instruction to force a trap. This can be handled by by the
- abi-specific code during establishment of the gdbarch vector. */
-
-#define ARM_LE_BREAKPOINT {0xFE,0xDE,0xFF,0xE7}
-#define ARM_BE_BREAKPOINT {0xE7,0xFF,0xDE,0xFE}
-#define THUMB_LE_BREAKPOINT {0xbe,0xbe}
-#define THUMB_BE_BREAKPOINT {0xbe,0xbe}
+ Cleanup: PC <- r7, r7 <- r8, r8 <-tmp[0]
-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;
+ (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.
-/* 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
- breakpoint should be used. It returns a pointer to a string of
- bytes that encode a breakpoint instruction, stores the length of
- the string to *lenptr, and adjusts the program counter (if
- necessary) to point to the actual memory location where the
- breakpoint should be inserted. */
+ POP {r0, r1, ...., rN};
-static const unsigned char *
-arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ 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 (arm_pc_is_thumb (gdbarch, *pcptr))
+ if (dsc->u.block.regmask == 0xff)
{
- *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+ dsc->tmp[0] = displaced_read_reg (regs, dsc, 8);
- /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
- check whether we are replacing a 32-bit instruction. */
- if (tdep->thumb2_breakpoint != NULL)
- {
- gdb_byte buf[2];
- if (target_read_memory (*pcptr, buf, 2) == 0)
- {
- unsigned short inst1;
- inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
- if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
- {
- *lenptr = tdep->thumb2_breakpoint_size;
- return tdep->thumb2_breakpoint;
- }
- }
- }
+ dsc->modinsn[0] = (insn1 & 0xfeff); /* POP {r0,r1,...,r6, r7} */
+ dsc->modinsn[1] = 0x46b8; /* MOV r8, r7 */
+ dsc->modinsn[2] = 0xbc80; /* POP {r7} */
- *lenptr = tdep->thumb_breakpoint_size;
- return tdep->thumb_breakpoint;
+ dsc->numinsns = 3;
+ dsc->cleanup = &cleanup_pop_pc_16bit_all;
}
else
{
- *lenptr = tdep->arm_breakpoint_size;
- return tdep->arm_breakpoint;
- }
-}
+ 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;
-static void
-arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
- int *kindptr)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ for (i = 0; i < num_in_list + 1; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, dsc, i);
- arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
+ new_regmask = (1 << (num_in_list + 1)) - 1;
- if (arm_pc_is_thumb (gdbarch, *pcptr) && *kindptr == 4)
- /* The documented magic value for a 32-bit Thumb-2 breakpoint, so
- that this is not confused with a 32-bit ARM breakpoint. */
- *kindptr = 3;
-}
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: POP "
+ "{..., pc}: original reg list %.4x,"
+ " modified list %.4x\n"),
+ (int) dsc->u.block.regmask, new_regmask);
-/* Extract from an array REGBUF containing the (raw) register state a
- function return value of type TYPE, and copy that, in virtual
- format, into VALBUF. */
+ dsc->u.block.regmask |= 0x8000;
+ dsc->u.block.writeback = 0;
+ dsc->u.block.cond = INST_AL;
+
+ dsc->modinsn[0] = (insn1 & ~0x1ff) | (new_regmask & 0xff);
+
+ dsc->cleanup = &cleanup_block_load_pc;
+ }
+
+ return 0;
+}
static void
-arm_extract_return_value (struct type *type, struct regcache *regs,
- gdb_byte *valbuf)
+thumb_process_displaced_16bit_insn (struct gdbarch *gdbarch, uint16_t insn1,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- struct gdbarch *gdbarch = get_regcache_arch (regs);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ unsigned short op_bit_12_15 = bits (insn1, 12, 15);
+ unsigned short op_bit_10_11 = bits (insn1, 10, 11);
+ int err = 0;
- if (TYPE_CODE_FLT == TYPE_CODE (type))
+ /* 16-bit thumb instructions. */
+ switch (op_bit_12_15)
{
- switch (gdbarch_tdep (gdbarch)->fp_model)
+ /* Shift (imme), add, subtract, move and compare. */
+ case 0: case 1: case 2: case 3:
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1,
+ "shift/add/sub/mov/cmp",
+ dsc);
+ break;
+ case 4:
+ switch (op_bit_10_11)
{
- case ARM_FLOAT_FPA:
+ case 0: /* Data-processing */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1,
+ "data-processing",
+ dsc);
+ break;
+ case 1: /* Special data instructions and branch and exchange. */
{
- /* The value is in register F0 in internal format. We need to
- extract the raw value and then convert it to the desired
- internal type. */
- bfd_byte tmpbuf[FP_REGISTER_SIZE];
-
- regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf);
- convert_from_extended (floatformat_from_type (type), tmpbuf,
- valbuf, gdbarch_byte_order (gdbarch));
+ unsigned short op = bits (insn1, 7, 9);
+ if (op == 6 || op == 7) /* BX or BLX */
+ err = thumb_copy_bx_blx_reg (gdbarch, insn1, regs, dsc);
+ else if (bits (insn1, 6, 7) != 0) /* ADD/MOV/CMP high registers. */
+ err = thumb_copy_alu_reg (gdbarch, insn1, regs, dsc);
+ else
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "special data",
+ dsc);
}
break;
-
- case ARM_FLOAT_SOFT_FPA:
- case ARM_FLOAT_SOFT_VFP:
- /* ARM_FLOAT_VFP can arise if this is a variadic function so
- not using the VFP ABI code. */
- case ARM_FLOAT_VFP:
- regcache_cooked_read (regs, ARM_A1_REGNUM, valbuf);
- if (TYPE_LENGTH (type) > 4)
- regcache_cooked_read (regs, ARM_A1_REGNUM + 1,
- valbuf + INT_REGISTER_SIZE);
- break;
-
- default:
- internal_error (__FILE__, __LINE__,
- _("arm_extract_return_value: "
- "Floating point model not supported"));
- break;
+ default: /* LDR (literal) */
+ err = thumb_copy_16bit_ldr_literal (gdbarch, insn1, regs, dsc);
}
+ break;
+ case 5: case 6: case 7: case 8: case 9: /* Load/Store single data item */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "ldr/str", dsc);
+ break;
+ case 10:
+ if (op_bit_10_11 < 2) /* Generate PC-relative address */
+ err = thumb_decode_pc_relative_16bit (gdbarch, insn1, regs, dsc);
+ else /* Generate SP-relative address */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "sp-relative", dsc);
+ break;
+ case 11: /* Misc 16-bit instructions */
+ {
+ switch (bits (insn1, 8, 11))
+ {
+ case 1: case 3: case 9: case 11: /* CBNZ, CBZ */
+ err = thumb_copy_cbnz_cbz (gdbarch, insn1, regs, dsc);
+ break;
+ case 12: case 13: /* POP */
+ if (bit (insn1, 8)) /* PC is in register list. */
+ err = thumb_copy_pop_pc_16bit (gdbarch, insn1, regs, dsc);
+ else
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "pop", dsc);
+ break;
+ case 15: /* If-Then, and hints */
+ if (bits (insn1, 0, 3))
+ /* If-Then makes up to four following instructions conditional.
+ IT instruction itself is not conditional, so handle it as a
+ common unmodified instruction. */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "If-Then",
+ dsc);
+ else
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "hints", dsc);
+ break;
+ default:
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "misc", dsc);
+ }
+ }
+ break;
+ case 12:
+ if (op_bit_10_11 < 2) /* Store multiple registers */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "stm", dsc);
+ else /* Load multiple registers */
+ err = thumb_copy_unmodified_16bit (gdbarch, insn1, "ldm", dsc);
+ break;
+ case 13: /* Conditional branch and supervisor call */
+ if (bits (insn1, 9, 11) != 7) /* conditional branch */
+ err = thumb_copy_b (gdbarch, insn1, dsc);
+ else
+ err = thumb_copy_svc (gdbarch, insn1, regs, dsc);
+ break;
+ case 14: /* Unconditional branch */
+ err = thumb_copy_b (gdbarch, insn1, dsc);
+ break;
+ default:
+ err = 1;
}
- else if (TYPE_CODE (type) == TYPE_CODE_INT
- || TYPE_CODE (type) == TYPE_CODE_CHAR
- || TYPE_CODE (type) == TYPE_CODE_BOOL
- || TYPE_CODE (type) == TYPE_CODE_PTR
- || TYPE_CODE (type) == TYPE_CODE_REF
- || TYPE_CODE (type) == TYPE_CODE_ENUM)
- {
- /* If the type is a plain integer, then the access is
- straight-forward. Otherwise we have to play around a bit
- more. */
- int len = TYPE_LENGTH (type);
- int regno = ARM_A1_REGNUM;
- ULONGEST tmp;
- while (len > 0)
+ if (err)
+ internal_error (__FILE__, __LINE__,
+ _("thumb_process_displaced_16bit_insn: Instruction decode error"));
+}
+
+static int
+decode_thumb_32bit_ld_mem_hints (struct gdbarch *gdbarch,
+ uint16_t insn1, uint16_t insn2,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ 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))
+ {
+ case 0: /* Load byte and memory hints */
+ if (rt == 0xf) /* PLD/PLI */
{
- /* By using store_unsigned_integer we avoid having to do
- anything special for small big-endian values. */
- regcache_cooked_read_unsigned (regs, regno++, &tmp);
- store_unsigned_integer (valbuf,
- (len > INT_REGISTER_SIZE
- ? INT_REGISTER_SIZE : len),
- byte_order, tmp);
- len -= INT_REGISTER_SIZE;
- valbuf += INT_REGISTER_SIZE;
+ if (rn == 0xf)
+ /* PLD literal or Encoding T3 of PLI(immediate, literal). */
+ return thumb2_copy_preload (gdbarch, insn1, insn2, regs, dsc);
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "pli/pld", dsc);
+ }
+ else
+ {
+ if (rn == 0xf) /* LDRB/LDRSB (literal) */
+ return thumb2_copy_load_literal (gdbarch, insn1, insn2, regs, dsc,
+ 1);
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "ldrb{reg, immediate}/ldrbt",
+ dsc);
}
- }
- else
- {
- /* For a structure or union the behaviour is as if the value had
- been stored to word-aligned memory and then loaded into
- registers with 32-bit load instruction(s). */
- int len = TYPE_LENGTH (type);
- int regno = ARM_A1_REGNUM;
- bfd_byte tmpbuf[INT_REGISTER_SIZE];
- while (len > 0)
+ break;
+ case 1: /* Load halfword and memory hints. */
+ if (rt == 0xf) /* PLD{W} and Unalloc memory hint. */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "pld/unalloc memhint", dsc);
+ else
{
- regcache_cooked_read (regs, regno++, tmpbuf);
- memcpy (valbuf, tmpbuf,
- len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len);
- len -= INT_REGISTER_SIZE;
- valbuf += INT_REGISTER_SIZE;
+ if (rn == 0xf)
+ return thumb2_copy_load_literal (gdbarch, insn1, insn2, regs, dsc,
+ 2);
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "ldrh/ldrht", dsc);
}
+ break;
+ case 2: /* Load word */
+ {
+ int insn2_bit_8_11 = bits (insn2, 8, 11);
+
+ if (rn == 0xf)
+ return thumb2_copy_load_literal (gdbarch, insn1, insn2, regs, dsc, 4);
+ else if (op1 == 0x1) /* Encoding T3 */
+ return thumb2_copy_load_reg_imm (gdbarch, insn1, insn2, regs, dsc,
+ 0, 1);
+ else /* op1 == 0x0 */
+ {
+ if (insn2_bit_8_11 == 0xc || (insn2_bit_8_11 & 0x9) == 0x9)
+ /* LDR (immediate) */
+ return thumb2_copy_load_reg_imm (gdbarch, insn1, insn2, regs,
+ dsc, bit (insn2, 8), 1);
+ else if (insn2_bit_8_11 == 0xe) /* LDRT */
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "ldrt", dsc);
+ else
+ /* LDR (register) */
+ return thumb2_copy_load_reg_imm (gdbarch, insn1, insn2, regs,
+ dsc, 0, 0);
+ }
+ break;
+ }
+ default:
+ return thumb_32bit_copy_undef (gdbarch, insn1, insn2, dsc);
+ break;
}
+ return 0;
}
-
-/* Will a function return an aggregate type in memory or in a
- register? Return 0 if an aggregate type can be returned in a
- register, 1 if it must be returned in memory. */
-
-static int
-arm_return_in_memory (struct gdbarch *gdbarch, struct type *type)
+static void
+thumb_process_displaced_32bit_insn (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- 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.
-
- 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. */
+ int err = 0;
+ unsigned short op = bit (insn2, 15);
+ unsigned int op1 = bits (insn1, 11, 12);
- /* All aggregate types that won't fit in a register must be returned
- in memory. */
- if (TYPE_LENGTH (type) > INT_REGISTER_SIZE)
+ switch (op1)
{
- return 1;
- }
-
- /* 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))
- {
- return 1;
- }
-
- /* 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
- */
+ case 1:
+ {
+ switch (bits (insn1, 9, 10))
+ {
+ case 0:
+ if (bit (insn1, 6))
+ {
+ /* Load/store {dual, execlusive}, table branch. */
+ if (bits (insn1, 7, 8) == 1 && bits (insn1, 4, 5) == 1
+ && bits (insn2, 5, 7) == 0)
+ err = thumb2_copy_table_branch (gdbarch, insn1, insn2, regs,
+ dsc);
+ else
+ /* PC is not allowed to use in load/store {dual, exclusive}
+ instructions. */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "load/store dual/ex", dsc);
+ }
+ else /* load/store multiple */
+ {
+ switch (bits (insn1, 7, 8))
+ {
+ case 0: case 3: /* SRS, RFE */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "srs/rfe", dsc);
+ break;
+ case 1: case 2: /* LDM/STM/PUSH/POP */
+ err = thumb2_copy_block_xfer (gdbarch, insn1, insn2, regs, dsc);
+ break;
+ }
+ }
+ break;
- for (i = 0; i < TYPE_NFIELDS (type); i++)
+ case 1:
+ /* Data-processing (shift register). */
+ err = thumb2_decode_dp_shift_reg (gdbarch, insn1, insn2, regs,
+ dsc);
+ break;
+ default: /* Coprocessor instructions. */
+ err = thumb2_decode_svc_copro (gdbarch, insn1, insn2, regs, dsc);
+ break;
+ }
+ break;
+ }
+ case 2: /* op1 = 2 */
+ if (op) /* Branch and misc control. */
{
- 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)
+ if (bit (insn2, 14) /* BLX/BL */
+ || bit (insn2, 12) /* Unconditional branch */
+ || (bits (insn1, 7, 9) != 0x7)) /* Conditional branch */
+ err = thumb2_copy_b_bl_blx (gdbarch, insn1, insn2, regs, dsc);
+ else
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "misc ctrl", dsc);
+ }
+ else
+ {
+ if (bit (insn1, 9)) /* Data processing (plain binary imm). */
{
- nRc = 1;
- break;
+ int op = bits (insn1, 4, 8);
+ int rn = bits (insn1, 0, 3);
+ if ((op == 0 || op == 0xa) && rn == 0xf)
+ err = thumb_copy_pc_relative_32bit (gdbarch, insn1, insn2,
+ regs, dsc);
+ else
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "dp/pb", dsc);
}
-
- /* If bitpos != 0, then we have to care about it. */
- if (TYPE_FIELD_BITPOS (type, i) != 0)
+ else /* Data processing (modified immeidate) */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "dp/mi", dsc);
+ }
+ break;
+ case 3: /* op1 = 3 */
+ switch (bits (insn1, 9, 10))
+ {
+ case 0:
+ if (bit (insn1, 4))
+ err = decode_thumb_32bit_ld_mem_hints (gdbarch, insn1, insn2,
+ regs, dsc);
+ else /* NEON Load/Store and Store single data item */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "neon elt/struct load/store",
+ dsc);
+ break;
+ case 1: /* op1 = 3, bits (9, 10) == 1 */
+ switch (bits (insn1, 7, 8))
{
- /* 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;
- }
+ case 0: case 1: /* Data processing (register) */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "dp(reg)", dsc);
+ break;
+ case 2: /* Multiply and absolute difference */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "mul/mua/diff", dsc);
+ break;
+ case 3: /* Long multiply and divide */
+ err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "lmul/lmua", dsc);
+ break;
}
+ break;
+ default: /* Coprocessor instructions */
+ err = thumb2_decode_svc_copro (gdbarch, insn1, insn2, regs, dsc);
+ break;
}
+ break;
+ default:
+ err = 1;
}
- return nRc;
-}
+ if (err)
+ internal_error (__FILE__, __LINE__,
+ _("thumb_process_displaced_32bit_insn: Instruction decode error"));
-/* Write into appropriate registers a function return value of type
- TYPE, given in virtual format. */
+}
static void
-arm_store_return_value (struct type *type, struct regcache *regs,
- const gdb_byte *valbuf)
+thumb_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
+ CORE_ADDR to, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- struct gdbarch *gdbarch = get_regcache_arch (regs);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ uint16_t insn1
+ = read_memory_unsigned_integer (from, 2, byte_order_for_code);
- if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: process thumb insn %.4x "
+ "at %.8lx\n", insn1, (unsigned long) from);
+
+ dsc->is_thumb = 1;
+ dsc->insn_size = thumb_insn_size (insn1);
+ if (thumb_insn_size (insn1) == 4)
{
- char buf[MAX_REGISTER_SIZE];
+ uint16_t insn2
+ = read_memory_unsigned_integer (from + 2, 2, byte_order_for_code);
+ thumb_process_displaced_32bit_insn (gdbarch, insn1, insn2, regs, dsc);
+ }
+ else
+ thumb_process_displaced_16bit_insn (gdbarch, insn1, regs, dsc);
+}
- switch (gdbarch_tdep (gdbarch)->fp_model)
- {
- case ARM_FLOAT_FPA:
+void
+arm_process_displaced_insn (struct gdbarch *gdbarch, CORE_ADDR from,
+ CORE_ADDR to, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ int err = 0;
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ uint32_t insn;
- convert_to_extended (floatformat_from_type (type), buf, valbuf,
- gdbarch_byte_order (gdbarch));
- regcache_cooked_write (regs, ARM_F0_REGNUM, buf);
- break;
+ /* Most displaced instructions use a 1-instruction scratch space, so set this
+ here and override below if/when necessary. */
+ dsc->numinsns = 1;
+ dsc->insn_addr = from;
+ dsc->scratch_base = to;
+ dsc->cleanup = NULL;
+ dsc->wrote_to_pc = 0;
- case ARM_FLOAT_SOFT_FPA:
- case ARM_FLOAT_SOFT_VFP:
- /* ARM_FLOAT_VFP can arise if this is a variadic function so
- not using the VFP ABI code. */
- case ARM_FLOAT_VFP:
- regcache_cooked_write (regs, ARM_A1_REGNUM, valbuf);
- if (TYPE_LENGTH (type) > 4)
- regcache_cooked_write (regs, ARM_A1_REGNUM + 1,
- valbuf + INT_REGISTER_SIZE);
- break;
+ if (!displaced_in_arm_mode (regs))
+ return thumb_process_displaced_insn (gdbarch, from, to, regs, dsc);
- default:
- internal_error (__FILE__, __LINE__,
- _("arm_store_return_value: Floating "
- "point model not supported"));
- break;
- }
- }
- else if (TYPE_CODE (type) == TYPE_CODE_INT
- || TYPE_CODE (type) == TYPE_CODE_CHAR
- || TYPE_CODE (type) == TYPE_CODE_BOOL
- || TYPE_CODE (type) == TYPE_CODE_PTR
- || TYPE_CODE (type) == TYPE_CODE_REF
- || TYPE_CODE (type) == TYPE_CODE_ENUM)
+ dsc->is_thumb = 0;
+ dsc->insn_size = 4;
+ insn = read_memory_unsigned_integer (from, 4, byte_order_for_code);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: stepping insn %.8lx "
+ "at %.8lx\n", (unsigned long) insn,
+ (unsigned long) from);
+
+ if ((insn & 0xf0000000) == 0xf0000000)
+ err = arm_decode_unconditional (gdbarch, insn, regs, dsc);
+ else switch (((insn & 0x10) >> 4) | ((insn & 0xe000000) >> 24))
{
- if (TYPE_LENGTH (type) <= 4)
- {
- /* Values of one word or less are zero/sign-extended and
- returned in r0. */
- bfd_byte tmpbuf[INT_REGISTER_SIZE];
- LONGEST val = unpack_long (type, valbuf);
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ err = arm_decode_dp_misc (gdbarch, insn, regs, dsc);
+ break;
- store_signed_integer (tmpbuf, INT_REGISTER_SIZE, byte_order, val);
- regcache_cooked_write (regs, ARM_A1_REGNUM, tmpbuf);
- }
- else
- {
- /* Integral values greater than one word are stored in consecutive
- registers starting with r0. This will always be a multiple of
- the regiser size. */
- int len = TYPE_LENGTH (type);
- int regno = ARM_A1_REGNUM;
+ case 0x4: case 0x5: case 0x6:
+ err = arm_decode_ld_st_word_ubyte (gdbarch, insn, regs, dsc);
+ break;
- while (len > 0)
- {
- regcache_cooked_write (regs, regno++, valbuf);
- len -= INT_REGISTER_SIZE;
- valbuf += INT_REGISTER_SIZE;
- }
- }
- }
- else
- {
- /* For a structure or union the behaviour is as if the value had
- been stored to word-aligned memory and then loaded into
- registers with 32-bit load instruction(s). */
- int len = TYPE_LENGTH (type);
- int regno = ARM_A1_REGNUM;
- bfd_byte tmpbuf[INT_REGISTER_SIZE];
+ case 0x7:
+ err = arm_decode_media (gdbarch, insn, dsc);
+ break;
- while (len > 0)
- {
- memcpy (tmpbuf, valbuf,
- len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len);
- regcache_cooked_write (regs, regno++, tmpbuf);
- len -= INT_REGISTER_SIZE;
- valbuf += INT_REGISTER_SIZE;
- }
+ case 0x8: case 0x9: case 0xa: case 0xb:
+ err = arm_decode_b_bl_ldmstm (gdbarch, insn, regs, dsc);
+ break;
+
+ case 0xc: case 0xd: case 0xe: case 0xf:
+ err = arm_decode_svc_copro (gdbarch, insn, to, regs, dsc);
+ break;
}
-}
+ if (err)
+ internal_error (__FILE__, __LINE__,
+ _("arm_process_displaced_insn: Instruction decode error"));
+}
-/* Handle function return values. */
+/* Actually set up the scratch space for a displaced instruction. */
-static enum return_value_convention
-arm_return_value (struct gdbarch *gdbarch, struct type *func_type,
- struct type *valtype, struct regcache *regcache,
- gdb_byte *readbuf, const gdb_byte *writebuf)
+void
+arm_displaced_init_closure (struct gdbarch *gdbarch, CORE_ADDR from,
+ CORE_ADDR to, struct displaced_step_closure *dsc)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- enum arm_vfp_cprc_base_type vfp_base_type;
- int vfp_base_count;
+ 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 gdb_byte *bkp_insn;
- if (arm_vfp_abi_for_function (gdbarch, func_type)
- && arm_vfp_call_candidate (valtype, &vfp_base_type, &vfp_base_count))
+ offset = 0;
+ /* Poke modified instruction(s). */
+ for (i = 0; i < dsc->numinsns; i++)
{
- int reg_char = arm_vfp_cprc_reg_char (vfp_base_type);
- int unit_length = arm_vfp_cprc_unit_length (vfp_base_type);
- int i;
- for (i = 0; i < vfp_base_count; i++)
+ if (debug_displaced)
{
- if (reg_char == 'q')
- {
- if (writebuf)
- arm_neon_quad_write (gdbarch, regcache, i,
- writebuf + i * unit_length);
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing insn ");
+ if (size == 4)
+ fprintf_unfiltered (gdb_stdlog, "%.8lx",
+ dsc->modinsn[i]);
+ else if (size == 2)
+ fprintf_unfiltered (gdb_stdlog, "%.4x",
+ (unsigned short)dsc->modinsn[i]);
- if (readbuf)
- arm_neon_quad_read (gdbarch, regcache, i,
- readbuf + i * unit_length);
- }
- else
- {
- char name_buf[4];
- int regnum;
+ fprintf_unfiltered (gdb_stdlog, " at %.8lx\n",
+ (unsigned long) to + offset);
- sprintf (name_buf, "%c%d", reg_char, i);
- regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
- if (writebuf)
- regcache_cooked_write (regcache, regnum,
- writebuf + i * unit_length);
- if (readbuf)
- regcache_cooked_read (regcache, regnum,
- readbuf + i * unit_length);
- }
}
- return RETURN_VALUE_REGISTER_CONVENTION;
+ write_memory_unsigned_integer (to + offset, size,
+ byte_order_for_code,
+ dsc->modinsn[i]);
+ offset += size;
}
- if (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
- || TYPE_CODE (valtype) == TYPE_CODE_UNION
- || TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
+ /* Choose the correct breakpoint instruction. */
+ if (dsc->is_thumb)
{
- if (tdep->struct_return == pcc_struct_return
- || arm_return_in_memory (gdbarch, valtype))
- return RETURN_VALUE_STRUCT_CONVENTION;
+ bkp_insn = tdep->thumb_breakpoint;
+ len = tdep->thumb_breakpoint_size;
+ }
+ else
+ {
+ bkp_insn = tdep->arm_breakpoint;
+ len = tdep->arm_breakpoint_size;
}
- if (writebuf)
- arm_store_return_value (valtype, regcache, writebuf);
-
- if (readbuf)
- arm_extract_return_value (valtype, regcache, readbuf);
+ /* Put breakpoint afterwards. */
+ write_memory (to + offset, bkp_insn, len);
- return RETURN_VALUE_REGISTER_CONVENTION;
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ",
+ paddress (gdbarch, from), paddress (gdbarch, to));
}
+/* Entry point for copying an instruction into scratch space for displaced
+ stepping. */
-static int
-arm_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+struct displaced_step_closure *
+arm_displaced_step_copy_insn (struct gdbarch *gdbarch,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
{
- struct gdbarch *gdbarch = get_frame_arch (frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- CORE_ADDR jb_addr;
- char buf[INT_REGISTER_SIZE];
-
- jb_addr = get_frame_register_unsigned (frame, ARM_A1_REGNUM);
-
- if (target_read_memory (jb_addr + tdep->jb_pc * tdep->jb_elt_size, buf,
- INT_REGISTER_SIZE))
- return 0;
+ 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);
- *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE, byte_order);
- return 1;
+ return dsc;
}
-/* Recognize GCC and GNU ld's trampolines. If we are in a trampoline,
- return the target PC. Otherwise return 0. */
+/* Entry point for cleaning things up after a displaced instruction has been
+ single-stepped. */
-CORE_ADDR
-arm_skip_stub (struct frame_info *frame, CORE_ADDR pc)
+void
+arm_displaced_step_fixup (struct gdbarch *gdbarch,
+ struct displaced_step_closure *dsc,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
{
- char *name;
- int namelen;
- CORE_ADDR start_addr;
+ if (dsc->cleanup)
+ dsc->cleanup (gdbarch, regs, dsc);
- /* 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;
+ if (!dsc->wrote_to_pc)
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM,
+ dsc->insn_addr + dsc->insn_size);
- /* 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)
- {
- /* Use the name suffix to determine which register contains the
- target PC. */
- static char *table[15] =
- {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
- "r8", "r9", "sl", "fp", "ip", "sp", "lr"
- };
- int regno;
- int offset = strlen (name) - 2;
+}
- for (regno = 0; regno <= 14; regno++)
- if (strcmp (&name[offset], table[regno]) == 0)
- return get_frame_register_unsigned (frame, regno);
- }
+#include "bfd-in2.h"
+#include "libcoff.h"
- /* GNU ld generates __foo_from_arm or __foo_from_thumb for
- non-interworking calls to foo. We could decode the stubs
- to find the target but it's easier to use the symbol table. */
- 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)
- || (namelen > 2 + strlen ("_from_arm")
- && strncmp (name + namelen - strlen ("_from_arm"), "_from_arm",
- strlen ("_from_arm")) == 0)))
+static int
+gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info)
+{
+ struct gdbarch *gdbarch = info->application_data;
+
+ if (arm_pc_is_thumb (gdbarch, memaddr))
{
- char *target_name;
- int target_len = namelen - 2;
- struct minimal_symbol *minsym;
- struct objfile *objfile;
- struct obj_section *sec;
+ static asymbol *asym;
+ static combined_entry_type ce;
+ static struct coff_symbol_struct csym;
+ static struct bfd fake_bfd;
+ static bfd_target fake_target;
- if (name[namelen - 1] == 'b')
- target_len -= strlen ("_from_thumb");
- else
- target_len -= strlen ("_from_arm");
+ if (csym.native == NULL)
+ {
+ /* Create a fake symbol vector containing a Thumb symbol.
+ This is solely so that the code in print_insn_little_arm()
+ and print_insn_big_arm() in opcodes/arm-dis.c will detect
+ the presence of a Thumb symbol and switch to decoding
+ Thumb instructions. */
- target_name = alloca (target_len + 1);
- memcpy (target_name, name + 2, target_len);
- target_name[target_len] = '\0';
+ fake_target.flavour = bfd_target_coff_flavour;
+ fake_bfd.xvec = &fake_target;
+ ce.u.syment.n_sclass = C_THUMBEXTFUNC;
+ csym.native = &ce;
+ csym.symbol.the_bfd = &fake_bfd;
+ csym.symbol.name = "fake";
+ asym = (asymbol *) & csym;
+ }
- 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);
- else
- return 0;
+ memaddr = UNMAKE_THUMB_ADDR (memaddr);
+ info->symbols = &asym;
}
+ else
+ info->symbols = NULL;
- return 0; /* not a stub */
-}
-
-static void
-set_arm_command (char *args, int from_tty)
-{
- printf_unfiltered (_("\
-\"set arm\" must be followed by an apporpriate subcommand.\n"));
- help_list (setarmcmdlist, "set arm ", all_commands, gdb_stdout);
-}
-
-static void
-show_arm_command (char *args, int from_tty)
-{
- cmd_show_list (showarmcmdlist, from_tty, "");
+ if (info->endian == BFD_ENDIAN_BIG)
+ return print_insn_big_arm (memaddr, info);
+ else
+ return print_insn_little_arm (memaddr, info);
}
-static void
-arm_update_current_architecture (void)
-{
- struct gdbarch_info info;
+/* The following define instruction sequences that will cause ARM
+ cpu's to take an undefined instruction trap. These are used to
+ signal a breakpoint to GDB.
+
+ The newer ARMv4T cpu's are capable of operating in ARM or Thumb
+ modes. A different instruction is required for each mode. The ARM
+ cpu's can also be big or little endian. Thus four different
+ instructions are needed to support all cases.
+
+ Note: ARMv4 defines several new instructions that will take the
+ undefined instruction trap. ARM7TDMI is nominally ARMv4T, but does
+ not in fact add the new instructions. The new undefined
+ instructions in ARMv4 are all instructions that had no defined
+ behaviour in earlier chips. There is no guarantee that they will
+ raise an exception, but may be treated as NOP's. In practice, it
+ may only safe to rely on instructions matching:
+
+ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ C C C C 0 1 1 x x x x x x x x x x x x x x x x x x x x 1 x x x x
+
+ Even this may only true if the condition predicate is true. The
+ following use a condition predicate of ALWAYS so it is always TRUE.
+
+ There are other ways of forcing a breakpoint. GNU/Linux, RISC iX,
+ and NetBSD all use a software interrupt rather than an undefined
+ instruction to force a trap. This can be handled by by the
+ abi-specific code during establishment of the gdbarch vector. */
- /* If the current architecture is not ARM, we have nothing to do. */
- if (gdbarch_bfd_arch_info (target_gdbarch)->arch != bfd_arch_arm)
- return;
+#define ARM_LE_BREAKPOINT {0xFE,0xDE,0xFF,0xE7}
+#define ARM_BE_BREAKPOINT {0xE7,0xFF,0xDE,0xFE}
+#define THUMB_LE_BREAKPOINT {0xbe,0xbe}
+#define THUMB_BE_BREAKPOINT {0xbe,0xbe}
- /* Update the architecture. */
- gdbarch_info_init (&info);
+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;
- if (!gdbarch_update_p (info))
- internal_error (__FILE__, __LINE__, _("could not update architecture"));
-}
+/* 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
+ breakpoint should be used. It returns a pointer to a string of
+ bytes that encode a breakpoint instruction, stores the length of
+ the string to *lenptr, and adjusts the program counter (if
+ necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
-static void
-set_fp_model_sfunc (char *args, int from_tty,
- struct cmd_list_element *c)
+static const unsigned char *
+arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
{
- enum arm_float_model fp_model;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- 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;
- break;
- }
+ if (arm_pc_is_thumb (gdbarch, *pcptr))
+ {
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
- if (fp_model == ARM_FLOAT_LAST)
- internal_error (__FILE__, __LINE__, _("Invalid fp model accepted: %s."),
- current_fp_model);
+ /* If we have a separate 32-bit breakpoint instruction for Thumb-2,
+ check whether we are replacing a 32-bit instruction. */
+ if (tdep->thumb2_breakpoint != NULL)
+ {
+ gdb_byte buf[2];
+ if (target_read_memory (*pcptr, buf, 2) == 0)
+ {
+ unsigned short inst1;
+ inst1 = extract_unsigned_integer (buf, 2, byte_order_for_code);
+ if (thumb_insn_size (inst1) == 4)
+ {
+ *lenptr = tdep->thumb2_breakpoint_size;
+ return tdep->thumb2_breakpoint;
+ }
+ }
+ }
- arm_update_current_architecture ();
+ *lenptr = tdep->thumb_breakpoint_size;
+ return tdep->thumb_breakpoint;
+ }
+ else
+ {
+ *lenptr = tdep->arm_breakpoint_size;
+ return tdep->arm_breakpoint;
+ }
}
static void
-show_fp_model (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
+arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
+ int *kindptr)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
+ arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
- if (arm_fp_model == ARM_FLOAT_AUTO
- && gdbarch_bfd_arch_info (target_gdbarch)->arch == bfd_arch_arm)
- fprintf_filtered (file, _("\
-The current ARM floating point model is \"auto\" (currently \"%s\").\n"),
- fp_model_strings[tdep->fp_model]);
- else
- fprintf_filtered (file, _("\
-The current ARM floating point model is \"%s\".\n"),
- fp_model_strings[arm_fp_model]);
+ if (arm_pc_is_thumb (gdbarch, *pcptr) && *kindptr == 4)
+ /* The documented magic value for a 32-bit Thumb-2 breakpoint, so
+ that this is not confused with a 32-bit ARM breakpoint. */
+ *kindptr = 3;
}
+/* Extract from an array REGBUF containing the (raw) register state a
+ function return value of type TYPE, and copy that, in virtual
+ format, into VALBUF. */
+
static void
-arm_set_abi (char *args, int from_tty,
- struct cmd_list_element *c)
+arm_extract_return_value (struct type *type, struct regcache *regs,
+ gdb_byte *valbuf)
{
- enum arm_abi_kind arm_abi;
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- 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;
- break;
- }
+ if (TYPE_CODE_FLT == TYPE_CODE (type))
+ {
+ switch (gdbarch_tdep (gdbarch)->fp_model)
+ {
+ case ARM_FLOAT_FPA:
+ {
+ /* The value is in register F0 in internal format. We need to
+ extract the raw value and then convert it to the desired
+ internal type. */
+ bfd_byte tmpbuf[FP_REGISTER_SIZE];
- if (arm_abi == ARM_ABI_LAST)
- internal_error (__FILE__, __LINE__, _("Invalid ABI accepted: %s."),
- arm_abi_string);
+ regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf);
+ convert_from_extended (floatformat_from_type (type), tmpbuf,
+ valbuf, gdbarch_byte_order (gdbarch));
+ }
+ break;
- arm_update_current_architecture ();
-}
+ case ARM_FLOAT_SOFT_FPA:
+ case ARM_FLOAT_SOFT_VFP:
+ /* ARM_FLOAT_VFP can arise if this is a variadic function so
+ not using the VFP ABI code. */
+ case ARM_FLOAT_VFP:
+ regcache_cooked_read (regs, ARM_A1_REGNUM, valbuf);
+ if (TYPE_LENGTH (type) > 4)
+ regcache_cooked_read (regs, ARM_A1_REGNUM + 1,
+ valbuf + INT_REGISTER_SIZE);
+ break;
-static void
-arm_show_abi (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("arm_extract_return_value: "
+ "Floating point model not supported"));
+ break;
+ }
+ }
+ else if (TYPE_CODE (type) == TYPE_CODE_INT
+ || TYPE_CODE (type) == TYPE_CODE_CHAR
+ || TYPE_CODE (type) == TYPE_CODE_BOOL
+ || TYPE_CODE (type) == TYPE_CODE_PTR
+ || TYPE_CODE (type) == TYPE_CODE_REF
+ || TYPE_CODE (type) == TYPE_CODE_ENUM)
+ {
+ /* If the type is a plain integer, then the access is
+ straight-forward. Otherwise we have to play around a bit
+ more. */
+ int len = TYPE_LENGTH (type);
+ int regno = ARM_A1_REGNUM;
+ ULONGEST tmp;
- if (arm_abi_global == ARM_ABI_AUTO
- && gdbarch_bfd_arch_info (target_gdbarch)->arch == bfd_arch_arm)
- fprintf_filtered (file, _("\
-The current ARM ABI is \"auto\" (currently \"%s\").\n"),
- arm_abi_strings[tdep->arm_abi]);
+ while (len > 0)
+ {
+ /* By using store_unsigned_integer we avoid having to do
+ anything special for small big-endian values. */
+ regcache_cooked_read_unsigned (regs, regno++, &tmp);
+ store_unsigned_integer (valbuf,
+ (len > INT_REGISTER_SIZE
+ ? INT_REGISTER_SIZE : len),
+ byte_order, tmp);
+ len -= INT_REGISTER_SIZE;
+ valbuf += INT_REGISTER_SIZE;
+ }
+ }
else
- fprintf_filtered (file, _("The current ARM ABI is \"%s\".\n"),
- arm_abi_string);
+ {
+ /* For a structure or union the behaviour is as if the value had
+ been stored to word-aligned memory and then loaded into
+ registers with 32-bit load instruction(s). */
+ int len = TYPE_LENGTH (type);
+ int regno = ARM_A1_REGNUM;
+ bfd_byte tmpbuf[INT_REGISTER_SIZE];
+
+ while (len > 0)
+ {
+ regcache_cooked_read (regs, regno++, tmpbuf);
+ memcpy (valbuf, tmpbuf,
+ len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len);
+ len -= INT_REGISTER_SIZE;
+ valbuf += INT_REGISTER_SIZE;
+ }
+ }
}
-static void
-arm_show_fallback_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 "
- "(when symbols are unavailable) is \"%s\".\n"),
- arm_fallback_mode_string);
-}
+/* Will a function return an aggregate type in memory or in a
+ register? Return 0 if an aggregate type can be returned in a
+ register, 1 if it must be returned in memory. */
-static void
-arm_show_force_mode (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
+static int
+arm_return_in_memory (struct gdbarch *gdbarch, struct type *type)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
+ int nRc;
+ enum type_code code;
- fprintf_filtered (file,
- _("The current execution mode assumed "
- "(even when symbols are available) is \"%s\".\n"),
- arm_force_mode_string);
-}
+ CHECK_TYPEDEF (type);
-/* If the user changes the register disassembly style used for info
- register and other commands, we have to also switch the style used
- in opcodes for disassembly output. This function is run in the "set
- arm disassembly" command, and does that. */
+ /* 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.
-static void
-set_disassembly_style_sfunc (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- set_disassembly_style ();
-}
-\f
-/* Return the ARM register name corresponding to register I. */
-static const char *
-arm_register_name (struct gdbarch *gdbarch, int i)
-{
- const int num_regs = gdbarch_num_regs (gdbarch);
+ This function is based on the behaviour of GCC 2.95.1.
+ See: gcc/arm.c: arm_return_in_memory() for details.
- if (gdbarch_tdep (gdbarch)->have_vfp_pseudos
- && i >= num_regs && i < num_regs + 32)
- {
- static const char *const vfp_pseudo_names[] = {
- "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
- "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
- "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
- "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
- };
+ 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. */
- return vfp_pseudo_names[i - num_regs];
+ /* 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 (gdbarch_tdep (gdbarch)->have_neon_pseudos
- && i >= num_regs + 32 && i < num_regs + 32 + 16)
- {
- static const char *const neon_pseudo_names[] = {
- "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
- "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15",
- };
+ /* 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;
- return neon_pseudo_names[i - num_regs - 32];
+ /* 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))
+ {
+ return 1;
}
- if (i >= ARRAY_SIZE (arm_register_names))
- /* These registers are only supported on targets which supply
- an XML description. */
- return "";
-
- return arm_register_names[i];
-}
+ /* Assume all other aggregate types can be returned in a register.
+ Run a check for structures, unions and arrays. */
+ nRc = 0;
-static void
-set_disassembly_style (void)
-{
- int current;
+ 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. */
- /* Find the style that the user wants. */
- for (current = 0; current < num_disassembly_options; current++)
- if (disassembly_style == valid_disassembly_styles[current])
- break;
- gdb_assert (current < num_disassembly_options);
+ /* 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
+ */
- /* Synchronize the disassembler. */
- set_arm_regname_option (current);
-}
+ 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)));
-/* Test whether the coff symbol specific value corresponds to a Thumb
- function. */
+ /* Is it a floating point type field? */
+ if (field_type_code == TYPE_CODE_FLT)
+ {
+ nRc = 1;
+ break;
+ }
-static int
-coff_sym_is_thumb (int val)
-{
- return (val == C_THUMBEXT
- || val == C_THUMBSTAT
- || val == C_THUMBEXTFUNC
- || val == C_THUMBSTATFUNC
- || val == C_THUMBLABEL);
-}
+ /* 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;
+ }
+ }
+ }
+ }
-/* arm_coff_make_msymbol_special()
- arm_elf_make_msymbol_special()
-
- These functions test whether the COFF or ELF symbol corresponds to
- an address in thumb code, and set a "special" bit in a minimal
- symbol to indicate that it does. */
-
-static void
-arm_elf_make_msymbol_special(asymbol *sym, struct minimal_symbol *msym)
-{
- if (ARM_SYM_BRANCH_TYPE (&((elf_symbol_type *)sym)->internal_elf_sym)
- == ST_BRANCH_TO_THUMB)
- MSYMBOL_SET_SPECIAL (msym);
+ return nRc;
}
-static void
-arm_coff_make_msymbol_special(int val, struct minimal_symbol *msym)
-{
- if (coff_sym_is_thumb (val))
- MSYMBOL_SET_SPECIAL (msym);
-}
+/* Write into appropriate registers a function return value of type
+ TYPE, given in virtual format. */
static void
-arm_objfile_data_free (struct objfile *objfile, void *arg)
+arm_store_return_value (struct type *type, struct regcache *regs,
+ const gdb_byte *valbuf)
{
- struct arm_per_objfile *data = arg;
- unsigned int i;
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- for (i = 0; i < objfile->obfd->section_count; i++)
- VEC_free (arm_mapping_symbol_s, data->section_maps[i]);
-}
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ {
+ gdb_byte buf[MAX_REGISTER_SIZE];
-static void
-arm_record_special_symbol (struct gdbarch *gdbarch, struct objfile *objfile,
- asymbol *sym)
-{
- const char *name = bfd_asymbol_name (sym);
- struct arm_per_objfile *data;
- VEC(arm_mapping_symbol_s) **map_p;
- struct arm_mapping_symbol new_map_sym;
+ switch (gdbarch_tdep (gdbarch)->fp_model)
+ {
+ case ARM_FLOAT_FPA:
- gdb_assert (name[0] == '$');
- if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
- return;
+ convert_to_extended (floatformat_from_type (type), buf, valbuf,
+ gdbarch_byte_order (gdbarch));
+ regcache_cooked_write (regs, ARM_F0_REGNUM, buf);
+ break;
- data = objfile_data (objfile, arm_objfile_data_key);
- if (data == NULL)
- {
- data = OBSTACK_ZALLOC (&objfile->objfile_obstack,
- struct arm_per_objfile);
- set_objfile_data (objfile, arm_objfile_data_key, data);
- data->section_maps = OBSTACK_CALLOC (&objfile->objfile_obstack,
- objfile->obfd->section_count,
- VEC(arm_mapping_symbol_s) *);
+ case ARM_FLOAT_SOFT_FPA:
+ case ARM_FLOAT_SOFT_VFP:
+ /* ARM_FLOAT_VFP can arise if this is a variadic function so
+ not using the VFP ABI code. */
+ case ARM_FLOAT_VFP:
+ regcache_cooked_write (regs, ARM_A1_REGNUM, valbuf);
+ if (TYPE_LENGTH (type) > 4)
+ regcache_cooked_write (regs, ARM_A1_REGNUM + 1,
+ valbuf + INT_REGISTER_SIZE);
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("arm_store_return_value: Floating "
+ "point model not supported"));
+ break;
+ }
}
- map_p = &data->section_maps[bfd_get_section (sym)->index];
+ else if (TYPE_CODE (type) == TYPE_CODE_INT
+ || TYPE_CODE (type) == TYPE_CODE_CHAR
+ || TYPE_CODE (type) == TYPE_CODE_BOOL
+ || TYPE_CODE (type) == TYPE_CODE_PTR
+ || TYPE_CODE (type) == TYPE_CODE_REF
+ || TYPE_CODE (type) == TYPE_CODE_ENUM)
+ {
+ if (TYPE_LENGTH (type) <= 4)
+ {
+ /* Values of one word or less are zero/sign-extended and
+ returned in r0. */
+ bfd_byte tmpbuf[INT_REGISTER_SIZE];
+ LONGEST val = unpack_long (type, valbuf);
- new_map_sym.value = sym->value;
- new_map_sym.type = name[1];
+ store_signed_integer (tmpbuf, INT_REGISTER_SIZE, byte_order, val);
+ regcache_cooked_write (regs, ARM_A1_REGNUM, tmpbuf);
+ }
+ else
+ {
+ /* Integral values greater than one word are stored in consecutive
+ registers starting with r0. This will always be a multiple of
+ the regiser size. */
+ int len = TYPE_LENGTH (type);
+ int regno = ARM_A1_REGNUM;
- /* Assume that most mapping symbols appear in order of increasing
- value. If they were randomly distributed, it would be faster to
- always push here and then sort at first use. */
- if (!VEC_empty (arm_mapping_symbol_s, *map_p))
+ while (len > 0)
+ {
+ regcache_cooked_write (regs, regno++, valbuf);
+ len -= INT_REGISTER_SIZE;
+ valbuf += INT_REGISTER_SIZE;
+ }
+ }
+ }
+ else
{
- struct arm_mapping_symbol *prev_map_sym;
+ /* For a structure or union the behaviour is as if the value had
+ been stored to word-aligned memory and then loaded into
+ registers with 32-bit load instruction(s). */
+ int len = TYPE_LENGTH (type);
+ int regno = ARM_A1_REGNUM;
+ bfd_byte tmpbuf[INT_REGISTER_SIZE];
- prev_map_sym = VEC_last (arm_mapping_symbol_s, *map_p);
- if (prev_map_sym->value >= sym->value)
+ while (len > 0)
{
- unsigned int idx;
- idx = VEC_lower_bound (arm_mapping_symbol_s, *map_p, &new_map_sym,
- arm_compare_mapping_symbols);
- VEC_safe_insert (arm_mapping_symbol_s, *map_p, idx, &new_map_sym);
- return;
+ memcpy (tmpbuf, valbuf,
+ len > INT_REGISTER_SIZE ? INT_REGISTER_SIZE : len);
+ regcache_cooked_write (regs, regno++, tmpbuf);
+ len -= INT_REGISTER_SIZE;
+ valbuf += INT_REGISTER_SIZE;
}
}
-
- VEC_safe_push (arm_mapping_symbol_s, *map_p, &new_map_sym);
}
-static void
-arm_write_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- regcache_cooked_write_unsigned (regcache, ARM_PC_REGNUM, pc);
-
- /* If necessary, set the T bit. */
- if (arm_apcs_32)
- {
- ULONGEST val, t_bit;
- regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
- t_bit = arm_psr_thumb_bit (gdbarch);
- if (arm_pc_is_thumb (gdbarch, pc))
- regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
- val | t_bit);
- else
- regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
- val & ~t_bit);
- }
-}
-/* Read the contents of a NEON quad register, by reading from two
- double registers. This is used to implement the quad pseudo
- registers, and for argument passing in case the quad registers are
- missing; vectors are passed in quad registers when using the VFP
- ABI, even if a NEON unit is not present. REGNUM is the index of
- the quad register, in [0, 15]. */
+/* Handle function return values. */
-static enum register_status
-arm_neon_quad_read (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, gdb_byte *buf)
+static enum return_value_convention
+arm_return_value (struct gdbarch *gdbarch, struct value *function,
+ struct type *valtype, struct regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
{
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
- enum register_status status;
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ struct type *func_type = function ? value_type (function) : NULL;
+ enum arm_vfp_cprc_base_type vfp_base_type;
+ int vfp_base_count;
- sprintf (name_buf, "d%d", regnum << 1);
- double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
+ if (arm_vfp_abi_for_function (gdbarch, func_type)
+ && arm_vfp_call_candidate (valtype, &vfp_base_type, &vfp_base_count))
+ {
+ int reg_char = arm_vfp_cprc_reg_char (vfp_base_type);
+ int unit_length = arm_vfp_cprc_unit_length (vfp_base_type);
+ int i;
+ for (i = 0; i < vfp_base_count; i++)
+ {
+ if (reg_char == 'q')
+ {
+ if (writebuf)
+ arm_neon_quad_write (gdbarch, regcache, i,
+ writebuf + i * unit_length);
- /* d0 is always the least significant half of q0. */
- if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
- offset = 8;
- else
- offset = 0;
-
- status = regcache_raw_read (regcache, double_regnum, reg_buf);
- if (status != REG_VALID)
- return status;
- memcpy (buf + offset, reg_buf, 8);
-
- offset = 8 - offset;
- status = regcache_raw_read (regcache, double_regnum + 1, reg_buf);
- if (status != REG_VALID)
- return status;
- memcpy (buf + offset, reg_buf, 8);
-
- return REG_VALID;
-}
-
-static enum register_status
-arm_pseudo_read (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, gdb_byte *buf)
-{
- const int num_regs = gdbarch_num_regs (gdbarch);
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
+ if (readbuf)
+ arm_neon_quad_read (gdbarch, regcache, i,
+ readbuf + i * unit_length);
+ }
+ else
+ {
+ char name_buf[4];
+ int regnum;
- gdb_assert (regnum >= num_regs);
- regnum -= num_regs;
+ xsnprintf (name_buf, sizeof (name_buf), "%c%d", reg_char, i);
+ regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+ if (writebuf)
+ regcache_cooked_write (regcache, regnum,
+ writebuf + i * unit_length);
+ if (readbuf)
+ regcache_cooked_read (regcache, regnum,
+ readbuf + i * unit_length);
+ }
+ }
+ return RETURN_VALUE_REGISTER_CONVENTION;
+ }
- if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
- /* Quad-precision register. */
- return arm_neon_quad_read (gdbarch, regcache, regnum - 32, buf);
- else
+ if (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
+ || TYPE_CODE (valtype) == TYPE_CODE_UNION
+ || TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
{
- enum register_status status;
+ if (tdep->struct_return == pcc_struct_return
+ || arm_return_in_memory (gdbarch, valtype))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+ }
- /* Single-precision register. */
- gdb_assert (regnum < 32);
+ /* 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;
- /* s0 is always the least significant half of d0. */
- if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
- offset = (regnum & 1) ? 0 : 4;
- else
- offset = (regnum & 1) ? 4 : 0;
+ if (writebuf)
+ arm_store_return_value (valtype, regcache, writebuf);
- sprintf (name_buf, "d%d", regnum >> 1);
- double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
+ if (readbuf)
+ arm_extract_return_value (valtype, regcache, readbuf);
- status = regcache_raw_read (regcache, double_regnum, reg_buf);
- if (status == REG_VALID)
- memcpy (buf, reg_buf + offset, 4);
- return status;
- }
+ return RETURN_VALUE_REGISTER_CONVENTION;
}
-/* Store the contents of BUF to a NEON quad register, by writing to
- two double registers. This is used to implement the quad pseudo
- registers, and for argument passing in case the quad registers are
- missing; vectors are passed in quad registers when using the VFP
- ABI, even if a NEON unit is not present. REGNUM is the index
- of the quad register, in [0, 15]. */
-static void
-arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, const gdb_byte *buf)
+static int
+arm_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
{
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
-
- sprintf (name_buf, "d%d", regnum << 1);
- double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR jb_addr;
+ gdb_byte buf[INT_REGISTER_SIZE];
+
+ jb_addr = get_frame_register_unsigned (frame, ARM_A1_REGNUM);
- /* d0 is always the least significant half of q0. */
- if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
- offset = 8;
- else
- offset = 0;
+ if (target_read_memory (jb_addr + tdep->jb_pc * tdep->jb_elt_size, buf,
+ INT_REGISTER_SIZE))
+ return 0;
- regcache_raw_write (regcache, double_regnum, buf + offset);
- offset = 8 - offset;
- regcache_raw_write (regcache, double_regnum + 1, buf + offset);
+ *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE, byte_order);
+ return 1;
}
-static void
-arm_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, const gdb_byte *buf)
+/* Recognize GCC and GNU ld's trampolines. If we are in a trampoline,
+ return the target PC. Otherwise return 0. */
+
+CORE_ADDR
+arm_skip_stub (struct frame_info *frame, CORE_ADDR pc)
{
- const int num_regs = gdbarch_num_regs (gdbarch);
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
+ const char *name;
+ int namelen;
+ CORE_ADDR start_addr;
- gdb_assert (regnum >= num_regs);
- regnum -= num_regs;
+ /* 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;
- if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
- /* Quad-precision register. */
- arm_neon_quad_write (gdbarch, regcache, regnum - 32, buf);
- else
+ /* 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)
{
- /* Single-precision register. */
- gdb_assert (regnum < 32);
+ /* Use the name suffix to determine which register contains the
+ target PC. */
+ static char *table[15] =
+ {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "sl", "fp", "ip", "sp", "lr"
+ };
+ int regno;
+ int offset = strlen (name) - 2;
- /* s0 is always the least significant half of d0. */
- if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
- offset = (regnum & 1) ? 0 : 4;
+ for (regno = 0; regno <= 14; regno++)
+ if (strcmp (&name[offset], table[regno]) == 0)
+ return get_frame_register_unsigned (frame, regno);
+ }
+
+ /* GNU ld generates __foo_from_arm or __foo_from_thumb for
+ non-interworking calls to foo. We could decode the stubs
+ to find the target but it's easier to use the symbol table. */
+ 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)
+ || (namelen > 2 + strlen ("_from_arm")
+ && strncmp (name + namelen - strlen ("_from_arm"), "_from_arm",
+ strlen ("_from_arm")) == 0)))
+ {
+ char *target_name;
+ int target_len = namelen - 2;
+ struct minimal_symbol *minsym;
+ struct objfile *objfile;
+ struct obj_section *sec;
+
+ if (name[namelen - 1] == 'b')
+ target_len -= strlen ("_from_thumb");
else
- offset = (regnum & 1) ? 4 : 0;
+ target_len -= strlen ("_from_arm");
- sprintf (name_buf, "d%d", regnum >> 1);
- double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
+ target_name = alloca (target_len + 1);
+ memcpy (target_name, name + 2, target_len);
+ target_name[target_len] = '\0';
- regcache_raw_read (regcache, double_regnum, reg_buf);
- memcpy (reg_buf + offset, buf, 4);
- regcache_raw_write (regcache, double_regnum, reg_buf);
+ 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);
+ else
+ return 0;
}
+
+ return 0; /* not a stub */
}
-static struct value *
-value_of_arm_user_reg (struct frame_info *frame, const void *baton)
+static void
+set_arm_command (char *args, int from_tty)
{
- const int *reg_p = baton;
- return value_of_register (*reg_p, frame);
+ printf_unfiltered (_("\
+\"set arm\" must be followed by an apporpriate subcommand.\n"));
+ help_list (setarmcmdlist, "set arm ", all_commands, gdb_stdout);
}
-\f
-static enum gdb_osabi
-arm_elf_osabi_sniffer (bfd *abfd)
+
+static void
+show_arm_command (char *args, int from_tty)
{
- unsigned int elfosabi;
- enum gdb_osabi osabi = GDB_OSABI_UNKNOWN;
+ cmd_show_list (showarmcmdlist, from_tty, "");
+}
- elfosabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
+static void
+arm_update_current_architecture (void)
+{
+ struct gdbarch_info info;
- if (elfosabi == ELFOSABI_ARM)
- /* GNU tools use this value. Check note sections in this case,
- as well. */
- bfd_map_over_sections (abfd,
- generic_elf_osabi_sniff_abi_tag_sections,
- &osabi);
+ /* If the current architecture is not ARM, we have nothing to do. */
+ if (gdbarch_bfd_arch_info (target_gdbarch ())->arch != bfd_arch_arm)
+ return;
- /* Anything else will be handled by the generic ELF sniffer. */
- return osabi;
+ /* Update the architecture. */
+ gdbarch_info_init (&info);
+
+ if (!gdbarch_update_p (info))
+ internal_error (__FILE__, __LINE__, _("could not update architecture"));
}
-static int
-arm_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
- struct reggroup *group)
+static void
+set_fp_model_sfunc (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- /* FPS register's type is INT, but belongs to float_reggroup. Beside
- this, FPS register belongs to save_regroup, restore_reggroup, and
- all_reggroup, of course. */
- if (regnum == ARM_FPS_REGNUM)
- return (group == float_reggroup
- || group == save_reggroup
- || group == restore_reggroup
- || group == all_reggroup);
+ enum arm_float_model 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;
+ break;
+ }
+
+ if (fp_model == ARM_FLOAT_LAST)
+ internal_error (__FILE__, __LINE__, _("Invalid fp model accepted: %s."),
+ current_fp_model);
+
+ arm_update_current_architecture ();
+}
+
+static void
+show_fp_model (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch ());
+
+ if (arm_fp_model == ARM_FLOAT_AUTO
+ && gdbarch_bfd_arch_info (target_gdbarch ())->arch == bfd_arch_arm)
+ fprintf_filtered (file, _("\
+The current ARM floating point model is \"auto\" (currently \"%s\").\n"),
+ fp_model_strings[tdep->fp_model]);
else
- return default_register_reggroup_p (gdbarch, regnum, group);
+ fprintf_filtered (file, _("\
+The current ARM floating point model is \"%s\".\n"),
+ fp_model_strings[arm_fp_model]);
+}
+
+static void
+arm_set_abi (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ enum arm_abi_kind 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;
+ break;
+ }
+
+ if (arm_abi == ARM_ABI_LAST)
+ internal_error (__FILE__, __LINE__, _("Invalid ABI accepted: %s."),
+ arm_abi_string);
+
+ arm_update_current_architecture ();
+}
+
+static void
+arm_show_abi (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch ());
+
+ if (arm_abi_global == ARM_ABI_AUTO
+ && gdbarch_bfd_arch_info (target_gdbarch ())->arch == bfd_arch_arm)
+ fprintf_filtered (file, _("\
+The current ARM ABI is \"auto\" (currently \"%s\").\n"),
+ arm_abi_strings[tdep->arm_abi]);
+ else
+ fprintf_filtered (file, _("The current ARM ABI is \"%s\".\n"),
+ arm_abi_string);
+}
+
+static void
+arm_show_fallback_mode (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file,
+ _("The current execution mode assumed "
+ "(when symbols are unavailable) is \"%s\".\n"),
+ arm_fallback_mode_string);
+}
+
+static void
+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"),
+ arm_force_mode_string);
}
+/* If the user changes the register disassembly style used for info
+ register and other commands, we have to also switch the style used
+ in opcodes for disassembly output. This function is run in the "set
+ arm disassembly" command, and does that. */
+
+static void
+set_disassembly_style_sfunc (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ set_disassembly_style ();
+}
\f
-/* Initialize the current architecture based on INFO. If possible,
- re-use an architecture from ARCHES, which is a list of
- architectures already created during this debugging session.
+/* Return the ARM register name corresponding to register I. */
+static const char *
+arm_register_name (struct gdbarch *gdbarch, int i)
+{
+ const int num_regs = gdbarch_num_regs (gdbarch);
- Called e.g. at program startup, when reading a core file, and when
- reading a binary file. */
+ if (gdbarch_tdep (gdbarch)->have_vfp_pseudos
+ && i >= num_regs && i < num_regs + 32)
+ {
+ static const char *const vfp_pseudo_names[] = {
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
+ "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
+ "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
+ };
-static struct gdbarch *
-arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+ return vfp_pseudo_names[i - num_regs];
+ }
+
+ if (gdbarch_tdep (gdbarch)->have_neon_pseudos
+ && i >= num_regs + 32 && i < num_regs + 32 + 16)
+ {
+ static const char *const neon_pseudo_names[] = {
+ "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
+ "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15",
+ };
+
+ return neon_pseudo_names[i - num_regs - 32];
+ }
+
+ if (i >= ARRAY_SIZE (arm_register_names))
+ /* These registers are only supported on targets which supply
+ an XML description. */
+ return "";
+
+ return arm_register_names[i];
+}
+
+static void
+set_disassembly_style (void)
{
- struct gdbarch_tdep *tdep;
- struct gdbarch *gdbarch;
- struct gdbarch_list *best_arch;
- enum arm_abi_kind arm_abi = arm_abi_global;
- 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 have_neon = 0;
- int have_fpa_registers = 1;
- const struct target_desc *tdesc = info.target_desc;
+ int current;
+
+ /* Find the style that the user wants. */
+ for (current = 0; current < num_disassembly_options; current++)
+ if (disassembly_style == valid_disassembly_styles[current])
+ break;
+ gdb_assert (current < num_disassembly_options);
+
+ /* Synchronize the disassembler. */
+ set_arm_regname_option (current);
+}
+
+/* Test whether the coff symbol specific value corresponds to a Thumb
+ function. */
+
+static int
+coff_sym_is_thumb (int val)
+{
+ return (val == C_THUMBEXT
+ || val == C_THUMBSTAT
+ || val == C_THUMBEXTFUNC
+ || val == C_THUMBSTATFUNC
+ || val == C_THUMBLABEL);
+}
+
+/* arm_coff_make_msymbol_special()
+ arm_elf_make_msymbol_special()
+
+ These functions test whether the COFF or ELF symbol corresponds to
+ an address in thumb code, and set a "special" bit in a minimal
+ symbol to indicate that it does. */
+
+static void
+arm_elf_make_msymbol_special(asymbol *sym, struct minimal_symbol *msym)
+{
+ if (ARM_SYM_BRANCH_TYPE (&((elf_symbol_type *)sym)->internal_elf_sym)
+ == ST_BRANCH_TO_THUMB)
+ MSYMBOL_SET_SPECIAL (msym);
+}
+
+static void
+arm_coff_make_msymbol_special(int val, struct minimal_symbol *msym)
+{
+ if (coff_sym_is_thumb (val))
+ MSYMBOL_SET_SPECIAL (msym);
+}
+
+static void
+arm_objfile_data_free (struct objfile *objfile, void *arg)
+{
+ struct arm_per_objfile *data = arg;
+ unsigned int i;
+
+ for (i = 0; i < objfile->obfd->section_count; i++)
+ VEC_free (arm_mapping_symbol_s, data->section_maps[i]);
+}
+
+static void
+arm_record_special_symbol (struct gdbarch *gdbarch, struct objfile *objfile,
+ asymbol *sym)
+{
+ const char *name = bfd_asymbol_name (sym);
+ struct arm_per_objfile *data;
+ VEC(arm_mapping_symbol_s) **map_p;
+ struct arm_mapping_symbol new_map_sym;
+
+ gdb_assert (name[0] == '$');
+ if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
+ return;
+
+ data = objfile_data (objfile, arm_objfile_data_key);
+ if (data == NULL)
+ {
+ data = OBSTACK_ZALLOC (&objfile->objfile_obstack,
+ struct arm_per_objfile);
+ set_objfile_data (objfile, arm_objfile_data_key, data);
+ data->section_maps = OBSTACK_CALLOC (&objfile->objfile_obstack,
+ objfile->obfd->section_count,
+ VEC(arm_mapping_symbol_s) *);
+ }
+ map_p = &data->section_maps[bfd_get_section (sym)->index];
+
+ new_map_sym.value = sym->value;
+ new_map_sym.type = name[1];
+
+ /* Assume that most mapping symbols appear in order of increasing
+ value. If they were randomly distributed, it would be faster to
+ always push here and then sort at first use. */
+ if (!VEC_empty (arm_mapping_symbol_s, *map_p))
+ {
+ struct arm_mapping_symbol *prev_map_sym;
+
+ prev_map_sym = VEC_last (arm_mapping_symbol_s, *map_p);
+ if (prev_map_sym->value >= sym->value)
+ {
+ unsigned int idx;
+ idx = VEC_lower_bound (arm_mapping_symbol_s, *map_p, &new_map_sym,
+ arm_compare_mapping_symbols);
+ VEC_safe_insert (arm_mapping_symbol_s, *map_p, idx, &new_map_sym);
+ return;
+ }
+ }
+
+ VEC_safe_push (arm_mapping_symbol_s, *map_p, &new_map_sym);
+}
+
+static void
+arm_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ regcache_cooked_write_unsigned (regcache, ARM_PC_REGNUM, pc);
+
+ /* If necessary, set the T bit. */
+ if (arm_apcs_32)
+ {
+ ULONGEST val, t_bit;
+ regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
+ t_bit = arm_psr_thumb_bit (gdbarch);
+ if (arm_pc_is_thumb (gdbarch, pc))
+ regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
+ val | t_bit);
+ else
+ regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
+ val & ~t_bit);
+ }
+}
+
+/* Read the contents of a NEON quad register, by reading from two
+ double registers. This is used to implement the quad pseudo
+ registers, and for argument passing in case the quad registers are
+ missing; vectors are passed in quad registers when using the VFP
+ ABI, even if a NEON unit is not present. REGNUM is the index of
+ the quad register, in [0, 15]. */
+
+static enum register_status
+arm_neon_quad_read (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, gdb_byte *buf)
+{
+ char name_buf[4];
+ gdb_byte reg_buf[8];
+ int offset, double_regnum;
+ enum register_status status;
+
+ xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum << 1);
+ double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+
+ /* d0 is always the least significant half of q0. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ offset = 8;
+ else
+ offset = 0;
+
+ status = regcache_raw_read (regcache, double_regnum, reg_buf);
+ if (status != REG_VALID)
+ return status;
+ memcpy (buf + offset, reg_buf, 8);
+
+ offset = 8 - offset;
+ status = regcache_raw_read (regcache, double_regnum + 1, reg_buf);
+ if (status != REG_VALID)
+ return status;
+ memcpy (buf + offset, reg_buf, 8);
+
+ return REG_VALID;
+}
+
+static enum register_status
+arm_pseudo_read (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, gdb_byte *buf)
+{
+ const int num_regs = gdbarch_num_regs (gdbarch);
+ char name_buf[4];
+ gdb_byte reg_buf[8];
+ int offset, double_regnum;
+
+ gdb_assert (regnum >= num_regs);
+ regnum -= num_regs;
+
+ if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
+ /* Quad-precision register. */
+ return arm_neon_quad_read (gdbarch, regcache, regnum - 32, buf);
+ else
+ {
+ enum register_status status;
+
+ /* Single-precision register. */
+ gdb_assert (regnum < 32);
+
+ /* s0 is always the least significant half of d0. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ offset = (regnum & 1) ? 0 : 4;
+ else
+ offset = (regnum & 1) ? 4 : 0;
+
+ xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum >> 1);
+ double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+
+ status = regcache_raw_read (regcache, double_regnum, reg_buf);
+ if (status == REG_VALID)
+ memcpy (buf, reg_buf + offset, 4);
+ return status;
+ }
+}
+
+/* Store the contents of BUF to a NEON quad register, by writing to
+ two double registers. This is used to implement the quad pseudo
+ registers, and for argument passing in case the quad registers are
+ missing; vectors are passed in quad registers when using the VFP
+ ABI, even if a NEON unit is not present. REGNUM is the index
+ of the quad register, in [0, 15]. */
+
+static void
+arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, const gdb_byte *buf)
+{
+ char name_buf[4];
+ int offset, double_regnum;
+
+ xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum << 1);
+ double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+
+ /* d0 is always the least significant half of q0. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ offset = 8;
+ else
+ offset = 0;
+
+ regcache_raw_write (regcache, double_regnum, buf + offset);
+ offset = 8 - offset;
+ regcache_raw_write (regcache, double_regnum + 1, buf + offset);
+}
+
+static void
+arm_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
+ int regnum, const gdb_byte *buf)
+{
+ const int num_regs = gdbarch_num_regs (gdbarch);
+ char name_buf[4];
+ gdb_byte reg_buf[8];
+ int offset, double_regnum;
+
+ gdb_assert (regnum >= num_regs);
+ regnum -= num_regs;
+
+ if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
+ /* Quad-precision register. */
+ arm_neon_quad_write (gdbarch, regcache, regnum - 32, buf);
+ else
+ {
+ /* Single-precision register. */
+ gdb_assert (regnum < 32);
+
+ /* s0 is always the least significant half of d0. */
+ if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ offset = (regnum & 1) ? 0 : 4;
+ else
+ offset = (regnum & 1) ? 4 : 0;
+
+ xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum >> 1);
+ double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+
+ regcache_raw_read (regcache, double_regnum, reg_buf);
+ memcpy (reg_buf + offset, buf, 4);
+ regcache_raw_write (regcache, double_regnum, reg_buf);
+ }
+}
+
+static struct value *
+value_of_arm_user_reg (struct frame_info *frame, const void *baton)
+{
+ const int *reg_p = baton;
+ return value_of_register (*reg_p, frame);
+}
+\f
+static enum gdb_osabi
+arm_elf_osabi_sniffer (bfd *abfd)
+{
+ unsigned int elfosabi;
+ enum gdb_osabi osabi = GDB_OSABI_UNKNOWN;
+
+ elfosabi = elf_elfheader (abfd)->e_ident[EI_OSABI];
+
+ if (elfosabi == ELFOSABI_ARM)
+ /* GNU tools use this value. Check note sections in this case,
+ as well. */
+ bfd_map_over_sections (abfd,
+ generic_elf_osabi_sniff_abi_tag_sections,
+ &osabi);
+
+ /* Anything else will be handled by the generic ELF sniffer. */
+ return osabi;
+}
+
+static int
+arm_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+ struct reggroup *group)
+{
+ /* FPS register's type is INT, but belongs to float_reggroup. Beside
+ this, FPS register belongs to save_regroup, restore_reggroup, and
+ all_reggroup, of course. */
+ if (regnum == ARM_FPS_REGNUM)
+ return (group == float_reggroup
+ || group == save_reggroup
+ || group == restore_reggroup
+ || group == all_reggroup);
+ else
+ return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+\f
+/* For backward-compatibility we allow two 'g' packet lengths with
+ the remote protocol depending on whether FPA registers are
+ supplied. M-profile targets do not have FPA registers, but some
+ stubs already exist in the wild which use a 'g' packet which
+ supplies them albeit with dummy values. The packet format which
+ includes FPA registers should be considered deprecated for
+ M-profile targets. */
+
+static void
+arm_register_g_packet_guesses (struct gdbarch *gdbarch)
+{
+ if (gdbarch_tdep (gdbarch)->is_m)
+ {
+ /* If we know from the executable this is an M-profile target,
+ cater for remote targets whose register set layout is the
+ same as the FPA layout. */
+ register_remote_g_packet_guess (gdbarch,
+ /* r0-r12,sp,lr,pc; f0-f7; fps,xpsr */
+ (16 * INT_REGISTER_SIZE)
+ + (8 * FP_REGISTER_SIZE)
+ + (2 * INT_REGISTER_SIZE),
+ tdesc_arm_with_m_fpa_layout);
+
+ /* The regular M-profile layout. */
+ register_remote_g_packet_guess (gdbarch,
+ /* r0-r12,sp,lr,pc; xpsr */
+ (16 * INT_REGISTER_SIZE)
+ + INT_REGISTER_SIZE,
+ tdesc_arm_with_m);
+
+ /* M-profile plus M4F VFP. */
+ register_remote_g_packet_guess (gdbarch,
+ /* r0-r12,sp,lr,pc; d0-d15; fpscr,xpsr */
+ (16 * INT_REGISTER_SIZE)
+ + (16 * VFP_REGISTER_SIZE)
+ + (2 * INT_REGISTER_SIZE),
+ tdesc_arm_with_m_vfp_d16);
+ }
+
+ /* Otherwise we don't have a useful guess. */
+}
+
+\f
+/* Initialize the current architecture based on INFO. If possible,
+ re-use an architecture from ARCHES, which is a list of
+ architectures already created during this debugging session.
+
+ Called e.g. at program startup, when reading a core file, and when
+ reading a binary file. */
+
+static struct gdbarch *
+arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+ struct gdbarch_tdep *tdep;
+ struct gdbarch *gdbarch;
+ struct gdbarch_list *best_arch;
+ enum arm_abi_kind arm_abi = arm_abi_global;
+ 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 have_neon = 0;
+ int have_fpa_registers = 1;
+ const struct target_desc *tdesc = info.target_desc;
+
+ /* If we have an object to base this architecture on, try to determine
+ its ABI. */
+
+ if (arm_abi == ARM_ABI_AUTO && info.abfd != NULL)
+ {
+ int ei_osabi, e_flags;
+
+ switch (bfd_get_flavour (info.abfd))
+ {
+ case bfd_target_aout_flavour:
+ /* Assume it's an old APCS-style ABI. */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case bfd_target_coff_flavour:
+ /* Assume it's an old APCS-style ABI. */
+ /* XXX WinCE? */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case bfd_target_elf_flavour:
+ ei_osabi = elf_elfheader (info.abfd)->e_ident[EI_OSABI];
+ e_flags = elf_elfheader (info.abfd)->e_flags;
+
+ if (ei_osabi == ELFOSABI_ARM)
+ {
+ /* GNU tools used to use this value, but do not for EABI
+ objects. There's nowhere to tag an EABI version
+ anyway, so assume APCS. */
+ arm_abi = ARM_ABI_APCS;
+ }
+ else if (ei_osabi == ELFOSABI_NONE)
+ {
+ int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
+ int attr_arch, attr_profile;
+
+ switch (eabi_ver)
+ {
+ case EF_ARM_EABI_UNKNOWN:
+ /* Assume GNU tools. */
+ arm_abi = ARM_ABI_APCS;
+ break;
+
+ case EF_ARM_EABI_VER4:
+ case EF_ARM_EABI_VER5:
+ arm_abi = ARM_ABI_AAPCS;
+ /* EABI binaries default to VFP float ordering.
+ They may also contain build attributes that can
+ be used to identify if the VFP argument-passing
+ ABI is in use. */
+ if (fp_model == ARM_FLOAT_AUTO)
+ {
+#ifdef HAVE_ELF
+ switch (bfd_elf_get_obj_attr_int (info.abfd,
+ OBJ_ATTR_PROC,
+ Tag_ABI_VFP_args))
+ {
+ case 0:
+ /* "The user intended FP parameter/result
+ passing to conform to AAPCS, base
+ variant". */
+ fp_model = ARM_FLOAT_SOFT_VFP;
+ break;
+ case 1:
+ /* "The user intended FP parameter/result
+ passing to conform to AAPCS, VFP
+ variant". */
+ fp_model = ARM_FLOAT_VFP;
+ break;
+ case 2:
+ /* "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;
+ default:
+ /* Attribute value not mentioned in the
+ October 2008 ABI, so leave it as
+ "auto". */
+ break;
+ }
+#else
+ fp_model = ARM_FLOAT_SOFT_VFP;
+#endif
+ }
+ break;
+
+ default:
+ /* Leave it as "auto". */
+ warning (_("unknown ARM EABI version 0x%x"), eabi_ver);
+ break;
+ }
+
+#ifdef HAVE_ELF
+ /* Detect M-profile programs. This only works if the
+ executable file includes build attributes; GCC does
+ copy them to the executable, but e.g. RealView does
+ not. */
+ attr_arch = bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC,
+ Tag_CPU_arch);
+ attr_profile = bfd_elf_get_obj_attr_int (info.abfd,
+ OBJ_ATTR_PROC,
+ Tag_CPU_arch_profile);
+ /* GCC specifies the profile for v6-M; RealView only
+ specifies the profile for architectures starting with
+ V7 (as opposed to architectures with a tag
+ numerically greater than TAG_CPU_ARCH_V7). */
+ if (!tdesc_has_registers (tdesc)
+ && (attr_arch == TAG_CPU_ARCH_V6_M
+ || attr_arch == TAG_CPU_ARCH_V6S_M
+ || attr_profile == 'M'))
+ is_m = 1;
+#endif
+ }
+
+ if (fp_model == ARM_FLOAT_AUTO)
+ {
+ int e_flags = elf_elfheader (info.abfd)->e_flags;
+
+ switch (e_flags & (EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT))
+ {
+ case 0:
+ /* Leave it as "auto". Strictly speaking this case
+ means FPA, but almost nobody uses that now, and
+ many toolchains fail to set the appropriate bits
+ for the floating-point model they use. */
+ break;
+ case EF_ARM_SOFT_FLOAT:
+ fp_model = ARM_FLOAT_SOFT_FPA;
+ break;
+ case EF_ARM_VFP_FLOAT:
+ fp_model = ARM_FLOAT_VFP;
+ break;
+ case EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT:
+ fp_model = ARM_FLOAT_SOFT_VFP;
+ break;
+ }
+ }
+
+ if (e_flags & EF_ARM_BE8)
+ info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
+ break;
+
+ default:
+ /* Leave it as "auto". */
+ break;
+ }
+ }
+
+ /* Check any target description for validity. */
+ if (tdesc_has_registers (tdesc))
+ {
+ /* For most registers we require GDB's default names; but also allow
+ the numeric names for sp / lr / pc, as a convenience. */
+ static const char *const arm_sp_names[] = { "r13", "sp", NULL };
+ static const char *const arm_lr_names[] = { "r14", "lr", NULL };
+ static const char *const arm_pc_names[] = { "r15", "pc", NULL };
+
+ const struct tdesc_feature *feature;
+ int valid_p;
+
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.core");
+ if (feature == NULL)
+ {
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.m-profile");
+ if (feature == NULL)
+ return NULL;
+ else
+ is_m = 1;
+ }
+
+ tdesc_data = tdesc_data_alloc ();
+
+ valid_p = 1;
+ for (i = 0; i < ARM_SP_REGNUM; i++)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
+ arm_register_names[i]);
+ valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
+ ARM_SP_REGNUM,
+ arm_sp_names);
+ valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
+ ARM_LR_REGNUM,
+ arm_lr_names);
+ valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
+ ARM_PC_REGNUM,
+ arm_pc_names);
+ if (is_m)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_PS_REGNUM, "xpsr");
+ else
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_PS_REGNUM, "cpsr");
+
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.fpa");
+ if (feature != NULL)
+ {
+ valid_p = 1;
+ for (i = ARM_F0_REGNUM; i <= ARM_FPS_REGNUM; i++)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
+ arm_register_names[i]);
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+ }
+ else
+ have_fpa_registers = 0;
+
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.xscale.iwmmxt");
+ if (feature != NULL)
+ {
+ static const char *const iwmmxt_names[] = {
+ "wR0", "wR1", "wR2", "wR3", "wR4", "wR5", "wR6", "wR7",
+ "wR8", "wR9", "wR10", "wR11", "wR12", "wR13", "wR14", "wR15",
+ "wCID", "wCon", "wCSSF", "wCASF", "", "", "", "",
+ "wCGR0", "wCGR1", "wCGR2", "wCGR3", "", "", "", "",
+ };
+
+ valid_p = 1;
+ for (i = ARM_WR0_REGNUM; i <= ARM_WR15_REGNUM; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data, i,
+ iwmmxt_names[i - ARM_WR0_REGNUM]);
+
+ /* Check for the control registers, but do not fail if they
+ are missing. */
+ for (i = ARM_WC0_REGNUM; i <= ARM_WCASF_REGNUM; i++)
+ tdesc_numbered_register (feature, tdesc_data, i,
+ iwmmxt_names[i - ARM_WR0_REGNUM]);
+
+ for (i = ARM_WCGR0_REGNUM; i <= ARM_WCGR3_REGNUM; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data, i,
+ iwmmxt_names[i - ARM_WR0_REGNUM]);
+
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+ }
+
+ /* If we have a VFP unit, check whether the single precision registers
+ are present. If not, then we will synthesize them as pseudo
+ registers. */
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.vfp");
+ if (feature != NULL)
+ {
+ static const char *const vfp_double_names[] = {
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
+ "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
+ "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
+ };
+
+ /* Require the double precision registers. There must be either
+ 16 or 32. */
+ valid_p = 1;
+ for (i = 0; i < 32; i++)
+ {
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_D0_REGNUM + i,
+ vfp_double_names[i]);
+ if (!valid_p)
+ break;
+ }
+ if (!valid_p && i == 16)
+ valid_p = 1;
+
+ /* Also require FPSCR. */
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ ARM_FPSCR_REGNUM, "fpscr");
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+
+ if (tdesc_unnumbered_register (feature, "s0") == 0)
+ have_vfp_pseudos = 1;
+
+ have_vfp_registers = 1;
+
+ /* If we have VFP, also check for NEON. The architecture allows
+ NEON without VFP (integer vector operations only), but GDB
+ does not support that. */
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.arm.neon");
+ if (feature != NULL)
+ {
+ /* NEON requires 32 double-precision registers. */
+ if (i != 32)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+
+ /* If there are quad registers defined by the stub, use
+ their type; otherwise (normally) provide them with
+ the default type. */
+ if (tdesc_unnumbered_register (feature, "q0") == 0)
+ have_neon_pseudos = 1;
+
+ have_neon = 1;
+ }
+ }
+ }
+
+ /* If there is already a candidate, use it. */
+ for (best_arch = gdbarch_list_lookup_by_info (arches, &info);
+ best_arch != NULL;
+ best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
+ {
+ if (arm_abi != ARM_ABI_AUTO
+ && arm_abi != gdbarch_tdep (best_arch->gdbarch)->arm_abi)
+ continue;
+
+ if (fp_model != ARM_FLOAT_AUTO
+ && fp_model != gdbarch_tdep (best_arch->gdbarch)->fp_model)
+ continue;
+
+ /* There are various other properties in tdep that we do not
+ need to check here: those derived from a target description,
+ since gdbarches with a different target description are
+ automatically disqualified. */
+
+ /* Do check is_m, though, since it might come from the binary. */
+ if (is_m != gdbarch_tdep (best_arch->gdbarch)->is_m)
+ continue;
+
+ /* Found a match. */
+ break;
+ }
+
+ if (best_arch != NULL)
+ {
+ if (tdesc_data != NULL)
+ tdesc_data_cleanup (tdesc_data);
+ return best_arch->gdbarch;
+ }
+
+ tdep = xcalloc (1, sizeof (struct gdbarch_tdep));
+ gdbarch = gdbarch_alloc (&info, tdep);
+
+ /* Record additional information about the architecture we are defining.
+ These are gdbarch discriminators, like the OSABI. */
+ tdep->arm_abi = arm_abi;
+ 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_vfp_pseudos = have_vfp_pseudos;
+ tdep->have_neon_pseudos = have_neon_pseudos;
+ tdep->have_neon = have_neon;
+
+ arm_register_g_packet_guesses (gdbarch);
+
+ /* Breakpoints. */
+ switch (info.byte_order_for_code)
+ {
+ case BFD_ENDIAN_BIG:
+ tdep->arm_breakpoint = arm_default_arm_be_breakpoint;
+ tdep->arm_breakpoint_size = sizeof (arm_default_arm_be_breakpoint);
+ tdep->thumb_breakpoint = arm_default_thumb_be_breakpoint;
+ tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_be_breakpoint);
+
+ break;
+
+ case BFD_ENDIAN_LITTLE:
+ tdep->arm_breakpoint = arm_default_arm_le_breakpoint;
+ tdep->arm_breakpoint_size = sizeof (arm_default_arm_le_breakpoint);
+ tdep->thumb_breakpoint = arm_default_thumb_le_breakpoint;
+ tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_le_breakpoint);
+
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("arm_gdbarch_init: bad byte order for float format"));
+ }
+
+ /* On ARM targets char defaults to unsigned. */
+ set_gdbarch_char_signed (gdbarch, 0);
+
+ /* Note: for displaced stepping, this includes the breakpoint, and one word
+ of additional scratch space. This setting isn't used for anything beside
+ displaced stepping at present. */
+ set_gdbarch_max_insn_length (gdbarch, 4 * DISPLACED_MODIFIED_INSNS);
+
+ /* This should be low enough for everything. */
+ tdep->lowest_pc = 0x20;
+ tdep->jb_pc = -1; /* Longjump support not enabled by default. */
+
+ /* The default, for both APCS and AAPCS, is to return small
+ structures in registers. */
+ tdep->struct_return = reg_struct_return;
+
+ set_gdbarch_push_dummy_call (gdbarch, arm_push_dummy_call);
+ set_gdbarch_frame_align (gdbarch, arm_frame_align);
+
+ set_gdbarch_write_pc (gdbarch, arm_write_pc);
+
+ /* Frame handling. */
+ set_gdbarch_dummy_id (gdbarch, arm_dummy_id);
+ set_gdbarch_unwind_pc (gdbarch, arm_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, arm_unwind_sp);
+
+ frame_base_set_default (gdbarch, &arm_normal_base);
+
+ /* Address manipulation. */
+ set_gdbarch_addr_bits_remove (gdbarch, arm_addr_bits_remove);
+
+ /* 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);
+
+ /* Skip trampolines. */
+ set_gdbarch_skip_trampoline_code (gdbarch, arm_skip_stub);
+
+ /* The stack grows downward. */
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+
+ /* Breakpoint manipulation. */
+ set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc);
+ set_gdbarch_remote_breakpoint_from_pc (gdbarch,
+ arm_remote_breakpoint_from_pc);
+
+ /* Information about registers, etc. */
+ set_gdbarch_sp_regnum (gdbarch, ARM_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, ARM_PC_REGNUM);
+ set_gdbarch_num_regs (gdbarch, ARM_NUM_REGS);
+ set_gdbarch_register_type (gdbarch, arm_register_type);
+ set_gdbarch_register_reggroup_p (gdbarch, arm_register_reggroup_p);
+
+ /* This "info float" is FPA-specific. Use the generic version if we
+ do not have FPA. */
+ if (gdbarch_tdep (gdbarch)->have_fpa_registers)
+ set_gdbarch_print_float_info (gdbarch, arm_print_float_info);
+
+ /* Internal <-> external register number maps. */
+ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum);
+ set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno);
+
+ set_gdbarch_register_name (gdbarch, arm_register_name);
+
+ /* Returning results. */
+ set_gdbarch_return_value (gdbarch, arm_return_value);
+
+ /* Disassembly. */
+ set_gdbarch_print_insn (gdbarch, gdb_print_insn_arm);
+
+ /* Minsymbol frobbing. */
+ set_gdbarch_elf_make_msymbol_special (gdbarch, arm_elf_make_msymbol_special);
+ set_gdbarch_coff_make_msymbol_special (gdbarch,
+ arm_coff_make_msymbol_special);
+ set_gdbarch_record_special_symbol (gdbarch, arm_record_special_symbol);
+
+ /* Thumb-2 IT block support. */
+ set_gdbarch_adjust_breakpoint_address (gdbarch,
+ arm_adjust_breakpoint_address);
+
+ /* Virtual tables. */
+ set_gdbarch_vbit_in_delta (gdbarch, 1);
+
+ /* Hook in the ABI-specific overrides, if they have been registered. */
+ gdbarch_init_osabi (info, gdbarch);
+
+ dwarf2_frame_set_init_reg (gdbarch, arm_dwarf2_frame_init_reg);
+
+ /* Add some default predicates. */
+ if (is_m)
+ frame_unwind_append_unwinder (gdbarch, &arm_m_exception_unwind);
+ frame_unwind_append_unwinder (gdbarch, &arm_stub_unwind);
+ dwarf2_append_unwinders (gdbarch);
+ frame_unwind_append_unwinder (gdbarch, &arm_exidx_unwind);
+ frame_unwind_append_unwinder (gdbarch, &arm_prologue_unwind);
+
+ /* Now we have tuned the configuration, set a few final things,
+ based on what the OS ABI has told us. */
+
+ /* If the ABI is not otherwise marked, assume the old GNU APCS. EABI
+ binaries are always marked. */
+ if (tdep->arm_abi == ARM_ABI_AUTO)
+ tdep->arm_abi = ARM_ABI_APCS;
+
+ /* Watchpoints are not steppable. */
+ set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
+
+ /* We used to default to FPA for generic ARM, but almost nobody
+ uses that now, and we now provide a way for the user to force
+ the model. So default to the most useful variant. */
+ if (tdep->fp_model == ARM_FLOAT_AUTO)
+ tdep->fp_model = ARM_FLOAT_SOFT_FPA;
+
+ if (tdep->jb_pc >= 0)
+ set_gdbarch_get_longjmp_target (gdbarch, arm_get_longjmp_target);
+
+ /* Floating point sizes and format. */
+ set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
+ if (tdep->fp_model == ARM_FLOAT_SOFT_FPA || tdep->fp_model == ARM_FLOAT_FPA)
+ {
+ set_gdbarch_double_format
+ (gdbarch, floatformats_ieee_double_littlebyte_bigword);
+ set_gdbarch_long_double_format
+ (gdbarch, floatformats_ieee_double_littlebyte_bigword);
+ }
+ else
+ {
+ set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
+ }
+
+ if (have_vfp_pseudos)
+ {
+ /* NOTE: These are the only pseudo registers used by
+ the ARM target at the moment. If more are added, a
+ little more care in numbering will be needed. */
+
+ int num_pseudos = 32;
+ if (have_neon_pseudos)
+ num_pseudos += 16;
+ set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
+ set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read);
+ set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write);
+ }
+
+ if (tdesc_data)
+ {
+ set_tdesc_pseudo_register_name (gdbarch, arm_register_name);
+
+ tdesc_use_registers (gdbarch, tdesc, tdesc_data);
+
+ /* Override tdesc_register_type to adjust the types of VFP
+ registers for NEON. */
+ set_gdbarch_register_type (gdbarch, arm_register_type);
+ }
+
+ /* Add standard register aliases. We add aliases even for those
+ nanes which are used by the current architecture - it's simpler,
+ and does no harm, since nothing ever lists user registers. */
+ for (i = 0; i < ARRAY_SIZE (arm_register_aliases); i++)
+ user_reg_add (gdbarch, arm_register_aliases[i].name,
+ value_of_arm_user_reg, &arm_register_aliases[i].regnum);
+
+ return gdbarch;
+}
+
+static void
+arm_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep == NULL)
+ return;
+
+ fprintf_unfiltered (file, _("arm_dump_tdep: Lowest pc = 0x%lx"),
+ (unsigned long) tdep->lowest_pc);
+}
+
+extern initialize_file_ftype _initialize_arm_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_arm_tdep (void)
+{
+ 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;
+ static char *helptext;
+ char regdesc[1024], *rdptr = regdesc;
+ size_t rest = sizeof (regdesc);
+
+ gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep);
+
+ arm_objfile_data_key
+ = register_objfile_data_with_cleanup (NULL, arm_objfile_data_free);
+
+ /* Add ourselves to objfile event chain. */
+ observer_attach_new_objfile (arm_exidx_new_objfile);
+ arm_exidx_data_key
+ = register_objfile_data_with_cleanup (NULL, arm_exidx_data_free);
+
+ /* Register an ELF OS ABI sniffer for ARM binaries. */
+ gdbarch_register_osabi_sniffer (bfd_arch_arm,
+ bfd_target_elf_flavour,
+ arm_elf_osabi_sniffer);
+
+ /* Initialize the standard target descriptions. */
+ initialize_tdesc_arm_with_m ();
+ initialize_tdesc_arm_with_m_fpa_layout ();
+ initialize_tdesc_arm_with_m_vfp_d16 ();
+ initialize_tdesc_arm_with_iwmmxt ();
+ initialize_tdesc_arm_with_vfpv2 ();
+ initialize_tdesc_arm_with_vfpv3 ();
+ initialize_tdesc_arm_with_neon ();
+
+ /* Get the number of possible sets of register names defined in opcodes. */
+ num_disassembly_options = get_arm_regname_num_options ();
+
+ /* Add root prefix command for all "set arm"/"show arm" commands. */
+ add_prefix_cmd ("arm", no_class, set_arm_command,
+ _("Various ARM-specific commands."),
+ &setarmcmdlist, "set arm ", 0, &setlist);
+
+ add_prefix_cmd ("arm", no_class, show_arm_command,
+ _("Various ARM-specific commands."),
+ &showarmcmdlist, "show arm ", 0, &showlist);
+
+ /* Sync the opcode insn printer with our register viewer. */
+ parse_arm_disassembler_option ("reg-names-std");
+
+ /* Initialize the array that will be passed to
+ add_setshow_enum_cmd(). */
+ valid_disassembly_styles
+ = xmalloc ((num_disassembly_options + 1) * sizeof (char *));
+ for (i = 0; i < num_disassembly_options; i++)
+ {
+ numregs = get_arm_regnames (i, &setname, &setdesc, ®names);
+ valid_disassembly_styles[i] = setname;
+ length = snprintf (rdptr, rest, "%s - %s\n", setname, setdesc);
+ rdptr += length;
+ rest -= length;
+ /* When we find the default names, tell the disassembler to use
+ them. */
+ if (!strcmp (setname, "std"))
+ {
+ disassembly_style = setname;
+ set_arm_regname_option (i);
+ }
+ }
+ /* Mark the end of valid options. */
+ valid_disassembly_styles[num_disassembly_options] = NULL;
+
+ /* Create the help text. */
+ stb = mem_fileopen ();
+ fprintf_unfiltered (stb, "%s%s%s",
+ _("The valid values are:\n"),
+ regdesc,
+ _("The default is \"std\"."));
+ helptext = ui_file_xstrdup (stb, NULL);
+ ui_file_delete (stb);
+
+ add_setshow_enum_cmd("disassembler", no_class,
+ valid_disassembly_styles, &disassembly_style,
+ _("Set the disassembly style."),
+ _("Show the disassembly style."),
+ helptext,
+ set_disassembly_style_sfunc,
+ NULL, /* FIXME: i18n: The disassembly style is
+ \"%s\". */
+ &setarmcmdlist, &showarmcmdlist);
+
+ add_setshow_boolean_cmd ("apcs32", no_class, &arm_apcs_32,
+ _("Set usage of ARM 32-bit mode."),
+ _("Show usage of ARM 32-bit mode."),
+ _("When off, a 26-bit PC will be used."),
+ NULL,
+ NULL, /* FIXME: i18n: Usage of ARM 32-bit
+ mode is %s. */
+ &setarmcmdlist, &showarmcmdlist);
+
+ /* Add a command to allow the user to force the FPU model. */
+ add_setshow_enum_cmd ("fpu", no_class, fp_model_strings, ¤t_fp_model,
+ _("Set the floating point type."),
+ _("Show the floating point type."),
+ _("auto - Determine the FP typefrom the OS-ABI.\n\
+softfpa - Software FP, mixed-endian doubles on little-endian ARMs.\n\
+fpa - FPA co-processor (GCC compiled).\n\
+softvfp - Software FP with pure-endian doubles.\n\
+vfp - VFP co-processor."),
+ set_fp_model_sfunc, show_fp_model,
+ &setarmcmdlist, &showarmcmdlist);
+
+ /* Add a command to allow the user to force the ABI. */
+ add_setshow_enum_cmd ("abi", class_support, arm_abi_strings, &arm_abi_string,
+ _("Set the ABI."),
+ _("Show the ABI."),
+ NULL, arm_set_abi, arm_show_abi,
+ &setarmcmdlist, &showarmcmdlist);
+
+ /* Add two commands to allow the user to force the assumed
+ execution mode. */
+ add_setshow_enum_cmd ("fallback-mode", class_support,
+ arm_mode_strings, &arm_fallback_mode_string,
+ _("Set the mode assumed when symbols are unavailable."),
+ _("Show the mode assumed when symbols are unavailable."),
+ NULL, NULL, arm_show_fallback_mode,
+ &setarmcmdlist, &showarmcmdlist);
+ add_setshow_enum_cmd ("force-mode", class_support,
+ arm_mode_strings, &arm_force_mode_string,
+ _("Set the mode assumed even when symbols are available."),
+ _("Show the mode assumed even when symbols are available."),
+ NULL, NULL, arm_show_force_mode,
+ &setarmcmdlist, &showarmcmdlist);
+
+ /* Debugging flag. */
+ add_setshow_boolean_cmd ("arm", class_maintenance, &arm_debug,
+ _("Set ARM debugging."),
+ _("Show ARM debugging."),
+ _("When on, arm-specific debugging is enabled."),
+ NULL,
+ NULL, /* FIXME: i18n: "ARM debugging is %s. */
+ &setdebuglist, &showdebuglist);
+}
+
+/* ARM-reversible process record data structures. */
+
+#define ARM_INSN_SIZE_BYTES 4
+#define THUMB_INSN_SIZE_BYTES 2
+#define THUMB2_INSN_SIZE_BYTES 4
+
+
+#define INSN_S_L_BIT_NUM 20
+
+#define REG_ALLOC(REGS, LENGTH, RECORD_BUF) \
+ do \
+ { \
+ unsigned int reg_len = LENGTH; \
+ if (reg_len) \
+ { \
+ REGS = XNEWVEC (uint32_t, reg_len); \
+ memcpy(®S[0], &RECORD_BUF[0], sizeof(uint32_t)*LENGTH); \
+ } \
+ } \
+ while (0)
+
+#define MEM_ALLOC(MEMS, LENGTH, RECORD_BUF) \
+ do \
+ { \
+ unsigned int mem_len = LENGTH; \
+ if (mem_len) \
+ { \
+ MEMS = XNEWVEC (struct arm_mem_r, mem_len); \
+ memcpy(&MEMS->len, &RECORD_BUF[0], \
+ sizeof(struct arm_mem_r) * LENGTH); \
+ } \
+ } \
+ while (0)
+
+/* Checks whether insn is already recorded or yet to be decoded. (boolean expression). */
+#define INSN_RECORDED(ARM_RECORD) \
+ (0 != (ARM_RECORD)->reg_rec_count || 0 != (ARM_RECORD)->mem_rec_count)
+
+/* ARM memory record structure. */
+struct arm_mem_r
+{
+ uint32_t len; /* Record length. */
+ CORE_ADDR addr; /* Memory address. */
+};
+
+/* ARM instruction record contains opcode of current insn
+ and execution state (before entry to decode_insn()),
+ contains list of to-be-modified registers and
+ memory blocks (on return from decode_insn()). */
+
+typedef struct insn_decode_record_t
+{
+ struct gdbarch *gdbarch;
+ struct regcache *regcache;
+ CORE_ADDR this_addr; /* Address of the insn being decoded. */
+ uint32_t arm_insn; /* Should accommodate thumb. */
+ uint32_t cond; /* Condition code. */
+ uint32_t opcode; /* Insn opcode. */
+ uint32_t decode; /* Insn decode bits. */
+ uint32_t mem_rec_count; /* No of mem records. */
+ uint32_t reg_rec_count; /* No of reg records. */
+ uint32_t *arm_regs; /* Registers to be saved for this record. */
+ struct arm_mem_r *arm_mems; /* Memory to be saved for this record. */
+} insn_decode_record;
+
+
+/* Checks ARM SBZ and SBO mandatory fields. */
+
+static int
+sbo_sbz (uint32_t insn, uint32_t bit_num, uint32_t len, uint32_t sbo)
+{
+ uint32_t ones = bits (insn, bit_num - 1, (bit_num -1) + (len - 1));
+
+ if (!len)
+ return 1;
+
+ if (!sbo)
+ ones = ~ones;
+
+ while (ones)
+ {
+ if (!(ones & sbo))
+ {
+ return 0;
+ }
+ ones = ones >> 1;
+ }
+ return 1;
+}
+
+typedef enum
+{
+ ARM_RECORD_STRH=1,
+ ARM_RECORD_STRD
+} arm_record_strx_t;
+
+typedef enum
+{
+ ARM_RECORD=1,
+ THUMB_RECORD,
+ THUMB2_RECORD
+} record_type_t;
+
+
+static int
+arm_record_strx (insn_decode_record *arm_insn_r, uint32_t *record_buf,
+ uint32_t *record_buf_mem, arm_record_strx_t str_type)
+{
+
+ struct regcache *reg_cache = arm_insn_r->regcache;
+ ULONGEST u_regval[2]= {0};
+
+ 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)
+ {
+ /* 1) Handle misc store, immediate offset. */
+ immed_low = bits (arm_insn_r->arm_insn, 0, 3);
+ immed_high = bits (arm_insn_r->arm_insn, 8, 11);
+ reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1,
+ &u_regval[0]);
+ if (ARM_PC_REGNUM == reg_src1)
+ {
+ /* If R15 was used as Rn, hence current PC+8. */
+ u_regval[0] = u_regval[0] + 8;
+ }
+ offset_8 = (immed_high << 4) | immed_low;
+ /* Calculate target store address. */
+ if (14 == arm_insn_r->opcode)
+ {
+ tgt_mem_addr = u_regval[0] + offset_8;
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[0] - offset_8;
+ }
+ if (ARM_RECORD_STRH == str_type)
+ {
+ record_buf_mem[0] = 2;
+ record_buf_mem[1] = tgt_mem_addr;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else if (ARM_RECORD_STRD == str_type)
+ {
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = tgt_mem_addr;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = tgt_mem_addr + 4;
+ arm_insn_r->mem_rec_count = 2;
+ }
+ }
+ else if (12 == arm_insn_r->opcode || 8 == arm_insn_r->opcode)
+ {
+ /* 2) Store, register offset. */
+ /* Get Rm. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 0, 3);
+ /* Get Rn. */
+ reg_src2 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ if (15 == reg_src2)
+ {
+ /* If R15 was used as Rn, hence current PC+8. */
+ u_regval[0] = u_regval[0] + 8;
+ }
+ /* Calculate target store address, Rn +/- Rm, register offset. */
+ if (12 == arm_insn_r->opcode)
+ {
+ tgt_mem_addr = u_regval[0] + u_regval[1];
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[1] - u_regval[0];
+ }
+ if (ARM_RECORD_STRH == str_type)
+ {
+ record_buf_mem[0] = 2;
+ record_buf_mem[1] = tgt_mem_addr;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else if (ARM_RECORD_STRD == str_type)
+ {
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = tgt_mem_addr;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = tgt_mem_addr + 4;
+ arm_insn_r->mem_rec_count = 2;
+ }
+ }
+ else if (11 == arm_insn_r->opcode || 15 == arm_insn_r->opcode
+ || 2 == arm_insn_r->opcode || 6 == arm_insn_r->opcode)
+ {
+ /* 3) Store, immediate pre-indexed. */
+ /* 5) Store, immediate post-indexed. */
+ immed_low = bits (arm_insn_r->arm_insn, 0, 3);
+ immed_high = bits (arm_insn_r->arm_insn, 8, 11);
+ offset_8 = (immed_high << 4) | immed_low;
+ reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ /* Calculate target store address, Rn +/- Rm, register offset. */
+ if (15 == arm_insn_r->opcode || 6 == arm_insn_r->opcode)
+ {
+ tgt_mem_addr = u_regval[0] + offset_8;
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[0] - offset_8;
+ }
+ if (ARM_RECORD_STRH == str_type)
+ {
+ record_buf_mem[0] = 2;
+ record_buf_mem[1] = tgt_mem_addr;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else if (ARM_RECORD_STRD == str_type)
+ {
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = tgt_mem_addr;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = tgt_mem_addr + 4;
+ arm_insn_r->mem_rec_count = 2;
+ }
+ /* Record Rn also as it changes. */
+ *(record_buf) = bits (arm_insn_r->arm_insn, 16, 19);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (9 == arm_insn_r->opcode || 13 == arm_insn_r->opcode
+ || 0 == arm_insn_r->opcode || 4 == arm_insn_r->opcode)
+ {
+ /* 4) Store, register pre-indexed. */
+ /* 6) Store, register post -indexed. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 0, 3);
+ reg_src2 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ /* Calculate target store address, Rn +/- Rm, register offset. */
+ if (13 == arm_insn_r->opcode || 4 == arm_insn_r->opcode)
+ {
+ tgt_mem_addr = u_regval[0] + u_regval[1];
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[1] - u_regval[0];
+ }
+ if (ARM_RECORD_STRH == str_type)
+ {
+ record_buf_mem[0] = 2;
+ record_buf_mem[1] = tgt_mem_addr;
+ arm_insn_r->mem_rec_count = 1;
+ }
+ else if (ARM_RECORD_STRD == str_type)
+ {
+ record_buf_mem[0] = 4;
+ record_buf_mem[1] = tgt_mem_addr;
+ record_buf_mem[2] = 4;
+ record_buf_mem[3] = tgt_mem_addr + 4;
+ arm_insn_r->mem_rec_count = 2;
+ }
+ /* Record Rn also as it changes. */
+ *(record_buf) = bits (arm_insn_r->arm_insn, 16, 19);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ return 0;
+}
+
+/* Handling ARM extension space insns. */
+
+static int
+arm_record_extension_space (insn_decode_record *arm_insn_r)
+{
+ uint32_t ret = 0; /* Return value: -1:record failure ; 0:success */
+ 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;
+
+ gdb_assert (!INSN_RECORDED(arm_insn_r));
+ /* Handle unconditional insn extension space. */
+
+ opcode1 = bits (arm_insn_r->arm_insn, 20, 27);
+ opcode2 = bits (arm_insn_r->arm_insn, 4, 7);
+ if (arm_insn_r->cond)
+ {
+ /* PLD has no affect on architectural state, it just affects
+ the caches. */
+ if (5 == ((opcode1 & 0xE0) >> 5))
+ {
+ /* BLX(1) */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ /* STC2, LDC2, MCR2, MRC2, CDP2: <TBD>, co-processor insn. */
+ }
+
+
+ opcode1 = bits (arm_insn_r->arm_insn, 25, 27);
+ if (3 == opcode1 && bit (arm_insn_r->arm_insn, 4))
+ {
+ ret = -1;
+ /* Undefined instruction on ARM V5; need to handle if later
+ versions define it. */
+ }
+
+ opcode1 = bits (arm_insn_r->arm_insn, 24, 27);
+ opcode2 = bits (arm_insn_r->arm_insn, 4, 7);
+ insn_op1 = bits (arm_insn_r->arm_insn, 20, 23);
+
+ /* Handle arithmetic insn extension space. */
+ if (!opcode1 && 9 == opcode2 && 1 != arm_insn_r->cond
+ && !INSN_RECORDED(arm_insn_r))
+ {
+ /* Handle MLA(S) and MUL(S). */
+ if (0 <= insn_op1 && 3 >= insn_op1)
+ {
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (4 <= insn_op1 && 15 >= insn_op1)
+ {
+ /* Handle SMLAL(S), SMULL(S), UMLAL(S), UMULL(S). */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 16, 19);
+ record_buf[1] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[2] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 3;
+ }
+ }
+
+ opcode1 = bits (arm_insn_r->arm_insn, 26, 27);
+ opcode2 = bits (arm_insn_r->arm_insn, 23, 24);
+ insn_op1 = bits (arm_insn_r->arm_insn, 21, 22);
+
+ /* Handle control insn extension space. */
+
+ if (!opcode1 && 2 == opcode2 && !bit (arm_insn_r->arm_insn, 20)
+ && 1 != arm_insn_r->cond && !INSN_RECORDED(arm_insn_r))
+ {
+ if (!bit (arm_insn_r->arm_insn,25))
+ {
+ if (!bits (arm_insn_r->arm_insn, 4, 7))
+ {
+ if ((0 == insn_op1) || (2 == insn_op1))
+ {
+ /* MRS. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (1 == insn_op1)
+ {
+ /* CSPR is going to be changed. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (3 == insn_op1)
+ {
+ /* 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;
+ }
+ }
+ else if (1 == bits (arm_insn_r->arm_insn, 4, 7))
+ {
+ if (1 == insn_op1)
+ {
+ /* BX. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (3 == insn_op1)
+ {
+ /* CLZ. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ else if (3 == bits (arm_insn_r->arm_insn, 4, 7))
+ {
+ /* BLX. */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (5 == bits (arm_insn_r->arm_insn, 4, 7))
+ {
+ /* QADD, QSUB, QDADD, QDSUB */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (7 == bits (arm_insn_r->arm_insn, 4, 7))
+ {
+ /* BKPT. */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ 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)
+ || 10 == bits (arm_insn_r->arm_insn, 4, 7)
+ || 12 == bits (arm_insn_r->arm_insn, 4, 7)
+ || 14 == bits (arm_insn_r->arm_insn, 4, 7)
+ )
+ {
+ if (0 == insn_op1 || 1 == insn_op1)
+ {
+ /* SMLA<x><y>, SMLAW<y>, SMULW<y>. */
+ /* We dont do optimization for SMULW<y> where we
+ need only Rd. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (2 == insn_op1)
+ {
+ /* SMLAL<x><y>. */
+ 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 if (3 == insn_op1)
+ {
+ /* SMUL<x><y>. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ }
+ else
+ {
+ /* MSR : immediate form. */
+ if (1 == insn_op1)
+ {
+ /* CSPR is going to be changed. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (3 == insn_op1)
+ {
+ /* 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;
+ }
+ }
+ }
+
+ opcode1 = bits (arm_insn_r->arm_insn, 25, 27);
+ opcode2 = bits (arm_insn_r->arm_insn, 20, 24);
+ insn_op1 = bits (arm_insn_r->arm_insn, 5, 6);
+
+ /* Handle load/store insn extension space. */
+
+ if (!opcode1 && bit (arm_insn_r->arm_insn, 7)
+ && bit (arm_insn_r->arm_insn, 4) && 1 != arm_insn_r->cond
+ && !INSN_RECORDED(arm_insn_r))
+ {
+ /* SWP/SWPB. */
+ if (0 == insn_op1)
+ {
+ /* These insn, changes register and memory as well. */
+ /* SWP or SWPB insn. */
+ /* Get memory address given by Rn. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
+ /* SWP insn ?, swaps word. */
+ if (8 == arm_insn_r->opcode)
+ {
+ record_buf_mem[0] = 4;
+ }
+ else
+ {
+ /* SWPB insn, swaps only byte. */
+ record_buf_mem[0] = 1;
+ }
+ record_buf_mem[1] = u_regval;
+ arm_insn_r->mem_rec_count = 1;
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (1 == insn_op1 && !bit (arm_insn_r->arm_insn, 20))
+ {
+ /* STRH. */
+ arm_record_strx(arm_insn_r, &record_buf[0], &record_buf_mem[0],
+ ARM_RECORD_STRH);
+ }
+ else if (2 == insn_op1 && !bit (arm_insn_r->arm_insn, 20))
+ {
+ /* LDRD. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = record_buf[0] + 1;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (3 == insn_op1 && !bit (arm_insn_r->arm_insn, 20))
+ {
+ /* STRD. */
+ arm_record_strx(arm_insn_r, &record_buf[0], &record_buf_mem[0],
+ ARM_RECORD_STRD);
+ }
+ else if (bit (arm_insn_r->arm_insn, 20) && insn_op1 <= 3)
+ {
+ /* LDRH, LDRSB, LDRSH. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+
+ }
+
+ opcode1 = bits (arm_insn_r->arm_insn, 23, 27);
+ if (24 == opcode1 && bit (arm_insn_r->arm_insn, 21)
+ && !INSN_RECORDED(arm_insn_r))
+ {
+ ret = -1;
+ /* Handle coprocessor insn extension space. */
+ }
+
+ /* 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));
+
+
+ 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 ret;
+}
+
+/* Handling opcode 000 insns. */
+
+static int
+arm_record_data_proc_misc_ld_str (insn_decode_record *arm_insn_r)
+{
+ struct regcache *reg_cache = arm_insn_r->regcache;
+ 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 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);
+
+ /* Data processing insn /multiply insn. */
+ if (9 == arm_insn_r->decode
+ && ((4 <= arm_insn_r->opcode && 7 >= arm_insn_r->opcode)
+ || (0 == arm_insn_r->opcode || 1 == arm_insn_r->opcode)))
+ {
+ /* Handle multiply instructions. */
+ /* MLA, MUL, SMLAL, SMULL, UMLAL, UMULL. */
+ if (0 == arm_insn_r->opcode || 1 == arm_insn_r->opcode)
+ {
+ /* Handle MLA and MUL. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 16, 19);
+ record_buf[1] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else if (4 <= arm_insn_r->opcode && 7 >= arm_insn_r->opcode)
+ {
+ /* Handle SMLAL, SMULL, UMLAL, UMULL. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 16, 19);
+ record_buf[1] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[2] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 3;
+ }
+ }
+ else if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM)
+ && (11 == arm_insn_r->decode || 13 == arm_insn_r->decode))
+ {
+ /* Handle misc load insns, as 20th bit (L = 1). */
+ /* LDR insn has a capability to do branching, if
+ MOV LR, PC is precceded 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. I am not sure this is right
+ place; as opcode = 010 LDR insn make this happen, if R15 was
+ used. */
+ reg_dest = bits (arm_insn_r->arm_insn, 12, 15);
+ if (15 != 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;
+ }
+ }
+ else if ((9 == arm_insn_r->opcode || 11 == arm_insn_r->opcode)
+ && sbo_sbz (arm_insn_r->arm_insn, 5, 12, 0)
+ && sbo_sbz (arm_insn_r->arm_insn, 13, 4, 1)
+ && 2 == bits (arm_insn_r->arm_insn, 20, 21))
+ {
+ /* Handle MSR insn. */
+ if (9 == arm_insn_r->opcode)
+ {
+ /* CSPR is going to be changed. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* 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;
+ }
+ }
+ else if (9 == arm_insn_r->decode
+ && (8 == arm_insn_r->opcode || 10 == arm_insn_r->opcode)
+ && !bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* Handling SWP, SWPB. */
+ /* These insn, changes register and memory as well. */
+ /* SWP or SWPB insn. */
+
+ reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ /* SWP insn ?, swaps word. */
+ if (8 == arm_insn_r->opcode)
+ {
+ record_buf_mem[0] = 4;
+ }
+ else
+ {
+ /* SWPB insn, swaps only byte. */
+ record_buf_mem[0] = 1;
+ }
+ record_buf_mem[1] = u_regval[0];
+ arm_insn_r->mem_rec_count = 1;
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (3 == arm_insn_r->decode && 0x12 == opcode1
+ && sbo_sbz (arm_insn_r->arm_insn, 9, 12, 1))
+ {
+ /* Handle BLX, branch and link/exchange. */
+ if (9 == arm_insn_r->opcode)
+ {
+ /* Branch is chosen by setting T bit of CSPR, bitp[0] of Rm,
+ and R14 stores the return address. */
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = ARM_LR_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ }
+ else if (7 == arm_insn_r->decode && 0x12 == opcode1)
+ {
+ /* 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 hit 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;
+ 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
+ && !bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* Handle enhanced store insns and DSP insns (e.g. LDRD). */
+
+ /* Handle str(x) insn */
+ arm_record_strx(arm_insn_r, &record_buf[0], &record_buf_mem[0],
+ ARM_RECORD_STRH);
+ }
+ else if (1 == arm_insn_r->decode && 0x12 == opcode1
+ && sbo_sbz (arm_insn_r->arm_insn, 9, 12, 1))
+ {
+ /* Handle BX, branch and link/exchange. */
+ /* Branch is chosen by setting T bit of CSPR, bitp[0] of Rm. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (1 == arm_insn_r->decode && 0x16 == opcode1
+ && sbo_sbz (arm_insn_r->arm_insn, 9, 4, 1)
+ && sbo_sbz (arm_insn_r->arm_insn, 17, 4, 1))
+ {
+ /* Count leading zeros: CLZ. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (!bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM)
+ && (8 == arm_insn_r->opcode || 10 == arm_insn_r->opcode)
+ && sbo_sbz (arm_insn_r->arm_insn, 17, 4, 1)
+ && sbo_sbz (arm_insn_r->arm_insn, 1, 12, 0)
+ )
+ {
+ /* Handle MRS insn. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else if (arm_insn_r->opcode <= 15)
+ {
+ /* Normal data processing insns. */
+ /* Out of 11 shifter operands mode, all the insn modifies destination
+ register, which is specified by 13-16 decode. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else
+ {
+ return -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 001 insns. */
+
+static int
+arm_record_data_proc_imm (insn_decode_record *arm_insn_r)
+{
+ uint32_t record_buf[8], record_buf_mem[8];
+
+ arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
+ arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
+
+ if ((9 == arm_insn_r->opcode || 11 == arm_insn_r->opcode)
+ && 2 == bits (arm_insn_r->arm_insn, 20, 21)
+ && sbo_sbz (arm_insn_r->arm_insn, 13, 4, 1)
+ )
+ {
+ /* Handle MSR insn. */
+ if (9 == arm_insn_r->opcode)
+ {
+ /* CSPR is going to be changed. */
+ record_buf[0] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* SPSR is going to be changed. */
+ }
+ }
+ else if (arm_insn_r->opcode <= 15)
+ {
+ /* Normal data processing insns. */
+ /* Out of 11 shifter operands mode, all the insn modifies destination
+ register, which is specified by 13-16 decode. */
+ record_buf[0] = bits (arm_insn_r->arm_insn, 12, 15);
+ record_buf[1] = ARM_PS_REGNUM;
+ arm_insn_r->reg_rec_count = 2;
+ }
+ else
+ {
+ return -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 010 insns. */
+
+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 record_buf[8], record_buf_mem[8];
+
+ ULONGEST u_regval = 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);
+
+ if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ 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;
+ }
+ }
+ else
+ {
+ /* Store, immediate offset, immediate pre-indexed,
+ immediate post-indexed. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 16, 19);
+ offset_12 = bits (arm_insn_r->arm_insn, 0, 11);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval);
+ /* U == 1 */
+ if (bit (arm_insn_r->arm_insn, 23))
+ {
+ tgt_mem_addr = u_regval + offset_12;
+ }
+ else
+ {
+ tgt_mem_addr = u_regval - offset_12;
+ }
+
+ 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;
+ }
+ }
+
+ 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 011 insns. */
+
+static int
+arm_record_ld_st_reg_offset (insn_decode_record *arm_insn_r)
+{
+ struct regcache *reg_cache = arm_insn_r->regcache;
+
+ uint32_t shift_imm = 0;
+ uint32_t reg_src1 = 0, reg_src2 = 0, reg_dest = 0;
+ uint32_t offset_12 = 0, tgt_mem_addr = 0;
+ uint32_t record_buf[8], record_buf_mem[8];
+
+ LONGEST s_word;
+ ULONGEST u_regval[2];
+
+ arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
+ arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
+
+ /* Handle enhanced store insns and LDRD DSP insn,
+ order begins according to addressing modes for store insns
+ STRH insn. */
+
+ /* LDR or STR? */
+ if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ 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 (15 != 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;
+ }
+ }
+ else
+ {
+ if (! bits (arm_insn_r->arm_insn, 4, 11))
+ {
+ /* Store insn, register offset and register pre-indexed,
+ register post-indexed. */
+ /* Get Rm. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 0, 3);
+ /* Get Rn. */
+ reg_src2 = bits (arm_insn_r->arm_insn, 16, 19);
+ regcache_raw_read_unsigned (reg_cache, reg_src1
+ , &u_regval[0]);
+ regcache_raw_read_unsigned (reg_cache, reg_src2
+ , &u_regval[1]);
+ if (15 == reg_src2)
+ {
+ /* If R15 was used as Rn, hence current PC+8. */
+ /* Pre-indexed mode doesnt reach here ; illegal insn. */
+ u_regval[0] = u_regval[0] + 8;
+ }
+ /* Calculate target store address, Rn +/- Rm, register offset. */
+ /* U == 1. */
+ if (bit (arm_insn_r->arm_insn, 23))
+ {
+ tgt_mem_addr = u_regval[0] + u_regval[1];
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[1] - u_regval[0];
+ }
+
+ switch (arm_insn_r->opcode)
+ {
+ /* STR. */
+ case 8:
+ case 12:
+ /* STR. */
+ case 9:
+ case 13:
+ /* STRT. */
+ case 1:
+ case 5:
+ /* STR. */
+ case 0:
+ case 4:
+ 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
+ )
+ {
+ /* Rn is going to be changed in pre-indexed mode and
+ post-indexed mode as well. */
+ record_buf[0] = reg_src2;
+ arm_insn_r->reg_rec_count = 1;
+ }
+ }
+ else
+ {
+ /* Store insn, scaled register offset; scaled pre-indexed. */
+ offset_12 = bits (arm_insn_r->arm_insn, 5, 6);
+ /* Get Rm. */
+ reg_src1 = bits (arm_insn_r->arm_insn, 0, 3);
+ /* Get Rn. */
+ reg_src2 = bits (arm_insn_r->arm_insn, 16, 19);
+ /* Get shift_imm. */
+ shift_imm = bits (arm_insn_r->arm_insn, 7, 11);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ regcache_raw_read_signed (reg_cache, reg_src1, &s_word);
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ /* Offset_12 used as shift. */
+ switch (offset_12)
+ {
+ case 0:
+ /* Offset_12 used as index. */
+ offset_12 = u_regval[0] << shift_imm;
+ break;
+
+ case 1:
+ offset_12 = (!shift_imm)?0:u_regval[0] >> shift_imm;
+ break;
+
+ case 2:
+ if (!shift_imm)
+ {
+ if (bit (u_regval[0], 31))
+ {
+ offset_12 = 0xFFFFFFFF;
+ }
+ else
+ {
+ offset_12 = 0;
+ }
+ }
+ else
+ {
+ /* This is arithmetic shift. */
+ offset_12 = s_word >> shift_imm;
+ }
+ break;
+
+ case 3:
+ if (!shift_imm)
+ {
+ regcache_raw_read_unsigned (reg_cache, ARM_PS_REGNUM,
+ &u_regval[1]);
+ /* Get C flag value and shift it by 31. */
+ offset_12 = (((bit (u_regval[1], 29)) << 31) \
+ | (u_regval[0]) >> 1);
+ }
+ else
+ {
+ offset_12 = (u_regval[0] >> shift_imm) \
+ | (u_regval[0] <<
+ (sizeof(uint32_t) - shift_imm));
+ }
+ break;
+
+ default:
+ gdb_assert_not_reached ("no decoding pattern found");
+ break;
+ }
+
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ /* bit U set. */
+ if (bit (arm_insn_r->arm_insn, 23))
+ {
+ tgt_mem_addr = u_regval[1] + offset_12;
+ }
+ else
+ {
+ tgt_mem_addr = u_regval[1] - offset_12;
+ }
+
+ switch (arm_insn_r->opcode)
+ {
+ /* STR. */
+ case 8:
+ case 12:
+ /* STR. */
+ case 9:
+ case 13:
+ /* STRT. */
+ case 1:
+ case 5:
+ /* STR. */
+ case 0:
+ case 4:
+ 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
+ )
+ {
+ /* Rn is going to be changed in register scaled pre-indexed
+ mode,and scaled post indexed mode. */
+ record_buf[0] = reg_src2;
+ 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;
+}
+
+/* Handling opcode 100 insns. */
+
+static int
+arm_record_ld_st_multiple (insn_decode_record *arm_insn_r)
+{
+ struct regcache *reg_cache = arm_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 record_buf[24], record_buf_mem[48];
+
+ ULONGEST u_regval[2] = {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. */
+
+ if (bit (arm_insn_r->arm_insn, INSN_S_L_BIT_NUM))
+ {
+ /* 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);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ register_list[register_count++] = 1;
+ register_bits = register_bits >> 1;
+ }
- /* If we have an object to base this architecture on, try to determine
- its ABI. */
+ /* 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;
- if (arm_abi == ARM_ABI_AUTO && info.abfd != NULL)
+ 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++;
+ }
+ }
+
+ }
+ else
{
- int ei_osabi, e_flags;
+ /* It handles both STM(1) and STM(2). */
+ addr_mode = bits (arm_insn_r->arm_insn, 23, 24);
- switch (bfd_get_flavour (info.abfd))
- {
- case bfd_target_aout_flavour:
- /* Assume it's an old APCS-style ABI. */
- arm_abi = ARM_ABI_APCS;
- break;
+ 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]);
+ while (register_bits)
+ {
+ if (register_bits & 0x00000001)
+ register_count++;
+ register_bits = register_bits >> 1;
+ }
- case bfd_target_coff_flavour:
- /* Assume it's an old APCS-style ABI. */
- /* XXX WinCE? */
- arm_abi = ARM_ABI_APCS;
- break;
+ switch (addr_mode)
+ {
+ /* 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;
+ }
- case bfd_target_elf_flavour:
- ei_osabi = elf_elfheader (info.abfd)->e_ident[EI_OSABI];
- e_flags = elf_elfheader (info.abfd)->e_flags;
+ /* Base register also changes; based on condition and W bit. */
+ /* We save it anyway without optimization. */
+ record_buf[0] = reg_src1;
+ arm_insn_r->reg_rec_count = 1;
+ }
- if (ei_osabi == ELFOSABI_ARM)
- {
- /* GNU tools used to use this value, but do not for EABI
- objects. There's nowhere to tag an EABI version
- anyway, so assume APCS. */
- arm_abi = ARM_ABI_APCS;
- }
- else if (ei_osabi == ELFOSABI_NONE)
- {
- int eabi_ver = EF_ARM_EABI_VERSION (e_flags);
- int attr_arch, attr_profile;
+ 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;
+}
- switch (eabi_ver)
- {
- case EF_ARM_EABI_UNKNOWN:
- /* Assume GNU tools. */
- arm_abi = ARM_ABI_APCS;
- break;
+/* Handling opcode 101 insns. */
- case EF_ARM_EABI_VER4:
- case EF_ARM_EABI_VER5:
- arm_abi = ARM_ABI_AAPCS;
- /* EABI binaries default to VFP float ordering.
- They may also contain build attributes that can
- be used to identify if the VFP argument-passing
- ABI is in use. */
- if (fp_model == ARM_FLOAT_AUTO)
- {
-#ifdef HAVE_ELF
- switch (bfd_elf_get_obj_attr_int (info.abfd,
- OBJ_ATTR_PROC,
- Tag_ABI_VFP_args))
- {
- case 0:
- /* "The user intended FP parameter/result
- passing to conform to AAPCS, base
- variant". */
- fp_model = ARM_FLOAT_SOFT_VFP;
- break;
- case 1:
- /* "The user intended FP parameter/result
- passing to conform to AAPCS, VFP
- variant". */
- fp_model = ARM_FLOAT_VFP;
- break;
- case 2:
- /* "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;
- default:
- /* Attribute value not mentioned in the
- October 2008 ABI, so leave it as
- "auto". */
- break;
- }
-#else
- fp_model = ARM_FLOAT_SOFT_VFP;
-#endif
- }
- break;
+static int
+arm_record_b_bl (insn_decode_record *arm_insn_r)
+{
+ uint32_t record_buf[8];
- default:
- /* Leave it as "auto". */
- warning (_("unknown ARM EABI version 0x%x"), eabi_ver);
- break;
- }
+ /* 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;
+ }
-#ifdef HAVE_ELF
- /* Detect M-profile programs. This only works if the
- executable file includes build attributes; GCC does
- copy them to the executable, but e.g. RealView does
- not. */
- attr_arch = bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_PROC,
- Tag_CPU_arch);
- attr_profile = bfd_elf_get_obj_attr_int (info.abfd,
- OBJ_ATTR_PROC,
- Tag_CPU_arch_profile);
- /* GCC specifies the profile for v6-M; RealView only
- specifies the profile for architectures starting with
- V7 (as opposed to architectures with a tag
- numerically greater than TAG_CPU_ARCH_V7). */
- if (!tdesc_has_registers (tdesc)
- && (attr_arch == TAG_CPU_ARCH_V6_M
- || attr_arch == TAG_CPU_ARCH_V6S_M
- || attr_profile == 'M'))
- tdesc = tdesc_arm_with_m;
-#endif
- }
+ REG_ALLOC (arm_insn_r->arm_regs, arm_insn_r->reg_rec_count, record_buf);
- if (fp_model == ARM_FLOAT_AUTO)
- {
- int e_flags = elf_elfheader (info.abfd)->e_flags;
+ return 0;
+}
- switch (e_flags & (EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT))
- {
- case 0:
- /* Leave it as "auto". Strictly speaking this case
- means FPA, but almost nobody uses that now, and
- many toolchains fail to set the appropriate bits
- for the floating-point model they use. */
- break;
- case EF_ARM_SOFT_FLOAT:
- fp_model = ARM_FLOAT_SOFT_FPA;
- break;
- case EF_ARM_VFP_FLOAT:
- fp_model = ARM_FLOAT_VFP;
- break;
- case EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT:
- fp_model = ARM_FLOAT_SOFT_VFP;
- break;
- }
- }
+/* Handling opcode 110 insns. */
- if (e_flags & EF_ARM_BE8)
- info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+static int
+arm_record_coproc (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));
- break;
+ return -1;
+}
- default:
- /* Leave it as "auto". */
- break;
- }
- }
+/* Handling opcode 111 insns. */
- /* Check any target description for validity. */
- if (tdesc_has_registers (tdesc))
- {
- /* For most registers we require GDB's default names; but also allow
- the numeric names for sp / lr / pc, as a convenience. */
- static const char *const arm_sp_names[] = { "r13", "sp", NULL };
- static const char *const arm_lr_names[] = { "r14", "lr", NULL };
- static const char *const arm_pc_names[] = { "r15", "pc", NULL };
+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 */
- const struct tdesc_feature *feature;
- int valid_p;
+ /* Handle SWI insn; system call would be handled over here. */
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.arm.core");
- if (feature == NULL)
- {
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.arm.m-profile");
- if (feature == NULL)
- return NULL;
- else
- is_m = 1;
- }
+ 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;
+ }
+ }
- tdesc_data = tdesc_data_alloc ();
+ 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;
+}
- valid_p = 1;
- for (i = 0; i < ARM_SP_REGNUM; i++)
- valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
- arm_register_names[i]);
- valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
- ARM_SP_REGNUM,
- arm_sp_names);
- valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
- ARM_LR_REGNUM,
- arm_lr_names);
- valid_p &= tdesc_numbered_register_choices (feature, tdesc_data,
- ARM_PC_REGNUM,
- arm_pc_names);
- if (is_m)
- valid_p &= tdesc_numbered_register (feature, tdesc_data,
- ARM_PS_REGNUM, "xpsr");
- else
- valid_p &= tdesc_numbered_register (feature, tdesc_data,
- ARM_PS_REGNUM, "cpsr");
+/* Handling opcode 000 insns. */
- if (!valid_p)
- {
- tdesc_data_cleanup (tdesc_data);
- return NULL;
- }
+static int
+thumb_record_shift_add_sub (insn_decode_record *thumb_insn_r)
+{
+ uint32_t record_buf[8];
+ uint32_t reg_src1 = 0;
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.arm.fpa");
- if (feature != NULL)
- {
- valid_p = 1;
- for (i = ARM_F0_REGNUM; i <= ARM_FPS_REGNUM; i++)
- valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
- arm_register_names[i]);
- if (!valid_p)
- {
- tdesc_data_cleanup (tdesc_data);
- return NULL;
- }
- }
- else
- have_fpa_registers = 0;
+ reg_src1 = bits (thumb_insn_r->arm_insn, 0, 2);
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.xscale.iwmmxt");
- if (feature != NULL)
- {
- static const char *const iwmmxt_names[] = {
- "wR0", "wR1", "wR2", "wR3", "wR4", "wR5", "wR6", "wR7",
- "wR8", "wR9", "wR10", "wR11", "wR12", "wR13", "wR14", "wR15",
- "wCID", "wCon", "wCSSF", "wCASF", "", "", "", "",
- "wCGR0", "wCGR1", "wCGR2", "wCGR3", "", "", "", "",
- };
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
- valid_p = 1;
- for (i = ARM_WR0_REGNUM; i <= ARM_WR15_REGNUM; i++)
- valid_p
- &= tdesc_numbered_register (feature, tdesc_data, i,
- iwmmxt_names[i - ARM_WR0_REGNUM]);
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- /* Check for the control registers, but do not fail if they
- are missing. */
- for (i = ARM_WC0_REGNUM; i <= ARM_WCASF_REGNUM; i++)
- tdesc_numbered_register (feature, tdesc_data, i,
- iwmmxt_names[i - ARM_WR0_REGNUM]);
+ 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;
- for (i = ARM_WCGR0_REGNUM; i <= ARM_WCGR3_REGNUM; i++)
- valid_p
- &= tdesc_numbered_register (feature, tdesc_data, i,
- iwmmxt_names[i - ARM_WR0_REGNUM]);
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- if (!valid_p)
- {
- tdesc_data_cleanup (tdesc_data);
- return NULL;
- }
- }
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
- /* If we have a VFP unit, check whether the single precision registers
- are present. If not, then we will synthesize them as pseudo
- registers. */
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.arm.vfp");
- if (feature != NULL)
- {
- static const char *const vfp_double_names[] = {
- "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
- "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
- "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
- "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
- };
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- /* Require the double precision registers. There must be either
- 16 or 32. */
- valid_p = 1;
- for (i = 0; i < 32; i++)
- {
- valid_p &= tdesc_numbered_register (feature, tdesc_data,
- ARM_D0_REGNUM + i,
- vfp_double_names[i]);
- if (!valid_p)
- break;
- }
- if (!valid_p && i == 16)
- valid_p = 1;
+ return 0;
+}
- /* Also require FPSCR. */
- valid_p &= tdesc_numbered_register (feature, tdesc_data,
- ARM_FPSCR_REGNUM, "fpscr");
- if (!valid_p)
- {
- tdesc_data_cleanup (tdesc_data);
- return NULL;
- }
+/* Handling opcode 010 insns. */
- if (tdesc_unnumbered_register (feature, "s0") == 0)
- have_vfp_pseudos = 1;
+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];
- have_vfp_registers = 1;
+ uint32_t reg_src1 = 0, reg_src2 = 0;
+ uint32_t opcode1 = 0, opcode2 = 0, opcode3 = 0;
- /* If we have VFP, also check for NEON. The architecture allows
- NEON without VFP (integer vector operations only), but GDB
- does not support that. */
- feature = tdesc_find_feature (tdesc,
- "org.gnu.gdb.arm.neon");
- if (feature != NULL)
- {
- /* NEON requires 32 double-precision registers. */
- if (i != 32)
- {
- tdesc_data_cleanup (tdesc_data);
- return NULL;
- }
+ ULONGEST u_regval[2] = {0};
- /* If there are quad registers defined by the stub, use
- their type; otherwise (normally) provide them with
- the default type. */
- if (tdesc_unnumbered_register (feature, "q0") == 0)
- have_neon_pseudos = 1;
+ opcode1 = bits (thumb_insn_r->arm_insn, 10, 12);
- have_neon = 1;
- }
- }
+ 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;
+ }
}
-
- /* If there is already a candidate, use it. */
- for (best_arch = gdbarch_list_lookup_by_info (arches, &info);
- best_arch != NULL;
- best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
+ else if (bit (thumb_insn_r->arm_insn, 11))
{
- if (arm_abi != ARM_ABI_AUTO
- && arm_abi != gdbarch_tdep (best_arch->gdbarch)->arm_abi)
- continue;
+ /* 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. */
+ 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;
+ }
+ }
+ 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;
+ }
- if (fp_model != ARM_FLOAT_AUTO
- && fp_model != gdbarch_tdep (best_arch->gdbarch)->fp_model)
- continue;
+ 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);
- /* There are various other properties in tdep that we do not
- need to check here: those derived from a target description,
- since gdbarches with a different target description are
- automatically disqualified. */
+ return 0;
+}
- /* Do check is_m, though, since it might come from the binary. */
- if (is_m != gdbarch_tdep (best_arch->gdbarch)->is_m)
- continue;
+/* Handling opcode 001 insns. */
- /* Found a match. */
- break;
- }
+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];
- if (best_arch != NULL)
+ 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)
{
- if (tdesc_data != NULL)
- tdesc_data_cleanup (tdesc_data);
- return best_arch->gdbarch;
+ /* 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;
}
- tdep = xcalloc (1, sizeof (struct gdbarch_tdep));
- gdbarch = gdbarch_alloc (&info, tdep);
+ 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 additional information about the architecture we are defining.
- These are gdbarch discriminators, like the OSABI. */
- tdep->arm_abi = arm_abi;
- 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_vfp_pseudos = have_vfp_pseudos;
- tdep->have_neon_pseudos = have_neon_pseudos;
- tdep->have_neon = have_neon;
+ return 0;
+}
- /* Breakpoints. */
- switch (info.byte_order_for_code)
- {
- case BFD_ENDIAN_BIG:
- tdep->arm_breakpoint = arm_default_arm_be_breakpoint;
- tdep->arm_breakpoint_size = sizeof (arm_default_arm_be_breakpoint);
- tdep->thumb_breakpoint = arm_default_thumb_be_breakpoint;
- tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_be_breakpoint);
+/* Handling opcode 100 insns. */
- break;
+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];
- case BFD_ENDIAN_LITTLE:
- tdep->arm_breakpoint = arm_default_arm_le_breakpoint;
- tdep->arm_breakpoint_size = sizeof (arm_default_arm_le_breakpoint);
- tdep->thumb_breakpoint = arm_default_thumb_le_breakpoint;
- tdep->thumb_breakpoint_size = sizeof (arm_default_thumb_le_breakpoint);
+ uint32_t reg_src1 = 0;
+ uint32_t opcode = 0, immed_8 = 0, immed_5 = 0;
- break;
+ ULONGEST u_regval = 0;
- default:
- internal_error (__FILE__, __LINE__,
- _("arm_gdbarch_init: bad byte order for float format"));
+ 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;
}
- /* On ARM targets char defaults to unsigned. */
- set_gdbarch_char_signed (gdbarch, 0);
+ 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);
- /* Note: for displaced stepping, this includes the breakpoint, and one word
- of additional scratch space. This setting isn't used for anything beside
- displaced stepping at present. */
- set_gdbarch_max_insn_length (gdbarch, 4 * DISPLACED_MODIFIED_INSNS);
+ return 0;
+}
- /* This should be low enough for everything. */
- tdep->lowest_pc = 0x20;
- tdep->jb_pc = -1; /* Longjump support not enabled by default. */
+/* Handling opcode 101 insns. */
- /* The default, for both APCS and AAPCS, is to return small
- structures in registers. */
- tdep->struct_return = reg_struct_return;
+static int
+thumb_record_misc (insn_decode_record *thumb_insn_r)
+{
+ struct regcache *reg_cache = thumb_insn_r->regcache;
- set_gdbarch_push_dummy_call (gdbarch, arm_push_dummy_call);
- set_gdbarch_frame_align (gdbarch, arm_frame_align);
+ 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;
- set_gdbarch_write_pc (gdbarch, arm_write_pc);
+ ULONGEST u_regval = 0;
- /* Frame handling. */
- set_gdbarch_dummy_id (gdbarch, arm_dummy_id);
- set_gdbarch_unwind_pc (gdbarch, arm_unwind_pc);
- set_gdbarch_unwind_sp (gdbarch, arm_unwind_sp);
+ 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);
- frame_base_set_default (gdbarch, &arm_normal_base);
+ 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)
+ {
+ /* 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 (register_bits & 0x00000001)
+ register_count++;
+ register_bits = register_bits >> 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)
+ {
+ 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] = 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;
+ 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 (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);
- /* Address manipulation. */
- set_gdbarch_smash_text_address (gdbarch, arm_smash_text_address);
- set_gdbarch_addr_bits_remove (gdbarch, arm_addr_bits_remove);
+ return 0;
+}
- /* Advance PC across function entry code. */
- set_gdbarch_skip_prologue (gdbarch, arm_skip_prologue);
+/* Handling opcode 110 insns. */
- /* Detect whether PC is in function epilogue. */
- set_gdbarch_in_function_epilogue_p (gdbarch, arm_in_function_epilogue_p);
+static int
+thumb_record_ldm_stm_swi (insn_decode_record *thumb_insn_r)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (thumb_insn_r->gdbarch);
+ struct regcache *reg_cache = thumb_insn_r->regcache;
- /* Skip trampolines. */
- set_gdbarch_skip_trampoline_code (gdbarch, arm_skip_stub);
+ 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];
- /* The stack grows downward. */
- set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ ULONGEST u_regval = 0;
- /* Breakpoint manipulation. */
- set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc);
- set_gdbarch_remote_breakpoint_from_pc (gdbarch,
- arm_remote_breakpoint_from_pc);
+ opcode1 = bits (thumb_insn_r->arm_insn, 8, 12);
+ opcode2 = bits (thumb_insn_r->arm_insn, 11, 12);
- /* Information about registers, etc. */
- set_gdbarch_sp_regnum (gdbarch, ARM_SP_REGNUM);
- set_gdbarch_pc_regnum (gdbarch, ARM_PC_REGNUM);
- set_gdbarch_num_regs (gdbarch, ARM_NUM_REGS);
- set_gdbarch_register_type (gdbarch, arm_register_type);
- set_gdbarch_register_reggroup_p (gdbarch, arm_register_reggroup_p);
+ if (1 == opcode2)
+ {
- /* This "info float" is FPA-specific. Use the generic version if we
- do not have FPA. */
- if (gdbarch_tdep (gdbarch)->have_fpa_registers)
- set_gdbarch_print_float_info (gdbarch, arm_print_float_info);
+ /* 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)
+ register_list[register_count++] = 1;
+ register_bits = register_bits >> 1;
+ }
+ record_buf[register_count] = reg_src1;
+ thumb_insn_r->reg_rec_count = register_count + 1;
+ for (register_count = 0; register_count < 8; register_count++)
+ {
+ if (register_list[register_count])
+ {
+ record_buf[index] = register_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_swi_record != NULL)
+ {
+ ret = tdep->arm_swi_record(reg_cache);
+ }
+ else
+ {
+ printf_unfiltered (_("no syscall record support\n"));
+ return -1;
+ }
+ }
- /* Internal <-> external register number maps. */
- set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum);
- set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno);
+ /* 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;
+}
+
+/* Handling opcode 111 insns. */
+
+static int
+thumb_record_branch (insn_decode_record *thumb_insn_r)
+{
+ uint32_t record_buf[8];
+ uint32_t bits_h = 0;
+
+ bits_h = bits (thumb_insn_r->arm_insn, 11, 12);
- set_gdbarch_register_name (gdbarch, arm_register_name);
+ 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;
+ }
- /* Returning results. */
- set_gdbarch_return_value (gdbarch, arm_return_value);
+ /* B(2) is automatically taken care in process_record, as PC is
+ saved there. */
- /* Disassembly. */
- set_gdbarch_print_insn (gdbarch, gdb_print_insn_arm);
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- /* Minsymbol frobbing. */
- set_gdbarch_elf_make_msymbol_special (gdbarch, arm_elf_make_msymbol_special);
- set_gdbarch_coff_make_msymbol_special (gdbarch,
- arm_coff_make_msymbol_special);
- set_gdbarch_record_special_symbol (gdbarch, arm_record_special_symbol);
+ return 0;
+}
- /* Thumb-2 IT block support. */
- set_gdbarch_adjust_breakpoint_address (gdbarch,
- arm_adjust_breakpoint_address);
- /* Virtual tables. */
- set_gdbarch_vbit_in_delta (gdbarch, 1);
+/* Extracts arm/thumb/thumb2 insn depending on the size, and returns 0 on success
+and positive val on fauilure. */
- /* Hook in the ABI-specific overrides, if they have been registered. */
- gdbarch_init_osabi (info, gdbarch);
+static int
+extract_arm_insn (insn_decode_record *insn_record, uint32_t insn_size)
+{
+ gdb_byte buf[insn_size];
- dwarf2_frame_set_init_reg (gdbarch, arm_dwarf2_frame_init_reg);
+ memset (&buf[0], 0, insn_size);
+
+ if (target_read_memory (insn_record->this_addr, &buf[0], insn_size))
+ return 1;
+ insn_record->arm_insn = (uint32_t) extract_unsigned_integer (&buf[0],
+ insn_size,
+ gdbarch_byte_order (insn_record->gdbarch));
+ return 0;
+}
- /* Add some default predicates. */
- frame_unwind_append_unwinder (gdbarch, &arm_stub_unwind);
- dwarf2_append_unwinders (gdbarch);
- frame_unwind_append_unwinder (gdbarch, &arm_exidx_unwind);
- frame_unwind_append_unwinder (gdbarch, &arm_prologue_unwind);
+typedef int (*sti_arm_hdl_fp_t) (insn_decode_record*);
- /* Now we have tuned the configuration, set a few final things,
- based on what the OS ABI has told us. */
+/* Decode arm/thumb insn depending on condition cods and opcodes; and
+ dispatch it. */
- /* If the ABI is not otherwise marked, assume the old GNU APCS. EABI
- binaries are always marked. */
- if (tdep->arm_abi == ARM_ABI_AUTO)
- tdep->arm_abi = ARM_ABI_APCS;
+static int
+decode_insn (insn_decode_record *arm_record, record_type_t record_type,
+ uint32_t insn_size)
+{
- /* Watchpoints are not steppable. */
- set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
+ /* (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] =
+ {
+ arm_record_data_proc_misc_ld_str, /* 000. */
+ arm_record_data_proc_imm, /* 001. */
+ arm_record_ld_st_imm_offset, /* 010. */
+ arm_record_ld_st_reg_offset, /* 011. */
+ arm_record_ld_st_multiple, /* 100. */
+ arm_record_b_bl, /* 101. */
+ arm_record_coproc, /* 110. */
+ arm_record_coproc_data_proc /* 111. */
+ };
- /* We used to default to FPA for generic ARM, but almost nobody
- uses that now, and we now provide a way for the user to force
- the model. So default to the most useful variant. */
- if (tdep->fp_model == ARM_FLOAT_AUTO)
- tdep->fp_model = ARM_FLOAT_SOFT_FPA;
+ /* (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] =
+ { \
+ thumb_record_shift_add_sub, /* 000. */
+ thumb_record_add_sub_cmp_mov, /* 001. */
+ thumb_record_ld_st_reg_offset, /* 010. */
+ thumb_record_ld_st_imm_offset, /* 011. */
+ thumb_record_ld_st_stack, /* 100. */
+ thumb_record_misc, /* 101. */
+ thumb_record_ldm_stm_swi, /* 110. */
+ thumb_record_branch /* 111. */
+ };
- if (tdep->jb_pc >= 0)
- set_gdbarch_get_longjmp_target (gdbarch, arm_get_longjmp_target);
+ uint32_t ret = 0; /* return value: negative:failure 0:success. */
+ uint32_t insn_id = 0;
- /* Floating point sizes and format. */
- set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
- if (tdep->fp_model == ARM_FLOAT_SOFT_FPA || tdep->fp_model == ARM_FLOAT_FPA)
+ if (extract_arm_insn (arm_record, insn_size))
{
- set_gdbarch_double_format
- (gdbarch, floatformats_ieee_double_littlebyte_bigword);
- set_gdbarch_long_double_format
- (gdbarch, floatformats_ieee_double_littlebyte_bigword);
+ 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);
+ }
+ return -1;
}
- else
+ else if (ARM_RECORD == record_type)
{
- set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
- set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double);
+ 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 (have_vfp_pseudos)
+ else if (THUMB_RECORD == record_type)
{
- /* NOTE: These are the only pseudo registers used by
- the ARM target at the moment. If more are added, a
- little more care in numbering will be needed. */
-
- int num_pseudos = 32;
- if (have_neon_pseudos)
- num_pseudos += 16;
- set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
- set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read);
- set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write);
+ /* As thumb does not have condition codes, we set negative. */
+ arm_record->cond = -1;
+ insn_id = bits (arm_record->arm_insn, 13, 15);
+ ret = thumb_handle_insn[insn_id] (arm_record);
}
-
- if (tdesc_data)
+ else if (THUMB2_RECORD == record_type)
{
- set_tdesc_pseudo_register_name (gdbarch, arm_register_name);
-
- tdesc_use_registers (gdbarch, tdesc, tdesc_data);
-
- /* Override tdesc_register_type to adjust the types of VFP
- registers for NEON. */
- set_gdbarch_register_type (gdbarch, arm_register_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;
+ }
+ else
+ {
+ /* Throw assertion. */
+ gdb_assert_not_reached ("not a valid instruction, could not decode");
}
- /* Add standard register aliases. We add aliases even for those
- nanes which are used by the current architecture - it's simpler,
- and does no harm, since nothing ever lists user registers. */
- for (i = 0; i < ARRAY_SIZE (arm_register_aliases); i++)
- user_reg_add (gdbarch, arm_register_aliases[i].name,
- value_of_arm_user_reg, &arm_register_aliases[i].regnum);
-
- return gdbarch;
+ return ret;
}
-static void
-arm_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
-{
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- if (tdep == NULL)
- return;
+/* Cleans up local record registers and memory allocations. */
- fprintf_unfiltered (file, _("arm_dump_tdep: Lowest pc = 0x%lx"),
- (unsigned long) tdep->lowest_pc);
+static void
+deallocate_reg_mem (insn_decode_record *record)
+{
+ xfree (record->arm_regs);
+ xfree (record->arm_mems);
}
-extern initialize_file_ftype _initialize_arm_tdep; /* -Wmissing-prototypes */
-void
-_initialize_arm_tdep (void)
+/* 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)
{
- 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;
- static char *helptext;
- char regdesc[1024], *rdptr = regdesc;
- size_t rest = sizeof (regdesc);
- gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep);
+ 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;
- arm_objfile_data_key
- = register_objfile_data_with_cleanup (NULL, arm_objfile_data_free);
+ ULONGEST u_regval = 0;
- /* Add ourselves to objfile event chain. */
- observer_attach_new_objfile (arm_exidx_new_objfile);
- arm_exidx_data_key
- = register_objfile_data_with_cleanup (NULL, arm_exidx_data_free);
+ insn_decode_record arm_record;
- /* Register an ELF OS ABI sniffer for ARM binaries. */
- gdbarch_register_osabi_sniffer (bfd_arch_arm,
- bfd_target_elf_flavour,
- arm_elf_osabi_sniffer);
+ memset (&arm_record, 0, sizeof (insn_decode_record));
+ arm_record.regcache = regcache;
+ arm_record.this_addr = insn_addr;
+ arm_record.gdbarch = gdbarch;
- /* Initialize the standard target descriptions. */
- initialize_tdesc_arm_with_m ();
- /* Get the number of possible sets of register names defined in opcodes. */
- num_disassembly_options = get_arm_regname_num_options ();
+ if (record_debug > 1)
+ {
+ fprintf_unfiltered (gdb_stdlog, "Process record: arm_process_record "
+ "addr = %s\n",
+ paddress (gdbarch, arm_record.this_addr));
+ }
- /* Add root prefix command for all "set arm"/"show arm" commands. */
- add_prefix_cmd ("arm", no_class, set_arm_command,
- _("Various ARM-specific commands."),
- &setarmcmdlist, "set arm ", 0, &setlist);
+ 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);
+ }
+ return -1;
+ }
- add_prefix_cmd ("arm", no_class, show_arm_command,
- _("Various ARM-specific commands."),
- &showarmcmdlist, "show arm ", 0, &showlist);
+ /* Check the insn, whether it is thumb or arm one. */
- /* Sync the opcode insn printer with our register viewer. */
- parse_arm_disassembler_option ("reg-names-std");
+ t_bit = arm_psr_thumb_bit (arm_record.gdbarch);
+ regcache_raw_read_unsigned (arm_record.regcache, ARM_PS_REGNUM, &u_regval);
- /* Initialize the array that will be passed to
- add_setshow_enum_cmd(). */
- valid_disassembly_styles
- = xmalloc ((num_disassembly_options + 1) * sizeof (char *));
- for (i = 0; i < num_disassembly_options; i++)
+
+ if (!(u_regval & t_bit))
{
- numregs = get_arm_regnames (i, &setname, &setdesc, ®names);
- valid_disassembly_styles[i] = setname;
- length = snprintf (rdptr, rest, "%s - %s\n", setname, setdesc);
- rdptr += length;
- rest -= length;
- /* When we find the default names, tell the disassembler to use
- them. */
- if (!strcmp (setname, "std"))
- {
- disassembly_style = setname;
- set_arm_regname_option (i);
- }
+ /* We are decoding arm insn. */
+ ret = decode_insn (&arm_record, ARM_RECORD, ARM_INSN_SIZE_BYTES);
+ }
+ else
+ {
+ 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);
+ }
+ else
+ {
+ /* We are decoding thumb insn. */
+ ret = decode_insn (&arm_record, THUMB_RECORD, THUMB_INSN_SIZE_BYTES);
+ }
}
- /* Mark the end of valid options. */
- valid_disassembly_styles[num_disassembly_options] = NULL;
-
- /* Create the help text. */
- stb = mem_fileopen ();
- fprintf_unfiltered (stb, "%s%s%s",
- _("The valid values are:\n"),
- regdesc,
- _("The default is \"std\"."));
- helptext = ui_file_xstrdup (stb, NULL);
- ui_file_delete (stb);
-
- add_setshow_enum_cmd("disassembler", no_class,
- valid_disassembly_styles, &disassembly_style,
- _("Set the disassembly style."),
- _("Show the disassembly style."),
- helptext,
- set_disassembly_style_sfunc,
- NULL, /* FIXME: i18n: The disassembly style is
- \"%s\". */
- &setarmcmdlist, &showarmcmdlist);
- add_setshow_boolean_cmd ("apcs32", no_class, &arm_apcs_32,
- _("Set usage of ARM 32-bit mode."),
- _("Show usage of ARM 32-bit mode."),
- _("When off, a 26-bit PC will be used."),
- NULL,
- NULL, /* FIXME: i18n: Usage of ARM 32-bit
- mode is %s. */
- &setarmcmdlist, &showarmcmdlist);
+ 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
+ (arm_record.regcache , arm_record.arm_regs[no_of_rec]))
+ 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,
+ arm_record.arm_mems[no_of_rec].len))
+ ret = -1;
+ }
+ }
- /* Add a command to allow the user to force the FPU model. */
- add_setshow_enum_cmd ("fpu", no_class, fp_model_strings, ¤t_fp_model,
- _("Set the floating point type."),
- _("Show the floating point type."),
- _("auto - Determine the FP typefrom the OS-ABI.\n\
-softfpa - Software FP, mixed-endian doubles on little-endian ARMs.\n\
-fpa - FPA co-processor (GCC compiled).\n\
-softvfp - Software FP with pure-endian doubles.\n\
-vfp - VFP co-processor."),
- set_fp_model_sfunc, show_fp_model,
- &setarmcmdlist, &showarmcmdlist);
+ if (record_full_arch_list_add_end ())
+ ret = -1;
+ }
- /* Add a command to allow the user to force the ABI. */
- add_setshow_enum_cmd ("abi", class_support, arm_abi_strings, &arm_abi_string,
- _("Set the ABI."),
- _("Show the ABI."),
- NULL, arm_set_abi, arm_show_abi,
- &setarmcmdlist, &showarmcmdlist);
- /* Add two commands to allow the user to force the assumed
- execution mode. */
- add_setshow_enum_cmd ("fallback-mode", class_support,
- arm_mode_strings, &arm_fallback_mode_string,
- _("Set the mode assumed when symbols are unavailable."),
- _("Show the mode assumed when symbols are unavailable."),
- NULL, NULL, arm_show_fallback_mode,
- &setarmcmdlist, &showarmcmdlist);
- add_setshow_enum_cmd ("force-mode", class_support,
- arm_mode_strings, &arm_force_mode_string,
- _("Set the mode assumed even when symbols are available."),
- _("Show the mode assumed even when symbols are available."),
- NULL, NULL, arm_show_force_mode,
- &setarmcmdlist, &showarmcmdlist);
+ deallocate_reg_mem (&arm_record);
- /* Debugging flag. */
- add_setshow_boolean_cmd ("arm", class_maintenance, &arm_debug,
- _("Set ARM debugging."),
- _("Show ARM debugging."),
- _("When on, arm-specific debugging is enabled."),
- NULL,
- NULL, /* FIXME: i18n: "ARM debugging is %s. */
- &setdebuglist, &showdebuglist);
+ return ret;
}
+