X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=gdb%2Farm-tdep.c;h=063e8f5af0cb433a2c1af51118552ee46450e77f;hb=6c95b8df7fef5273da71c34775918c554aae0ea8;hp=c4824db0a66fc14fde4f835919a07653d093bae4;hpb=ba2b1c567fe2be8e4287a7e59edd26da9943b0f7;p=thirdparty%2Fbinutils-gdb.git diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index c4824db0a66..063e8f5af0c 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1,7 +1,7 @@ /* 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 + 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -63,11 +63,10 @@ static int arm_debug; MSYMBOL_IS_SPECIAL Tests the "special" bit in a minimal symbol. */ #define MSYMBOL_SET_SPECIAL(msym) \ - MSYMBOL_INFO (msym) = (char *) (((long) MSYMBOL_INFO (msym)) \ - | 0x80000000) + MSYMBOL_TARGET_FLAG_1 (msym) = 1 #define MSYMBOL_IS_SPECIAL(msym) \ - (((long) MSYMBOL_INFO (msym) & 0x80000000) != 0) + MSYMBOL_TARGET_FLAG_1 (msym) /* Per-objfile data used for mapping symbols. */ static const struct objfile_data *arm_objfile_data_key; @@ -209,6 +208,13 @@ static void convert_from_extended (const struct floatformat *, const void *, 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 @@ -229,6 +235,11 @@ struct arm_prologue_cache struct trad_frame_saved_reg *saved_regs; }; +/* 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) @@ -290,7 +301,8 @@ arm_pc_is_thumb (CORE_ADDR memaddr) { struct arm_per_objfile *data; VEC(arm_mapping_symbol_s) *map; - struct arm_mapping_symbol map_key = { memaddr - sec->addr, 0 }; + struct arm_mapping_symbol map_key = { memaddr - obj_section_addr (sec), + 0 }; unsigned int idx; data = objfile_data (sec->objfile, arm_objfile_data_key); @@ -349,7 +361,7 @@ arm_pc_is_thumb (CORE_ADDR memaddr) /* Remove useless bits from addresses in a running program. */ static CORE_ADDR -arm_addr_bits_remove (CORE_ADDR val) +arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val) { if (arm_apcs_32) return UNMAKE_THUMB_ADDR (val); @@ -360,7 +372,7 @@ arm_addr_bits_remove (CORE_ADDR val) /* When reading symbols, we need to zap the low bit of the address, which may be set to 1 for Thumb functions. */ static CORE_ADDR -arm_smash_text_address (CORE_ADDR val) +arm_smash_text_address (struct gdbarch *gdbarch, CORE_ADDR val) { return val & ~1; } @@ -374,6 +386,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR start, CORE_ADDR limit, struct arm_prologue_cache *cache) { + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); int i; pv_t regs[16]; struct pv_area *stack; @@ -382,14 +395,14 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, for (i = 0; i < 16; i++) regs[i] = pv_register (i, 0); - stack = make_pv_area (ARM_SP_REGNUM); + stack = make_pv_area (ARM_SP_REGNUM, gdbarch_addr_bit (gdbarch)); back_to = make_cleanup_free_pv_area (stack); while (start < limit) { unsigned short insn; - insn = read_memory_unsigned_integer (start, 2); + insn = read_memory_unsigned_integer (start, 2, byte_order_for_code); if ((insn & 0xfe00) == 0xb400) /* push { rlist } */ { @@ -517,47 +530,46 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, 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, func_end = 0; - char *func_name; + 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 (pc)) + if (deprecated_pc_in_call_dummy (gdbarch, pc)) return pc; - /* See what the symbol table says. */ - - if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) + /* 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. */ + if (find_pc_partial_function (pc, NULL, &func_addr, NULL)) { - struct symbol *sym; - - /* Found a function. */ - sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL); - if (sym && SYMBOL_LANGUAGE (sym) != language_asm) - { - /* Don't use this trick for assembly source files. */ - sal = find_pc_line (func_addr, 0); - if ((sal.line != 0) && (sal.end < func_end)) - return sal.end; - } + CORE_ADDR post_prologue_pc + = skip_prologue_using_sal (gdbarch, func_addr); + if (post_prologue_pc != 0) + return max (pc, post_prologue_pc); } - /* Can't find the prologue end in the symbol table, try it the hard way - by disassembling the instructions. */ + /* Can't determine prologue from the symbol table, need to examine + instructions. */ + /* Find an upper limit on the function prologue using the debug + information. If the debug information could not be used to provide + that bound, then use an arbitrary large number as the upper bound. */ /* Like arm_scan_prologue, stop no later than pc + 64. */ - if (func_end == 0 || func_end > pc + 64) - func_end = pc + 64; + limit_pc = skip_prologue_using_sal (gdbarch, pc); + if (limit_pc == 0) + limit_pc = pc + 64; /* Magic. */ + /* Check if this is Thumb code. */ if (arm_pc_is_thumb (pc)) - return thumb_analyze_prologue (gdbarch, pc, func_end, NULL); + return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL); - for (skip_pc = pc; skip_pc < func_end; skip_pc += 4) + for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4) { - inst = read_memory_unsigned_integer (skip_pc, 4); + inst = read_memory_unsigned_integer (skip_pc, 4, byte_order_for_code); /* "mov ip, sp" is no longer a required part of the prologue. */ if (inst == 0xe1a0c00d) /* mov ip, sp */ @@ -598,14 +610,14 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) 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. */ @@ -737,6 +749,8 @@ 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); + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); int regno; CORE_ADDR prologue_start, prologue_end, current_pc; CORE_ADDR prev_pc = get_frame_pc (this_frame); @@ -810,7 +824,7 @@ arm_scan_prologue (struct frame_info *this_frame, LONGEST return_value; frame_loc = get_frame_register_unsigned (this_frame, ARM_FP_REGNUM); - if (!safe_read_memory_integer (frame_loc, 4, &return_value)) + if (!safe_read_memory_integer (frame_loc, 4, byte_order, &return_value)) return; else { @@ -846,14 +860,15 @@ arm_scan_prologue (struct frame_info *this_frame, for (regno = 0; regno < ARM_FPS_REGNUM; regno++) regs[regno] = pv_register (regno, 0); - stack = make_pv_area (ARM_SP_REGNUM); + stack = make_pv_area (ARM_SP_REGNUM, gdbarch_addr_bit (gdbarch)); back_to = make_cleanup_free_pv_area (stack); for (current_pc = prologue_start; current_pc < prologue_end; current_pc += 4) { - unsigned int insn = read_memory_unsigned_integer (current_pc, 4); + unsigned int insn + = read_memory_unsigned_integer (current_pc, 4, byte_order_for_code); if (insn == 0xe1a0c00d) /* mov ip, sp */ { @@ -902,16 +917,16 @@ arm_scan_prologue (struct frame_info *this_frame, 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 & 0xffffc000) == 0xe54b0000 /* strb rx,[r11,#-n] */ + || (insn & 0xffffc0f0) == 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 & 0xffffc000) == 0xe5cd0000 /* strb rx,[sp,#n] */ + || (insn & 0xffffc0f0) == 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; @@ -1073,6 +1088,7 @@ arm_prologue_prev_register (struct frame_info *this_frame, void **this_cache, int prev_regnum) { + struct gdbarch *gdbarch = get_frame_arch (this_frame); struct arm_prologue_cache *cache; if (*this_cache == NULL) @@ -1090,7 +1106,7 @@ arm_prologue_prev_register (struct frame_info *this_frame, lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM); return frame_unwind_got_constant (this_frame, prev_regnum, - arm_addr_bits_remove (lr)); + arm_addr_bits_remove (gdbarch, lr)); } /* SP is generally not saved to the stack, but this frame is @@ -1228,7 +1244,7 @@ arm_unwind_pc (struct gdbarch *gdbarch, struct frame_info *this_frame) { CORE_ADDR pc; pc = frame_unwind_register_unsigned (this_frame, ARM_PC_REGNUM); - return arm_addr_bits_remove (pc); + return arm_addr_bits_remove (gdbarch, pc); } static CORE_ADDR @@ -1241,6 +1257,7 @@ static struct value * arm_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache, int regnum) { + struct gdbarch * gdbarch = get_frame_arch (this_frame); CORE_ADDR lr, cpsr; switch (regnum) @@ -1252,7 +1269,7 @@ arm_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache, part of the PC. */ lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM); return frame_unwind_got_constant (this_frame, regnum, - arm_addr_bits_remove (lr)); + arm_addr_bits_remove (gdbarch, lr)); case ARM_PS_REGNUM: /* Reconstruct the T bit; see arm_prologue_prev_register for details. */ @@ -1368,8 +1385,222 @@ arm_type_align (struct type *t) } } -/* 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 @@ -1378,14 +1609,26 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, 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 @@ -1401,9 +1644,9 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, if (struct_return) { if (arm_debug) - fprintf_unfiltered (gdb_stdlog, "struct return in %s = 0x%s\n", + fprintf_unfiltered (gdb_stdlog, "struct return in %s = %s\n", gdbarch_register_name (gdbarch, argreg), - paddr (struct_addr)); + paddress (gdbarch, struct_addr)); regcache_cooked_write_unsigned (regcache, argreg, struct_addr); argreg++; } @@ -1416,6 +1659,9 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, enum type_code typecode; 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); @@ -1439,6 +1685,65 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, 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)) { @@ -1447,7 +1752,8 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, } /* 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++; @@ -1459,11 +1765,12 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, && 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)); + store_unsigned_integer (val, len, byte_order, + MAKE_THUMB_ADDR (regval)); } } @@ -1474,12 +1781,13 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, { 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", @@ -1571,24 +1879,139 @@ arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, print_fpu_flags (status); } +/* Construct the ARM extended floating point type. */ +static struct type * +arm_ext_type (struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (!tdep->arm_ext_type) + tdep->arm_ext_type + = arch_float_type (gdbarch, -1, "builtin_type_arm_ext", + floatformats_arm_ext); + + 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 builtin_type_arm_ext; + { + 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_void_data_ptr; + return builtin_type (gdbarch)->builtin_data_ptr; else if (regnum == ARM_PC_REGNUM) - return builtin_type_void_func_ptr; + return builtin_type (gdbarch)->builtin_func_ptr; else if (regnum >= ARRAY_SIZE (arm_register_names)) /* These registers are only supported on targets which supply an XML description. */ - return builtin_type_int0; + return builtin_type (gdbarch)->builtin_int0; else - return builtin_type_uint32; + return builtin_type (gdbarch)->builtin_uint32; } /* Map a DWARF register REGNUM onto the appropriate GDB register @@ -1622,6 +2045,34 @@ arm_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) 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; } @@ -1722,11 +2173,13 @@ condition_true (unsigned long cond, unsigned long status_reg) 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; } @@ -1807,10 +2260,48 @@ static CORE_ADDR thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { 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) 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, it; + + 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); + it = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3); + + /* 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 ((inst1 & 0xff00) == 0xbf00 || (it & 0x0f)) + error (_("Stepping through Thumb-2 IT blocks is not yet supported")); + + if (it & 0x0f) + { + /* We are in a conditional block. Check the condition. */ + int cond = it >> 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 pc + 4; + else + return pc + 2; + } + } if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */ { @@ -1820,14 +2311,13 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) 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 = read_memory_unsigned_integer (sp + offset, 4, byte_order); nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc); if (nextpc == pc) error (_("Infinite loop detected")); } 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); @@ -1836,14 +2326,166 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { 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); - 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; + + 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) == 0xc000 && (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)); + } + 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 table, offset, length; + + table = get_frame_register_unsigned (frame, bits (inst1, 0, 3)); + 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) == 0xf000) + { + /* TBH. */ + CORE_ADDR table, offset, length; + + table = get_frame_register_unsigned (frame, bits (inst1, 0, 3)); + 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 */ { @@ -1856,6 +2498,17 @@ thumb_get_next_pc (struct frame_info *frame, CORE_ADDR pc) 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; } @@ -1864,6 +2517,8 @@ CORE_ADDR arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { 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; @@ -1873,7 +2528,8 @@ arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) return thumb_get_next_pc (frame, pc); pc_val = (unsigned long) pc; - this_instr = read_memory_unsigned_integer (pc, 4); + 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 */ @@ -2053,7 +2709,7 @@ arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) base -= offset; } nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base, - 4); + 4, byte_order); nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc); @@ -2091,7 +2747,7 @@ arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) (rn_val + offset), - 4); + 4, byte_order); } nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc); @@ -2135,60 +2791,1914 @@ arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) 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 (next_pc); + insert_single_step_breakpoint (gdbarch, aspace, next_pc); return 1; } -#include "bfd-in2.h" -#include "libcoff.h" +/* ARM displaced stepping support. -static int -gdb_print_insn_arm (bfd_vma memaddr, disassemble_info *info) -{ - if (arm_pc_is_thumb (memaddr)) - { - static asymbol *asym; - static combined_entry_type ce; - static struct coff_symbol_struct csym; - static struct bfd fake_bfd; - static bfd_target fake_target; + Generally ARM displaced stepping works as follows: - if (csym.native == NULL) - { - /* Create a fake symbol vector containing a Thumb symbol. - This is solely so that the code in print_insn_little_arm() - and print_insn_big_arm() in opcodes/arm-dis.c will detect - the presence of a Thumb symbol and switch to decoding - Thumb instructions. */ + 1. 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. - fake_target.flavour = bfd_target_coff_flavour; - fake_bfd.xvec = &fake_target; - ce.u.syment.n_sclass = C_THUMBEXTFUNC; - csym.native = &ce; - csym.symbol.the_bfd = &fake_bfd; - csym.symbol.name = "fake"; - asym = (asymbol *) & csym; - } + 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. - memaddr = UNMAKE_THUMB_ADDR (memaddr); - info->symbols = &asym; - } - else - info->symbols = NULL; + 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. */ - if (info->endian == BFD_ENDIAN_BIG) - return print_insn_big_arm (memaddr, info); - else - return print_insn_little_arm (memaddr, info); -} +/* NOP instruction (mov r0, r0). */ +#define ARM_NOP 0xe1a00000 -/* The following define instruction sequences that will cause ARM +/* 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 ATTRIBUTE_UNUSED, 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 ATTRIBUTE_UNUSED, + 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 ATTRIBUTE_UNUSED, + 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} [, #+/-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 ATTRIBUTE_UNUSED, 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 ATTRIBUTE_UNUSED, 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