/* 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
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GDB.
#define MSYMBOL_IS_SPECIAL(msym) \
MSYMBOL_TARGET_FLAG_1 (msym)
-/* Macros for swapping shorts and ints. In the unlikely case that anybody else needs these,
- move to a general header. (A better solution might be to define memory read routines that
- know whether they are reading code or data.) */
-
-#define SWAP_SHORT(x) \
- ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8));
-
-#define SWAP_INT(x) \
- ( ((x & 0xff000000) >> 24) \
- | ((x & 0x00ff0000) >> 8) \
- | ((x & 0x0000ff00) << 8) \
- | ((x & 0x000000ff) << 24))
-
/* Per-objfile data used for mapping symbols. */
static const struct objfile_data *arm_objfile_data_key;
static void convert_to_extended (const struct floatformat *, void *,
const void *, int);
+static void arm_neon_quad_read (struct gdbarch *gdbarch,
+ struct regcache *regcache,
+ int regnum, gdb_byte *buf);
+static void arm_neon_quad_write (struct gdbarch *gdbarch,
+ struct regcache *regcache,
+ int regnum, const gdb_byte *buf);
+
struct arm_prologue_cache
{
/* The stack pointer at the time this frame was created; i.e. the
struct trad_frame_saved_reg *saved_regs;
};
+static CORE_ADDR arm_analyze_prologue (struct gdbarch *gdbarch,
+ CORE_ADDR prologue_start,
+ CORE_ADDR prologue_end,
+ struct arm_prologue_cache *cache);
+
+/* Architecture version for displaced stepping. This effects the behaviour of
+ certain instructions, and really should not be hard-wired. */
+
+#define DISPLACED_STEPPING_ARCH_VERSION 5
+
/* Addresses for calling Thumb functions have the bit 0 set.
Here are some macros to test, set, or clear bit 0 of addresses. */
#define IS_THUMB_ADDR(addr) ((addr) & 1)
return lhs->value < rhs->value;
}
-/* Determine if the program counter specified in MEMADDR is in a Thumb
- function. This function should be called for addresses unrelated to
- any executing frame; otherwise, prefer arm_frame_is_thumb. */
+/* Search for the mapping symbol covering MEMADDR. If one is found,
+ return its type. Otherwise, return 0. If START is non-NULL,
+ set *START to the location of the mapping symbol. */
-static int
-arm_pc_is_thumb (CORE_ADDR memaddr)
+static char
+arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start)
{
struct obj_section *sec;
- struct minimal_symbol *sym;
-
- /* If bit 0 of the address is set, assume this is a Thumb address. */
- if (IS_THUMB_ADDR (memaddr))
- return 1;
-
- /* If the user wants to override the symbol table, let him. */
- if (strcmp (arm_force_mode_string, "arm") == 0)
- return 0;
- if (strcmp (arm_force_mode_string, "thumb") == 0)
- return 1;
/* If there are mapping symbols, consult them. */
sec = find_pc_section (memaddr);
{
map_sym = VEC_index (arm_mapping_symbol_s, map, idx);
if (map_sym->value == map_key.value)
- return map_sym->type == 't';
+ {
+ if (start)
+ *start = map_sym->value + obj_section_addr (sec);
+ return map_sym->type;
+ }
}
if (idx > 0)
{
map_sym = VEC_index (arm_mapping_symbol_s, map, idx - 1);
- return map_sym->type == 't';
+ if (start)
+ *start = map_sym->value + obj_section_addr (sec);
+ return map_sym->type;
}
}
}
}
+ return 0;
+}
+
+static CORE_ADDR arm_get_next_pc_raw (struct frame_info *frame,
+ CORE_ADDR pc, int insert_bkpt);
+
+/* Determine if the program counter specified in MEMADDR is in a Thumb
+ function. This function should be called for addresses unrelated to
+ any executing frame; otherwise, prefer arm_frame_is_thumb. */
+
+static int
+arm_pc_is_thumb (CORE_ADDR memaddr)
+{
+ struct obj_section *sec;
+ struct minimal_symbol *sym;
+ char type;
+
+ /* If bit 0 of the address is set, assume this is a Thumb address. */
+ if (IS_THUMB_ADDR (memaddr))
+ return 1;
+
+ /* If the user wants to override the symbol table, let him. */
+ if (strcmp (arm_force_mode_string, "arm") == 0)
+ return 0;
+ if (strcmp (arm_force_mode_string, "thumb") == 0)
+ return 1;
+
+ /* If there are mapping symbols, consult them. */
+ type = arm_find_mapping_symbol (memaddr, NULL);
+ if (type)
+ return type == 't';
+
/* Thumb functions have a "special" bit set in minimal symbols. */
sym = lookup_minimal_symbol_by_pc (memaddr);
if (sym)
target, then trust the current value of $cpsr. This lets
"display/i $pc" always show the correct mode (though if there is
a symbol table we will not reach here, so it still may not be
- displayed in the mode it will be executed). */
+ displayed in the mode it will be executed).
+
+ As a further heuristic if we detect that we are doing a single-step we
+ see what state executing the current instruction ends up with us being
+ in. */
if (target_has_registers)
- return arm_frame_is_thumb (get_current_frame ());
+ {
+ struct frame_info *current_frame = get_current_frame ();
+ CORE_ADDR current_pc = get_frame_pc (current_frame);
+ int is_thumb = arm_frame_is_thumb (current_frame);
+ CORE_ADDR next_pc;
+ if (memaddr == current_pc)
+ return is_thumb;
+ else
+ {
+ struct gdbarch *gdbarch = get_frame_arch (current_frame);
+ next_pc = arm_get_next_pc_raw (current_frame, current_pc, FALSE);
+ if (memaddr == gdbarch_addr_bits_remove (gdbarch, next_pc))
+ return IS_THUMB_ADDR (next_pc);
+ else
+ return is_thumb;
+ }
+ }
/* Otherwise we're out of luck; we assume ARM. */
return 0;
return val & ~1;
}
+/* Return 1 if PC is the start of a compiler helper function which
+ can be safely ignored during prologue skipping. */
+static int
+skip_prologue_function (CORE_ADDR pc)
+{
+ struct minimal_symbol *msym;
+ const char *name;
+
+ msym = lookup_minimal_symbol_by_pc (pc);
+ if (msym == NULL || SYMBOL_VALUE_ADDRESS (msym) != pc)
+ return 0;
+
+ name = SYMBOL_LINKAGE_NAME (msym);
+ if (name == NULL)
+ return 0;
+
+ /* The GNU linker's Thumb call stub to foo is named
+ __foo_from_thumb. */
+ if (strstr (name, "_from_thumb") != NULL)
+ name += 2;
+
+ /* On soft-float targets, __truncdfsf2 is called to convert promoted
+ arguments to their argument types in non-prototyped
+ functions. */
+ if (strncmp (name, "__truncdfsf2", strlen ("__truncdfsf2")) == 0)
+ return 1;
+ if (strncmp (name, "__aeabi_d2f", strlen ("__aeabi_d2f")) == 0)
+ return 1;
+
+ return 0;
+}
+
+/* Support routines for instruction parsing. */
+#define submask(x) ((1L << ((x) + 1)) - 1)
+#define bit(obj,st) (((obj) >> (st)) & 1)
+#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
+#define sbits(obj,st,fn) \
+ ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
+#define BranchDest(addr,instr) \
+ ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
+
/* Analyze a Thumb prologue, looking for a recognizable stack frame
and frame pointer. Scan until we encounter a store that could
- clobber the stack frame unexpectedly, or an unknown instruction. */
+ clobber the stack frame unexpectedly, or an unknown instruction.
+ Return the last address which is definitely safe to skip for an
+ initial breakpoint. */
static CORE_ADDR
thumb_analyze_prologue (struct gdbarch *gdbarch,
CORE_ADDR start, CORE_ADDR limit,
struct arm_prologue_cache *cache)
{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
int i;
pv_t regs[16];
struct pv_area *stack;
{
unsigned short insn;
- insn = read_memory_unsigned_integer (start, 2);
-
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- insn = SWAP_SHORT (insn);
+ insn = read_memory_unsigned_integer (start, 2, byte_order_for_code);
if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
{
regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
offset);
}
- else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */
- regs[THUMB_FP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
- (insn & 0xff) << 2);
+ else if ((insn & 0xf800) == 0xa800) /* add Rd, sp, #imm */
+ regs[bits (insn, 8, 10)] = pv_add_constant (regs[ARM_SP_REGNUM],
+ (insn & 0xff) << 2);
+ else if ((insn & 0xfe00) == 0x1c00 /* add Rd, Rn, #imm */
+ && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM))
+ regs[bits (insn, 0, 2)] = pv_add_constant (regs[bits (insn, 3, 5)],
+ bits (insn, 6, 8));
+ else if ((insn & 0xf800) == 0x3000 /* add Rd, #imm */
+ && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM))
+ regs[bits (insn, 8, 10)] = pv_add_constant (regs[bits (insn, 8, 10)],
+ bits (insn, 0, 7));
+ else if ((insn & 0xfe00) == 0x1800 /* add Rd, Rn, Rm */
+ && pv_is_register (regs[bits (insn, 6, 8)], ARM_SP_REGNUM)
+ && pv_is_constant (regs[bits (insn, 3, 5)]))
+ regs[bits (insn, 0, 2)] = pv_add (regs[bits (insn, 3, 5)],
+ regs[bits (insn, 6, 8)]);
+ else if ((insn & 0xff00) == 0x4400 /* add Rd, Rm */
+ && pv_is_constant (regs[bits (insn, 3, 6)]))
+ {
+ int rd = (bit (insn, 7) << 3) + bits (insn, 0, 2);
+ int rm = bits (insn, 3, 6);
+ regs[rd] = pv_add (regs[rd], regs[rm]);
+ }
else if ((insn & 0xff00) == 0x4600) /* mov hi, lo or mov lo, hi */
{
int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4);
pv_area_store (stack, addr, 4, regs[regno]);
}
+ else if ((insn & 0xf800) == 0x6000) /* str rd, [rn, #off] */
+ {
+ int rd = bits (insn, 0, 2);
+ int rn = bits (insn, 3, 5);
+ pv_t addr;
+
+ offset = bits (insn, 6, 10) << 2;
+ addr = pv_add_constant (regs[rn], offset);
+
+ if (pv_area_store_would_trash (stack, addr))
+ break;
+
+ pv_area_store (stack, addr, 4, regs[rd]);
+ }
+ else if (((insn & 0xf800) == 0x7000 /* strb Rd, [Rn, #off] */
+ || (insn & 0xf800) == 0x8000) /* strh Rd, [Rn, #off] */
+ && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM))
+ /* Ignore stores of argument registers to the stack. */
+ ;
+ else if ((insn & 0xf800) == 0xc800 /* ldmia Rn!, { registers } */
+ && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM))
+ /* Ignore block loads from the stack, potentially copying
+ parameters from memory. */
+ ;
+ else if ((insn & 0xf800) == 0x9800 /* ldr Rd, [Rn, #immed] */
+ || ((insn & 0xf800) == 0x6800 /* ldr Rd, [sp, #immed] */
+ && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM)))
+ /* Similarly ignore single loads from the stack. */
+ ;
+ else if ((insn & 0xffc0) == 0x0000 /* lsls Rd, Rm, #0 */
+ || (insn & 0xffc0) == 0x1c00) /* add Rd, Rn, #0 */
+ /* Skip register copies, i.e. saves to another register
+ instead of the stack. */
+ ;
+ else if ((insn & 0xf800) == 0x2000) /* movs Rd, #imm */
+ /* Recognize constant loads; even with small stacks these are necessary
+ on Thumb. */
+ regs[bits (insn, 8, 10)] = pv_constant (bits (insn, 0, 7));
+ else if ((insn & 0xf800) == 0x4800) /* ldr Rd, [pc, #imm] */
+ {
+ /* Constant pool loads, for the same reason. */
+ unsigned int constant;
+ CORE_ADDR loc;
+
+ loc = start + 4 + bits (insn, 0, 7) * 4;
+ constant = read_memory_unsigned_integer (loc, 4, byte_order);
+ regs[bits (insn, 8, 10)] = pv_constant (constant);
+ }
+ else if ((insn & 0xe000) == 0xe000 && cache == NULL)
+ {
+ /* Only recognize 32-bit instructions for prologue skipping. */
+ unsigned short inst2;
+
+ inst2 = read_memory_unsigned_integer (start + 2, 2,
+ byte_order_for_code);
+
+ if ((insn & 0xf800) == 0xf000 && (inst2 & 0xe800) == 0xe800)
+ {
+ /* BL, BLX. Allow some special function calls when
+ skipping the prologue; GCC generates these before
+ storing arguments to the stack. */
+ CORE_ADDR nextpc;
+ int j1, j2, imm1, imm2;
+
+ imm1 = sbits (insn, 0, 10);
+ imm2 = bits (inst2, 0, 10);
+ j1 = bit (inst2, 13);
+ j2 = bit (inst2, 11);
+
+ offset = ((imm1 << 12) + (imm2 << 1));
+ offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+ nextpc = start + 4 + offset;
+ /* For BLX make sure to clear the low bits. */
+ if (bit (inst2, 12) == 0)
+ nextpc = nextpc & 0xfffffffc;
+
+ if (!skip_prologue_function (nextpc))
+ break;
+ }
+ else if ((insn & 0xfe50) == 0xe800 /* stm{db,ia} Rn[!], { registers } */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else if ((insn & 0xfe50) == 0xe840 /* strd Rt, Rt2, [Rn, #imm] */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else if ((insn & 0xffd0) == 0xe890 /* ldmia Rn[!], { registers } */
+ && (inst2 & 0x8000) == 0x0000
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */
+ && (inst2 & 0x8000) == 0x0000)
+ /* Since we only recognize this for prologue skipping, do not bother
+ to compute the constant. */
+ regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
+ else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm12 */
+ && (inst2 & 0x8000) == 0x0000)
+ /* Since we only recognize this for prologue skipping, do not bother
+ to compute the constant. */
+ regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
+ else if ((insn & 0xfbf0) == 0xf2a0 /* sub.w Rd, Rn, #imm8 */
+ && (inst2 & 0x8000) == 0x0000)
+ /* Since we only recognize this for prologue skipping, do not bother
+ to compute the constant. */
+ regs[bits (inst2, 8, 11)] = regs[bits (insn, 0, 3)];
+ else if ((insn & 0xff50) == 0xf850 /* ldr.w Rd, [Rn, #imm]{!} */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else if ((insn & 0xff50) == 0xe950 /* ldrd Rt, Rt2, [Rn, #imm]{!} */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else if ((insn & 0xff50) == 0xf800 /* strb.w or strh.w */
+ && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM))
+ ;
+ else
+ {
+ /* We don't know what this instruction is. We're finished
+ scanning. NOTE: Recognizing more safe-to-ignore
+ instructions here will improve support for optimized
+ code. */
+ break;
+ }
+
+ start += 2;
+ }
else
{
/* We don't know what this instruction is. We're finished
start += 2;
}
+ if (arm_debug)
+ fprintf_unfiltered (gdb_stdlog, "Prologue scan stopped at %s\n",
+ paddress (gdbarch, start));
+
if (cache == NULL)
{
do_cleanups (back_to);
static CORE_ADDR
arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
{
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned long inst;
CORE_ADDR skip_pc;
CORE_ADDR func_addr, limit_pc;
struct symtab_and_line sal;
- /* If we're in a dummy frame, don't even try to skip the prologue. */
- if (deprecated_pc_in_call_dummy (gdbarch, pc))
- return pc;
-
/* 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
is greater. */
{
CORE_ADDR post_prologue_pc
= skip_prologue_using_sal (gdbarch, func_addr);
+ struct symtab *s = find_pc_symtab (func_addr);
+
+ /* GCC always emits a line note before the prologue and another
+ one after, even if the two are at the same address or on the
+ same line. Take advantage of this so that we do not need to
+ know every instruction that might appear in the prologue. We
+ will have producer information for most binaries; if it is
+ missing (e.g. for -gstabs), assuming the GNU tools. */
+ if (post_prologue_pc
+ && (s == NULL
+ || s->producer == NULL
+ || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0))
+ return post_prologue_pc;
+
if (post_prologue_pc != 0)
- return max (pc, post_prologue_pc);
+ {
+ CORE_ADDR analyzed_limit;
+
+ /* For non-GCC compilers, make sure the entire line is an
+ acceptable prologue; GDB will round this function's
+ return value up to the end of the following line so we
+ can not skip just part of a line (and we do not want to).
+
+ RealView does not treat the prologue specially, but does
+ associate prologue code with the opening brace; so this
+ lets us skip the first line if we think it is the opening
+ brace. */
+ if (arm_pc_is_thumb (func_addr))
+ analyzed_limit = thumb_analyze_prologue (gdbarch, func_addr,
+ post_prologue_pc, NULL);
+ else
+ analyzed_limit = arm_analyze_prologue (gdbarch, func_addr,
+ post_prologue_pc, NULL);
+
+ if (analyzed_limit != post_prologue_pc)
+ return func_addr;
+
+ return post_prologue_pc;
+ }
}
/* Can't determine prologue from the symbol table, need to examine
for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4)
{
- inst = read_memory_unsigned_integer (skip_pc, 4);
-
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- inst = SWAP_INT (inst);
+ inst = read_memory_unsigned_integer (skip_pc, 4, byte_order_for_code);
/* "mov ip, sp" is no longer a required part of the prologue. */
if (inst == 0xe1a0c00d) /* mov ip, sp */
if ((inst & 0xfffff000) == 0xe24dd000) /* sub sp, sp, #nn */
continue;
- if ((inst & 0xffffc000) == 0xe54b0000 || /* strb r(0123),[r11,#-nn] */
- (inst & 0xffffc0f0) == 0xe14b00b0 || /* strh r(0123),[r11,#-nn] */
- (inst & 0xffffc000) == 0xe50b0000) /* str r(0123),[r11,#-nn] */
+ if ((inst & 0xffffc000) == 0xe54b0000 /* strb r(0123),[r11,#-nn] */
+ || (inst & 0xffffc0f0) == 0xe14b00b0 /* strh r(0123),[r11,#-nn] */
+ || (inst & 0xffffc000) == 0xe50b0000) /* str r(0123),[r11,#-nn] */
continue;
- if ((inst & 0xffffc000) == 0xe5cd0000 || /* strb r(0123),[sp,#nn] */
- (inst & 0xffffc0f0) == 0xe1cd00b0 || /* strh r(0123),[sp,#nn] */
- (inst & 0xffffc000) == 0xe58d0000) /* str r(0123),[sp,#nn] */
+ if ((inst & 0xffffc000) == 0xe5cd0000 /* strb r(0123),[sp,#nn] */
+ || (inst & 0xffffc0f0) == 0xe1cd00b0 /* strh r(0123),[sp,#nn] */
+ || (inst & 0xffffc000) == 0xe58d0000) /* str r(0123),[sp,#nn] */
continue;
/* Un-recognized instruction; stop scanning. */
thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
}
-/* This function decodes an ARM function prologue to determine:
- 1) the size of the stack frame
- 2) which registers are saved on it
- 3) the offsets of saved regs
- 4) the offset from the stack pointer to the frame pointer
- This information is stored in the "extra" fields of the frame_info.
-
- There are two basic forms for the ARM prologue. The fixed argument
- function call will look like:
-
- mov ip, sp
- stmfd sp!, {fp, ip, lr, pc}
- sub fp, ip, #4
- [sub sp, sp, #4]
-
- Which would create this stack frame (offsets relative to FP):
- IP -> 4 (caller's stack)
- FP -> 0 PC (points to address of stmfd instruction + 8 in callee)
- -4 LR (return address in caller)
- -8 IP (copy of caller's SP)
- -12 FP (caller's FP)
- SP -> -28 Local variables
-
- The frame size would thus be 32 bytes, and the frame offset would be
- 28 bytes. The stmfd call can also save any of the vN registers it
- plans to use, which increases the frame size accordingly.
-
- Note: The stored PC is 8 off of the STMFD instruction that stored it
- because the ARM Store instructions always store PC + 8 when you read
- the PC register.
-
- A variable argument function call will look like:
-
- mov ip, sp
- stmfd sp!, {a1, a2, a3, a4}
- stmfd sp!, {fp, ip, lr, pc}
- sub fp, ip, #20
-
- Which would create this stack frame (offsets relative to FP):
- IP -> 20 (caller's stack)
- 16 A4
- 12 A3
- 8 A2
- 4 A1
- FP -> 0 PC (points to address of stmfd instruction + 8 in callee)
- -4 LR (return address in caller)
- -8 IP (copy of caller's SP)
- -12 FP (caller's FP)
- SP -> -28 Local variables
-
- The frame size would thus be 48 bytes, and the frame offset would be
- 28 bytes.
-
- There is another potential complication, which is that the optimizer
- will try to separate the store of fp in the "stmfd" instruction from
- the "sub fp, ip, #NN" instruction. Almost anything can be there, so
- we just key on the stmfd, and then scan for the "sub fp, ip, #NN"...
-
- Also, note, the original version of the ARM toolchain claimed that there
- should be an
-
- instruction at the end of the prologue. I have never seen GCC produce
- this, and the ARM docs don't mention it. We still test for it below in
- case it happens...
-
- */
+/* Return 1 if THIS_INSTR might change control flow, 0 otherwise. */
-static void
-arm_scan_prologue (struct frame_info *this_frame,
- struct arm_prologue_cache *cache)
+static int
+arm_instruction_changes_pc (uint32_t this_instr)
{
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- int regno;
- CORE_ADDR prologue_start, prologue_end, current_pc;
- CORE_ADDR prev_pc = get_frame_pc (this_frame);
- CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
- pv_t regs[ARM_FPS_REGNUM];
- struct pv_area *stack;
- struct cleanup *back_to;
- CORE_ADDR offset;
-
- /* Assume there is no frame until proven otherwise. */
- cache->framereg = ARM_SP_REGNUM;
- cache->framesize = 0;
-
- /* Check for Thumb prologue. */
- if (arm_frame_is_thumb (this_frame))
- {
- thumb_scan_prologue (gdbarch, prev_pc, block_addr, cache);
- return;
- }
+ if (bits (this_instr, 28, 31) == INST_NV)
+ /* Unconditional instructions. */
+ switch (bits (this_instr, 24, 27))
+ {
+ case 0xa:
+ case 0xb:
+ /* Branch with Link and change to Thumb. */
+ return 1;
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ /* Coprocessor register transfer. */
+ if (bits (this_instr, 12, 15) == 15)
+ error (_("Invalid update to pc in instruction"));
+ return 0;
+ default:
+ return 0;
+ }
+ else
+ switch (bits (this_instr, 25, 27))
+ {
+ case 0x0:
+ if (bits (this_instr, 23, 24) == 2 && bit (this_instr, 20) == 0)
+ {
+ /* Multiplies and extra load/stores. */
+ if (bit (this_instr, 4) == 1 && bit (this_instr, 7) == 1)
+ /* Neither multiplies nor extension load/stores are allowed
+ to modify PC. */
+ return 0;
- /* Find the function prologue. If we can't find the function in
- the symbol table, peek in the stack frame to find the PC. */
- if (find_pc_partial_function (block_addr, NULL, &prologue_start,
- &prologue_end))
- {
- /* One way to find the end of the prologue (which works well
- for unoptimized code) is to do the following:
+ /* Otherwise, miscellaneous instructions. */
- struct symtab_and_line sal = find_pc_line (prologue_start, 0);
+ /* BX <reg>, BXJ <reg>, BLX <reg> */
+ if (bits (this_instr, 4, 27) == 0x12fff1
+ || bits (this_instr, 4, 27) == 0x12fff2
+ || bits (this_instr, 4, 27) == 0x12fff3)
+ return 1;
- if (sal.line == 0)
- prologue_end = prev_pc;
- else if (sal.end < prologue_end)
- prologue_end = sal.end;
+ /* Other miscellaneous instructions are unpredictable if they
+ modify PC. */
+ return 0;
+ }
+ /* Data processing instruction. Fall through. */
+
+ case 0x1:
+ if (bits (this_instr, 12, 15) == 15)
+ return 1;
+ else
+ return 0;
+
+ case 0x2:
+ case 0x3:
+ /* Media instructions and architecturally undefined instructions. */
+ if (bits (this_instr, 25, 27) == 3 && bit (this_instr, 4) == 1)
+ return 0;
+
+ /* Stores. */
+ if (bit (this_instr, 20) == 0)
+ return 0;
+
+ /* Loads. */
+ if (bits (this_instr, 12, 15) == ARM_PC_REGNUM)
+ return 1;
+ else
+ return 0;
+
+ case 0x4:
+ /* Load/store multiple. */
+ if (bit (this_instr, 20) == 1 && bit (this_instr, 15) == 1)
+ return 1;
+ else
+ return 0;
+
+ case 0x5:
+ /* Branch and branch with link. */
+ return 1;
+
+ case 0x6:
+ case 0x7:
+ /* Coprocessor transfers or SWIs can not affect PC. */
+ return 0;
- This mechanism is very accurate so long as the optimizer
- doesn't move any instructions from the function body into the
- prologue. If this happens, sal.end will be the last
- instruction in the first hunk of prologue code just before
- the first instruction that the scheduler has moved from
- the body to the prologue.
+ default:
+ internal_error (__FILE__, __LINE__, "bad value in switch");
+ }
+}
- In order to make sure that we scan all of the prologue
- instructions, we use a slightly less accurate mechanism which
- may scan more than necessary. To help compensate for this
- lack of accuracy, the prologue scanning loop below contains
- several clauses which'll cause the loop to terminate early if
- an implausible prologue instruction is encountered.
-
- The expression
-
- prologue_start + 64
-
- is a suitable endpoint since it accounts for the largest
- possible prologue plus up to five instructions inserted by
- the scheduler. */
-
- if (prologue_end > prologue_start + 64)
- {
- prologue_end = prologue_start + 64; /* See above. */
- }
- }
- else
- {
- /* We have no symbol information. Our only option is to assume this
- function has a standard stack frame and the normal frame register.
- Then, we can find the value of our frame pointer on entrance to
- the callee (or at the present moment if this is the innermost frame).
- The value stored there should be the address of the stmfd + 8. */
- CORE_ADDR frame_loc;
- LONGEST return_value;
+/* Analyze an ARM mode prologue starting at PROLOGUE_START and
+ continuing no further than PROLOGUE_END. If CACHE is non-NULL,
+ fill it in. Return the first address not recognized as a prologue
+ instruction.
- frame_loc = get_frame_register_unsigned (this_frame, ARM_FP_REGNUM);
- if (!safe_read_memory_integer (frame_loc, 4, &return_value))
- return;
- else
- {
- prologue_start = gdbarch_addr_bits_remove
- (gdbarch, return_value) - 8;
- prologue_end = prologue_start + 64; /* See above. */
- }
- }
+ We recognize all the instructions typically found in ARM prologues,
+ plus harmless instructions which can be skipped (either for analysis
+ purposes, or a more restrictive set that can be skipped when finding
+ the end of the prologue). */
- if (prev_pc < prologue_end)
- prologue_end = prev_pc;
+static CORE_ADDR
+arm_analyze_prologue (struct gdbarch *gdbarch,
+ CORE_ADDR prologue_start, CORE_ADDR prologue_end,
+ struct arm_prologue_cache *cache)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ int regno;
+ CORE_ADDR offset, current_pc;
+ pv_t regs[ARM_FPS_REGNUM];
+ struct pv_area *stack;
+ struct cleanup *back_to;
+ int framereg, framesize;
+ CORE_ADDR unrecognized_pc = 0;
- /* Now search the prologue looking for instructions that set up the
+ /* Search the prologue looking for instructions that set up the
frame pointer, adjust the stack pointer, and save registers.
Be careful, however, and if it doesn't look like a prologue,
begins with stmfd sp!, then we will tell ourselves there is
a frame, which will confuse stack traceback, as well as "finish"
and other operations that rely on a knowledge of the stack
- traceback.
-
- In the APCS, the prologue should start with "mov ip, sp" so
- if we don't see this as the first insn, we will stop.
-
- [Note: This doesn't seem to be true any longer, so it's now an
- optional part of the prologue. - Kevin Buettner, 2001-11-20]
-
- [Note further: The "mov ip,sp" only seems to be missing in
- frameless functions at optimization level "-O2" or above,
- in which case it is often (but not always) replaced by
- "str lr, [sp, #-4]!". - Michael Snyder, 2002-04-23] */
+ traceback. */
for (regno = 0; regno < ARM_FPS_REGNUM; regno++)
regs[regno] = pv_register (regno, 0);
current_pc < prologue_end;
current_pc += 4)
{
- unsigned int insn = read_memory_unsigned_integer (current_pc, 4);
-
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- insn = SWAP_INT (insn);
+ unsigned int insn
+ = read_memory_unsigned_integer (current_pc, 4, byte_order_for_code);
if (insn == 0xe1a0c00d) /* mov ip, sp */
{
regs[ARM_IP_REGNUM] = regs[ARM_SP_REGNUM];
continue;
}
- else if ((insn & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */
+ else if ((insn & 0xfff00000) == 0xe2800000 /* add Rd, Rn, #n */
+ && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
{
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
+ int rd = bits (insn, 12, 15);
imm = (imm >> rot) | (imm << (32 - rot));
- regs[ARM_IP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], imm);
+ regs[rd] = pv_add_constant (regs[bits (insn, 16, 19)], imm);
continue;
}
- else if ((insn & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */
+ else if ((insn & 0xfff00000) == 0xe2400000 /* sub Rd, Rn, #n */
+ && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
{
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
+ int rd = bits (insn, 12, 15);
imm = (imm >> rot) | (imm << (32 - rot));
- regs[ARM_IP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -imm);
+ regs[rd] = pv_add_constant (regs[bits (insn, 16, 19)], -imm);
continue;
}
- else if (insn == 0xe52de004) /* str lr, [sp, #-4]! */
+ else if ((insn & 0xffff0fff) == 0xe52d0004) /* str Rd, [sp, #-4]! */
{
if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
break;
regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -4);
- pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[ARM_LR_REGNUM]);
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 4,
+ regs[bits (insn, 12, 15)]);
continue;
}
else if ((insn & 0xffff0000) == 0xe92d0000)
pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]);
}
}
- else if ((insn & 0xffffc000) == 0xe54b0000 || /* strb rx,[r11,#-n] */
- (insn & 0xffffc0f0) == 0xe14b00b0 || /* strh rx,[r11,#-n] */
- (insn & 0xffffc000) == 0xe50b0000) /* str rx,[r11,#-n] */
+ else if ((insn & 0xffff0000) == 0xe54b0000 /* strb rx,[r11,#-n] */
+ || (insn & 0xffff00f0) == 0xe14b00b0 /* strh rx,[r11,#-n] */
+ || (insn & 0xffffc000) == 0xe50b0000) /* str rx,[r11,#-n] */
{
/* No need to add this to saved_regs -- it's just an arg reg. */
continue;
}
- else if ((insn & 0xffffc000) == 0xe5cd0000 || /* strb rx,[sp,#n] */
- (insn & 0xffffc0f0) == 0xe1cd00b0 || /* strh rx,[sp,#n] */
- (insn & 0xffffc000) == 0xe58d0000) /* str rx,[sp,#n] */
+ else if ((insn & 0xffff0000) == 0xe5cd0000 /* strb rx,[sp,#n] */
+ || (insn & 0xffff00f0) == 0xe1cd00b0 /* strh rx,[sp,#n] */
+ || (insn & 0xffffc000) == 0xe58d0000) /* str rx,[sp,#n] */
{
/* No need to add this to saved_regs -- it's just an arg reg. */
continue;
}
+ else if ((insn & 0xfff00000) == 0xe8800000 /* stm Rn, { registers } */
+ && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
+ {
+ /* No need to add this to saved_regs -- it's just arg regs. */
+ continue;
+ }
else if ((insn & 0xfffff000) == 0xe24cb000) /* sub fp, ip #n */
{
unsigned imm = insn & 0xff; /* immediate value */
regs[fp_start_reg++]);
}
}
+ else if ((insn & 0xff000000) == 0xeb000000 && cache == NULL) /* bl */
+ {
+ /* Allow some special function calls when skipping the
+ prologue; GCC generates these before storing arguments to
+ the stack. */
+ CORE_ADDR dest = BranchDest (current_pc, insn);
+
+ if (skip_prologue_function (dest))
+ continue;
+ else
+ break;
+ }
else if ((insn & 0xf0000000) != 0xe0000000)
break; /* Condition not true, exit early */
- else if ((insn & 0xfe200000) == 0xe8200000) /* ldm? */
- break; /* Don't scan past a block load */
- else
- /* The optimizer might shove anything into the prologue,
- so we just skip what we don't recognize. */
+ else if (arm_instruction_changes_pc (insn))
+ /* Don't scan past anything that might change control flow. */
+ break;
+ else if ((insn & 0xfe500000) == 0xe8100000) /* ldm */
+ {
+ /* Ignore block loads from the stack, potentially copying
+ parameters from memory. */
+ if (pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
+ continue;
+ else
+ break;
+ }
+ else if ((insn & 0xfc500000) == 0xe4100000)
+ {
+ /* Similarly ignore single loads from the stack. */
+ if (pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM))
+ continue;
+ else
+ break;
+ }
+ else if ((insn & 0xffff0ff0) == 0xe1a00000)
+ /* MOV Rd, Rm. Skip register copies, i.e. saves to another
+ register instead of the stack. */
continue;
+ else
+ {
+ /* The optimizer might shove anything into the prologue,
+ so we just skip what we don't recognize. */
+ unrecognized_pc = current_pc;
+ continue;
+ }
}
+ if (unrecognized_pc == 0)
+ unrecognized_pc = current_pc;
+
/* The frame size is just the distance from the frame register
to the original stack pointer. */
if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
{
/* Frame pointer is fp. */
- cache->framereg = ARM_FP_REGNUM;
- cache->framesize = -regs[ARM_FP_REGNUM].k;
+ framereg = ARM_FP_REGNUM;
+ framesize = -regs[ARM_FP_REGNUM].k;
}
else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
{
/* Try the stack pointer... this is a bit desperate. */
- cache->framereg = ARM_SP_REGNUM;
- cache->framesize = -regs[ARM_SP_REGNUM].k;
+ 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. */
- cache->framereg = -1;
- cache->framesize = 0;
+ framereg = -1;
+ framesize = 0;
}
- for (regno = 0; regno < ARM_FPS_REGNUM; regno++)
- if (pv_area_find_reg (stack, gdbarch, regno, &offset))
- cache->saved_regs[regno].addr = offset;
+ if (cache)
+ {
+ cache->framereg = framereg;
+ cache->framesize = framesize;
+
+ for (regno = 0; regno < ARM_FPS_REGNUM; regno++)
+ if (pv_area_find_reg (stack, gdbarch, regno, &offset))
+ cache->saved_regs[regno].addr = offset;
+ }
+
+ if (arm_debug)
+ fprintf_unfiltered (gdb_stdlog, "Prologue scan stopped at %s\n",
+ paddress (gdbarch, unrecognized_pc));
do_cleanups (back_to);
+ return unrecognized_pc;
+}
+
+static void
+arm_scan_prologue (struct frame_info *this_frame,
+ struct arm_prologue_cache *cache)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int regno;
+ CORE_ADDR prologue_start, prologue_end, current_pc;
+ CORE_ADDR prev_pc = get_frame_pc (this_frame);
+ CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
+ pv_t regs[ARM_FPS_REGNUM];
+ struct pv_area *stack;
+ struct cleanup *back_to;
+ CORE_ADDR offset;
+
+ /* Assume there is no frame until proven otherwise. */
+ cache->framereg = ARM_SP_REGNUM;
+ cache->framesize = 0;
+
+ /* Check for Thumb prologue. */
+ if (arm_frame_is_thumb (this_frame))
+ {
+ thumb_scan_prologue (gdbarch, prev_pc, block_addr, cache);
+ return;
+ }
+
+ /* Find the function prologue. If we can't find the function in
+ the symbol table, peek in the stack frame to find the PC. */
+ if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+ &prologue_end))
+ {
+ /* One way to find the end of the prologue (which works well
+ for unoptimized code) is to do the following:
+
+ struct symtab_and_line sal = find_pc_line (prologue_start, 0);
+
+ if (sal.line == 0)
+ prologue_end = prev_pc;
+ else if (sal.end < prologue_end)
+ prologue_end = sal.end;
+
+ This mechanism is very accurate so long as the optimizer
+ doesn't move any instructions from the function body into the
+ prologue. If this happens, sal.end will be the last
+ instruction in the first hunk of prologue code just before
+ the first instruction that the scheduler has moved from
+ the body to the prologue.
+
+ In order to make sure that we scan all of the prologue
+ instructions, we use a slightly less accurate mechanism which
+ may scan more than necessary. To help compensate for this
+ lack of accuracy, the prologue scanning loop below contains
+ several clauses which'll cause the loop to terminate early if
+ an implausible prologue instruction is encountered.
+
+ The expression
+
+ prologue_start + 64
+
+ is a suitable endpoint since it accounts for the largest
+ possible prologue plus up to five instructions inserted by
+ the scheduler. */
+
+ if (prologue_end > prologue_start + 64)
+ {
+ prologue_end = prologue_start + 64; /* See above. */
+ }
+ }
+ else
+ {
+ /* We have no symbol information. Our only option is to assume this
+ function has a standard stack frame and the normal frame register.
+ Then, we can find the value of our frame pointer on entrance to
+ the callee (or at the present moment if this is the innermost frame).
+ The value stored there should be the address of the stmfd + 8. */
+ CORE_ADDR frame_loc;
+ LONGEST return_value;
+
+ frame_loc = get_frame_register_unsigned (this_frame, ARM_FP_REGNUM);
+ if (!safe_read_memory_integer (frame_loc, 4, byte_order, &return_value))
+ return;
+ else
+ {
+ prologue_start = gdbarch_addr_bits_remove
+ (gdbarch, return_value) - 8;
+ prologue_end = prologue_start + 64; /* See above. */
+ }
+ }
+
+ if (prev_pc < prologue_end)
+ prologue_end = prev_pc;
+
+ arm_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
}
static struct arm_prologue_cache *
static struct arm_prologue_cache *
arm_make_stub_cache (struct frame_info *this_frame)
{
- int reg;
struct arm_prologue_cache *cache;
- CORE_ADDR unwound_fp;
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
addr_in_block = get_frame_address_in_block (this_frame);
if (in_plt_section (addr_in_block, NULL)
+ /* 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)
return 1;
};
static struct stack_item *
-push_stack_item (struct stack_item *prev, void *contents, int len)
+push_stack_item (struct stack_item *prev, const void *contents, int len)
{
struct stack_item *si;
si = xmalloc (sizeof (struct stack_item));
}
}
-/* We currently only support passing parameters in integer registers. This
- conforms with GCC's default model. Several other variants exist and
+/* Possible base types for a candidate for passing and returning in
+ VFP registers. */
+
+enum arm_vfp_cprc_base_type
+{
+ VFP_CPRC_UNKNOWN,
+ VFP_CPRC_SINGLE,
+ VFP_CPRC_DOUBLE,
+ VFP_CPRC_VEC64,
+ VFP_CPRC_VEC128
+};
+
+/* The length of one element of base type B. */
+
+static unsigned
+arm_vfp_cprc_unit_length (enum arm_vfp_cprc_base_type b)
+{
+ switch (b)
+ {
+ case VFP_CPRC_SINGLE:
+ return 4;
+ case VFP_CPRC_DOUBLE:
+ return 8;
+ case VFP_CPRC_VEC64:
+ return 8;
+ case VFP_CPRC_VEC128:
+ return 16;
+ default:
+ internal_error (__FILE__, __LINE__, _("Invalid VFP CPRC type: %d."),
+ (int) b);
+ }
+}
+
+/* The character ('s', 'd' or 'q') for the type of VFP register used
+ for passing base type B. */
+
+static int
+arm_vfp_cprc_reg_char (enum arm_vfp_cprc_base_type b)
+{
+ switch (b)
+ {
+ case VFP_CPRC_SINGLE:
+ return 's';
+ case VFP_CPRC_DOUBLE:
+ return 'd';
+ case VFP_CPRC_VEC64:
+ return 'd';
+ case VFP_CPRC_VEC128:
+ return 'q';
+ default:
+ internal_error (__FILE__, __LINE__, _("Invalid VFP CPRC type: %d."),
+ (int) b);
+ }
+}
+
+/* Determine whether T may be part of a candidate for passing and
+ returning in VFP registers, ignoring the limit on the total number
+ of components. If *BASE_TYPE is VFP_CPRC_UNKNOWN, set it to the
+ classification of the first valid component found; if it is not
+ VFP_CPRC_UNKNOWN, all components must have the same classification
+ as *BASE_TYPE. If it is found that T contains a type not permitted
+ for passing and returning in VFP registers, a type differently
+ classified from *BASE_TYPE, or two types differently classified
+ from each other, return -1, otherwise return the total number of
+ base-type elements found (possibly 0 in an empty structure or
+ array). Vectors and complex types are not currently supported,
+ matching the generic AAPCS support. */
+
+static int
+arm_vfp_cprc_sub_candidate (struct type *t,
+ enum arm_vfp_cprc_base_type *base_type)
+{
+ t = check_typedef (t);
+ switch (TYPE_CODE (t))
+ {
+ case TYPE_CODE_FLT:
+ switch (TYPE_LENGTH (t))
+ {
+ case 4:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_SINGLE;
+ else if (*base_type != VFP_CPRC_SINGLE)
+ return -1;
+ return 1;
+
+ case 8:
+ if (*base_type == VFP_CPRC_UNKNOWN)
+ *base_type = VFP_CPRC_DOUBLE;
+ else if (*base_type != VFP_CPRC_DOUBLE)
+ return -1;
+ return 1;
+
+ default:
+ return -1;
+ }
+ break;
+
+ case TYPE_CODE_ARRAY:
+ {
+ int count;
+ unsigned unitlen;
+ count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t), base_type);
+ if (count == -1)
+ return -1;
+ if (TYPE_LENGTH (t) == 0)
+ {
+ gdb_assert (count == 0);
+ return 0;
+ }
+ else if (count == 0)
+ return -1;
+ unitlen = arm_vfp_cprc_unit_length (*base_type);
+ gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0);
+ return TYPE_LENGTH (t) / unitlen;
+ }
+ break;
+
+ case TYPE_CODE_STRUCT:
+ {
+ int count = 0;
+ unsigned unitlen;
+ int i;
+ for (i = 0; i < TYPE_NFIELDS (t); i++)
+ {
+ int sub_count = arm_vfp_cprc_sub_candidate (TYPE_FIELD_TYPE (t, i),
+ base_type);
+ if (sub_count == -1)
+ return -1;
+ count += sub_count;
+ }
+ if (TYPE_LENGTH (t) == 0)
+ {
+ gdb_assert (count == 0);
+ return 0;
+ }
+ else if (count == 0)
+ return -1;
+ unitlen = arm_vfp_cprc_unit_length (*base_type);
+ if (TYPE_LENGTH (t) != unitlen * count)
+ return -1;
+ return count;
+ }
+
+ case TYPE_CODE_UNION:
+ {
+ int count = 0;
+ unsigned unitlen;
+ int i;
+ for (i = 0; i < TYPE_NFIELDS (t); i++)
+ {
+ int sub_count = arm_vfp_cprc_sub_candidate (TYPE_FIELD_TYPE (t, i),
+ base_type);
+ if (sub_count == -1)
+ return -1;
+ count = (count > sub_count ? count : sub_count);
+ }
+ if (TYPE_LENGTH (t) == 0)
+ {
+ gdb_assert (count == 0);
+ return 0;
+ }
+ else if (count == 0)
+ return -1;
+ unitlen = arm_vfp_cprc_unit_length (*base_type);
+ if (TYPE_LENGTH (t) != unitlen * count)
+ return -1;
+ return count;
+ }
+
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/* Determine whether T is a VFP co-processor register candidate (CPRC)
+ if passed to or returned from a non-variadic function with the VFP
+ ABI in effect. Return 1 if it is, 0 otherwise. If it is, set
+ *BASE_TYPE to the base type for T and *COUNT to the number of
+ elements of that base type before returning. */
+
+static int
+arm_vfp_call_candidate (struct type *t, enum arm_vfp_cprc_base_type *base_type,
+ int *count)
+{
+ enum arm_vfp_cprc_base_type b = VFP_CPRC_UNKNOWN;
+ int c = arm_vfp_cprc_sub_candidate (t, &b);
+ if (c <= 0 || c > 4)
+ return 0;
+ *base_type = b;
+ *count = c;
+ return 1;
+}
+
+/* Return 1 if the VFP ABI should be used for passing arguments to and
+ returning values from a function of type FUNC_TYPE, 0
+ otherwise. */
+
+static int
+arm_vfp_abi_for_function (struct gdbarch *gdbarch, struct type *func_type)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ /* Variadic functions always use the base ABI. Assume that functions
+ without debug info are not variadic. */
+ if (func_type && TYPE_VARARGS (check_typedef (func_type)))
+ return 0;
+ /* The VFP ABI is only supported as a variant of AAPCS. */
+ if (tdep->arm_abi != ARM_ABI_AAPCS)
+ return 0;
+ return gdbarch_tdep (gdbarch)->fp_model == ARM_FLOAT_VFP;
+}
+
+/* We currently only support passing parameters in integer registers, which
+ conforms with GCC's default model, and VFP argument passing following
+ the VFP variant of AAPCS. Several other variants exist and
we should probably support some of them based on the selected ABI. */
static CORE_ADDR
struct value **args, CORE_ADDR sp, int struct_return,
CORE_ADDR struct_addr)
{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int argnum;
int argreg;
int nstack;
struct stack_item *si = NULL;
+ int use_vfp_abi;
+ struct type *ftype;
+ unsigned vfp_regs_free = (1 << 16) - 1;
+
+ /* Determine the type of this function and whether the VFP ABI
+ applies. */
+ ftype = check_typedef (value_type (function));
+ if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
+ ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
+ use_vfp_abi = arm_vfp_abi_for_function (gdbarch, ftype);
/* Set the return address. For the ARM, the return breakpoint is
always at BP_ADDR. */
- /* XXX Fix for Thumb. */
+ if (arm_pc_is_thumb (bp_addr))
+ bp_addr |= 1;
regcache_cooked_write_unsigned (regcache, ARM_LR_REGNUM, bp_addr);
/* Walk through the list of args and determine how large a temporary
struct type *arg_type;
struct type *target_type;
enum type_code typecode;
- bfd_byte *val;
+ const bfd_byte *val;
int align;
+ enum arm_vfp_cprc_base_type vfp_base_type;
+ int vfp_base_count;
+ int may_use_core_reg = 1;
arg_type = check_typedef (value_type (args[argnum]));
len = TYPE_LENGTH (arg_type);
target_type = TYPE_TARGET_TYPE (arg_type);
typecode = TYPE_CODE (arg_type);
- val = value_contents_writeable (args[argnum]);
+ val = value_contents (args[argnum]);
align = arm_type_align (arg_type);
/* Round alignment up to a whole number of words. */
align = INT_REGISTER_SIZE * 2;
}
+ if (use_vfp_abi
+ && arm_vfp_call_candidate (arg_type, &vfp_base_type,
+ &vfp_base_count))
+ {
+ int regno;
+ int unit_length;
+ int shift;
+ unsigned mask;
+
+ /* Because this is a CPRC it cannot go in a core register or
+ cause a core register to be skipped for alignment.
+ Either it goes in VFP registers and the rest of this loop
+ iteration is skipped for this argument, or it goes on the
+ stack (and the stack alignment code is correct for this
+ case). */
+ may_use_core_reg = 0;
+
+ unit_length = arm_vfp_cprc_unit_length (vfp_base_type);
+ shift = unit_length / 4;
+ mask = (1 << (shift * vfp_base_count)) - 1;
+ for (regno = 0; regno < 16; regno += shift)
+ if (((vfp_regs_free >> regno) & mask) == mask)
+ break;
+
+ if (regno < 16)
+ {
+ int reg_char;
+ int reg_scaled;
+ int i;
+
+ vfp_regs_free &= ~(mask << regno);
+ reg_scaled = regno / shift;
+ reg_char = arm_vfp_cprc_reg_char (vfp_base_type);
+ for (i = 0; i < vfp_base_count; i++)
+ {
+ char name_buf[4];
+ int regnum;
+ if (reg_char == 'q')
+ arm_neon_quad_write (gdbarch, regcache, reg_scaled + i,
+ val + i * unit_length);
+ else
+ {
+ sprintf (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,
+ val + i * unit_length);
+ }
+ }
+ continue;
+ }
+ else
+ {
+ /* This CPRC could not go in VFP registers, so all VFP
+ registers are now marked as used. */
+ vfp_regs_free = 0;
+ }
+ }
+
/* Push stack padding for dowubleword alignment. */
if (nstack & (align - 1))
{
}
/* Doubleword aligned quantities must go in even register pairs. */
- if (argreg <= ARM_LAST_ARG_REGNUM
+ if (may_use_core_reg
+ && argreg <= ARM_LAST_ARG_REGNUM
&& align > INT_REGISTER_SIZE
&& argreg & 1)
argreg++;
&& target_type != NULL
&& TYPE_CODE_FUNC == TYPE_CODE (target_type))
{
- CORE_ADDR regval = extract_unsigned_integer (val, len);
+ CORE_ADDR regval = extract_unsigned_integer (val, len, byte_order);
if (arm_pc_is_thumb (regval))
{
- val = alloca (len);
- store_unsigned_integer (val, len, MAKE_THUMB_ADDR (regval));
+ bfd_byte *copy = alloca (len);
+ store_unsigned_integer (copy, len, byte_order,
+ MAKE_THUMB_ADDR (regval));
+ val = copy;
}
}
{
int partial_len = len < INT_REGISTER_SIZE ? len : INT_REGISTER_SIZE;
- if (argreg <= ARM_LAST_ARG_REGNUM)
+ if (may_use_core_reg && argreg <= ARM_LAST_ARG_REGNUM)
{
/* The argument is being passed in a general purpose
register. */
- CORE_ADDR regval = extract_unsigned_integer (val, partial_len);
- if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ CORE_ADDR regval
+ = extract_unsigned_integer (val, partial_len, byte_order);
+ if (byte_order == BFD_ENDIAN_BIG)
regval <<= (INT_REGISTER_SIZE - partial_len) * 8;
if (arm_debug)
fprintf_unfiltered (gdb_stdlog, "arg %d in %s = 0x%s\n",
return tdep->arm_ext_type;
}
+static struct type *
+arm_neon_double_type (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->neon_double_type == NULL)
+ {
+ struct type *t, *elem;
+
+ t = arch_composite_type (gdbarch, "__gdb_builtin_type_neon_d",
+ TYPE_CODE_UNION);
+ elem = builtin_type (gdbarch)->builtin_uint8;
+ append_composite_type_field (t, "u8", init_vector_type (elem, 8));
+ elem = builtin_type (gdbarch)->builtin_uint16;
+ append_composite_type_field (t, "u16", init_vector_type (elem, 4));
+ elem = builtin_type (gdbarch)->builtin_uint32;
+ append_composite_type_field (t, "u32", init_vector_type (elem, 2));
+ elem = builtin_type (gdbarch)->builtin_uint64;
+ append_composite_type_field (t, "u64", elem);
+ elem = builtin_type (gdbarch)->builtin_float;
+ append_composite_type_field (t, "f32", init_vector_type (elem, 2));
+ elem = builtin_type (gdbarch)->builtin_double;
+ append_composite_type_field (t, "f64", elem);
+
+ TYPE_VECTOR (t) = 1;
+ TYPE_NAME (t) = "neon_d";
+ tdep->neon_double_type = t;
+ }
+
+ return tdep->neon_double_type;
+}
+
+/* FIXME: The vector types are not correctly ordered on big-endian
+ targets. Just as s0 is the low bits of d0, d0[0] is also the low
+ bits of d0 - regardless of what unit size is being held in d0. So
+ the offset of the first uint8 in d0 is 7, but the offset of the
+ first float is 4. This code works as-is for little-endian
+ targets. */
+
+static struct type *
+arm_neon_quad_type (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->neon_quad_type == NULL)
+ {
+ struct type *t, *elem;
+
+ t = arch_composite_type (gdbarch, "__gdb_builtin_type_neon_q",
+ TYPE_CODE_UNION);
+ elem = builtin_type (gdbarch)->builtin_uint8;
+ append_composite_type_field (t, "u8", init_vector_type (elem, 16));
+ elem = builtin_type (gdbarch)->builtin_uint16;
+ append_composite_type_field (t, "u16", init_vector_type (elem, 8));
+ elem = builtin_type (gdbarch)->builtin_uint32;
+ append_composite_type_field (t, "u32", init_vector_type (elem, 4));
+ elem = builtin_type (gdbarch)->builtin_uint64;
+ append_composite_type_field (t, "u64", init_vector_type (elem, 2));
+ elem = builtin_type (gdbarch)->builtin_float;
+ append_composite_type_field (t, "f32", init_vector_type (elem, 4));
+ elem = builtin_type (gdbarch)->builtin_double;
+ append_composite_type_field (t, "f64", init_vector_type (elem, 2));
+
+ TYPE_VECTOR (t) = 1;
+ TYPE_NAME (t) = "neon_q";
+ tdep->neon_quad_type = t;
+ }
+
+ return tdep->neon_quad_type;
+}
+
/* Return the GDB type object for the "standard" data type of data in
register N. */
static struct type *
arm_register_type (struct gdbarch *gdbarch, int regnum)
{
+ int num_regs = gdbarch_num_regs (gdbarch);
+
+ if (gdbarch_tdep (gdbarch)->have_vfp_pseudos
+ && regnum >= num_regs && regnum < num_regs + 32)
+ return builtin_type (gdbarch)->builtin_float;
+
+ if (gdbarch_tdep (gdbarch)->have_neon_pseudos
+ && regnum >= num_regs + 32 && regnum < num_regs + 32 + 16)
+ return arm_neon_quad_type (gdbarch);
+
+ /* If the target description has register information, we are only
+ in this function so that we can override the types of
+ double-precision registers for NEON. */
+ if (tdesc_has_registers (gdbarch_target_desc (gdbarch)))
+ {
+ struct type *t = tdesc_register_type (gdbarch, regnum);
+
+ if (regnum >= ARM_D0_REGNUM && regnum < ARM_D0_REGNUM + 32
+ && TYPE_CODE (t) == TYPE_CODE_FLT
+ && gdbarch_tdep (gdbarch)->have_neon)
+ return arm_neon_double_type (gdbarch);
+ else
+ return t;
+ }
+
if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS)
- return arm_ext_type (gdbarch);
+ {
+ if (!gdbarch_tdep (gdbarch)->have_fpa_registers)
+ return builtin_type (gdbarch)->builtin_void;
+
+ return arm_ext_type (gdbarch);
+ }
else if (regnum == ARM_SP_REGNUM)
return builtin_type (gdbarch)->builtin_data_ptr;
else if (regnum == ARM_PC_REGNUM)
if (reg >= 192 && reg <= 199)
return ARM_WC0_REGNUM + reg - 192;
+ /* VFP v2 registers. A double precision value is actually
+ in d1 rather than s2, but the ABI only defines numbering
+ for the single precision registers. This will "just work"
+ in GDB for little endian targets (we'll read eight bytes,
+ starting in s0 and then progressing to s1), but will be
+ reversed on big endian targets with VFP. This won't
+ be a problem for the new Neon quad registers; you're supposed
+ to use DW_OP_piece for those. */
+ if (reg >= 64 && reg <= 95)
+ {
+ char name_buf[4];
+
+ sprintf (name_buf, "s%d", reg - 64);
+ return user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+ }
+
+ /* VFP v3 / Neon registers. This range is also used for VFP v2
+ registers, except that it now describes d0 instead of s0. */
+ if (reg >= 256 && reg <= 287)
+ {
+ char name_buf[4];
+
+ sprintf (name_buf, "d%d", reg - 256);
+ return user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+ }
+
return -1;
}
case INST_LT:
return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0));
case INST_GT:
- return (((status_reg & FLAG_Z) == 0) &&
- (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0)));
+ return (((status_reg & FLAG_Z) == 0)
+ && (((status_reg & FLAG_N) == 0)
+ == ((status_reg & FLAG_V) == 0)));
case INST_LE:
- return (((status_reg & FLAG_Z) != 0) ||
- (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0)));
+ return (((status_reg & FLAG_Z) != 0)
+ || (((status_reg & FLAG_N) == 0)
+ != ((status_reg & FLAG_V) == 0)));
}
return 1;
}
-/* Support routines for single stepping. Calculate the next PC value. */
-#define submask(x) ((1L << ((x) + 1)) - 1)
-#define bit(obj,st) (((obj) >> (st)) & 1)
-#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st)))
-#define sbits(obj,st,fn) \
- ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st))))
-#define BranchDest(addr,instr) \
- ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
-#define ARM_PC_32 1
-
static unsigned long
shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry,
unsigned long pc_val, unsigned long status_reg)
shift = bits (inst, 7, 11);
res = (rm == 15
- ? ((pc_val | (ARM_PC_32 ? 0 : status_reg))
- + (bit (inst, 4) ? 12 : 8))
+ ? (pc_val + (bit (inst, 4) ? 12 : 8))
: get_frame_register_unsigned (frame, rm));
switch (shifttype)
return nbits;
}
+/* Return the size in bytes of the complete Thumb instruction whose
+ first halfword is INST1. */
+
+static int
+thumb_insn_size (unsigned short inst1)
+{
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return 4;
+ else
+ return 2;
+}
+
+static int
+thumb_advance_itstate (unsigned int itstate)
+{
+ /* Preserve IT[7:5], the first three bits of the condition. Shift
+ the upcoming condition flags left by one bit. */
+ itstate = (itstate & 0xe0) | ((itstate << 1) & 0x1f);
+
+ /* If we have finished the IT block, clear the state. */
+ if ((itstate & 0x0f) == 0)
+ itstate = 0;
+
+ return itstate;
+}
+
+/* Find the next PC after the current instruction executes. In some
+ cases we can not statically determine the answer (see the IT state
+ handling in this function); in that case, a breakpoint may be
+ inserted in addition to the returned PC, which will be used to set
+ another breakpoint by our caller. */
+
static CORE_ADDR
-thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct address_space *aspace = get_frame_address_space (frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned long pc_val = ((unsigned long) pc) + 4; /* PC after prefetch */
- unsigned short inst1 = read_memory_unsigned_integer (pc, 2);
+ unsigned short inst1;
CORE_ADDR nextpc = pc + 2; /* default is next instruction */
unsigned long offset;
+ ULONGEST status, itstate;
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- inst1 = SWAP_SHORT (inst1);
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+ pc_val = MAKE_THUMB_ADDR (pc_val);
- if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
+ inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+
+ /* Thumb-2 conditional execution support. There are eight bits in
+ the CPSR which describe conditional execution state. Once
+ reconstructed (they're in a funny order), the low five bits
+ describe the low bit of the condition for each instruction and
+ how many instructions remain. The high three bits describe the
+ base condition. One of the low four bits will be set if an IT
+ block is active. These bits read as zero on earlier
+ processors. */
+ status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+ itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3);
+
+ /* If-Then handling. On GNU/Linux, where this routine is used, we
+ use an undefined instruction as a breakpoint. Unlike BKPT, IT
+ can disable execution of the undefined instruction. So we might
+ miss the breakpoint if we set it on a skipped conditional
+ instruction. Because conditional instructions can change the
+ flags, affecting the execution of further instructions, we may
+ need to set two breakpoints. */
+
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint != NULL)
{
- CORE_ADDR sp;
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+ {
+ /* An IT instruction. Because this instruction does not
+ modify the flags, we can accurately predict the next
+ executed instruction. */
+ itstate = inst1 & 0x00ff;
+ pc += thumb_insn_size (inst1);
+
+ while (itstate != 0 && ! condition_true (itstate >> 4, status))
+ {
+ inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ return MAKE_THUMB_ADDR (pc);
+ }
+ else if (itstate != 0)
+ {
+ /* We are in a conditional block. Check the condition. */
+ if (! condition_true (itstate >> 4, status))
+ {
+ /* Advance to the next executed instruction. */
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+
+ while (itstate != 0 && ! condition_true (itstate >> 4, status))
+ {
+ inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+
+ return MAKE_THUMB_ADDR (pc);
+ }
+ else if ((itstate & 0x0f) == 0x08)
+ {
+ /* This is the last instruction of the conditional
+ block, and it is executed. We can handle it normally
+ because the following instruction is not conditional,
+ and we must handle it normally because it is
+ permitted to branch. Fall through. */
+ }
+ else
+ {
+ int cond_negated;
+
+ /* There are conditional instructions after this one.
+ If this instruction modifies the flags, then we can
+ not predict what the next executed instruction will
+ be. Fortunately, this instruction is architecturally
+ forbidden to branch; we know it will fall through.
+ Start by skipping past it. */
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+
+ /* Set a breakpoint on the following instruction. */
+ gdb_assert ((itstate & 0x0f) != 0);
+ if (insert_bkpt)
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
+ cond_negated = (itstate >> 4) & 1;
+
+ /* Skip all following instructions with the same
+ condition. If there is a later instruction in the IT
+ block with the opposite condition, set the other
+ breakpoint there. If not, then set a breakpoint on
+ the instruction after the IT block. */
+ do
+ {
+ inst1 = read_memory_unsigned_integer (pc, 2, byte_order_for_code);
+ pc += thumb_insn_size (inst1);
+ itstate = thumb_advance_itstate (itstate);
+ }
+ while (itstate != 0 && ((itstate >> 4) & 1) == cond_negated);
+
+ return MAKE_THUMB_ADDR (pc);
+ }
+ }
+ }
+ else if (itstate & 0x0f)
+ {
+ /* We are in a conditional block. Check the condition. */
+ int cond = itstate >> 4;
+
+ if (! condition_true (cond, status))
+ {
+ /* Advance to the next instruction. All the 32-bit
+ instructions share a common prefix. */
+ if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0)
+ return MAKE_THUMB_ADDR (pc + 4);
+ else
+ return MAKE_THUMB_ADDR (pc + 2);
+ }
+
+ /* Otherwise, handle the instruction normally. */
+ }
+
+ if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
+ {
+ CORE_ADDR sp;
/* Fetch the saved PC from the stack. It's stored above
all of the other registers. */
offset = bitcount (bits (inst1, 0, 7)) * INT_REGISTER_SIZE;
sp = get_frame_register_unsigned (frame, ARM_SP_REGNUM);
- nextpc = (CORE_ADDR) read_memory_unsigned_integer (sp + offset, 4);
- nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
+ nextpc = read_memory_unsigned_integer (sp + offset, 4, byte_order);
}
else if ((inst1 & 0xf000) == 0xd000) /* conditional branch */
{
- unsigned long status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
unsigned long cond = bits (inst1, 8, 11);
if (cond != 0x0f && condition_true (cond, status)) /* 0x0f = SWI */
nextpc = pc_val + (sbits (inst1, 0, 7) << 1);
{
nextpc = pc_val + (sbits (inst1, 0, 10) << 1);
}
- else if ((inst1 & 0xf800) == 0xf000) /* long branch with link, and blx */
+ else if ((inst1 & 0xe000) == 0xe000) /* 32-bit instruction */
{
- unsigned short inst2 = read_memory_unsigned_integer (pc + 2, 2);
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- inst2 = SWAP_SHORT (inst2);
- offset = (sbits (inst1, 0, 10) << 12) + (bits (inst2, 0, 10) << 1);
- nextpc = pc_val + offset;
- /* For BLX make sure to clear the low bits. */
- if (bits (inst2, 11, 12) == 1)
- nextpc = nextpc & 0xfffffffc;
+ unsigned short inst2;
+ inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code);
+
+ /* Default to the next instruction. */
+ nextpc = pc + 4;
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+
+ if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000)
+ {
+ /* Branches and miscellaneous control instructions. */
+
+ if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000)
+ {
+ /* B, BL, BLX. */
+ int j1, j2, imm1, imm2;
+
+ imm1 = sbits (inst1, 0, 10);
+ imm2 = bits (inst2, 0, 10);
+ j1 = bit (inst2, 13);
+ j2 = bit (inst2, 11);
+
+ offset = ((imm1 << 12) + (imm2 << 1));
+ offset ^= ((!j2) << 22) | ((!j1) << 23);
+
+ nextpc = pc_val + offset;
+ /* For BLX make sure to clear the low bits. */
+ if (bit (inst2, 12) == 0)
+ nextpc = nextpc & 0xfffffffc;
+ }
+ else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00)
+ {
+ /* SUBS PC, LR, #imm8. */
+ nextpc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
+ nextpc -= inst2 & 0x00ff;
+ }
+ else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380)
+ {
+ /* Conditional branch. */
+ if (condition_true (bits (inst1, 6, 9), status))
+ {
+ int sign, j1, j2, imm1, imm2;
+
+ sign = sbits (inst1, 10, 10);
+ imm1 = bits (inst1, 0, 5);
+ imm2 = bits (inst2, 0, 10);
+ j1 = bit (inst2, 13);
+ j2 = bit (inst2, 11);
+
+ offset = (sign << 20) + (j2 << 19) + (j1 << 18);
+ offset += (imm1 << 12) + (imm2 << 1);
+
+ nextpc = pc_val + offset;
+ }
+ }
+ }
+ else if ((inst1 & 0xfe50) == 0xe810)
+ {
+ /* Load multiple or RFE. */
+ int rn, offset, load_pc = 1;
+
+ rn = bits (inst1, 0, 3);
+ if (bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* LDMIA or POP */
+ if (!bit (inst2, 15))
+ load_pc = 0;
+ offset = bitcount (inst2) * 4 - 4;
+ }
+ else if (!bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* LDMDB */
+ if (!bit (inst2, 15))
+ load_pc = 0;
+ offset = -4;
+ }
+ else if (bit (inst1, 7) && bit (inst1, 8))
+ {
+ /* RFEIA */
+ offset = 0;
+ }
+ else if (!bit (inst1, 7) && !bit (inst1, 8))
+ {
+ /* RFEDB */
+ offset = -8;
+ }
+ else
+ load_pc = 0;
+
+ if (load_pc)
+ {
+ CORE_ADDR addr = get_frame_register_unsigned (frame, rn);
+ nextpc = get_frame_memory_unsigned (frame, addr + offset, 4);
+ }
+ }
+ else if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00)
+ {
+ /* MOV PC or MOVS PC. */
+ nextpc = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
+ nextpc = MAKE_THUMB_ADDR (nextpc);
+ }
+ else if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000)
+ {
+ /* LDR PC. */
+ CORE_ADDR base;
+ int rn, load_pc = 1;
+
+ rn = bits (inst1, 0, 3);
+ base = get_frame_register_unsigned (frame, rn);
+ if (rn == 15)
+ {
+ base = (base + 4) & ~(CORE_ADDR) 0x3;
+ if (bit (inst1, 7))
+ base += bits (inst2, 0, 11);
+ else
+ base -= bits (inst2, 0, 11);
+ }
+ else if (bit (inst1, 7))
+ base += bits (inst2, 0, 11);
+ else if (bit (inst2, 11))
+ {
+ if (bit (inst2, 10))
+ {
+ if (bit (inst2, 9))
+ base += bits (inst2, 0, 7);
+ else
+ base -= bits (inst2, 0, 7);
+ }
+ }
+ else if ((inst2 & 0x0fc0) == 0x0000)
+ {
+ int shift = bits (inst2, 4, 5), rm = bits (inst2, 0, 3);
+ base += get_frame_register_unsigned (frame, rm) << shift;
+ }
+ else
+ /* Reserved. */
+ load_pc = 0;
+
+ if (load_pc)
+ nextpc = get_frame_memory_unsigned (frame, base, 4);
+ }
+ else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000)
+ {
+ /* TBB. */
+ CORE_ADDR tbl_reg, table, offset, length;
+
+ tbl_reg = bits (inst1, 0, 3);
+ if (tbl_reg == 0x0f)
+ table = pc + 4; /* Regcache copy of PC isn't right yet. */
+ else
+ table = get_frame_register_unsigned (frame, tbl_reg);
+
+ offset = get_frame_register_unsigned (frame, bits (inst2, 0, 3));
+ length = 2 * get_frame_memory_unsigned (frame, table + offset, 1);
+ nextpc = pc_val + length;
+ }
+ else if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010)
+ {
+ /* TBH. */
+ CORE_ADDR tbl_reg, table, offset, length;
+
+ tbl_reg = bits (inst1, 0, 3);
+ if (tbl_reg == 0x0f)
+ table = pc + 4; /* Regcache copy of PC isn't right yet. */
+ else
+ table = get_frame_register_unsigned (frame, tbl_reg);
+
+ offset = 2 * get_frame_register_unsigned (frame, bits (inst2, 0, 3));
+ length = 2 * get_frame_memory_unsigned (frame, table + offset, 2);
+ nextpc = pc_val + length;
+ }
}
else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */
{
nextpc = pc_val;
else
nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6));
-
- nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
}
-
+ else if ((inst1 & 0xf500) == 0xb100)
+ {
+ /* CBNZ or CBZ. */
+ int imm = (bit (inst1, 9) << 6) + (bits (inst1, 3, 7) << 1);
+ ULONGEST reg = get_frame_register_unsigned (frame, bits (inst1, 0, 2));
+
+ if (bit (inst1, 11) && reg != 0)
+ nextpc = pc_val + imm;
+ else if (!bit (inst1, 11) && reg == 0)
+ nextpc = pc_val + imm;
+ }
return nextpc;
}
-CORE_ADDR
-arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+/* Get the raw next address. PC is the current program counter, in
+ FRAME. INSERT_BKPT should be TRUE if we want a breakpoint set on
+ the alternative next instruction if there are two options.
+
+ The value returned has the execution state of the next instruction
+ encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is
+ in Thumb-State, and gdbarch_addr_bits_remove () to get the plain memory
+ address.
+*/
+static CORE_ADDR
+arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
unsigned long pc_val;
unsigned long this_instr;
unsigned long status;
CORE_ADDR nextpc;
if (arm_frame_is_thumb (frame))
- return thumb_get_next_pc (frame, pc);
+ return thumb_get_next_pc_raw (frame, pc, insert_bkpt);
pc_val = (unsigned long) pc;
- this_instr = read_memory_unsigned_integer (pc, 4);
-
- if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
- this_instr = SWAP_INT (this_instr);
+ this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code);
status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */
/* Branch with Link and change to Thumb. */
nextpc = BranchDest (pc, this_instr);
nextpc |= bit (this_instr, 24) << 1;
-
- nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
+ nextpc = MAKE_THUMB_ADDR (nextpc);
break;
}
case 0xc:
|| bits (this_instr, 4, 27) == 0x12fff3)
{
rn = bits (this_instr, 0, 3);
- result = (rn == 15) ? pc_val + 8
+ nextpc = (rn == 15) ? pc_val + 8
: get_frame_register_unsigned (frame, rn);
- nextpc = (CORE_ADDR) gdbarch_addr_bits_remove
- (gdbarch, result);
-
- if (nextpc == pc)
- error (_("Infinite loop detected"));
-
return nextpc;
}
result = ~operand2;
break;
}
- nextpc = (CORE_ADDR) gdbarch_addr_bits_remove
- (gdbarch, result);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- break;
- }
+ /* In 26-bit APCS the bottom two bits of the result are
+ ignored, and we always end up in ARM state. */
+ if (!arm_apcs_32)
+ nextpc = arm_addr_bits_remove (gdbarch, result);
+ else
+ nextpc = result;
+
+ break;
+ }
+
+ case 0x4:
+ case 0x5: /* data transfer */
+ case 0x6:
+ case 0x7:
+ if (bit (this_instr, 20))
+ {
+ /* load */
+ if (bits (this_instr, 12, 15) == 15)
+ {
+ /* rd == pc */
+ unsigned long rn;
+ unsigned long base;
+
+ if (bit (this_instr, 22))
+ error (_("Invalid update to pc in instruction"));
+
+ /* byte write to PC */
+ rn = bits (this_instr, 16, 19);
+ base = (rn == 15) ? pc_val + 8
+ : get_frame_register_unsigned (frame, rn);
+ if (bit (this_instr, 24))
+ {
+ /* pre-indexed */
+ int c = (status & FLAG_C) ? 1 : 0;
+ unsigned long offset =
+ (bit (this_instr, 25)
+ ? shifted_reg_val (frame, this_instr, c, pc_val, status)
+ : bits (this_instr, 0, 11));
+
+ if (bit (this_instr, 23))
+ base += offset;
+ else
+ base -= offset;
+ }
+ nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base,
+ 4, byte_order);
+ }
+ }
+ break;
+
+ case 0x8:
+ case 0x9: /* block transfer */
+ if (bit (this_instr, 20))
+ {
+ /* LDM */
+ if (bit (this_instr, 15))
+ {
+ /* loading pc */
+ int offset = 0;
+
+ if (bit (this_instr, 23))
+ {
+ /* up */
+ unsigned long reglist = bits (this_instr, 0, 14);
+ offset = bitcount (reglist) * 4;
+ if (bit (this_instr, 24)) /* pre */
+ offset += 4;
+ }
+ else if (bit (this_instr, 24))
+ offset = -4;
+
+ {
+ 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);
+ }
+ }
+ }
+ break;
+
+ case 0xb: /* branch & link */
+ case 0xa: /* branch */
+ {
+ nextpc = BranchDest (pc, this_instr);
+ break;
+ }
+
+ case 0xc:
+ case 0xd:
+ case 0xe: /* coproc ops */
+ case 0xf: /* SWI */
+ break;
+
+ default:
+ fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n"));
+ return (pc);
+ }
+ }
+
+ return nextpc;
+}
+
+CORE_ADDR
+arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ CORE_ADDR nextpc =
+ gdbarch_addr_bits_remove (gdbarch,
+ arm_get_next_pc_raw (frame, pc, TRUE));
+ if (nextpc == pc)
+ error (_("Infinite loop detected"));
+ return nextpc;
+}
+
+/* single_step() is called just before we want to resume the inferior,
+ if we want to single-step it but there is no hardware or kernel
+ single-step support. We find the target of the coming instruction
+ and breakpoint it. */
+
+int
+arm_software_single_step (struct frame_info *frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct address_space *aspace = get_frame_address_space (frame);
+
+ /* NOTE: This may insert the wrong breakpoint instruction when
+ single-stepping over a mode-changing instruction, if the
+ CPSR heuristics are used. */
+
+ CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
+ insert_single_step_breakpoint (gdbarch, aspace, next_pc);
+
+ return 1;
+}
+
+/* Given BUF, which is OLD_LEN bytes ending at ENDADDR, expand
+ the buffer to be NEW_LEN bytes ending at ENDADDR. Return
+ NULL if an error occurs. BUF is freed. */
+
+static gdb_byte *
+extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr,
+ int old_len, int new_len)
+{
+ gdb_byte *new_buf, *middle;
+ int bytes_to_read = new_len - old_len;
+
+ new_buf = xmalloc (new_len);
+ memcpy (new_buf + bytes_to_read, buf, old_len);
+ xfree (buf);
+ if (target_read_memory (endaddr - new_len, new_buf, bytes_to_read) != 0)
+ {
+ xfree (new_buf);
+ return NULL;
+ }
+ return new_buf;
+}
+
+/* An IT block is at most the 2-byte IT instruction followed by
+ four 4-byte instructions. The furthest back we must search to
+ find an IT block that affects the current instruction is thus
+ 2 + 3 * 4 == 14 bytes. */
+#define MAX_IT_BLOCK_PREFIX 14
+
+/* Use a quick scan if there are more than this many bytes of
+ code. */
+#define IT_SCAN_THRESHOLD 32
+
+/* Adjust a breakpoint's address to move breakpoints out of IT blocks.
+ A breakpoint in an IT block may not be hit, depending on the
+ condition flags. */
+static CORE_ADDR
+arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr)
+{
+ gdb_byte *buf;
+ char map_type;
+ CORE_ADDR boundary, func_start;
+ int buf_len, buf2_len;
+ enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch);
+ int i, any, last_it, last_it_count;
+
+ /* If we are using BKPT breakpoints, none of this is necessary. */
+ if (gdbarch_tdep (gdbarch)->thumb2_breakpoint == NULL)
+ return bpaddr;
+
+ /* ARM mode does not have this problem. */
+ if (!arm_pc_is_thumb (bpaddr))
+ return bpaddr;
+
+ /* We are setting a breakpoint in Thumb code that could potentially
+ contain an IT block. The first step is to find how much Thumb
+ code there is; we do not need to read outside of known Thumb
+ sequences. */
+ map_type = arm_find_mapping_symbol (bpaddr, &boundary);
+ if (map_type == 0)
+ /* Thumb-2 code must have mapping symbols to have a chance. */
+ return bpaddr;
+
+ bpaddr = gdbarch_addr_bits_remove (gdbarch, bpaddr);
+
+ if (find_pc_partial_function (bpaddr, NULL, &func_start, NULL)
+ && func_start > boundary)
+ boundary = func_start;
+
+ /* Search for a candidate IT instruction. We have to do some fancy
+ footwork to distinguish a real IT instruction from the second
+ half of a 32-bit instruction, but there is no need for that if
+ there's no candidate. */
+ buf_len = min (bpaddr - boundary, MAX_IT_BLOCK_PREFIX);
+ if (buf_len == 0)
+ /* No room for an IT instruction. */
+ return bpaddr;
+
+ buf = xmalloc (buf_len);
+ if (target_read_memory (bpaddr - buf_len, buf, buf_len) != 0)
+ return bpaddr;
+ any = 0;
+ for (i = 0; i < buf_len; i += 2)
+ {
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+ {
+ any = 1;
+ break;
+ }
+ }
+ if (any == 0)
+ {
+ xfree (buf);
+ return bpaddr;
+ }
+
+ /* OK, the code bytes before this instruction contain at least one
+ halfword which resembles an IT instruction. We know that it's
+ Thumb code, but there are still two possibilities. Either the
+ halfword really is an IT instruction, or it is the second half of
+ a 32-bit Thumb instruction. The only way we can tell is to
+ scan forwards from a known instruction boundary. */
+ if (bpaddr - boundary > IT_SCAN_THRESHOLD)
+ {
+ int definite;
+
+ /* There's a lot of code before this instruction. Start with an
+ optimistic search; it's easy to recognize halfwords that can
+ not be the start of a 32-bit instruction, and use that to
+ lock on to the instruction boundaries. */
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len, IT_SCAN_THRESHOLD);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = IT_SCAN_THRESHOLD;
+
+ definite = 0;
+ for (i = 0; i < buf_len - sizeof (buf) && ! definite; i += 2)
+ {
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ if (thumb_insn_size (inst1) == 2)
+ {
+ definite = 1;
+ break;
+ }
+ }
+
+ /* At this point, if DEFINITE, BUF[I] is the first place we
+ are sure that we know the instruction boundaries, and it is far
+ enough from BPADDR that we could not miss an IT instruction
+ affecting BPADDR. If ! DEFINITE, give up - start from a
+ known boundary. */
+ if (! definite)
+ {
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = bpaddr - boundary;
+ i = 0;
+ }
+ }
+ else
+ {
+ buf = extend_buffer_earlier (buf, bpaddr, buf_len, bpaddr - boundary);
+ if (buf == NULL)
+ return bpaddr;
+ buf_len = bpaddr - boundary;
+ i = 0;
+ }
+
+ /* Scan forwards. Find the last IT instruction before BPADDR. */
+ last_it = -1;
+ last_it_count = 0;
+ while (i < buf_len)
+ {
+ unsigned short inst1 = extract_unsigned_integer (&buf[i], 2, order);
+ last_it_count--;
+ if ((inst1 & 0xff00) == 0xbf00 && (inst1 & 0x000f) != 0)
+ {
+ last_it = i;
+ if (inst1 & 0x0001)
+ last_it_count = 4;
+ else if (inst1 & 0x0002)
+ last_it_count = 3;
+ else if (inst1 & 0x0004)
+ last_it_count = 2;
+ else
+ last_it_count = 1;
+ }
+ i += thumb_insn_size (inst1);
+ }
+
+ xfree (buf);
+
+ if (last_it == -1)
+ /* There wasn't really an IT instruction after all. */
+ return bpaddr;
+
+ if (last_it_count < 1)
+ /* It was too far away. */
+ return bpaddr;
+
+ /* This really is a trouble spot. Move the breakpoint to the IT
+ instruction. */
+ return bpaddr - buf_len + last_it;
+}
+
+/* ARM displaced stepping support.
+
+ Generally ARM displaced stepping works as follows:
+
+ 1. When an instruction is to be single-stepped, it is first decoded by
+ arm_process_displaced_insn (called from arm_displaced_step_copy_insn).
+ Depending on the type of instruction, it is then copied to a scratch
+ location, possibly in a modified form. The copy_* set of functions
+ performs such modification, as necessary. A breakpoint is placed after
+ the modified instruction in the scratch space to return control to GDB.
+ Note in particular that instructions which modify the PC will no longer
+ do so after modification.
+
+ 2. The instruction is single-stepped, by setting the PC to the scratch
+ location address, and resuming. Control returns to GDB when the
+ breakpoint is hit.
+
+ 3. A cleanup function (cleanup_*) is called corresponding to the copy_*
+ function used for the current instruction. This function's job is to
+ put the CPU/memory state back to what it would have been if the
+ instruction had been executed unmodified in its original location. */
+
+/* NOP instruction (mov r0, r0). */
+#define ARM_NOP 0xe1a00000
+
+/* Helper for register reads for displaced stepping. In particular, this
+ returns the PC as it would be seen by the instruction at its original
+ location. */
+
+ULONGEST
+displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno)
+{
+ ULONGEST ret;
+
+ if (regno == 15)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: read pc value %.8lx\n",
+ (unsigned long) from + 8);
+ return (ULONGEST) from + 8; /* Pipeline offset. */
+ }
+ else
+ {
+ regcache_cooked_read_unsigned (regs, regno, &ret);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: read r%d value %.8lx\n",
+ regno, (unsigned long) ret);
+ return ret;
+ }
+}
+
+static int
+displaced_in_arm_mode (struct regcache *regs)
+{
+ ULONGEST ps;
+
+ regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
+
+ return (ps & CPSR_T) == 0;
+}
+
+/* Write to the PC as from a branch instruction. */
+
+static void
+branch_write_pc (struct regcache *regs, ULONGEST val)
+{
+ if (displaced_in_arm_mode (regs))
+ /* Note: If bits 0/1 are set, this branch would be unpredictable for
+ architecture versions < 6. */
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & ~(ULONGEST) 0x3);
+ else
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & ~(ULONGEST) 0x1);
+}
+
+/* Write to the PC as from a branch-exchange instruction. */
+
+static void
+bx_write_pc (struct regcache *regs, ULONGEST val)
+{
+ ULONGEST ps;
+
+ regcache_cooked_read_unsigned (regs, ARM_PS_REGNUM, &ps);
+
+ if ((val & 1) == 1)
+ {
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM, ps | CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffe);
+ }
+ else if ((val & 2) == 0)
+ {
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM,
+ ps & ~(ULONGEST) CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val);
+ }
+ else
+ {
+ /* Unpredictable behaviour. Try to do something sensible (switch to ARM
+ mode, align dest to 4 bytes). */
+ warning (_("Single-stepping BX to non-word-aligned ARM instruction."));
+ regcache_cooked_write_unsigned (regs, ARM_PS_REGNUM,
+ ps & ~(ULONGEST) CPSR_T);
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, val & 0xfffffffc);
+ }
+}
+
+/* Write to the PC as if from a load instruction. */
+
+static void
+load_write_pc (struct regcache *regs, ULONGEST val)
+{
+ if (DISPLACED_STEPPING_ARCH_VERSION >= 5)
+ bx_write_pc (regs, val);
+ else
+ branch_write_pc (regs, val);
+}
+
+/* Write to the PC as if from an ALU instruction. */
+
+static void
+alu_write_pc (struct regcache *regs, ULONGEST val)
+{
+ if (DISPLACED_STEPPING_ARCH_VERSION >= 7 && displaced_in_arm_mode (regs))
+ bx_write_pc (regs, val);
+ else
+ branch_write_pc (regs, val);
+}
+
+/* Helper for writing to registers for displaced stepping. Writing to the PC
+ has a varying effects depending on the instruction which does the write:
+ this is controlled by the WRITE_PC argument. */
+
+void
+displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc,
+ int regno, ULONGEST val, enum pc_write_style write_pc)
+{
+ if (regno == 15)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing pc %.8lx\n",
+ (unsigned long) val);
+ switch (write_pc)
+ {
+ case BRANCH_WRITE_PC:
+ branch_write_pc (regs, val);
+ break;
+
+ case BX_WRITE_PC:
+ bx_write_pc (regs, val);
+ break;
+
+ case LOAD_WRITE_PC:
+ load_write_pc (regs, val);
+ break;
+
+ case ALU_WRITE_PC:
+ alu_write_pc (regs, val);
+ break;
+
+ case CANNOT_WRITE_PC:
+ warning (_("Instruction wrote to PC in an unexpected way when "
+ "single-stepping"));
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid argument to displaced_write_reg"));
+ }
+
+ dsc->wrote_to_pc = 1;
+ }
+ else
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing r%d value %.8lx\n",
+ regno, (unsigned long) val);
+ regcache_cooked_write_unsigned (regs, regno, val);
+ }
+}
+
+/* This function is used to concisely determine if an instruction INSN
+ references PC. Register fields of interest in INSN should have the
+ corresponding fields of BITMASK set to 0b1111. The function returns return 1
+ if any of these fields in INSN reference the PC (also 0b1111, r15), else it
+ returns 0. */
+
+static int
+insn_references_pc (uint32_t insn, uint32_t bitmask)
+{
+ uint32_t lowbit = 1;
+
+ while (bitmask != 0)
+ {
+ uint32_t mask;
+
+ for (; lowbit && (bitmask & lowbit) == 0; lowbit <<= 1)
+ ;
+
+ if (!lowbit)
+ break;
+
+ mask = lowbit * 0xf;
+
+ if ((insn & mask) == mask)
+ return 1;
+
+ bitmask &= ~mask;
+ }
+
+ return 0;
+}
+
+/* The simplest copy function. Many instructions have the same effect no
+ matter what address they are executed at: in those cases, use this. */
+
+static int
+copy_unmodified (struct gdbarch *gdbarch, uint32_t insn,
+ const char *iname, struct displaced_step_closure *dsc)
+{
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, "
+ "opcode/class '%s' unmodified\n", (unsigned long) insn,
+ iname);
+
+ dsc->modinsn[0] = insn;
+
+ return 0;
+}
+
+/* Preload instructions with immediate offset. */
+
+static void
+cleanup_preload (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ if (!dsc->u.preload.immed)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+}
+
+static int
+copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ ULONGEST rn_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return copy_unmodified (gdbarch, insn, "preload", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ (unsigned long) insn);
+
+ /* Preload instructions:
+
+ {pli/pld} [rn, #+/-imm]
+ ->
+ {pli/pld} [r0, #+/-imm]. */
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ rn_val = displaced_read_reg (regs, from, rn);
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+
+ dsc->u.preload.immed = 1;
+
+ dsc->modinsn[0] = insn & 0xfff0ffff;
+
+ dsc->cleanup = &cleanup_preload;
+
+ return 0;
+}
+
+/* Preload instructions with register offset. */
+
+static int
+copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3);
+ ULONGEST rn_val, rm_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000f000ful))
+ return copy_unmodified (gdbarch, insn, "preload reg", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n",
+ (unsigned long) insn);
+
+ /* Preload register-offset instructions:
+
+ {pli/pld} [rn, rm {, shift}]
+ ->
+ {pli/pld} [r0, r1 {, shift}]. */
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, from, 1);
+ rn_val = displaced_read_reg (regs, from, rn);
+ rm_val = displaced_read_reg (regs, from, rm);
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rm_val, CANNOT_WRITE_PC);
+
+ dsc->u.preload.immed = 0;
+
+ dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1;
+
+ dsc->cleanup = &cleanup_preload;
+
+ return 0;
+}
+
+/* Copy/cleanup coprocessor load and store instructions. */
+
+static void
+cleanup_copro_load_store (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST rn_val = displaced_read_reg (regs, dsc->insn_addr, 0);
+
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, LOAD_WRITE_PC);
+}
+
+static int
+copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ ULONGEST rn_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000f0000ul))
+ return copy_unmodified (gdbarch, insn, "copro load/store", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor "
+ "load/store insn %.8lx\n", (unsigned long) insn);
+
+ /* Coprocessor load/store instructions:
+
+ {stc/stc2} [<Rn>, #+/-imm] (and other immediate addressing modes)
+ ->
+ {stc/stc2} [r0, #+/-imm].
+
+ ldc/ldc2 are handled identically. */
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ rn_val = displaced_read_reg (regs, from, rn);
+ displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC);
+
+ dsc->u.ldst.writeback = bit (insn, 25);
+ dsc->u.ldst.rn = rn;
+
+ dsc->modinsn[0] = insn & 0xfff0ffff;
+
+ dsc->cleanup = &cleanup_copro_load_store;
+
+ return 0;
+}
+
+/* Clean up branch instructions (actually perform the branch, by setting
+ PC). */
+
+static void
+cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST from = dsc->insn_addr;
+ uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
+ int branch_taken = condition_true (dsc->u.branch.cond, status);
+ enum pc_write_style write_pc = dsc->u.branch.exchange
+ ? BX_WRITE_PC : BRANCH_WRITE_PC;
+
+ if (!branch_taken)
+ return;
+
+ if (dsc->u.branch.link)
+ {
+ ULONGEST pc = displaced_read_reg (regs, from, 15);
+ displaced_write_reg (regs, dsc, 14, pc - 4, CANNOT_WRITE_PC);
+ }
+
+ displaced_write_reg (regs, dsc, 15, dsc->u.branch.dest, write_pc);
+}
+
+/* Copy B/BL/BLX instructions with immediate destinations. */
+
+static int
+copy_b_bl_blx (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int cond = bits (insn, 28, 31);
+ int exchange = (cond == 0xf);
+ int link = exchange || bit (insn, 24);
+ CORE_ADDR from = dsc->insn_addr;
+ long offset;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s immediate insn "
+ "%.8lx\n", (exchange) ? "blx" : (link) ? "bl" : "b",
+ (unsigned long) insn);
+
+ /* Implement "BL<cond> <label>" as:
+
+ Preparation: cond <- instruction condition
+ Insn: mov r0, r0 (nop)
+ Cleanup: if (condition true) { r14 <- pc; pc <- label }.
+
+ B<cond> similar, but don't set r14 in cleanup. */
+
+ if (exchange)
+ /* For BLX, set bit 0 of the destination. The cleanup_branch function will
+ then arrange the switch into Thumb mode. */
+ offset = (bits (insn, 0, 23) << 2) | (bit (insn, 24) << 1) | 1;
+ else
+ offset = bits (insn, 0, 23) << 2;
+
+ if (bit (offset, 25))
+ offset = offset | ~0x3ffffff;
+
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = link;
+ dsc->u.branch.exchange = exchange;
+ dsc->u.branch.dest = from + 8 + offset;
+
+ dsc->modinsn[0] = ARM_NOP;
+
+ dsc->cleanup = &cleanup_branch;
+
+ return 0;
+}
+
+/* Copy BX/BLX with register-specified destinations. */
+
+static int
+copy_bx_blx_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int cond = bits (insn, 28, 31);
+ /* BX: x12xxx1x
+ BLX: x12xxx3x. */
+ int link = bit (insn, 5);
+ unsigned int rm = bits (insn, 0, 3);
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s register insn "
+ "%.8lx\n", (link) ? "blx" : "bx", (unsigned long) insn);
+
+ /* 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, from, rm);
+
+ dsc->u.branch.cond = cond;
+ dsc->u.branch.link = link;
+ dsc->u.branch.exchange = 1;
+
+ dsc->modinsn[0] = ARM_NOP;
+
+ dsc->cleanup = &cleanup_branch;
+
+ return 0;
+}
+
+/* Copy/cleanup arithmetic/logic instruction with immediate RHS. */
+
+static void
+cleanup_alu_imm (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ ULONGEST rd_val = displaced_read_reg (regs, dsc->insn_addr, 0);
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
+}
+
+static int
+copy_alu_imm (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rd = bits (insn, 12, 15);
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd);
+ ULONGEST rd_val, rn_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000ff000ul))
+ return copy_unmodified (gdbarch, insn, "ALU immediate", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying immediate %s insn "
+ "%.8lx\n", is_mov ? "move" : "ALU",
+ (unsigned long) insn);
+
+ /* 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, from, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, from, 1);
+ rn_val = displaced_read_reg (regs, from, rn);
+ rd_val = displaced_read_reg (regs, from, 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;
+
+ if (is_mov)
+ dsc->modinsn[0] = insn & 0xfff00fff;
+ else
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x10000;
+
+ dsc->cleanup = &cleanup_alu_imm;
+
+ return 0;
+}
+
+/* Copy/cleanup arithmetic/logic insns with register RHS. */
+
+static void
+cleanup_alu_reg (struct gdbarch *gdbarch,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ ULONGEST rd_val;
+ int i;
+
+ rd_val = displaced_read_reg (regs, dsc->insn_addr, 0);
+
+ for (i = 0; i < 3; i++)
+ displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
+
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
+}
+
+static int
+copy_alu_reg (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3);
+ unsigned int rd = bits (insn, 12, 15);
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd);
+ ULONGEST rd_val, rn_val, rm_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return copy_unmodified (gdbarch, insn, "ALU reg", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying reg %s insn %.8lx\n",
+ is_mov ? "move" : "ALU", (unsigned long) insn);
+
+ /* Instruction is of form:
+
+ <op><cond> rd, [rn,] rm [, <shift>]
+
+ Rewrite as:
+
+ Preparation: tmp1, tmp2, tmp3 <- r0, r1, r2;
+ r0, r1, r2 <- rd, rn, rm
+ Insn: <op><cond> r0, r1, r2 [, <shift>]
+ Cleanup: rd <- r0; r0, r1, r2 <- tmp1, tmp2, tmp3
+ */
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, from, 1);
+ dsc->tmp[2] = displaced_read_reg (regs, from, 2);
+ rd_val = displaced_read_reg (regs, from, rd);
+ rn_val = displaced_read_reg (regs, from, rn);
+ rm_val = displaced_read_reg (regs, from, rm);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
+
+ if (is_mov)
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x2;
+ else
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x10002;
+
+ dsc->cleanup = &cleanup_alu_reg;
+
+ return 0;
+}
+
+/* Cleanup/copy arithmetic/logic insns with shifted register RHS. */
+
+static void
+cleanup_alu_shifted_reg (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST rd_val = displaced_read_reg (regs, dsc->insn_addr, 0);
+ int i;
+
+ for (i = 0; i < 4; i++)
+ displaced_write_reg (regs, dsc, i, dsc->tmp[i], CANNOT_WRITE_PC);
+
+ displaced_write_reg (regs, dsc, dsc->rd, rd_val, ALU_WRITE_PC);
+}
+
+static int
+copy_alu_shifted_reg (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3);
+ unsigned int rd = bits (insn, 12, 15);
+ unsigned int rs = bits (insn, 8, 11);
+ unsigned int op = bits (insn, 21, 24);
+ int is_mov = (op == 0xd), i;
+ ULONGEST rd_val, rn_val, rm_val, rs_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000fff0ful))
+ return copy_unmodified (gdbarch, insn, "ALU shifted reg", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying shifted reg %s insn "
+ "%.8lx\n", is_mov ? "move" : "ALU",
+ (unsigned long) insn);
+
+ /* Instruction is of form:
+
+ <op><cond> rd, [rn,] rm, <shift> rs
+
+ Rewrite as:
+
+ Preparation: tmp1, tmp2, tmp3, tmp4 <- r0, r1, r2, r3
+ r0, r1, r2, r3 <- rd, rn, rm, rs
+ Insn: <op><cond> r0, r1, r2, <shift> r3
+ Cleanup: tmp5 <- r0
+ r0, r1, r2, r3 <- tmp1, tmp2, tmp3, tmp4
+ rd <- tmp5
+ */
+
+ for (i = 0; i < 4; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, from, i);
+
+ rd_val = displaced_read_reg (regs, from, rd);
+ rn_val = displaced_read_reg (regs, from, rn);
+ rm_val = displaced_read_reg (regs, from, rm);
+ rs_val = displaced_read_reg (regs, from, rs);
+ displaced_write_reg (regs, dsc, 0, rd_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 1, rn_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rm_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 3, rs_val, CANNOT_WRITE_PC);
+ dsc->rd = rd;
+
+ if (is_mov)
+ dsc->modinsn[0] = (insn & 0xfff000f0) | 0x302;
+ else
+ dsc->modinsn[0] = (insn & 0xfff000f0) | 0x10302;
+
+ dsc->cleanup = &cleanup_alu_shifted_reg;
+
+ return 0;
+}
+
+/* Clean up load instructions. */
+
+static void
+cleanup_load (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST rt_val, rt_val2 = 0, rn_val;
+ CORE_ADDR from = dsc->insn_addr;
+
+ rt_val = displaced_read_reg (regs, from, 0);
+ if (dsc->u.ldst.xfersize == 8)
+ rt_val2 = displaced_read_reg (regs, from, 1);
+ rn_val = displaced_read_reg (regs, from, 2);
+
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ if (dsc->u.ldst.xfersize > 4)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.immed)
+ displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
+
+ /* Handle register writeback. */
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
+ /* Put result in right place. */
+ displaced_write_reg (regs, dsc, dsc->rd, rt_val, LOAD_WRITE_PC);
+ if (dsc->u.ldst.xfersize == 8)
+ displaced_write_reg (regs, dsc, dsc->rd + 1, rt_val2, LOAD_WRITE_PC);
+}
+
+/* Clean up store instructions. */
+
+static void
+cleanup_store (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ CORE_ADDR from = dsc->insn_addr;
+ ULONGEST rn_val = displaced_read_reg (regs, from, 2);
+
+ displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC);
+ if (dsc->u.ldst.xfersize > 4)
+ displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, dsc->tmp[2], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.immed)
+ displaced_write_reg (regs, dsc, 3, dsc->tmp[3], CANNOT_WRITE_PC);
+ if (!dsc->u.ldst.restore_r4)
+ displaced_write_reg (regs, dsc, 4, dsc->tmp[4], CANNOT_WRITE_PC);
+
+ /* Writeback. */
+ if (dsc->u.ldst.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, CANNOT_WRITE_PC);
+}
+
+/* Copy "extra" load/store instructions. These are halfword/doubleword
+ transfers, which have a different encoding to byte/word transfers. */
+
+static int
+copy_extra_ld_st (struct gdbarch *gdbarch, uint32_t insn, int unpriveleged,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int op1 = bits (insn, 20, 24);
+ unsigned int op2 = bits (insn, 5, 6);
+ unsigned int rt = bits (insn, 12, 15);
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3);
+ char load[12] = {0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1};
+ char bytesize[12] = {2, 2, 2, 2, 8, 1, 8, 1, 8, 2, 8, 2};
+ int immed = (op1 & 0x4) != 0;
+ int opcode;
+ ULONGEST rt_val, rt_val2 = 0, rn_val, rm_val = 0;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return copy_unmodified (gdbarch, insn, "extra load/store", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %sextra load/store "
+ "insn %.8lx\n", unpriveleged ? "unpriveleged " : "",
+ (unsigned long) insn);
+
+ opcode = ((op2 << 2) | (op1 & 0x1) | ((op1 & 0x4) >> 1)) - 4;
+
+ if (opcode < 0)
+ internal_error (__FILE__, __LINE__,
+ _("copy_extra_ld_st: instruction decode error"));
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ dsc->tmp[1] = displaced_read_reg (regs, from, 1);
+ dsc->tmp[2] = displaced_read_reg (regs, from, 2);
+ if (!immed)
+ dsc->tmp[3] = displaced_read_reg (regs, from, 3);
+
+ rt_val = displaced_read_reg (regs, from, rt);
+ if (bytesize[opcode] == 8)
+ rt_val2 = displaced_read_reg (regs, from, rt + 1);
+ rn_val = displaced_read_reg (regs, from, rn);
+ if (!immed)
+ rm_val = displaced_read_reg (regs, from, rm);
+
+ displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
+ if (bytesize[opcode] == 8)
+ displaced_write_reg (regs, dsc, 1, rt_val2, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
+ if (!immed)
+ displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
+
+ dsc->rd = rt;
+ dsc->u.ldst.xfersize = bytesize[opcode];
+ dsc->u.ldst.rn = rn;
+ dsc->u.ldst.immed = immed;
+ dsc->u.ldst.writeback = bit (insn, 24) == 0 || bit (insn, 21) != 0;
+ dsc->u.ldst.restore_r4 = 0;
+
+ if (immed)
+ /* {ldr,str}<width><cond> rt, [rt2,] [rn, #imm]
+ ->
+ {ldr,str}<width><cond> r0, [r1,] [r2, #imm]. */
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
+ else
+ /* {ldr,str}<width><cond> rt, [rt2,] [rn, +/-rm]
+ ->
+ {ldr,str}<width><cond> r0, [r1,] [r2, +/-r3]. */
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
+
+ dsc->cleanup = load[opcode] ? &cleanup_load : &cleanup_store;
+
+ return 0;
+}
+
+/* Copy byte/word loads and stores. */
+
+static int
+copy_ldr_str_ldrb_strb (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc, int load, int byte,
+ int usermode)
+{
+ int immed = !bit (insn, 25);
+ unsigned int rt = bits (insn, 12, 15);
+ unsigned int rn = bits (insn, 16, 19);
+ unsigned int rm = bits (insn, 0, 3); /* Only valid if !immed. */
+ ULONGEST rt_val, rn_val, rm_val = 0;
+ CORE_ADDR from = dsc->insn_addr;
+
+ if (!insn_references_pc (insn, 0x000ff00ful))
+ return copy_unmodified (gdbarch, insn, "load/store", dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying %s%s insn %.8lx\n",
+ load ? (byte ? "ldrb" : "ldr")
+ : (byte ? "strb" : "str"), usermode ? "t" : "",
+ (unsigned long) insn);
+
+ dsc->tmp[0] = displaced_read_reg (regs, from, 0);
+ dsc->tmp[2] = displaced_read_reg (regs, from, 2);
+ if (!immed)
+ dsc->tmp[3] = displaced_read_reg (regs, from, 3);
+ if (!load)
+ dsc->tmp[4] = displaced_read_reg (regs, from, 4);
+
+ rt_val = displaced_read_reg (regs, from, rt);
+ rn_val = displaced_read_reg (regs, from, rn);
+ if (!immed)
+ rm_val = displaced_read_reg (regs, from, rm);
+
+ displaced_write_reg (regs, dsc, 0, rt_val, CANNOT_WRITE_PC);
+ displaced_write_reg (regs, dsc, 2, rn_val, CANNOT_WRITE_PC);
+ if (!immed)
+ displaced_write_reg (regs, dsc, 3, rm_val, CANNOT_WRITE_PC);
+
+ dsc->rd = rt;
+ dsc->u.ldst.xfersize = byte ? 1 : 4;
+ dsc->u.ldst.rn = rn;
+ dsc->u.ldst.immed = immed;
+ dsc->u.ldst.writeback = bit (insn, 24) == 0 || bit (insn, 21) != 0;
+
+ /* To write PC we can do:
+
+ scratch+0: str pc, temp (*temp = scratch + 8 + offset)
+ scratch+4: ldr r4, temp
+ scratch+8: sub r4, r4, pc (r4 = scratch + 8 + offset - scratch - 8 - 8)
+ scratch+12: add r4, r4, #8 (r4 = offset)
+ scratch+16: add r0, r0, r4
+ scratch+20: str r0, [r2, #imm] (or str r0, [r2, r3])
+ scratch+24: <temp>
+
+ Otherwise we don't know what value to write for PC, since the offset is
+ architecture-dependent (sometimes PC+8, sometimes PC+12). */
+
+ if (load || rt != 15)
+ {
+ dsc->u.ldst.restore_r4 = 0;
+
+ if (immed)
+ /* {ldr,str}[b]<cond> rt, [rn, #imm], etc.
+ ->
+ {ldr,str}[b]<cond> r0, [r2, #imm]. */
+ dsc->modinsn[0] = (insn & 0xfff00fff) | 0x20000;
+ else
+ /* {ldr,str}[b]<cond> rt, [rn, rm], etc.
+ ->
+ {ldr,str}[b]<cond> r0, [r2, r3]. */
+ dsc->modinsn[0] = (insn & 0xfff00ff0) | 0x20003;
+ }
+ else
+ {
+ /* We need to use r4 as scratch. Make sure it's restored afterwards. */
+ dsc->u.ldst.restore_r4 = 1;
+
+ dsc->modinsn[0] = 0xe58ff014; /* str pc, [pc, #20]. */
+ dsc->modinsn[1] = 0xe59f4010; /* ldr r4, [pc, #16]. */
+ dsc->modinsn[2] = 0xe044400f; /* sub r4, r4, pc. */
+ dsc->modinsn[3] = 0xe2844008; /* add r4, r4, #8. */
+ dsc->modinsn[4] = 0xe0800004; /* add r0, r0, r4. */
+
+ /* As above. */
+ if (immed)
+ dsc->modinsn[5] = (insn & 0xfff00fff) | 0x20000;
+ else
+ dsc->modinsn[5] = (insn & 0xfff00ff0) | 0x20003;
+
+ dsc->modinsn[6] = 0x0; /* breakpoint location. */
+ dsc->modinsn[7] = 0x0; /* scratch space. */
+
+ dsc->numinsns = 6;
+ }
+
+ dsc->cleanup = load ? &cleanup_load : &cleanup_store;
+
+ return 0;
+}
+
+/* Cleanup LDM instructions with fully-populated register list. This is an
+ unfortunate corner case: it's impossible to implement correctly by modifying
+ the instruction. The issue is as follows: we have an instruction,
+
+ ldm rN, {r0-r15}
+
+ which we must rewrite to avoid loading PC. A possible solution would be to
+ do the load in two halves, something like (with suitable cleanup
+ afterwards):
+
+ mov r8, rN
+ ldm[id][ab] r8!, {r0-r7}
+ str r7, <temp>
+ ldm[id][ab] r8, {r7-r14}
+ <bkpt>
+
+ but at present there's no suitable place for <temp>, since the scratch space
+ is overwritten before the cleanup routine is called. For now, we simply
+ emulate the instruction. */
+
+static void
+cleanup_block_load_all (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST from = dsc->insn_addr;
+ int inc = dsc->u.block.increment;
+ int bump_before = dsc->u.block.before ? (inc ? 4 : -4) : 0;
+ int bump_after = dsc->u.block.before ? 0 : (inc ? 4 : -4);
+ uint32_t regmask = dsc->u.block.regmask;
+ int regno = inc ? 0 : 15;
+ CORE_ADDR xfer_addr = dsc->u.block.xfer_addr;
+ int exception_return = dsc->u.block.load && dsc->u.block.user
+ && (regmask & 0x8000) != 0;
+ uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
+ int do_transfer = condition_true (dsc->u.block.cond, status);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ if (!do_transfer)
+ return;
+
+ /* If the instruction is ldm rN, {...pc}^, I don't think there's anything
+ sensible we can do here. Complain loudly. */
+ if (exception_return)
+ error (_("Cannot single-step exception return"));
+
+ /* We don't handle any stores here for now. */
+ gdb_assert (dsc->u.block.load != 0);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: emulating block transfer: "
+ "%s %s %s\n", dsc->u.block.load ? "ldm" : "stm",
+ dsc->u.block.increment ? "inc" : "dec",
+ dsc->u.block.before ? "before" : "after");
+
+ while (regmask)
+ {
+ uint32_t memword;
+
+ if (inc)
+ while (regno <= 15 && (regmask & (1 << regno)) == 0)
+ regno++;
+ else
+ while (regno >= 0 && (regmask & (1 << regno)) == 0)
+ regno--;
+
+ xfer_addr += bump_before;
+
+ memword = read_memory_unsigned_integer (xfer_addr, 4, byte_order);
+ displaced_write_reg (regs, dsc, regno, memword, LOAD_WRITE_PC);
+
+ xfer_addr += bump_after;
+
+ regmask &= ~(1 << regno);
+ }
+
+ if (dsc->u.block.writeback)
+ displaced_write_reg (regs, dsc, dsc->u.block.rn, xfer_addr,
+ CANNOT_WRITE_PC);
+}
+
+/* Clean up an STM which included the PC in the register list. */
+
+static void
+cleanup_block_store_pc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST from = dsc->insn_addr;
+ uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
+ int store_executed = condition_true (dsc->u.block.cond, status);
+ CORE_ADDR pc_stored_at, transferred_regs = bitcount (dsc->u.block.regmask);
+ CORE_ADDR stm_insn_addr;
+ uint32_t pc_val;
+ long offset;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ /* If condition code fails, there's nothing else to do. */
+ if (!store_executed)
+ return;
+
+ if (dsc->u.block.increment)
+ {
+ pc_stored_at = dsc->u.block.xfer_addr + 4 * transferred_regs;
+
+ if (dsc->u.block.before)
+ pc_stored_at += 4;
+ }
+ else
+ {
+ pc_stored_at = dsc->u.block.xfer_addr;
+
+ if (dsc->u.block.before)
+ pc_stored_at -= 4;
+ }
+
+ pc_val = read_memory_unsigned_integer (pc_stored_at, 4, byte_order);
+ stm_insn_addr = dsc->scratch_base;
+ offset = pc_val - stm_insn_addr;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: detected PC offset %.8lx for "
+ "STM instruction\n", offset);
+
+ /* Rewrite the stored PC to the proper value for the non-displaced original
+ instruction. */
+ write_memory_unsigned_integer (pc_stored_at, 4, byte_order,
+ dsc->insn_addr + offset);
+}
+
+/* Clean up an LDM which includes the PC in the register list. We clumped all
+ the registers in the transferred list into a contiguous range r0...rX (to
+ avoid loading PC directly and losing control of the debugged program), so we
+ must undo that here. */
+
+static void
+cleanup_block_load_pc (struct gdbarch *gdbarch,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ ULONGEST from = dsc->insn_addr;
+ uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM);
+ int load_executed = condition_true (dsc->u.block.cond, status), i;
+ unsigned int mask = dsc->u.block.regmask, write_reg = 15;
+ unsigned int regs_loaded = bitcount (mask);
+ unsigned int num_to_shuffle = regs_loaded, clobbered;
+
+ /* The method employed here will fail if the register list is fully populated
+ (we need to avoid loading PC directly). */
+ gdb_assert (num_to_shuffle < 16);
+
+ if (!load_executed)
+ return;
+
+ clobbered = (1 << num_to_shuffle) - 1;
+
+ while (num_to_shuffle > 0)
+ {
+ if ((mask & (1 << write_reg)) != 0)
+ {
+ unsigned int read_reg = num_to_shuffle - 1;
+
+ if (read_reg != write_reg)
+ {
+ ULONGEST rval = displaced_read_reg (regs, from, read_reg);
+ displaced_write_reg (regs, dsc, write_reg, rval, LOAD_WRITE_PC);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: move "
+ "loaded register r%d to r%d\n"), read_reg,
+ write_reg);
+ }
+ else if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: register "
+ "r%d already in the right place\n"),
+ write_reg);
+
+ clobbered &= ~(1 << write_reg);
+
+ num_to_shuffle--;
+ }
+
+ write_reg--;
+ }
+
+ /* Restore any registers we scribbled over. */
+ for (write_reg = 0; clobbered != 0; write_reg++)
+ {
+ if ((clobbered & (1 << write_reg)) != 0)
+ {
+ displaced_write_reg (regs, dsc, write_reg, dsc->tmp[write_reg],
+ CANNOT_WRITE_PC);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, _("displaced: LDM: restored "
+ "clobbered register r%d\n"), write_reg);
+ clobbered &= ~(1 << write_reg);
+ }
+ }
+
+ /* Perform register writeback manually. */
+ if (dsc->u.block.writeback)
+ {
+ ULONGEST new_rn_val = dsc->u.block.xfer_addr;
+
+ if (dsc->u.block.increment)
+ new_rn_val += regs_loaded * 4;
+ else
+ new_rn_val -= regs_loaded * 4;
+
+ displaced_write_reg (regs, dsc, dsc->u.block.rn, new_rn_val,
+ CANNOT_WRITE_PC);
+ }
+}
+
+/* Handle ldm/stm, apart from some tricky cases which are unlikely to occur
+ in user-level code (in particular exception return, ldm rn, {...pc}^). */
+
+static int
+copy_block_xfer (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ int load = bit (insn, 20);
+ int user = bit (insn, 22);
+ int increment = bit (insn, 23);
+ int before = bit (insn, 24);
+ int writeback = bit (insn, 21);
+ int rn = bits (insn, 16, 19);
+ CORE_ADDR from = dsc->insn_addr;
+
+ /* Block transfers which don't mention PC can be run directly out-of-line. */
+ if (rn != 15 && (insn & 0x8000) == 0)
+ return copy_unmodified (gdbarch, insn, "ldm/stm", dsc);
+
+ if (rn == 15)
+ {
+ warning (_("displaced: Unpredictable LDM or STM with base register r15"));
+ return copy_unmodified (gdbarch, insn, "unpredictable ldm/stm", dsc);
+ }
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying block transfer insn "
+ "%.8lx\n", (unsigned long) insn);
+
+ dsc->u.block.xfer_addr = displaced_read_reg (regs, from, rn);
+ dsc->u.block.rn = rn;
+
+ dsc->u.block.load = load;
+ dsc->u.block.user = user;
+ dsc->u.block.increment = increment;
+ dsc->u.block.before = before;
+ dsc->u.block.writeback = writeback;
+ dsc->u.block.cond = bits (insn, 28, 31);
+
+ dsc->u.block.regmask = insn & 0xffff;
+
+ if (load)
+ {
+ if ((insn & 0xffff) == 0xffff)
+ {
+ /* LDM with a fully-populated register list. This case is
+ particularly tricky. Implement for now by fully emulating the
+ instruction (which might not behave perfectly in all cases, but
+ these instructions should be rare enough for that not to matter
+ too much). */
+ dsc->modinsn[0] = ARM_NOP;
+
+ dsc->cleanup = &cleanup_block_load_all;
+ }
+ else
+ {
+ /* LDM of a list of registers which includes PC. Implement by
+ rewriting the list of registers to be transferred into a
+ contiguous chunk r0...rX before doing the transfer, then shuffling
+ registers into the correct places in the cleanup routine. */
+ unsigned int regmask = insn & 0xffff;
+ unsigned int num_in_list = bitcount (regmask), new_regmask, bit = 1;
+ unsigned int to = 0, from = 0, i, new_rn;
+
+ for (i = 0; i < num_in_list; i++)
+ dsc->tmp[i] = displaced_read_reg (regs, from, i);
+
+ /* Writeback makes things complicated. We need to avoid clobbering
+ the base register with one of the registers in our modified
+ register list, but just using a different register can't work in
+ all cases, e.g.:
+
+ ldm r14!, {r0-r13,pc}
+
+ which would need to be rewritten as:
+
+ ldm rN!, {r0-r14}
+
+ but that can't work, because there's no free register for N.
+
+ Solve this by turning off the writeback bit, and emulating
+ writeback manually in the cleanup routine. */
+
+ if (writeback)
+ insn &= ~(1 << 21);
+
+ 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) insn & 0xffff, new_regmask);
+
+ dsc->modinsn[0] = (insn & ~0xffff) | (new_regmask & 0xffff);
+
+ dsc->cleanup = &cleanup_block_load_pc;
+ }
+ }
+ else
+ {
+ /* STM of a list of registers which includes PC. Run the instruction
+ as-is, but out of line: this will store the wrong value for the PC,
+ so we must manually fix up the memory in the cleanup routine.
+ Doing things this way has the advantage that we can auto-detect
+ the offset of the PC write (which is architecture-dependent) in
+ the cleanup routine. */
+ dsc->modinsn[0] = insn;
+
+ dsc->cleanup = &cleanup_block_store_pc;
+ }
+
+ return 0;
+}
+
+/* Cleanup/copy SVC (SWI) instructions. These two functions are overridden
+ for Linux, where some SVC instructions must be treated specially. */
+
+static void
+cleanup_svc (struct gdbarch *gdbarch, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ CORE_ADDR from = dsc->insn_addr;
+ CORE_ADDR resume_addr = from + 4;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: cleanup for svc, resume at "
+ "%.8lx\n", (unsigned long) resume_addr);
+
+ displaced_write_reg (regs, dsc, ARM_PC_REGNUM, resume_addr, BRANCH_WRITE_PC);
+}
+
+static int
+copy_svc (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ CORE_ADDR from = dsc->insn_addr;
+
+ /* Allow OS-specific code to override SVC handling. */
+ if (dsc->u.svc.copy_svc_os)
+ return dsc->u.svc.copy_svc_os (gdbarch, insn, to, regs, dsc);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying svc insn %.8lx\n",
+ (unsigned long) insn);
+
+ /* Preparation: none.
+ Insn: unmodified svc.
+ Cleanup: pc <- insn_addr + 4. */
+
+ dsc->modinsn[0] = insn;
+
+ dsc->cleanup = &cleanup_svc;
+ /* Pretend we wrote to the PC, so cleanup doesn't set PC to the next
+ instruction. */
+ dsc->wrote_to_pc = 1;
+
+ return 0;
+}
+
+/* Copy undefined instructions. */
+
+static int
+copy_undef (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
+{
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying undefined insn %.8lx\n",
+ (unsigned long) insn);
+
+ dsc->modinsn[0] = insn;
+
+ return 0;
+}
+
+/* Copy unpredictable instructions. */
+
+static int
+copy_unpred (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
+{
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copying unpredictable insn "
+ "%.8lx\n", (unsigned long) insn);
+
+ dsc->modinsn[0] = insn;
+
+ return 0;
+}
+
+/* The decode_* functions are instruction decoding helpers. They mostly follow
+ the presentation in the ARM ARM. */
+
+static int
+decode_misc_memhint_neon (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ unsigned int op1 = bits (insn, 20, 26), op2 = bits (insn, 4, 7);
+ unsigned int rn = bits (insn, 16, 19);
+
+ if (op1 == 0x10 && (op2 & 0x2) == 0x0 && (rn & 0xe) == 0x0)
+ return copy_unmodified (gdbarch, insn, "cps", dsc);
+ else if (op1 == 0x10 && op2 == 0x0 && (rn & 0xe) == 0x1)
+ return copy_unmodified (gdbarch, insn, "setend", dsc);
+ else if ((op1 & 0x60) == 0x20)
+ return copy_unmodified (gdbarch, insn, "neon dataproc", dsc);
+ else if ((op1 & 0x71) == 0x40)
+ return copy_unmodified (gdbarch, insn, "neon elt/struct load/store", dsc);
+ else if ((op1 & 0x77) == 0x41)
+ return copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ else if ((op1 & 0x77) == 0x45)
+ return copy_preload (gdbarch, insn, regs, dsc); /* pli. */
+ else if ((op1 & 0x77) == 0x51)
+ {
+ if (rn != 0xf)
+ return copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ else
+ return copy_unpred (gdbarch, insn, dsc);
+ }
+ else if ((op1 & 0x77) == 0x55)
+ return copy_preload (gdbarch, insn, regs, dsc); /* pld/pldw. */
+ else if (op1 == 0x57)
+ switch (op2)
+ {
+ case 0x1: return copy_unmodified (gdbarch, insn, "clrex", dsc);
+ case 0x4: return copy_unmodified (gdbarch, insn, "dsb", dsc);
+ case 0x5: return copy_unmodified (gdbarch, insn, "dmb", dsc);
+ case 0x6: return copy_unmodified (gdbarch, insn, "isb", dsc);
+ default: return copy_unpred (gdbarch, insn, dsc);
+ }
+ else if ((op1 & 0x63) == 0x43)
+ return copy_unpred (gdbarch, insn, dsc);
+ else if ((op2 & 0x1) == 0x0)
+ switch (op1 & ~0x80)
+ {
+ case 0x61:
+ return copy_unmodified (gdbarch, insn, "unallocated mem hint", dsc);
+ case 0x65:
+ return copy_preload_reg (gdbarch, insn, regs, dsc); /* pli reg. */
+ case 0x71: case 0x75:
+ /* pld/pldw reg. */
+ return copy_preload_reg (gdbarch, insn, regs, dsc);
+ case 0x63: case 0x67: case 0x73: case 0x77:
+ return copy_unpred (gdbarch, insn, dsc);
+ default:
+ return copy_undef (gdbarch, insn, dsc);
+ }
+ else
+ return copy_undef (gdbarch, insn, dsc); /* Probably unreachable. */
+}
+
+static int
+decode_unconditional (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ if (bit (insn, 27) == 0)
+ return decode_misc_memhint_neon (gdbarch, insn, regs, dsc);
+ /* Switch on bits: 0bxxxxx321xxx0xxxxxxxxxxxxxxxxxxxx. */
+ else switch (((insn & 0x7000000) >> 23) | ((insn & 0x100000) >> 20))
+ {
+ case 0x0: case 0x2:
+ return copy_unmodified (gdbarch, insn, "srs", dsc);
+
+ case 0x1: case 0x3:
+ return copy_unmodified (gdbarch, insn, "rfe", dsc);
+
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+
+ case 0x8:
+ switch ((insn & 0xe00000) >> 21)
+ {
+ case 0x1: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
+ /* stc/stc2. */
+ return copy_copro_load_store (gdbarch, insn, regs, dsc);
+
+ case 0x2:
+ return copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
+
+ default:
+ return copy_undef (gdbarch, insn, dsc);
+ }
+
+ case 0x9:
+ {
+ int rn_f = (bits (insn, 16, 19) == 0xf);
+ switch ((insn & 0xe00000) >> 21)
+ {
+ case 0x1: case 0x3:
+ /* ldc/ldc2 imm (undefined for rn == pc). */
+ return rn_f ? copy_undef (gdbarch, insn, dsc)
+ : copy_copro_load_store (gdbarch, insn, regs, dsc);
+
+ case 0x2:
+ return copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ /* ldc/ldc2 lit (undefined for rn != pc). */
+ return rn_f ? copy_copro_load_store (gdbarch, insn, regs, dsc)
+ : copy_undef (gdbarch, insn, dsc);
+
+ default:
+ return copy_undef (gdbarch, insn, dsc);
+ }
+ }
+
+ case 0xa:
+ return copy_unmodified (gdbarch, insn, "stc/stc2", dsc);
+
+ case 0xb:
+ if (bits (insn, 16, 19) == 0xf)
+ /* ldc/ldc2 lit. */
+ return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0xc:
+ if (bit (insn, 4))
+ return copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ else
+ return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+
+ case 0xd:
+ if (bit (insn, 4))
+ return copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ else
+ return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+
+ default:
+ return copy_undef (gdbarch, insn, dsc);
+ }
+}
+
+/* Decode miscellaneous instructions in dp/misc encoding space. */
+
+static int
+decode_miscellaneous (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int op2 = bits (insn, 4, 6);
+ unsigned int op = bits (insn, 21, 22);
+ unsigned int op1 = bits (insn, 16, 19);
+
+ switch (op2)
+ {
+ case 0x0:
+ return copy_unmodified (gdbarch, insn, "mrs/msr", dsc);
+
+ case 0x1:
+ if (op == 0x1) /* bx. */
+ return copy_bx_blx_reg (gdbarch, insn, regs, dsc);
+ else if (op == 0x3)
+ return copy_unmodified (gdbarch, insn, "clz", dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x2:
+ if (op == 0x1)
+ /* Not really supported. */
+ return copy_unmodified (gdbarch, insn, "bxj", dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x3:
+ if (op == 0x1)
+ return copy_bx_blx_reg (gdbarch, insn, regs, dsc); /* blx register. */
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x5:
+ return copy_unmodified (gdbarch, insn, "saturating add/sub", dsc);
+
+ case 0x7:
+ if (op == 0x1)
+ return copy_unmodified (gdbarch, insn, "bkpt", dsc);
+ else if (op == 0x3)
+ /* Not really supported. */
+ return copy_unmodified (gdbarch, insn, "smc", dsc);
+
+ default:
+ return copy_undef (gdbarch, insn, dsc);
+ }
+}
+
+static int
+decode_dp_misc (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ if (bit (insn, 25))
+ switch (bits (insn, 20, 24))
+ {
+ case 0x10:
+ return copy_unmodified (gdbarch, insn, "movw", dsc);
+
+ case 0x14:
+ return copy_unmodified (gdbarch, insn, "movt", dsc);
+
+ case 0x12: case 0x16:
+ return copy_unmodified (gdbarch, insn, "msr imm", dsc);
+
+ default:
+ return copy_alu_imm (gdbarch, insn, regs, dsc);
+ }
+ else
+ {
+ uint32_t op1 = bits (insn, 20, 24), op2 = bits (insn, 4, 7);
+
+ if ((op1 & 0x19) != 0x10 && (op2 & 0x1) == 0x0)
+ return copy_alu_reg (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) != 0x10 && (op2 & 0x9) == 0x1)
+ return copy_alu_shifted_reg (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) == 0x10 && (op2 & 0x8) == 0x0)
+ return decode_miscellaneous (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x19) == 0x10 && (op2 & 0x9) == 0x8)
+ return copy_unmodified (gdbarch, insn, "halfword mul/mla", dsc);
+ else if ((op1 & 0x10) == 0x00 && op2 == 0x9)
+ return copy_unmodified (gdbarch, insn, "mul/mla", dsc);
+ else if ((op1 & 0x10) == 0x10 && op2 == 0x9)
+ return copy_unmodified (gdbarch, insn, "synch", dsc);
+ else if (op2 == 0xb || (op2 & 0xd) == 0xd)
+ /* 2nd arg means "unpriveleged". */
+ return copy_extra_ld_st (gdbarch, insn, (op1 & 0x12) == 0x02, regs,
+ dsc);
+ }
+
+ /* Should be unreachable. */
+ return 1;
+}
+
+static int
+decode_ld_st_word_ubyte (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ int a = bit (insn, 25), b = bit (insn, 4);
+ uint32_t op1 = bits (insn, 20, 24);
+ int rn_f = bits (insn, 16, 19) == 0xf;
+
+ if ((!a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02)
+ || (a && (op1 & 0x05) == 0x00 && (op1 & 0x17) != 0x02 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 0, 0);
+ else if ((!a && (op1 & 0x17) == 0x02)
+ || (a && (op1 & 0x17) == 0x02 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 0, 1);
+ else if ((!a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03)
+ || (a && (op1 & 0x05) == 0x01 && (op1 & 0x17) != 0x03 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 0, 0);
+ else if ((!a && (op1 & 0x17) == 0x03)
+ || (a && (op1 & 0x17) == 0x03 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 0, 1);
+ else if ((!a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06)
+ || (a && (op1 & 0x05) == 0x04 && (op1 & 0x17) != 0x06 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 0);
+ else if ((!a && (op1 & 0x17) == 0x06)
+ || (a && (op1 & 0x17) == 0x06 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 0, 1, 1);
+ else if ((!a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07)
+ || (a && (op1 & 0x05) == 0x05 && (op1 & 0x17) != 0x07 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 0);
+ else if ((!a && (op1 & 0x17) == 0x07)
+ || (a && (op1 & 0x17) == 0x07 && !b))
+ return copy_ldr_str_ldrb_strb (gdbarch, insn, regs, dsc, 1, 1, 1);
+
+ /* Should be unreachable. */
+ return 1;
+}
+
+static int
+decode_media (struct gdbarch *gdbarch, uint32_t insn,
+ struct displaced_step_closure *dsc)
+{
+ switch (bits (insn, 20, 24))
+ {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ return copy_unmodified (gdbarch, insn, "parallel add/sub signed", dsc);
+
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ return copy_unmodified (gdbarch, insn, "parallel add/sub unsigned", dsc);
+
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ return copy_unmodified (gdbarch, insn,
+ "decode/pack/unpack/saturate/reverse", dsc);
+
+ case 0x18:
+ if (bits (insn, 5, 7) == 0) /* op2. */
+ {
+ if (bits (insn, 12, 15) == 0xf)
+ return copy_unmodified (gdbarch, insn, "usad8", dsc);
+ else
+ return copy_unmodified (gdbarch, insn, "usada8", dsc);
+ }
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x1a: case 0x1b:
+ if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
+ return copy_unmodified (gdbarch, insn, "sbfx", dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x1c: case 0x1d:
+ if (bits (insn, 5, 6) == 0x0) /* op2[1:0]. */
+ {
+ if (bits (insn, 0, 3) == 0xf)
+ return copy_unmodified (gdbarch, insn, "bfc", dsc);
+ else
+ return copy_unmodified (gdbarch, insn, "bfi", dsc);
+ }
+ else
+ return copy_undef (gdbarch, insn, dsc);
+
+ case 0x1e: case 0x1f:
+ if (bits (insn, 5, 6) == 0x2) /* op2[1:0]. */
+ return copy_unmodified (gdbarch, insn, "ubfx", dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc);
+ }
+
+ /* Should be unreachable. */
+ return 1;
+}
+
+static int
+decode_b_bl_ldmstm (struct gdbarch *gdbarch, int32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ if (bit (insn, 25))
+ return copy_b_bl_blx (gdbarch, insn, regs, dsc);
+ else
+ return copy_block_xfer (gdbarch, insn, regs, dsc);
+}
- case 0x4:
- case 0x5: /* data transfer */
- case 0x6:
- case 0x7:
- if (bit (this_instr, 20))
- {
- /* load */
- if (bits (this_instr, 12, 15) == 15)
- {
- /* rd == pc */
- unsigned long rn;
- unsigned long base;
+static int
+decode_ext_reg_ld_st (struct gdbarch *gdbarch, uint32_t insn,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int opcode = bits (insn, 20, 24);
- if (bit (this_instr, 22))
- error (_("Invalid update to pc in instruction"));
+ switch (opcode)
+ {
+ case 0x04: case 0x05: /* VFP/Neon mrrc/mcrr. */
+ return copy_unmodified (gdbarch, insn, "vfp/neon mrrc/mcrr", dsc);
+
+ case 0x08: case 0x0a: case 0x0c: case 0x0e:
+ case 0x12: case 0x16:
+ return copy_unmodified (gdbarch, insn, "vfp/neon vstm/vpush", dsc);
+
+ case 0x09: case 0x0b: case 0x0d: case 0x0f:
+ case 0x13: case 0x17:
+ return copy_unmodified (gdbarch, insn, "vfp/neon vldm/vpop", dsc);
+
+ case 0x10: case 0x14: case 0x18: case 0x1c: /* vstr. */
+ case 0x11: case 0x15: case 0x19: case 0x1d: /* vldr. */
+ /* Note: no writeback for these instructions. Bit 25 will always be
+ zero though (via caller), so the following works OK. */
+ return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ }
- /* byte write to PC */
- rn = bits (this_instr, 16, 19);
- base = (rn == 15) ? pc_val + 8
- : get_frame_register_unsigned (frame, rn);
- if (bit (this_instr, 24))
- {
- /* pre-indexed */
- int c = (status & FLAG_C) ? 1 : 0;
- unsigned long offset =
- (bit (this_instr, 25)
- ? shifted_reg_val (frame, this_instr, c, pc_val, status)
- : bits (this_instr, 0, 11));
+ /* Should be unreachable. */
+ return 1;
+}
- if (bit (this_instr, 23))
- base += offset;
- else
- base -= offset;
- }
- nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base,
- 4);
+static int
+decode_svc_copro (struct gdbarch *gdbarch, uint32_t insn, CORE_ADDR to,
+ struct regcache *regs, struct displaced_step_closure *dsc)
+{
+ unsigned int op1 = bits (insn, 20, 25);
+ int op = bit (insn, 4);
+ unsigned int coproc = bits (insn, 8, 11);
+ unsigned int rn = bits (insn, 16, 19);
+
+ if ((op1 & 0x20) == 0x00 && (op1 & 0x3a) != 0x00 && (coproc & 0xe) == 0xa)
+ return decode_ext_reg_ld_st (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x21) == 0x00 && (op1 & 0x3a) != 0x00
+ && (coproc & 0xe) != 0xa)
+ /* stc/stc2. */
+ return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x21) == 0x01 && (op1 & 0x3a) != 0x00
+ && (coproc & 0xe) != 0xa)
+ /* ldc/ldc2 imm/lit. */
+ return copy_copro_load_store (gdbarch, insn, regs, dsc);
+ else if ((op1 & 0x3e) == 0x00)
+ return copy_undef (gdbarch, insn, dsc);
+ else if ((op1 & 0x3e) == 0x04 && (coproc & 0xe) == 0xa)
+ return copy_unmodified (gdbarch, insn, "neon 64bit xfer", dsc);
+ else if (op1 == 0x04 && (coproc & 0xe) != 0xa)
+ return copy_unmodified (gdbarch, insn, "mcrr/mcrr2", dsc);
+ else if (op1 == 0x05 && (coproc & 0xe) != 0xa)
+ return copy_unmodified (gdbarch, insn, "mrrc/mrrc2", dsc);
+ else if ((op1 & 0x30) == 0x20 && !op)
+ {
+ if ((coproc & 0xe) == 0xa)
+ return copy_unmodified (gdbarch, insn, "vfp dataproc", dsc);
+ else
+ return copy_unmodified (gdbarch, insn, "cdp/cdp2", dsc);
+ }
+ else if ((op1 & 0x30) == 0x20 && op)
+ return copy_unmodified (gdbarch, insn, "neon 8/16/32 bit xfer", dsc);
+ else if ((op1 & 0x31) == 0x20 && op && (coproc & 0xe) != 0xa)
+ return copy_unmodified (gdbarch, insn, "mcr/mcr2", dsc);
+ else if ((op1 & 0x31) == 0x21 && op && (coproc & 0xe) != 0xa)
+ return copy_unmodified (gdbarch, insn, "mrc/mrc2", dsc);
+ else if ((op1 & 0x30) == 0x30)
+ return copy_svc (gdbarch, insn, to, regs, dsc);
+ else
+ return copy_undef (gdbarch, insn, dsc); /* Possibly unreachable. */
+}
- nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
+void
+arm_process_displaced_insn (struct gdbarch *gdbarch, uint32_t insn,
+ CORE_ADDR from, CORE_ADDR to, struct regcache *regs,
+ struct displaced_step_closure *dsc)
+{
+ int err = 0;
+
+ if (!displaced_in_arm_mode (regs))
+ error (_("Displaced stepping is only supported in ARM mode"));
+
+ /* 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;
+
+ if ((insn & 0xf0000000) == 0xf0000000)
+ err = decode_unconditional (gdbarch, insn, regs, dsc);
+ else switch (((insn & 0x10) >> 4) | ((insn & 0xe000000) >> 24))
+ {
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ err = decode_dp_misc (gdbarch, insn, regs, dsc);
+ break;
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- }
- }
- break;
+ case 0x4: case 0x5: case 0x6:
+ err = decode_ld_st_word_ubyte (gdbarch, insn, regs, dsc);
+ break;
- case 0x8:
- case 0x9: /* block transfer */
- if (bit (this_instr, 20))
- {
- /* LDM */
- if (bit (this_instr, 15))
- {
- /* loading pc */
- int offset = 0;
+ case 0x7:
+ err = decode_media (gdbarch, insn, dsc);
+ break;
- if (bit (this_instr, 23))
- {
- /* up */
- unsigned long reglist = bits (this_instr, 0, 14);
- offset = bitcount (reglist) * 4;
- if (bit (this_instr, 24)) /* pre */
- offset += 4;
- }
- else if (bit (this_instr, 24))
- offset = -4;
+ case 0x8: case 0x9: case 0xa: case 0xb:
+ err = decode_b_bl_ldmstm (gdbarch, insn, regs, dsc);
+ break;
- {
- 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);
- }
- nextpc = gdbarch_addr_bits_remove
- (gdbarch, nextpc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- }
- }
- break;
+ case 0xc: case 0xd: case 0xe: case 0xf:
+ err = decode_svc_copro (gdbarch, insn, to, regs, dsc);
+ break;
+ }
- case 0xb: /* branch & link */
- case 0xa: /* branch */
- {
- nextpc = BranchDest (pc, this_instr);
+ if (err)
+ internal_error (__FILE__, __LINE__,
+ _("arm_process_displaced_insn: Instruction decode error"));
+}
- nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
- if (nextpc == pc)
- error (_("Infinite loop detected"));
- break;
- }
+/* Actually set up the scratch space for a displaced instruction. */
- case 0xc:
- case 0xd:
- case 0xe: /* coproc ops */
- case 0xf: /* SWI */
- break;
+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);
+ unsigned int i;
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
- default:
- fprintf_filtered (gdb_stderr, _("Bad bit-field extraction\n"));
- return (pc);
- }
+ /* Poke modified instruction(s). */
+ for (i = 0; i < dsc->numinsns; i++)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: writing insn %.8lx at "
+ "%.8lx\n", (unsigned long) dsc->modinsn[i],
+ (unsigned long) to + i * 4);
+ write_memory_unsigned_integer (to + i * 4, 4, byte_order_for_code,
+ dsc->modinsn[i]);
}
- return nextpc;
+ /* Put breakpoint afterwards. */
+ write_memory (to + dsc->numinsns * 4, tdep->arm_breakpoint,
+ tdep->arm_breakpoint_size);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ",
+ paddress (gdbarch, from), paddress (gdbarch, to));
}
-/* single_step() is called just before we want to resume the inferior,
- if we want to single-step it but there is no hardware or kernel
- single-step support. We find the target of the coming instruction
- and breakpoint it. */
+/* Entry point for copying an instruction into scratch space for displaced
+ stepping. */
-int
-arm_software_single_step (struct frame_info *frame)
+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 displaced_step_closure *dsc
+ = xmalloc (sizeof (struct displaced_step_closure));
+ enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
+ uint32_t insn = read_memory_unsigned_integer (from, 4, byte_order_for_code);
- /* NOTE: This may insert the wrong breakpoint instruction when
- single-stepping over a mode-changing instruction, if the
- CPSR heuristics are used. */
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: stepping insn %.8lx "
+ "at %.8lx\n", (unsigned long) insn,
+ (unsigned long) from);
- CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
- insert_single_step_breakpoint (gdbarch, next_pc);
+ arm_process_displaced_insn (gdbarch, insn, from, to, regs, dsc);
+ arm_displaced_init_closure (gdbarch, from, to, dsc);
- return 1;
+ return dsc;
+}
+
+/* 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)
+{
+ if (dsc->cleanup)
+ dsc->cleanup (gdbarch, regs, dsc);
+
+ if (!dsc->wrote_to_pc)
+ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, dsc->insn_addr + 4);
}
#include "bfd-in2.h"
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 (arm_pc_is_thumb (*pcptr))
{
*pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+ /* 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;
+ }
+ }
+ }
+
*lenptr = tdep->thumb_breakpoint_size;
return tdep->thumb_breakpoint;
}
}
}
+static void
+arm_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr,
+ int *kindptr)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ arm_breakpoint_from_pc (gdbarch, pcptr, kindptr);
+
+ if (arm_pc_is_thumb (*pcptr) && *kindptr == 4)
+ /* The documented magic value for a 32-bit Thumb-2 breakpoint, so
+ that this is not confused with a 32-bit ARM breakpoint. */
+ *kindptr = 3;
+}
+
/* Extract from an array REGBUF containing the (raw) register state a
function return value of type TYPE, and copy that, in virtual
format, into VALBUF. */
gdb_byte *valbuf)
{
struct gdbarch *gdbarch = get_regcache_arch (regs);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
if (TYPE_CODE_FLT == TYPE_CODE (type))
{
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,
store_unsigned_integer (valbuf,
(len > INT_REGISTER_SIZE
? INT_REGISTER_SIZE : len),
- tmp);
+ byte_order, tmp);
len -= INT_REGISTER_SIZE;
valbuf += INT_REGISTER_SIZE;
}
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)
{
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,
bfd_byte tmpbuf[INT_REGISTER_SIZE];
LONGEST val = unpack_long (type, valbuf);
- store_signed_integer (tmpbuf, INT_REGISTER_SIZE, val);
+ store_signed_integer (tmpbuf, INT_REGISTER_SIZE, byte_order, val);
regcache_cooked_write (regs, ARM_A1_REGNUM, tmpbuf);
}
else
gdb_byte *readbuf, const gdb_byte *writebuf)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ 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;
+
+ 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;
+ }
if (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
|| TYPE_CODE (valtype) == TYPE_CODE_UNION
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;
char buf[INT_REGISTER_SIZE];
- struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
jb_addr = get_frame_register_unsigned (frame, ARM_A1_REGNUM);
INT_REGISTER_SIZE))
return 0;
- *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE);
+ *pc = extract_unsigned_integer (buf, INT_REGISTER_SIZE, byte_order);
return 1;
}
/* 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. */
- if (strncmp (name, "_call_via_", 10) == 0)
+ 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 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. */
static int
coff_sym_is_thumb (int val)
{
- return (val == C_THUMBEXT ||
- val == C_THUMBSTAT ||
- val == C_THUMBEXTFUNC ||
- val == C_THUMBSTATFUNC ||
- val == C_THUMBLABEL);
+ return (val == C_THUMBEXT
+ || val == C_THUMBSTAT
+ || val == C_THUMBEXTFUNC
+ || val == C_THUMBSTATFUNC
+ || val == C_THUMBLABEL);
}
/* arm_coff_make_msymbol_special()
}
static void
-arm_objfile_data_cleanup (struct objfile *objfile, void *arg)
+arm_objfile_data_free (struct objfile *objfile, void *arg)
{
struct arm_per_objfile *data = arg;
unsigned int i;
}
}
+/* 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 void
+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;
+
+ 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;
+
+ regcache_raw_read (regcache, double_regnum, reg_buf);
+ memcpy (buf + offset, reg_buf, 8);
+
+ offset = 8 - offset;
+ regcache_raw_read (regcache, double_regnum + 1, reg_buf);
+ memcpy (buf + offset, reg_buf, 8);
+}
+
+static void
+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. */
+ arm_neon_quad_read (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;
+
+ 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 (buf, reg_buf + offset, 4);
+ }
+}
+
+/* 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];
+ gdb_byte reg_buf[8];
+ int offset, double_regnum;
+
+ sprintf (name_buf, "d%d", regnum << 1);
+ double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+ strlen (name_buf));
+
+ /* 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;
+
+ 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);
+ }
+}
+
static struct value *
value_of_arm_user_reg (struct frame_info *frame, const void *baton)
{
enum arm_float_model fp_model = arm_fp_model;
struct tdesc_arch_data *tdesc_data = NULL;
int i;
+ int have_vfp_registers = 0, have_vfp_pseudos = 0, have_neon_pseudos = 0;
+ int have_neon = 0;
int have_fpa_registers = 1;
/* Check any target description for validity. */
static const char *const arm_pc_names[] = { "r15", "pc", NULL };
const struct tdesc_feature *feature;
- int i, valid_p;
+ int valid_p;
feature = tdesc_find_feature (info.target_desc,
"org.gnu.gdb.arm.core");
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 (info.target_desc,
+ "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)
+ {
+ 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 (info.target_desc,
+ "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 we have an object to base this architecture on, try to determine
case EF_ARM_EABI_VER4:
case EF_ARM_EABI_VER5:
arm_abi = ARM_ABI_AAPCS;
- /* EABI binaries default to VFP float ordering. */
+ /* 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)
- fp_model = ARM_FLOAT_SOFT_VFP;
+ {
+#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:
&& 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. */
+
/* Found a match. */
break;
}
tdep->arm_abi = arm_abi;
tdep->fp_model = fp_model;
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;
/* Breakpoints. */
switch (info.byte_order_for_code)
/* 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. */
/* 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_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM); /* ??? */
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);
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)
- tdesc_use_registers (gdbarch, info.target_desc, tdesc_data);
+ {
+ set_tdesc_pseudo_register_name (gdbarch, arm_register_name);
+
+ tdesc_use_registers (gdbarch, info.target_desc, 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,
gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep);
arm_objfile_data_key
- = register_objfile_data_with_cleanup (arm_objfile_data_cleanup);
+ = register_objfile_data_with_cleanup (NULL, arm_objfile_data_free);
/* Register an ELF OS ABI sniffer for ARM binaries. */
gdbarch_register_osabi_sniffer (bfd_arch_arm,
_("The valid values are:\n"),
regdesc,
_("The default is \"std\"."));
- helptext = ui_file_xstrdup (stb, &length);
+ helptext = ui_file_xstrdup (stb, NULL);
ui_file_delete (stb);
add_setshow_enum_cmd("disassembler", no_class,