From patchwork Fri Jul 3 00:25:11 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Buettner X-Patchwork-Id: 7478 Received: (qmail 49744 invoked by alias); 3 Jul 2015 00:25:17 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 49729 invoked by uid 89); 3 Jul 2015 00:25:17 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.9 required=5.0 tests=AWL, BAYES_20, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=no version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 03 Jul 2015 00:25:15 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id B9A973A80EA for ; Fri, 3 Jul 2015 00:25:13 +0000 (UTC) Received: from pinnacle.lan (ovpn-113-20.phx2.redhat.com [10.3.113.20]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t630PDxf009538 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA256 bits=256 verify=NO) for ; Thu, 2 Jul 2015 20:25:13 -0400 Date: Thu, 2 Jul 2015 17:25:11 -0700 From: Kevin Buettner To: gdb-patches@sourceware.org Subject: [Committed PATCH] Add support for backtracing through Renesas RX exception frames Message-ID: <20150702172511.731078b5@pinnacle.lan> MIME-Version: 1.0 X-IsSubscribed: yes This change adds support for backtracing through Renesas RX exception frames. Determination about the type of frame is made by scanning the remainder of the function for a return instruction and then looking at which, if any, return instruction is found. A normal RTS instruction indicates that the frame is a normal frame. An RTFI instruction indicates that it's a fast interrupt, and an RTE instruction indicates that the frame is a (normal) exception frame. If no return instruction is found within the scanned region - which can happen when the end of the function cannot be found - it is assumed to be a normal frame. I was able to test that normal prologue scanning still works by disabling the dwarf2 sniffer. I've tested this code for normal interrupts. The fast interrupt case has not been tested. gdb/ChangeLog: * 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. --- gdb/ChangeLog | 24 ++++++ gdb/rx-tdep.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 265 insertions(+), 21 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1dbae20..201af51 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,29 @@ 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. (struct gdbarch_tdep): Add fields rx_psw_type and rx_fpsw_type. (rx_register_type): Add cases for RX_PSW_REGNUM, RX_BPSW_REGNUM, diff --git a/gdb/rx-tdep.c b/gdb/rx-tdep.c index 15c4cde..8442c76 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. */ + 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 - return frame_unwind_got_register (this_frame, regnum, regnum); + { + 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);