diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1dbae2065e..201af51ac8 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,27 @@ +2015-07-02 Kevin Buettner + + * rx-tdep.c (RX_USP_REGNUM, RX_BPC_REGNUM): New constants. + (enum rx_frame_type): New. + (struct rx_prologue): Add new field `frame_type'. + (rx_analyze_prologue): Add `frame_type' parameter. Cache this + parameter in the prologue struct. Add code for recording + locations of PC and PSW for fast interrupt and exception frames. + (rx_skip_prologue): Adjust call to rx_analyze_prologue. + (rx_analyze_frame_prologue): Add `frame_type' parameter. + (rx_frame_type): New function. + (rx_frame_base): Fetch frame type and pass it to rx_analyze_prologue. + (rx_frame_this_id): Rename parameter `this_prologue_cache' to + `this_cache'. + (rx_frame_prev_register): Rename parameter `this_prologue_cache' to + `this_cache'. Add cases for RX_FRAME_TYPE_EXCEPTION and + RX_FRAME_TYPE_FAST_INTERRUPT. + (normal_frame_p, exception_frame_p, rx_frame_sniffer_common) + (rx_frame_sniffer, rx_exception_sniffer): New functions. + (rx_frame_unwind): Use rx_frame_sniffer instead of + default_frame_sniffer. + (rx_frame_unwind): New unwinder. + (rx_gdbarch_init): Register new unwinder. + 2015-07-02 Kevin Buettner * rx-tdep.c (RX_BPSW_REGNUM, RX_FPSW_REGNUM): New constants. diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c index 15c4cdedad..8442c765f8 100644 --- a/gdb/rx-tdep.c +++ b/gdb/rx-tdep.c @@ -45,14 +45,23 @@ enum RX_R4_REGNUM = 4, RX_FP_REGNUM = 6, RX_R15_REGNUM = 15, + RX_USP_REGNUM = 16, RX_PSW_REGNUM = 18, RX_PC_REGNUM = 19, RX_BPSW_REGNUM = 21, + RX_BPC_REGNUM = 22, RX_FPSW_REGNUM = 24, RX_ACC_REGNUM = 25, RX_NUM_REGS = 26 }; +/* RX frame types. */ +enum rx_frame_type { + RX_FRAME_TYPE_NORMAL, + RX_FRAME_TYPE_EXCEPTION, + RX_FRAME_TYPE_FAST_INTERRUPT +}; + /* Architecture specific data. */ struct gdbarch_tdep { @@ -69,6 +78,10 @@ struct gdbarch_tdep /* This structure holds the results of a prologue analysis. */ struct rx_prologue { + /* Frame type, either a normal frame or one of two types of exception + frames. */ + enum rx_frame_type frame_type; + /* The offset from the frame base to the stack pointer --- always zero or negative. @@ -203,9 +216,11 @@ rx_get_opcode_byte (void *handle) /* Analyze a prologue starting at START_PC, going no further than LIMIT_PC. Fill in RESULT as appropriate. */ + static void -rx_analyze_prologue (CORE_ADDR start_pc, - CORE_ADDR limit_pc, struct rx_prologue *result) +rx_analyze_prologue (CORE_ADDR start_pc, CORE_ADDR limit_pc, + enum rx_frame_type frame_type, + struct rx_prologue *result) { CORE_ADDR pc, next_pc; int rn; @@ -216,6 +231,8 @@ rx_analyze_prologue (CORE_ADDR start_pc, memset (result, 0, sizeof (*result)); + result->frame_type = frame_type; + for (rn = 0; rn < RX_NUM_REGS; rn++) { reg[rn] = pv_register (rn, 0); @@ -225,9 +242,30 @@ rx_analyze_prologue (CORE_ADDR start_pc, stack = make_pv_area (RX_SP_REGNUM, gdbarch_addr_bit (target_gdbarch ())); back_to = make_cleanup_free_pv_area (stack); - /* The call instruction has saved the return address on the stack. */ - reg[RX_SP_REGNUM] = pv_add_constant (reg[RX_SP_REGNUM], -4); - pv_area_store (stack, reg[RX_SP_REGNUM], 4, reg[RX_PC_REGNUM]); + if (frame_type == RX_FRAME_TYPE_FAST_INTERRUPT) + { + /* This code won't do anything useful at present, but this is + what happens for fast interrupts. */ + reg[RX_BPSW_REGNUM] = reg[RX_PSW_REGNUM]; + reg[RX_BPC_REGNUM] = reg[RX_PC_REGNUM]; + } + else + { + /* When an exception occurs, the PSW is saved to the interrupt stack + first. */ + if (frame_type == RX_FRAME_TYPE_EXCEPTION) + { + reg[RX_SP_REGNUM] = pv_add_constant (reg[RX_SP_REGNUM], -4); + pv_area_store (stack, reg[RX_SP_REGNUM], 4, reg[RX_PSW_REGNUM]); + } + + /* The call instruction (or an exception/interrupt) has saved the return + address on the stack. */ + reg[RX_SP_REGNUM] = pv_add_constant (reg[RX_SP_REGNUM], -4); + pv_area_store (stack, reg[RX_SP_REGNUM], 4, reg[RX_PC_REGNUM]); + + } + pc = start_pc; while (pc < limit_pc) @@ -376,7 +414,9 @@ rx_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) if (!find_pc_partial_function (pc, &name, &func_addr, &func_end)) return pc; - rx_analyze_prologue (pc, func_end, &p); + /* The frame type doesn't matter here, since we only care about + where the prologue ends. We'll use RX_FRAME_TYPE_NORMAL. */ + rx_analyze_prologue (pc, func_end, RX_FRAME_TYPE_NORMAL, &p); return p.prologue_end; } @@ -384,8 +424,10 @@ rx_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) associated function if there is not cache entry as specified by THIS_PROLOGUE_CACHE. Save the decoded prologue in the cache and return that struct as the value of this function. */ + static struct rx_prologue * rx_analyze_frame_prologue (struct frame_info *this_frame, + enum rx_frame_type frame_type, void **this_prologue_cache) { if (!*this_prologue_cache) @@ -402,19 +444,76 @@ rx_analyze_frame_prologue (struct frame_info *this_frame, if (!func_start) stop_addr = func_start; - rx_analyze_prologue (func_start, stop_addr, *this_prologue_cache); + rx_analyze_prologue (func_start, stop_addr, frame_type, + *this_prologue_cache); } return *this_prologue_cache; } +/* Determine type of frame by scanning the function for a return + instruction. */ + +static enum rx_frame_type +rx_frame_type (struct frame_info *this_frame, void **this_cache) +{ + const char *name; + CORE_ADDR pc, start_pc, lim_pc; + int bytes_read; + struct rx_get_opcode_byte_handle opcode_handle; + RX_Opcode_Decoded opc; + + gdb_assert (this_cache != NULL); + + /* If we have a cached value, return it. */ + + if (*this_cache != NULL) + { + struct rx_prologue *p = *this_cache; + + return p->frame_type; + } + + /* No cached value; scan the function. The frame type is cached in + rx_analyze_prologue / rx_analyze_frame_prologue. */ + + pc = get_frame_pc (this_frame); + + /* Attempt to find the last address in the function. If it cannot + be determined, set the limit to be a short ways past the frame's + pc. */ + if (!find_pc_partial_function (pc, &name, &start_pc, &lim_pc)) + lim_pc = pc + 20; + + while (pc < lim_pc) + { + opcode_handle.pc = pc; + bytes_read = rx_decode_opcode (pc, &opc, rx_get_opcode_byte, + &opcode_handle); + + if (bytes_read <= 0 || opc.id == RXO_rts) + return RX_FRAME_TYPE_NORMAL; + else if (opc.id == RXO_rtfi) + return RX_FRAME_TYPE_FAST_INTERRUPT; + else if (opc.id == RXO_rte) + return RX_FRAME_TYPE_EXCEPTION; + + pc += bytes_read; + } + + return RX_FRAME_TYPE_NORMAL; +} + + /* Given the next frame and a prologue cache, return this frame's base. */ + static CORE_ADDR -rx_frame_base (struct frame_info *this_frame, void **this_prologue_cache) +rx_frame_base (struct frame_info *this_frame, void **this_cache) { + enum rx_frame_type frame_type = rx_frame_type (this_frame, this_cache); struct rx_prologue *p - = rx_analyze_frame_prologue (this_frame, this_prologue_cache); + = rx_analyze_frame_prologue (this_frame, frame_type, this_cache); /* In functions that use alloca, the distance between the stack pointer and the frame base varies dynamically, so we can't use @@ -435,46 +534,166 @@ rx_frame_base (struct frame_info *this_frame, void **this_prologue_cache) } /* Implement the "frame_this_id" method for unwinding frames. */ + static void -rx_frame_this_id (struct frame_info *this_frame, - void **this_prologue_cache, struct frame_id *this_id) +rx_frame_this_id (struct frame_info *this_frame, void **this_cache, + struct frame_id *this_id) { - *this_id = frame_id_build (rx_frame_base (this_frame, this_prologue_cache), + *this_id = frame_id_build (rx_frame_base (this_frame, this_cache), get_frame_func (this_frame)); } /* Implement the "frame_prev_register" method for unwinding frames. */ + static struct value * -rx_frame_prev_register (struct frame_info *this_frame, - void **this_prologue_cache, int regnum) +rx_frame_prev_register (struct frame_info *this_frame, void **this_cache, + int regnum) { + enum rx_frame_type frame_type = rx_frame_type (this_frame, this_cache); struct rx_prologue *p - = rx_analyze_frame_prologue (this_frame, this_prologue_cache); - CORE_ADDR frame_base = rx_frame_base (this_frame, this_prologue_cache); - int reg_size = register_size (get_frame_arch (this_frame), regnum); + = rx_analyze_frame_prologue (this_frame, frame_type, this_cache); + CORE_ADDR frame_base = rx_frame_base (this_frame, this_cache); if (regnum == RX_SP_REGNUM) - return frame_unwind_got_constant (this_frame, regnum, frame_base); + { + if (frame_type == RX_FRAME_TYPE_EXCEPTION) + { + struct value *psw_val; + CORE_ADDR psw; + + psw_val = rx_frame_prev_register (this_frame, this_cache, + RX_PSW_REGNUM); + psw = extract_unsigned_integer (value_contents_all (psw_val), 4, + gdbarch_byte_order ( + get_frame_arch (this_frame))); + + if ((psw & 0x20000 /* U bit */) != 0) + return rx_frame_prev_register (this_frame, this_cache, + RX_USP_REGNUM); + + /* Fall through for the case where U bit is zero. */ + } + + return frame_unwind_got_constant (this_frame, regnum, frame_base); + } + + if (frame_type == RX_FRAME_TYPE_FAST_INTERRUPT) + { + if (regnum == RX_PC_REGNUM) + return rx_frame_prev_register (this_frame, this_cache, + RX_BPC_REGNUM); + if (regnum == RX_PSW_REGNUM) + return rx_frame_prev_register (this_frame, this_cache, + RX_BPSW_REGNUM); + } /* If prologue analysis says we saved this register somewhere, return a description of the stack slot holding it. */ - else if (p->reg_offset[regnum] != 1) + if (p->reg_offset[regnum] != 1) return frame_unwind_got_memory (this_frame, regnum, frame_base + p->reg_offset[regnum]); /* Otherwise, presume we haven't changed the value of this register, and get it from the next frame. */ - else - return frame_unwind_got_register (this_frame, regnum, regnum); + return frame_unwind_got_register (this_frame, regnum, regnum); } +/* Return TRUE if the frame indicated by FRAME_TYPE is a normal frame. */ + +static int +normal_frame_p (enum rx_frame_type frame_type) +{ + return (frame_type == RX_FRAME_TYPE_NORMAL); +} + +/* Return TRUE if the frame indicated by FRAME_TYPE is an exception + frame. */ + +static int +exception_frame_p (enum rx_frame_type frame_type) +{ + return (frame_type == RX_FRAME_TYPE_EXCEPTION + || frame_type == RX_FRAME_TYPE_FAST_INTERRUPT); +} + +/* Common code used by both normal and exception frame sniffers. */ + +static int +rx_frame_sniffer_common (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache, + int (*sniff_p)(enum rx_frame_type) ) +{ + gdb_assert (this_cache != NULL); + + if (*this_cache == NULL) + { + enum rx_frame_type frame_type = rx_frame_type (this_frame, this_cache); + + if (sniff_p (frame_type)) + { + /* The call below will fill in the cache, including the frame + type. */ + (void) rx_analyze_frame_prologue (this_frame, frame_type, this_cache); + + return 1; + } + else + return 0; + } + else + { + struct rx_prologue *p = *this_cache; + + return sniff_p (p->frame_type); + } +} + +/* Frame sniffer for normal (non-exception) frames. */ + +static int +rx_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + return rx_frame_sniffer_common (self, this_frame, this_cache, + normal_frame_p); +} + +/* Frame sniffer for exception frames. */ + +static int +rx_exception_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + return rx_frame_sniffer_common (self, this_frame, this_cache, + exception_frame_p); +} + +/* Data structure for normal code using instruction-based prologue + analyzer. */ + static const struct frame_unwind rx_frame_unwind = { NORMAL_FRAME, default_frame_unwind_stop_reason, rx_frame_this_id, rx_frame_prev_register, NULL, - default_frame_sniffer + rx_frame_sniffer +}; + +/* Data structure for exception code using instruction-based prologue + analyzer. */ + +static const struct frame_unwind rx_exception_unwind = { + /* SIGTRAMP_FRAME could be used here, but backtraces are less informative. */ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + rx_frame_this_id, + rx_frame_prev_register, + NULL, + rx_exception_sniffer }; /* Implement the "unwind_pc" gdbarch method. */ @@ -913,6 +1132,7 @@ rx_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_dwarf2_reg_to_regnum (gdbarch, rx_dwarf_reg_to_regnum); /* Frame unwinding. */ + frame_unwind_append_unwinder (gdbarch, &rx_exception_unwind); dwarf2_append_unwinders (gdbarch); frame_unwind_append_unwinder (gdbarch, &rx_frame_unwind);