X-Git-Url: http://drtracing.org/?a=blobdiff_plain;ds=sidebyside;f=gdb%2Fsparc-tdep.c;h=863ef8fdfe4110ee6db285d4b67fb88e1a6532a1;hb=a12ac51333cf97f4da0597d049cc694b4535e7dd;hp=faa7b3a4adc6a9848e2d9b2b3431be725f326e1d;hpb=369c397ba4feb2e14ba5ce9ad34b95b886382273;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c index faa7b3a4ad..863ef8fdfe 100644 --- a/gdb/sparc-tdep.c +++ b/gdb/sparc-tdep.c @@ -1,7 +1,6 @@ /* Target-dependent code for SPARC. - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 - Free Software Foundation, Inc. + Copyright (C) 2003-2016 Free Software Foundation, Inc. This file is part of GDB. @@ -36,10 +35,8 @@ #include "target.h" #include "value.h" -#include "gdb_assert.h" -#include "gdb_string.h" - #include "sparc-tdep.h" +#include "sparc-ravenscar-thread.h" struct regset; @@ -86,7 +83,11 @@ struct regset; /* Sign extension macros. */ #define X_DISP22(i) ((X_IMM22 (i) ^ 0x200000) - 0x200000) #define X_DISP19(i) ((((i) & 0x7ffff) ^ 0x40000) - 0x40000) +#define X_DISP10(i) ((((((i) >> 11) && 0x300) | (((i) >> 5) & 0xff)) ^ 0x200) - 0x200) #define X_SIMM13(i) ((((i) & 0x1fff) ^ 0x1000) - 0x1000) +/* Macros to identify some instructions. */ +/* RETURN (RETT in V8) */ +#define X_RETTURN(i) ((X_OP (i) == 0x2) && (X_OP3 (i) == 0x39)) /* Fetch the instruction at PC. Instructions are always big-endian even if the processor operates in little-endian mode. */ @@ -120,6 +121,37 @@ sparc_is_unimp_insn (CORE_ADDR pc) return ((insn & 0xc1c00000) == 0); } +/* Return non-zero if the instruction corresponding to PC is an + "annulled" branch, i.e. the annul bit is set. */ + +int +sparc_is_annulled_branch_insn (CORE_ADDR pc) +{ + /* The branch instructions featuring an annul bit can be identified + by the following bit patterns: + + OP=0 + OP2=1: Branch on Integer Condition Codes with Prediction (BPcc). + OP2=2: Branch on Integer Condition Codes (Bcc). + OP2=5: Branch on FP Condition Codes with Prediction (FBfcc). + OP2=6: Branch on FP Condition Codes (FBcc). + OP2=3 && Bit28=0: + Branch on Integer Register with Prediction (BPr). + + This leaves out ILLTRAP (OP2=0), SETHI/NOP (OP2=4) and the V8 + coprocessor branch instructions (Op2=7). */ + + const unsigned long insn = sparc_fetch_instruction (pc); + const unsigned op2 = X_OP2 (insn); + + if ((X_OP (insn) == 0) + && ((op2 == 1) || (op2 == 2) || (op2 == 5) || (op2 == 6) + || ((op2 == 3) && ((insn & 0x10000000) == 0)))) + return X_A (insn); + else + return 0; +} + /* OpenBSD/sparc includes StackGhost, which according to the author's website http://stackghost.cerias.purdue.edu "... transparently and automatically protects applications' stack frames; more @@ -221,6 +253,25 @@ sparc_floating_p (const struct type *type) return 0; } +/* Check whether TYPE is "Complex Floating". */ + +static int +sparc_complex_floating_p (const struct type *type) +{ + switch (TYPE_CODE (type)) + { + case TYPE_CODE_COMPLEX: + { + int len = TYPE_LENGTH (type); + return (len == 8 || len == 16 || len == 32); + } + default: + break; + } + + return 0; +} + /* Check whether TYPE is "Structure or Union". In terms of Ada subprogram calls, arrays are treated the same as @@ -401,6 +452,29 @@ sparc32_pseudo_register_write (struct gdbarch *gdbarch, regcache_raw_write (regcache, regnum + 1, buf + 4); } +/* Implement the stack_frame_destroyed_p gdbarch method. */ + +int +sparc_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + /* This function must return true if we are one instruction after an + instruction that destroyed the stack frame of the current + function. The SPARC instructions used to restore the callers + stack frame are RESTORE and RETURN/RETT. + + Of these RETURN/RETT is a branch instruction and thus we return + true if we are in its delay slot. + + RESTORE is almost always found in the delay slot of a branch + instruction that transfers control to the caller, such as JMPL. + Thus the next instruction is in the caller frame and we don't + need to do anything about it. */ + + unsigned int insn = sparc_fetch_instruction (pc - 4); + + return X_RETTURN (insn); +} + static CORE_ADDR sparc32_frame_align (struct gdbarch *gdbarch, CORE_ADDR address) @@ -454,7 +528,8 @@ sparc32_store_arguments (struct regcache *regcache, int nargs, int len = TYPE_LENGTH (type); if (sparc_structure_or_union_p (type) - || (sparc_floating_p (type) && len == 16)) + || (sparc_floating_p (type) && len == 16) + || sparc_complex_floating_p (type)) { /* Structure, Union and Quad-Precision Arguments. */ sp -= len; @@ -582,7 +657,6 @@ static struct sparc_frame_cache * sparc_alloc_frame_cache (void) { struct sparc_frame_cache *cache; - int i; cache = FRAME_OBSTACK_ZALLOC (struct sparc_frame_cache); @@ -835,7 +909,7 @@ sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, dynamic linker patches up the first PLT with some code that starts with a SAVE instruction. Patch up PC such that it points at the start of our PLT entry. */ - if (tdep->plt_entry_size > 0 && in_plt_section (current_pc, NULL)) + if (tdep->plt_entry_size > 0 && in_plt_section (current_pc)) pc = current_pc - ((current_pc - pc) % tdep->plt_entry_size); insn = sparc_fetch_instruction (pc); @@ -1026,7 +1100,7 @@ sparc_frame_cache (struct frame_info *this_frame, void **this_cache) struct sparc_frame_cache *cache; if (*this_cache) - return *this_cache; + return (struct sparc_frame_cache *) *this_cache; cache = sparc_alloc_frame_cache (); *this_cache = cache; @@ -1084,7 +1158,7 @@ sparc32_frame_cache (struct frame_info *this_frame, void **this_cache) struct symbol *sym; if (*this_cache) - return *this_cache; + return (struct sparc_frame_cache *) *this_cache; cache = sparc_frame_cache (this_frame, this_cache); @@ -1233,17 +1307,29 @@ sparc32_extract_return_value (struct type *type, struct regcache *regcache, gdb_byte *valbuf) { int len = TYPE_LENGTH (type); - gdb_byte buf[8]; + gdb_byte buf[32]; gdb_assert (!sparc_structure_or_union_p (type)); gdb_assert (!(sparc_floating_p (type) && len == 16)); - if (sparc_floating_p (type)) + if (sparc_floating_p (type) || sparc_complex_floating_p (type)) { /* Floating return values. */ regcache_cooked_read (regcache, SPARC_F0_REGNUM, buf); if (len > 4) regcache_cooked_read (regcache, SPARC_F1_REGNUM, buf + 4); + if (len > 8) + { + regcache_cooked_read (regcache, SPARC_F2_REGNUM, buf + 8); + regcache_cooked_read (regcache, SPARC_F3_REGNUM, buf + 12); + } + if (len > 16) + { + regcache_cooked_read (regcache, SPARC_F4_REGNUM, buf + 16); + regcache_cooked_read (regcache, SPARC_F5_REGNUM, buf + 20); + regcache_cooked_read (regcache, SPARC_F6_REGNUM, buf + 24); + regcache_cooked_read (regcache, SPARC_F7_REGNUM, buf + 28); + } memcpy (valbuf, buf, len); } else @@ -1281,13 +1367,25 @@ sparc32_store_return_value (struct type *type, struct regcache *regcache, gdb_assert (!(sparc_floating_p (type) && len == 16)); gdb_assert (len <= 8); - if (sparc_floating_p (type)) + if (sparc_floating_p (type) || sparc_complex_floating_p (type)) { /* Floating return values. */ memcpy (buf, valbuf, len); regcache_cooked_write (regcache, SPARC_F0_REGNUM, buf); if (len > 4) regcache_cooked_write (regcache, SPARC_F1_REGNUM, buf + 4); + if (len > 8) + { + regcache_cooked_write (regcache, SPARC_F2_REGNUM, buf + 8); + regcache_cooked_write (regcache, SPARC_F3_REGNUM, buf + 12); + } + if (len > 16) + { + regcache_cooked_write (regcache, SPARC_F4_REGNUM, buf + 16); + regcache_cooked_write (regcache, SPARC_F5_REGNUM, buf + 20); + regcache_cooked_write (regcache, SPARC_F6_REGNUM, buf + 24); + regcache_cooked_write (regcache, SPARC_F7_REGNUM, buf + 28); + } } else { @@ -1310,7 +1408,7 @@ sparc32_store_return_value (struct type *type, struct regcache *regcache, } static enum return_value_convention -sparc32_return_value (struct gdbarch *gdbarch, struct type *func_type, +sparc32_return_value (struct gdbarch *gdbarch, struct value *function, struct type *type, struct regcache *regcache, gdb_byte *readbuf, const gdb_byte *writebuf) { @@ -1326,15 +1424,21 @@ sparc32_return_value (struct gdbarch *gdbarch, struct type *func_type, if (sparc_structure_or_union_p (type) || (sparc_floating_p (type) && TYPE_LENGTH (type) == 16)) { + ULONGEST sp; + CORE_ADDR addr; + if (readbuf) { - ULONGEST sp; - CORE_ADDR addr; - regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp); addr = read_memory_unsigned_integer (sp + 64, 4, byte_order); read_memory (addr, readbuf, TYPE_LENGTH (type)); } + if (writebuf) + { + regcache_cooked_read_unsigned (regcache, SPARC_SP_REGNUM, &sp); + addr = read_memory_unsigned_integer (sp + 64, 4, byte_order); + write_memory (addr, writebuf, TYPE_LENGTH (type)); + } return RETURN_VALUE_ABI_PRESERVES_ADDRESS; } @@ -1351,7 +1455,8 @@ static int sparc32_stabs_argument_has_addr (struct gdbarch *gdbarch, struct type *type) { return (sparc_structure_or_union_p (type) - || (sparc_floating_p (type) && TYPE_LENGTH (type) == 16)); + || (sparc_floating_p (type) && TYPE_LENGTH (type) == 16) + || sparc_complex_floating_p (type)); } static int @@ -1407,14 +1512,24 @@ sparc_analyze_control_transfer (struct frame_info *frame, { unsigned long insn = sparc_fetch_instruction (pc); int conditional_p = X_COND (insn) & 0x7; - int branch_p = 0; + int branch_p = 0, fused_p = 0; long offset = 0; /* Must be signed for sign-extend. */ - if (X_OP (insn) == 0 && X_OP2 (insn) == 3 && (insn & 0x1000000) == 0) + if (X_OP (insn) == 0 && X_OP2 (insn) == 3) { - /* Branch on Integer Register with Prediction (BPr). */ - branch_p = 1; - conditional_p = 1; + if ((insn & 0x10000000) == 0) + { + /* Branch on Integer Register with Prediction (BPr). */ + branch_p = 1; + conditional_p = 1; + } + else + { + /* Compare and Branch */ + branch_p = 1; + fused_p = 1; + offset = 4 * X_DISP10 (insn); + } } else if (X_OP (insn) == 0 && X_OP2 (insn) == 6) { @@ -1451,7 +1566,16 @@ sparc_analyze_control_transfer (struct frame_info *frame, if (branch_p) { - if (conditional_p) + if (fused_p) + { + /* Fused compare-and-branch instructions are non-delayed, + and do not have an annuling capability. So we need to + always set a breakpoint on both the NPC and the branch + target address. */ + gdb_assert (offset != 0); + return pc + offset; + } + else if (conditional_p) { /* For conditional branches, return nPC + 4 iff the annul bit is 1. */ @@ -1468,7 +1592,6 @@ sparc_analyze_control_transfer (struct frame_info *frame, if (X_A (insn)) *npc = 0; - gdb_assert (offset != 0); return pc + offset; } } @@ -1482,7 +1605,7 @@ sparc_step_trap (struct frame_info *frame, unsigned long insn) return 0; } -int +static int sparc_software_single_step (struct frame_info *frame) { struct gdbarch *arch = get_frame_arch (frame); @@ -1522,22 +1645,18 @@ sparc_write_pc (struct regcache *regcache, CORE_ADDR pc) } -/* Return the appropriate register set for the core section identified - by SECT_NAME and SECT_SIZE. */ +/* Iterate over core file register note sections. */ -static const struct regset * -sparc_regset_from_core_section (struct gdbarch *gdbarch, - const char *sect_name, size_t sect_size) +static void +sparc_iterate_over_regset_sections (struct gdbarch *gdbarch, + iterate_over_regset_sections_cb *cb, + void *cb_data, + const struct regcache *regcache) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - if (strcmp (sect_name, ".reg") == 0 && sect_size >= tdep->sizeof_gregset) - return tdep->gregset; - - if (strcmp (sect_name, ".reg2") == 0 && sect_size >= tdep->sizeof_fpregset) - return tdep->fpregset; - - return NULL; + cb (".reg", tdep->sizeof_gregset, tdep->gregset, NULL, cb_data); + cb (".reg2", tdep->sizeof_fpregset, tdep->fpregset, NULL, cb_data); } @@ -1553,7 +1672,7 @@ sparc32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return arches->gdbarch; /* Allocate space for the new architecture. */ - tdep = XZALLOC (struct gdbarch_tdep); + tdep = XCNEW (struct gdbarch_tdep); gdbarch = gdbarch_alloc (&info, tdep); tdep->pc_regnum = SPARC32_PC_REGNUM; @@ -1617,8 +1736,10 @@ sparc32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* If we have register sets, enable the generic core file support. */ if (tdep->gregset) - set_gdbarch_regset_from_core_section (gdbarch, - sparc_regset_from_core_section); + set_gdbarch_iterate_over_regset_sections + (gdbarch, sparc_iterate_over_regset_sections); + + register_sparc_ravenscar_ops (gdbarch); return gdbarch; } @@ -1770,36 +1891,36 @@ sparc_collect_rwindow (const struct regcache *regcache, /* Helper functions for dealing with register sets. */ void -sparc32_supply_gregset (const struct sparc_gregset *gregset, +sparc32_supply_gregset (const struct sparc_gregmap *gregmap, struct regcache *regcache, int regnum, const void *gregs) { - const gdb_byte *regs = gregs; + const gdb_byte *regs = (const gdb_byte *) gregs; gdb_byte zero[4] = { 0 }; int i; if (regnum == SPARC32_PSR_REGNUM || regnum == -1) regcache_raw_supply (regcache, SPARC32_PSR_REGNUM, - regs + gregset->r_psr_offset); + regs + gregmap->r_psr_offset); if (regnum == SPARC32_PC_REGNUM || regnum == -1) regcache_raw_supply (regcache, SPARC32_PC_REGNUM, - regs + gregset->r_pc_offset); + regs + gregmap->r_pc_offset); if (regnum == SPARC32_NPC_REGNUM || regnum == -1) regcache_raw_supply (regcache, SPARC32_NPC_REGNUM, - regs + gregset->r_npc_offset); + regs + gregmap->r_npc_offset); if (regnum == SPARC32_Y_REGNUM || regnum == -1) regcache_raw_supply (regcache, SPARC32_Y_REGNUM, - regs + gregset->r_y_offset); + regs + gregmap->r_y_offset); if (regnum == SPARC_G0_REGNUM || regnum == -1) regcache_raw_supply (regcache, SPARC_G0_REGNUM, &zero); if ((regnum >= SPARC_G1_REGNUM && regnum <= SPARC_O7_REGNUM) || regnum == -1) { - int offset = gregset->r_g1_offset; + int offset = gregmap->r_g1_offset; for (i = SPARC_G1_REGNUM; i <= SPARC_O7_REGNUM; i++) { @@ -1813,7 +1934,7 @@ sparc32_supply_gregset (const struct sparc_gregset *gregset, { /* Not all of the register set variants include Locals and Inputs. For those that don't, we read them off the stack. */ - if (gregset->r_l0_offset == -1) + if (gregmap->r_l0_offset == -1) { ULONGEST sp; @@ -1822,7 +1943,7 @@ sparc32_supply_gregset (const struct sparc_gregset *gregset, } else { - int offset = gregset->r_l0_offset; + int offset = gregmap->r_l0_offset; for (i = SPARC_L0_REGNUM; i <= SPARC_I7_REGNUM; i++) { @@ -1835,32 +1956,32 @@ sparc32_supply_gregset (const struct sparc_gregset *gregset, } void -sparc32_collect_gregset (const struct sparc_gregset *gregset, +sparc32_collect_gregset (const struct sparc_gregmap *gregmap, const struct regcache *regcache, int regnum, void *gregs) { - gdb_byte *regs = gregs; + gdb_byte *regs = (gdb_byte *) gregs; int i; if (regnum == SPARC32_PSR_REGNUM || regnum == -1) regcache_raw_collect (regcache, SPARC32_PSR_REGNUM, - regs + gregset->r_psr_offset); + regs + gregmap->r_psr_offset); if (regnum == SPARC32_PC_REGNUM || regnum == -1) regcache_raw_collect (regcache, SPARC32_PC_REGNUM, - regs + gregset->r_pc_offset); + regs + gregmap->r_pc_offset); if (regnum == SPARC32_NPC_REGNUM || regnum == -1) regcache_raw_collect (regcache, SPARC32_NPC_REGNUM, - regs + gregset->r_npc_offset); + regs + gregmap->r_npc_offset); if (regnum == SPARC32_Y_REGNUM || regnum == -1) regcache_raw_collect (regcache, SPARC32_Y_REGNUM, - regs + gregset->r_y_offset); + regs + gregmap->r_y_offset); if ((regnum >= SPARC_G1_REGNUM && regnum <= SPARC_O7_REGNUM) || regnum == -1) { - int offset = gregset->r_g1_offset; + int offset = gregmap->r_g1_offset; /* %g0 is always zero. */ for (i = SPARC_G1_REGNUM; i <= SPARC_O7_REGNUM; i++) @@ -1875,9 +1996,9 @@ sparc32_collect_gregset (const struct sparc_gregset *gregset, { /* Not all of the register set variants include Locals and Inputs. For those that don't, we read them off the stack. */ - if (gregset->r_l0_offset != -1) + if (gregmap->r_l0_offset != -1) { - int offset = gregset->r_l0_offset; + int offset = gregmap->r_l0_offset; for (i = SPARC_L0_REGNUM; i <= SPARC_I7_REGNUM; i++) { @@ -1890,44 +2011,50 @@ sparc32_collect_gregset (const struct sparc_gregset *gregset, } void -sparc32_supply_fpregset (struct regcache *regcache, +sparc32_supply_fpregset (const struct sparc_fpregmap *fpregmap, + struct regcache *regcache, int regnum, const void *fpregs) { - const gdb_byte *regs = fpregs; + const gdb_byte *regs = (const gdb_byte *) fpregs; int i; for (i = 0; i < 32; i++) { if (regnum == (SPARC_F0_REGNUM + i) || regnum == -1) - regcache_raw_supply (regcache, SPARC_F0_REGNUM + i, regs + (i * 4)); + regcache_raw_supply (regcache, SPARC_F0_REGNUM + i, + regs + fpregmap->r_f0_offset + (i * 4)); } if (regnum == SPARC32_FSR_REGNUM || regnum == -1) - regcache_raw_supply (regcache, SPARC32_FSR_REGNUM, regs + (32 * 4) + 4); + regcache_raw_supply (regcache, SPARC32_FSR_REGNUM, + regs + fpregmap->r_fsr_offset); } void -sparc32_collect_fpregset (const struct regcache *regcache, +sparc32_collect_fpregset (const struct sparc_fpregmap *fpregmap, + const struct regcache *regcache, int regnum, void *fpregs) { - gdb_byte *regs = fpregs; + gdb_byte *regs = (gdb_byte *) fpregs; int i; for (i = 0; i < 32; i++) { if (regnum == (SPARC_F0_REGNUM + i) || regnum == -1) - regcache_raw_collect (regcache, SPARC_F0_REGNUM + i, regs + (i * 4)); + regcache_raw_collect (regcache, SPARC_F0_REGNUM + i, + regs + fpregmap->r_f0_offset + (i * 4)); } if (regnum == SPARC32_FSR_REGNUM || regnum == -1) - regcache_raw_collect (regcache, SPARC32_FSR_REGNUM, regs + (32 * 4) + 4); + regcache_raw_collect (regcache, SPARC32_FSR_REGNUM, + regs + fpregmap->r_fsr_offset); } /* SunOS 4. */ /* From . */ -const struct sparc_gregset sparc32_sunos4_gregset = +const struct sparc_gregmap sparc32_sunos4_gregmap = { 0 * 4, /* %psr */ 1 * 4, /* %pc */ @@ -1938,6 +2065,18 @@ const struct sparc_gregset sparc32_sunos4_gregset = 4 * 4, /* %g1 */ -1 /* %l0 */ }; + +const struct sparc_fpregmap sparc32_sunos4_fpregmap = +{ + 0 * 4, /* %f0 */ + 33 * 4, /* %fsr */ +}; + +const struct sparc_fpregmap sparc32_bsd_fpregmap = +{ + 0 * 4, /* %f0 */ + 32 * 4, /* %fsr */ +}; /* Provide a prototype to silence -Wmissing-prototypes. */