/* 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"
/* 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)
{
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
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
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);
+
+ 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;
+
+ /* {pli/pld} [r0, r1] */
+ dsc->modinsn[0] = insn1 & 0xfff0;
+ dsc->modinsn[1] = 0xf001;
+ dsc->numinsns = 2;
+
+ dsc->cleanup = &cleanup_preload;
+ return 0;
+}
+
/* Preload instructions with register offset. */
static void
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 = writeback;
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;
+}
+
/* Clean up branch instructions (actually perform the branch, by setting
PC). */
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 = dsc->insn_addr + 4 + offset;
+ dsc->u.branch.dest += 4 + offset;
else
- dsc->u.branch.dest = dsc->insn_addr + 8 + offset;
+ dsc->u.branch.dest += 8 + offset;
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)
+static int
+thumb2_copy_b_bl_blx (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
{
- /* Implement {BX,BLX}<cond> <reg>" as:
+ 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));
- Preparation: cond <- instruction condition
- Insn: mov r0, r0 (nop)
- Cleanup: if (condition true) { r14 <- pc; pc <- dest; }.
+ 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);
+ }
- Don't set r14 in cleanup for BX. */
+ 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->u.branch.dest = displaced_read_reg (regs, dsc, rm);
+ dsc->modinsn[0] = THUMB_NOP;
- dsc->u.branch.cond = cond;
+ install_b_bl_blx (gdbarch, regs, dsc, cond, exchange, link, offset);
+ return 0;
+}
+
+/* Copy B Thumb instructions. */
+static int
+thumb_copy_b (struct gdbarch *gdbarch, unsigned short insn,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int cond = 0;
+ int offset = 0;
+ unsigned short bit_12_15 = bits (insn, 12, 15);
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (bit_12_15 == 0xd)
+ {
+ /* offset = SignExtend (imm8:0, 32) */
+ offset = sbits ((insn << 1), 0, 8);
+ cond = bits (insn, 8, 11);
+ }
+ else if (bit_12_15 == 0xe) /* Encoding T2 */
+ {
+ offset = sbits ((insn << 1), 0, 11);
+ cond = INST_AL;
+ }
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: copying b immediate insn %.4x "
+ "with offset %d\n", insn, offset);
+
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = 0;
+ dsc->u.branch.exchange = 0;
+ dsc->u.branch.dest = from + 4 + offset;
+
+ dsc->modinsn[0] = THUMB_NOP;
+
+ dsc->cleanup = &cleanup_branch;
+
+ 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
+ Insn: mov r0, r0 (nop)
+ Cleanup: if (condition true) { r14 <- pc; pc <- dest; }.
+
+ Don't set r14 in cleanup for BX. */
+
+ dsc->u.branch.dest = displaced_read_reg (regs, dsc, rm);
+
+ dsc->u.branch.cond = cond;
dsc->u.branch.link = link;
dsc->u.branch.exchange = 1;
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
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
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;
+}
+
/* Cleanup/copy arithmetic/logic insns with shifted register RHS. */
static void
return 0;
}
-/* Copy byte/word loads and stores. */
+/* Copy byte/half word/word loads and stores. */
static void
-install_ldr_str_ldrb_strb (struct gdbarch *gdbarch, struct regcache *regs,
- struct displaced_step_closure *dsc, int load,
- int immed, int writeback, int byte, int usermode,
- int rt, int rm, int rn)
+install_load_store (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc, int load,
+ int immed, int writeback, int size, int usermode,
+ int rt, int rm, int rn)
{
ULONGEST rt_val, rn_val, rm_val = 0;
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 = writeback;
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 byte, int usermode)
+ int load, int size, int usermode)
{
int immed = !bit (insn, 25);
int writeback = (bit (insn, 24) == 0 || bit (insn, 21) != 0);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
"displaced: copying %s%s r%d [r%d] insn %.8lx\n",
- load ? (byte ? "ldrb" : "ldr")
- : (byte ? "strb" : "str"), usermode ? "t" : "",
+ load ? (size == 1 ? "ldrb" : "ldr")
+ : (size == 1 ? "strb" : "str"), usermode ? "t" : "",
rt, rn,
(unsigned long) insn);
- install_ldr_str_ldrb_strb (gdbarch, regs, dsc, load, immed, writeback, byte,
- usermode, rt, rm, rn);
+ install_load_store (gdbarch, regs, dsc, load, immed, writeback, size,
+ usermode, rt, rm, rn);
if (load || rt != ARM_PC_REGNUM)
{
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;
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
-
-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);
+/* 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. */
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
if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
|| (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
- return arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 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 arm_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 arm_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 arm_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 arm_copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 0);
return 1;
}
+/* Decode shifted register instructions. */
+
+static int
+thumb2_decode_dp_shift_reg (struct gdbarch *gdbarch, uint16_t insn1,
+ uint16_t insn2, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ /* PC is only allowed to be used in instruction MOV. */
+
+ unsigned int op = bits (insn1, 5, 8);
+ unsigned int rn = bits (insn1, 0, 3);
+
+ if (op == 0x2 && rn == 0xf) /* MOV */
+ return thumb2_copy_alu_imm (gdbarch, insn1, insn2, regs, dsc);
+ else
+ return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
+ "dp (shift reg)", dsc);
+}
+
+
+/* Decode extension register load/store. Exactly the same as
+ arm_decode_ext_reg_ld_st. */
+
+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)
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 = arm_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 = arm_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 = arm_decode_ld_st_word_ubyte (gdbarch, insn, regs, dsc);
- break;
+ install_pc_relative (gdbarch, regs, dsc, rd);
- case 0x7:
- err = arm_decode_media (gdbarch, insn, dsc);
- break;
+ return 0;
+}
- case 0x8: case 0x9: case 0xa: case 0xb:
- err = arm_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 = arm_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. */
+ Cleanup: PC <- r7, r7 <- r8, r8 <-tmp[0]
-#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}
+ (2) register list is not full, supposing there are N registers in
+ register list (except PC, 0 <= N <= 7).
+ Prepare: for each i, 0 - N, tmp[i] <- ri.
-static 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;
-
-/* 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);
-
- if (readbuf)
- arm_neon_quad_read (gdbarch, regcache, i,
- readbuf + i * unit_length);
- }
- else
- {
- char name_buf[4];
- int regnum;
+ 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]);
+
+ 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 */
+ if (info->endian == BFD_ENDIAN_BIG)
+ return print_insn_big_arm (memaddr, info);
+ else
+ return print_insn_little_arm (memaddr, info);
}
-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);
-}
+/* 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. */
-static void
-show_arm_command (char *args, int from_tty)
-{
- cmd_show_list (showarmcmdlist, from_tty, "");
-}
+#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}
-static void
-arm_update_current_architecture (void)
-{
- struct gdbarch_info 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 the current architecture is not ARM, we have nothing to do. */
- if (gdbarch_bfd_arch_info (target_gdbarch)->arch != bfd_arch_arm)
- return;
+/* 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. */
- /* Update the architecture. */
- gdbarch_info_init (&info);
+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);
- if (!gdbarch_update_p (info))
- internal_error (__FILE__, __LINE__, _("could not update architecture"));
-}
-
-static void
-set_fp_model_sfunc (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- 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 ();
-}
+ if (arm_pc_is_thumb (gdbarch, *pcptr))
+ {
+ *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
-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 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;
+ }
+ }
+ }
- 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]);
+ *lenptr = tdep->thumb_breakpoint_size;
+ return tdep->thumb_breakpoint;
+ }
else
- 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 ();
+ {
+ *lenptr = tdep->arm_breakpoint_size;
+ return tdep->arm_breakpoint;
+ }
}
static void
-arm_show_abi (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_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);
+ 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;
}
-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);
-}
+/* 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_show_force_mode (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
+arm_extract_return_value (struct type *type, struct regcache *regs,
+ gdb_byte *valbuf)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- fprintf_filtered (file,
- _("The current execution mode assumed "
- "(even when symbols are available) is \"%s\".\n"),
- arm_force_mode_string);
-}
+ 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 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. */
+ regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf);
+ convert_from_extended (floatformat_from_type (type), tmpbuf,
+ valbuf, gdbarch_byte_order (gdbarch));
+ }
+ break;
-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);
+ 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;
- if (gdbarch_tdep (gdbarch)->have_vfp_pseudos
- && i >= num_regs && i < num_regs + 32)
+ 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)
{
- 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",
- };
+ /* 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;
- return vfp_pseudo_names[i - num_regs];
+ 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;
+ }
}
-
- if (gdbarch_tdep (gdbarch)->have_neon_pseudos
- && i >= num_regs + 32 && i < num_regs + 32 + 16)
+ else
{
- static const char *const neon_pseudo_names[] = {
- "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7",
- "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15",
- };
+ /* 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];
- return neon_pseudo_names[i - num_regs - 32];
+ 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;
+ }
}
+}
- 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];
-}
+/* 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
-set_disassembly_style (void)
+static int
+arm_return_in_memory (struct gdbarch *gdbarch, struct type *type)
{
- int current;
+ int nRc;
+ enum type_code code;
- /* 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);
+ CHECK_TYPEDEF (type);
- /* Synchronize the disassembler. */
- set_arm_regname_option (current);
-}
+ /* 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.
-/* Test whether the coff symbol specific value corresponds to a Thumb
+ 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. */
+
+ /* All aggregate types that won't fit in a register must be returned
+ in memory. */
+ if (TYPE_LENGTH (type) > INT_REGISTER_SIZE)
+ {
+ 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
+ */
+
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
+ {
+ enum type_code field_type_code;
+ field_type_code = TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type,
+ i)));
+
+ /* Is it a floating point type field? */
+ if (field_type_code == TYPE_CODE_FLT)
+ {
+ nRc = 1;
+ break;
+ }
+
+ /* If bitpos != 0, then we have to care about it. */
+ if (TYPE_FIELD_BITPOS (type, i) != 0)
+ {
+ /* Bitfields are not addressable. If the field bitsize is
+ zero, then the field is not packed. Hence it cannot be
+ a bitfield or any other packed type. */
+ if (TYPE_FIELD_BITSIZE (type, i) == 0)
+ {
+ nRc = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ return nRc;
+}
+
+/* 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)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ if (TYPE_CODE (type) == TYPE_CODE_FLT)
+ {
+ gdb_byte buf[MAX_REGISTER_SIZE];
+
+ switch (gdbarch_tdep (gdbarch)->fp_model)
+ {
+ case ARM_FLOAT_FPA:
+
+ convert_to_extended (floatformat_from_type (type), buf, valbuf,
+ gdbarch_byte_order (gdbarch));
+ regcache_cooked_write (regs, ARM_F0_REGNUM, buf);
+ 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_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;
+ }
+ }
+ 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);
+
+ 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;
+
+ 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];
+
+ 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;
+ }
+ }
+}
+
+
+/* Handle function return values. */
+
+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)
+{
+ 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;
+
+ 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);
+
+ if (readbuf)
+ arm_neon_quad_read (gdbarch, regcache, i,
+ readbuf + i * unit_length);
+ }
+ else
+ {
+ char name_buf[4];
+ int regnum;
+
+ 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 (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
+ || TYPE_CODE (valtype) == TYPE_CODE_UNION
+ || TYPE_CODE (valtype) == TYPE_CODE_ARRAY)
+ {
+ if (tdep->struct_return == pcc_struct_return
+ || arm_return_in_memory (gdbarch, valtype))
+ return RETURN_VALUE_STRUCT_CONVENTION;
+ }
+
+ /* AAPCS returns complex types longer than a register in memory. */
+ if (tdep->arm_abi != ARM_ABI_APCS
+ && TYPE_CODE (valtype) == TYPE_CODE_COMPLEX
+ && TYPE_LENGTH (valtype) > INT_REGISTER_SIZE)
+ return RETURN_VALUE_STRUCT_CONVENTION;
+
+ if (writebuf)
+ arm_store_return_value (valtype, regcache, writebuf);
+
+ if (readbuf)
+ arm_extract_return_value (valtype, regcache, readbuf);
+
+ return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+
+static int
+arm_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+{
+ 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);
+
+ if (target_read_memory (jb_addr + tdep->jb_pc * tdep->jb_elt_size, buf,
+ INT_REGISTER_SIZE))
+ return 0;
+
+ *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE, byte_order);
+ return 1;
+}
+
+/* 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 char *name;
+ int namelen;
+ CORE_ADDR start_addr;
+
+ /* 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 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);
+ }
+
+ /* 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
+ target_len -= strlen ("_from_arm");
+
+ target_name = alloca (target_len + 1);
+ memcpy (target_name, name + 2, target_len);
+ target_name[target_len] = '\0';
+
+ sec = find_pc_section (pc);
+ objfile = (sec == NULL) ? NULL : sec->objfile;
+ minsym = lookup_minimal_symbol (target_name, NULL, objfile);
+ if (minsym != NULL)
+ return SYMBOL_VALUE_ADDRESS (minsym);
+ else
+ return 0;
+ }
+
+ 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, "");
+}
+
+static void
+arm_update_current_architecture (void)
+{
+ struct gdbarch_info info;
+
+ /* If the current architecture is not ARM, we have nothing to do. */
+ if (gdbarch_bfd_arch_info (target_gdbarch ())->arch != bfd_arch_arm)
+ return;
+
+ /* Update the architecture. */
+ gdbarch_info_init (&info);
+
+ if (!gdbarch_update_p (info))
+ internal_error (__FILE__, __LINE__, _("could not update architecture"));
+}
+
+static void
+set_fp_model_sfunc (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ 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
+ 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
+/* 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);
+
+ 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",
+ };
+
+ 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)
+{
+ 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)
+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)
{
- return (val == C_THUMBEXT
- || val == C_THUMBSTAT
- || val == C_THUMBEXTFUNC
- || val == C_THUMBSTATFUNC
- || val == C_THUMBLABEL);
+ 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;
}
-/* 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)
+typedef enum
{
- if (ARM_SYM_BRANCH_TYPE (&((elf_symbol_type *)sym)->internal_elf_sym)
- == ST_BRANCH_TO_THUMB)
- MSYMBOL_SET_SPECIAL (msym);
-}
+ ARM_RECORD_STRH=1,
+ ARM_RECORD_STRD
+} arm_record_strx_t;
-static void
-arm_coff_make_msymbol_special(int val, struct minimal_symbol *msym)
+typedef enum
{
- if (coff_sym_is_thumb (val))
- MSYMBOL_SET_SPECIAL (msym);
-}
+ ARM_RECORD=1,
+ THUMB_RECORD,
+ THUMB2_RECORD
+} record_type_t;
-static void
-arm_objfile_data_free (struct objfile *objfile, void *arg)
+
+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 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]);
+ 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;
}
-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;
+/* Handling ARM extension space insns. */
- gdb_assert (name[0] == '$');
- if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
- return;
+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. */
+ }
- data = objfile_data (objfile, arm_objfile_data_key);
- if (data == NULL)
+
+ opcode1 = bits (arm_insn_r->arm_insn, 25, 27);
+ if (3 == opcode1 && bit (arm_insn_r->arm_insn, 4))
{
- 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) *);
+ ret = -1;
+ /* Undefined instruction on ARM V5; need to handle if later
+ versions define it. */
}
- map_p = &data->section_maps[bfd_get_section (sym)->index];
- new_map_sym.value = sym->value;
- new_map_sym.type = name[1];
+ 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);
- /* 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))
+ /* 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
{
- 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;
- }
+ return -1;
}
- VEC_safe_push (arm_mapping_symbol_s, *map_p, &new_map_sym);
+ 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;
}
-static void
-arm_write_pc (struct regcache *regcache, CORE_ADDR pc)
+/* Handling opcode 001 insns. */
+
+static int
+arm_record_data_proc_imm (insn_decode_record *arm_insn_r)
{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- regcache_cooked_write_unsigned (regcache, ARM_PC_REGNUM, pc);
+ uint32_t record_buf[8], record_buf_mem[8];
- /* If necessary, set the T bit. */
- if (arm_apcs_32)
+ 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)
+ )
{
- 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);
+ /* 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
- regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
- val & ~t_bit);
+ {
+ /* 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;
}
-/* 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]. */
+/* Handling opcode 010 insns. */
-static enum register_status
-arm_neon_quad_read (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, gdb_byte *buf)
+static int
+arm_record_ld_st_imm_offset (insn_decode_record *arm_insn_r)
{
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
- enum register_status status;
-
- sprintf (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);
+ struct regcache *reg_cache = arm_insn_r->regcache;
- return REG_VALID;
-}
+ 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];
-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;
+ ULONGEST u_regval = 0;
- gdb_assert (regnum >= num_regs);
- regnum -= num_regs;
+ 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 (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
- /* Quad-precision register. */
- return arm_neon_quad_read (gdbarch, regcache, regnum - 32, buf);
+ 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
{
- 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;
+ /* 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
- offset = (regnum & 1) ? 4 : 0;
-
- sprintf (name_buf, "d%d", regnum >> 1);
- double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
- strlen (name_buf));
+ {
+ tgt_mem_addr = u_regval - offset_12;
+ }
- status = regcache_raw_read (regcache, double_regnum, reg_buf);
- if (status == REG_VALID)
- memcpy (buf, reg_buf + offset, 4);
- return status;
+ 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;
}
-/* 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]. */
+/* Handling opcode 011 insns. */
-static void
-arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache,
- int regnum, const gdb_byte *buf)
+static int
+arm_record_ld_st_reg_offset (insn_decode_record *arm_insn_r)
{
- char name_buf[4];
- gdb_byte reg_buf[8];
- int offset, double_regnum;
+ struct regcache *reg_cache = arm_insn_r->regcache;
- sprintf (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;
+ 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];
- regcache_raw_write (regcache, double_regnum, buf + offset);
- offset = 8 - offset;
- regcache_raw_write (regcache, double_regnum + 1, buf + offset);
-}
+ LONGEST s_word;
+ ULONGEST u_regval[2];
-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;
+ arm_insn_r->opcode = bits (arm_insn_r->arm_insn, 21, 24);
+ arm_insn_r->decode = bits (arm_insn_r->arm_insn, 4, 7);
- gdb_assert (regnum >= num_regs);
- regnum -= num_regs;
+ /* Handle enhanced store insns and LDRD DSP insn,
+ order begins according to addressing modes for store insns
+ STRH insn. */
- if (gdbarch_tdep (gdbarch)->have_neon_pseudos && regnum >= 32 && regnum < 48)
- /* Quad-precision register. */
- arm_neon_quad_write (gdbarch, regcache, regnum - 32, buf);
+ /* 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
{
- /* 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;
+ 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
- offset = (regnum & 1) ? 4 : 0;
-
- sprintf (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);
+ {
+ /* 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;
+ }
+ }
}
-}
-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);
+ 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;
}
-\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;
-}
+/* Handling opcode 100 insns. */
static int
-arm_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
- struct reggroup *group)
+arm_record_ld_st_multiple (insn_decode_record *arm_insn_r)
{
- /* 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);
-}
+ struct regcache *reg_cache = arm_insn_r->regcache;
-\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.
+ 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];
- Called e.g. at program startup, when reading a core file, and when
- reading a binary file. */
+ ULONGEST u_regval[2] = {0};
-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;
+ /* 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;
+}
- 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;
- }
- }
+/* Handling opcode 001 insns. */
+
+static int
+thumb_record_add_sub_cmp_mov (insn_decode_record *thumb_insn_r)
+{
+ uint32_t record_buf[8];
+ uint32_t reg_src1 = 0;
+
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
- /* 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",
- };
+ record_buf[0] = ARM_PS_REGNUM;
+ record_buf[1] = reg_src1;
+ thumb_insn_r->reg_rec_count = 2;
- /* 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;
+ REG_ALLOC (thumb_insn_r->arm_regs, thumb_insn_r->reg_rec_count, record_buf);
- /* 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;
- }
+ return 0;
+}
- if (tdesc_unnumbered_register (feature, "s0") == 0)
- have_vfp_pseudos = 1;
+/* Handling opcode 010 insns. */
- have_vfp_registers = 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];
- /* 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;
- }
+ uint32_t reg_src1 = 0, reg_src2 = 0;
+ uint32_t opcode1 = 0, opcode2 = 0, opcode3 = 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;
+ ULONGEST u_regval[2] = {0};
- have_neon = 1;
- }
- }
- }
+ opcode1 = bits (thumb_insn_r->arm_insn, 10, 12);
- /* 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 (bit (thumb_insn_r->arm_insn, 12))
{
- if (arm_abi != ARM_ABI_AUTO
- && arm_abi != gdbarch_tdep (best_arch->gdbarch)->arm_abi)
- continue;
+ /* Handle load/store register offset. */
+ opcode2 = bits (thumb_insn_r->arm_insn, 9, 10);
+ if (opcode2 >= 12 && opcode2 <= 15)
+ {
+ /* LDR(2), LDRB(2) , LDRH(2), LDRSB, LDRSH. */
+ reg_src1 = bits (thumb_insn_r->arm_insn,0, 2);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (opcode2 >= 8 && opcode2 <= 10)
+ {
+ /* STR(2), STRB(2), STRH(2) . */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 3, 5);
+ reg_src2 = bits (thumb_insn_r->arm_insn, 6, 8);
+ regcache_raw_read_unsigned (reg_cache, reg_src1, &u_regval[0]);
+ regcache_raw_read_unsigned (reg_cache, reg_src2, &u_regval[1]);
+ if (8 == opcode2)
+ record_buf_mem[0] = 4; /* STR (2). */
+ else if (10 == opcode2)
+ record_buf_mem[0] = 1; /* STRB (2). */
+ else if (9 == opcode2)
+ record_buf_mem[0] = 2; /* STRH (2). */
+ record_buf_mem[1] = u_regval[0] + u_regval[1];
+ thumb_insn_r->mem_rec_count = 1;
+ }
+ }
+ else if (bit (thumb_insn_r->arm_insn, 11))
+ {
+ /* Handle load from literal pool. */
+ /* LDR(3). */
+ reg_src1 = bits (thumb_insn_r->arm_insn, 8, 10);
+ record_buf[0] = reg_src1;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else if (opcode1)
+ {
+ opcode2 = bits (thumb_insn_r->arm_insn, 8, 9);
+ opcode3 = bits (thumb_insn_r->arm_insn, 0, 2);
+ if ((3 == opcode2) && (!opcode3))
+ {
+ /* Branch with exchange. */
+ record_buf[0] = ARM_PS_REGNUM;
+ thumb_insn_r->reg_rec_count = 1;
+ }
+ else
+ {
+ /* Format 8; special data processing insns. */
+ 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. */
- set_gdbarch_register_name (gdbarch, arm_register_name);
+ 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);
+
+ 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 ();
- 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 ();
+ 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;
}
+