diff options
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 251 |
1 files changed, 232 insertions, 19 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 54d1bccfa46..25a5416c7e0 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -41,6 +41,8 @@ #include "objfiles.h" #include "dwarf2-frame.h" #include "prologue-value.h" +#include "available.h" +#include "user-regs.h" #include "arm-tdep.h" #include "gdb/sim-arm.h" @@ -806,13 +808,15 @@ arm_scan_prologue (struct frame_info *next_frame, struct arm_prologue_cache *cac imm = (imm >> rot) | (imm << (32 - rot)); sp_offset -= imm; } - else if ((insn & 0xffff7fff) == 0xed6d0103) /* stfe f?, [sp, -#c]! */ + else if ((insn & 0xffff7fff) == 0xed6d0103 /* stfe f?, [sp, -#c]! */ + && gdbarch_tdep (current_gdbarch)->have_fpa_registers) { sp_offset -= 12; regno = ARM_F0_REGNUM + ((insn >> 12) & 0x07); cache->saved_regs[regno].addr = sp_offset; } - else if ((insn & 0xffbf0fff) == 0xec2d0200) /* sfmfd f0, 4, [sp!] */ + else if ((insn & 0xffbf0fff) == 0xec2d0200 /* sfmfd f0, 4, [sp!] */ + && gdbarch_tdep (current_gdbarch)->have_fpa_registers) { int n_saved_fp_regs; unsigned int fp_start_reg, fp_bound_reg; @@ -1351,8 +1355,21 @@ arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, static struct type * arm_register_type (struct gdbarch *gdbarch, int regnum) { + struct type *avail_type; + + avail_type = available_register_type (gdbarch, regnum); + if (avail_type) + return avail_type; + + if (gdbarch_tdep (current_gdbarch)->have_vfp_pseudos + && regnum >= NUM_REGS && regnum < NUM_REGS + 32) + return builtin_type_float; + if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS) { + if (!gdbarch_tdep (gdbarch)->have_fpa_registers) + return builtin_type_void; + if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) return builtin_type_arm_ext_big; else @@ -1362,21 +1379,54 @@ arm_register_type (struct gdbarch *gdbarch, int regnum) return builtin_type_int32; } -/* Index within `registers' of the first byte of the space for - register N. */ - +/* Map DWARF register numbers onto internal GDB register numbers. */ static int -arm_register_byte (int regnum) -{ - if (regnum < ARM_F0_REGNUM) - return regnum * INT_REGISTER_SIZE; - else if (regnum < ARM_PS_REGNUM) - return (NUM_GREGS * INT_REGISTER_SIZE - + (regnum - ARM_F0_REGNUM) * FP_REGISTER_SIZE); - else - return (NUM_GREGS * INT_REGISTER_SIZE - + NUM_FREGS * FP_REGISTER_SIZE - + (regnum - ARM_FPS_REGNUM) * STATUS_REGISTER_SIZE); +arm_dwarf_reg_to_regnum (int reg) +{ + /* Core integer regs. */ + if (reg >= 0 && reg <= 15) + return reg; + + /* Legacy FPA encoding. These were once used in a way which + overlapped with VFP register numbering, so their use is + discouraged, but GDB doesn't support the ARM toolchain + which did that. */ + if (reg >= 16 && reg <= 23) + return ARM_F0_REGNUM + reg - 16; + + /* New assignments for the FPA registers. */ + if (reg >= 96 && reg <= 103) + return ARM_F0_REGNUM + reg - 96; + + /* 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 (current_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 (current_gdbarch, name_buf, + strlen (name_buf)); + } + + return -1; } /* Map GDB internal REGNUM onto the Arm simulator register numbers. */ @@ -2474,7 +2524,35 @@ set_disassembly_style_sfunc (char *args, int from_tty, static const char * arm_register_name (int i) { - return arm_register_names[i]; + const char *avail_name; + + /* Allow arm_register_names to override the names for standard + registers from the target, for "set arm disassembler". */ + if (i <= ARM_PC_REGNUM || i == ARM_PS_REGNUM) + return arm_register_names[i]; + if (i <= ARM_FPS_REGNUM) + { + if (gdbarch_tdep (current_gdbarch)->have_fpa_registers) + return arm_register_names[i]; + else + return ""; + } + + if (gdbarch_tdep (current_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]; + } + + /* Check for target-supplied register numbers. */ + return available_register_name (current_gdbarch, i); } static void @@ -2577,6 +2655,57 @@ arm_write_pc (CORE_ADDR pc, ptid_t ptid) } #endif } + +static void +arm_pseudo_vfp_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; + + gdb_assert (regnum >= NUM_REGS && regnum <= NUM_REGS + 32); + regnum -= NUM_REGS; + + /* s0 is always the least significant half of d0. */ + if (TARGET_BYTE_ORDER == 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); +} + +static void +arm_pseudo_vfp_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; + + gdb_assert (regnum >= NUM_REGS && regnum <= NUM_REGS + 32); + regnum -= NUM_REGS; + + /* s0 is always the least significant half of d0. */ + if (TARGET_BYTE_ORDER == 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 enum gdb_osabi arm_elf_osabi_sniffer (bfd *abfd) @@ -2597,6 +2726,72 @@ arm_elf_osabi_sniffer (bfd *abfd) return osabi; } +static void +arm_require_register (struct gdb_feature_set *feature_set, + const char *name, int regnum) +{ + if (!available_find_named_register (feature_set, name, regnum)) + error (_("target does not provide required register \"%s\""), name); +} + +static void +arm_check_feature_set (struct gdbarch *gdbarch, + struct gdb_feature_set *feature_set) +{ + static const char *const arm_standard_names[] = + { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "fps", + "cpsr" + }; + int i; + + if (!available_find_named_feature (feature_set, "org.gnu.gdb.arm.core")) + error (_("target does not provide ARM core registers")); + + for (i = 0; i < 16; i++) + arm_require_register (feature_set, arm_standard_names[i], i); + + arm_require_register (feature_set, arm_standard_names[ARM_PS_REGNUM], + ARM_PS_REGNUM); + + /* If we have an FPA unit, require the FPA registers and assign them + fixed numbers. If we don't have FPA, these register numbers will + remain unused, since various ARM subtargets hardcode the + numbering. */ + + if (available_find_named_feature (feature_set, "org.gnu.gdb.arm.fpa")) + { + for (i = ARM_F0_REGNUM; i <= ARM_FPS_REGNUM; i++) + arm_require_register (feature_set, arm_standard_names[i], i); + gdbarch_tdep (gdbarch)->have_fpa_registers = 1; + } + else + gdbarch_tdep (gdbarch)->have_fpa_registers = 0; + + /* If we have a VFP unit, check whether the single precision registers + are present. If not, then we will synthesize them as pseudo + registers. */ + + if (available_find_named_feature (feature_set, "org.gnu.gdb.arm.vfp")) + { + if (available_find_named_register (feature_set, "d0", -1) + && !available_find_named_register (feature_set, "s0", -1)) + { + /* NOTE: This is the only set of pseudo registers used by + the ARM target at the moment. If more are added, a + little more care in numbering will be needed. */ + + set_gdbarch_num_pseudo_regs (gdbarch, 32); + set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_vfp_read); + set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_vfp_write); + gdbarch_tdep (gdbarch)->have_vfp_pseudos = 1; + } + gdbarch_tdep (gdbarch)->have_vfp_registers = 1; + } +} + /* Initialize the current architecture based on INFO. If possible, re-use an architecture from ARCHES, which is a list of @@ -2788,15 +2983,31 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_breakpoint_from_pc (gdbarch, arm_breakpoint_from_pc); /* Information about registers, etc. */ - set_gdbarch_print_float_info (gdbarch, arm_print_float_info); set_gdbarch_deprecated_fp_regnum (gdbarch, ARM_FP_REGNUM); /* ??? */ set_gdbarch_sp_regnum (gdbarch, ARM_SP_REGNUM); set_gdbarch_pc_regnum (gdbarch, ARM_PC_REGNUM); - set_gdbarch_deprecated_register_byte (gdbarch, arm_register_byte); set_gdbarch_num_regs (gdbarch, NUM_GREGS + NUM_FREGS + NUM_SREGS); set_gdbarch_register_type (gdbarch, arm_register_type); + set_gdbarch_register_reggroup_p (gdbarch, available_register_reggroup_p); + + if (info.feature_set) + { + arm_check_feature_set (gdbarch, info.feature_set); + record_available_features (gdbarch, info.feature_set); + } + else + /* The legacy layout of the remote "g" packet assumes we have + the FPA registers, as do some native targets. */ + gdbarch_tdep (gdbarch)->have_fpa_registers = 1; + + /* This "info float" is FPA-specific. Use the generic version if we + do not have FPA. */ + if (gdbarch_tdep (gdbarch)->have_fpa_registers) + set_gdbarch_print_float_info (gdbarch, arm_print_float_info); /* Internal <-> external register number maps. */ + set_gdbarch_dwarf_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum); set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno); /* Integer registers are 4 bytes. */ @@ -2863,6 +3074,8 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) _("arm_gdbarch_init: bad byte order for float format")); } + set_gdbarch_available_features_support (gdbarch, 1); + return gdbarch; } |