aboutsummaryrefslogtreecommitdiff
path: root/gdb/m68hc11-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/m68hc11-tdep.c')
-rw-r--r--gdb/m68hc11-tdep.c379
1 files changed, 322 insertions, 57 deletions
diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
index 6d62b2c..70a1649 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -21,6 +21,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
#include "frame.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "dwarf2-frame.h"
+#include "trad-frame.h"
#include "symtab.h"
#include "gdbtypes.h"
#include "gdbcmd.h"
@@ -157,6 +161,25 @@ struct gdbarch_tdep
#define STACK_CORRECTION (M6811_TDEP->stack_correction)
#define USE_PAGE_REGISTER (M6811_TDEP->use_page_register)
+struct m68hc11_unwind_cache
+{
+ /* The previous frame's inner most stack address. Used as this
+ frame ID's stack_addr. */
+ CORE_ADDR prev_sp;
+ /* The frame's base, optionally used by the high-level debug info. */
+ CORE_ADDR base;
+ CORE_ADDR pc;
+ int size;
+ int prologue_type;
+ CORE_ADDR return_pc;
+ CORE_ADDR sp_offset;
+ int frameless;
+ enum insn_return_kind return_kind;
+
+ /* Table indicating the location of each and every register. */
+ struct trad_frame_saved_reg *saved_regs;
+};
+
struct frame_extra_info
{
CORE_ADDR return_pc;
@@ -412,20 +435,6 @@ m68hc11_frame_saved_pc (struct frame_info *frame)
return get_frame_extra_info (frame)->return_pc;
}
-static CORE_ADDR
-m68hc11_frame_args_address (struct frame_info *frame)
-{
- CORE_ADDR addr;
-
- addr = get_frame_base (frame) + get_frame_extra_info (frame)->size + STACK_CORRECTION + 2;
- if (get_frame_extra_info (frame)->return_kind == RETURN_RTC)
- addr += 1;
- else if (get_frame_extra_info (frame)->return_kind == RETURN_RTI)
- addr += 7;
-
- return addr;
-}
-
/* Discard from the stack the innermost frame, restoring all saved
registers. */
@@ -660,35 +669,35 @@ m68hc11_get_return_insn (CORE_ADDR pc)
return RETURN_RTS;
}
-
/* Analyze the function prologue to find some information
about the function:
- the PC of the first line (for m68hc11_skip_prologue)
- the offset of the previous frame saved address (from current frame)
- the soft registers which are pushed. */
-static void
-m68hc11_guess_from_prologue (CORE_ADDR pc, CORE_ADDR current_pc, CORE_ADDR fp,
- CORE_ADDR *first_line,
- int *frame_offset, CORE_ADDR *pushed_regs)
+static CORE_ADDR
+m68hc11_scan_prologue (CORE_ADDR pc, CORE_ADDR current_pc,
+ struct m68hc11_unwind_cache *info)
{
- CORE_ADDR save_addr;
+ LONGEST save_addr;
CORE_ADDR func_end;
int size;
int found_frame_point;
int saved_reg;
- CORE_ADDR first_pc;
int done = 0;
struct insn_sequence *seq_table;
-
- first_pc = get_pc_function_start (pc);
+
+ info->size = 0;
+ info->sp_offset = 0;
+ if (pc >= current_pc)
+ return current_pc;
+
size = 0;
m68hc11_initialize_register_info ();
- if (first_pc == 0)
+ if (pc == 0)
{
- *frame_offset = 0;
- *first_line = pc;
- return;
+ info->size = 0;
+ return pc;
}
seq_table = gdbarch_tdep (current_gdbarch)->prologue;
@@ -734,16 +743,15 @@ m68hc11_guess_from_prologue (CORE_ADDR pc, CORE_ADDR current_pc, CORE_ADDR fp,
we find an instruction which is not supposed to appear in the
prologue (as generated by gcc 2.95, 2.96).
*/
- pc = first_pc;
func_end = pc + 128;
found_frame_point = 0;
- *frame_offset = 0;
- save_addr = fp + STACK_CORRECTION;
+ info->size = 0;
+ save_addr = 0;
while (!done && pc + 2 < func_end)
{
struct insn_sequence *seq;
CORE_ADDR val;
-
+
seq = m68hc11_analyze_instruction (seq_table, pc, &val);
if (seq == 0)
break;
@@ -764,8 +772,7 @@ m68hc11_guess_from_prologue (CORE_ADDR pc, CORE_ADDR current_pc, CORE_ADDR fp,
break;
save_addr -= 2;
- if (pushed_regs)
- pushed_regs[saved_reg] = save_addr;
+ info->saved_regs[saved_reg].addr = save_addr;
}
else
{
@@ -775,7 +782,7 @@ m68hc11_guess_from_prologue (CORE_ADDR pc, CORE_ADDR current_pc, CORE_ADDR fp,
else if (seq->type == P_SET_FRAME)
{
found_frame_point = 1;
- *frame_offset = size;
+ info->size = size;
}
else if (seq->type == P_LOCAL_1)
{
@@ -794,7 +801,11 @@ m68hc11_guess_from_prologue (CORE_ADDR pc, CORE_ADDR current_pc, CORE_ADDR fp,
size -= val;
}
}
- *first_line = pc;
+ if (found_frame_point == 0)
+ info->sp_offset = size;
+ else
+ info->sp_offset = -1;
+ return pc;
}
static CORE_ADDR
@@ -802,7 +813,7 @@ m68hc11_skip_prologue (CORE_ADDR pc)
{
CORE_ADDR func_addr, func_end;
struct symtab_and_line sal;
- int frame_offset;
+ struct m68hc11_unwind_cache tmp_cache = { 0 };
/* If we have line debugging information, then the end of the
prologue should be the first assembly instruction of the
@@ -814,7 +825,7 @@ m68hc11_skip_prologue (CORE_ADDR pc)
return sal.end;
}
- m68hc11_guess_from_prologue (pc, pc, 0, &pc, &frame_offset, 0);
+ pc = m68hc11_scan_prologue (pc, (CORE_ADDR) -1, &tmp_cache);
return pc;
}
@@ -846,7 +857,7 @@ m68hc11_frame_chain (struct frame_info *frame)
addr = read_memory_unsigned_integer (addr, 2) & 0x0FFFF;
return addr;
}
-
+#if 0
/* Put here the code to store, into a struct frame_saved_regs, the
addresses of the saved registers of frame described by FRAME_INFO.
This includes special registers such as pc and fp saved in special
@@ -928,6 +939,261 @@ m68hc11_init_extra_frame_info (int fromleaf, struct frame_info *fi)
get_frame_extra_info (fi)->return_pc = addr;
}
}
+#endif
+
+static CORE_ADDR
+m68hc11_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST pc;
+
+ frame_unwind_unsigned_register (next_frame, gdbarch_pc_regnum (gdbarch),
+ &pc);
+ return pc;
+}
+
+/* Put here the code to store, into fi->saved_regs, the addresses of
+ the saved registers of frame described by FRAME_INFO. This
+ includes special registers such as pc and fp saved in special ways
+ in the stack frame. sp is even more special: the address we return
+ for it IS the sp for the next frame. */
+
+struct m68hc11_unwind_cache *
+m68hc11_frame_unwind_cache (struct frame_info *next_frame,
+ void **this_prologue_cache)
+{
+ ULONGEST prev_sp;
+ ULONGEST this_base;
+ struct m68hc11_unwind_cache *info;
+ CORE_ADDR current_pc;
+ int i;
+
+ if ((*this_prologue_cache))
+ return (*this_prologue_cache);
+
+ info = FRAME_OBSTACK_ZALLOC (struct m68hc11_unwind_cache);
+ (*this_prologue_cache) = info;
+ info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+ info->pc = frame_func_unwind (next_frame);
+
+ info->size = 0;
+ info->return_kind = m68hc11_get_return_insn (info->pc);
+
+ /* The SP was moved to the FP. This indicates that a new frame
+ was created. Get THIS frame's FP value by unwinding it from
+ the next frame. */
+ frame_unwind_unsigned_register (next_frame, SOFT_FP_REGNUM, &this_base);
+ if (this_base == 0)
+ {
+ info->base = 0;
+ return info;
+ }
+
+ current_pc = frame_pc_unwind (next_frame);
+ if (info->pc != 0)
+ m68hc11_scan_prologue (info->pc, current_pc, info);
+
+ info->saved_regs[HARD_PC_REGNUM].addr = info->size;
+
+ if (info->sp_offset != (CORE_ADDR) -1)
+ {
+ info->saved_regs[HARD_PC_REGNUM].addr = info->sp_offset;
+ frame_unwind_unsigned_register (next_frame, HARD_SP_REGNUM, &this_base);
+ prev_sp = this_base + info->sp_offset + 2;
+ this_base += STACK_CORRECTION;
+ }
+ else
+ {
+ /* The FP points at the last saved register. Adjust the FP back
+ to before the first saved register giving the SP. */
+ prev_sp = this_base + info->size + 2;
+
+ this_base += STACK_CORRECTION;
+ if (soft_regs[SOFT_FP_REGNUM].name)
+ info->saved_regs[SOFT_FP_REGNUM].addr = info->size - 2;
+ }
+
+ if (info->return_kind == RETURN_RTC)
+ {
+ prev_sp += 1;
+ info->saved_regs[HARD_PAGE_REGNUM].addr = info->size;
+ info->saved_regs[HARD_PC_REGNUM].addr = info->size + 1;
+ }
+ else if (info->return_kind == RETURN_RTI)
+ {
+ prev_sp += 7;
+ info->saved_regs[HARD_CCR_REGNUM].addr = info->size;
+ info->saved_regs[HARD_D_REGNUM].addr = info->size + 1;
+ info->saved_regs[HARD_X_REGNUM].addr = info->size + 3;
+ info->saved_regs[HARD_Y_REGNUM].addr = info->size + 5;
+ info->saved_regs[HARD_PC_REGNUM].addr = info->size + 7;
+ }
+
+ /* Add 1 here to adjust for the post-decrement nature of the push
+ instruction.*/
+ info->prev_sp = prev_sp;
+
+ info->base = this_base;
+
+ /* Adjust all the saved registers so that they contain addresses and not
+ offsets. */
+ for (i = 0; i < NUM_REGS + NUM_PSEUDO_REGS - 1; i++)
+ if (trad_frame_addr_p (info->saved_regs, i))
+ {
+ info->saved_regs[i].addr += this_base;
+ }
+
+ /* The previous frame's SP needed to be computed. Save the computed
+ value. */
+ trad_frame_set_value (info->saved_regs, HARD_SP_REGNUM, info->prev_sp);
+
+ return info;
+}
+
+/* Given a GDB frame, determine the address of the calling function's
+ frame. This will be used to create a new GDB frame struct. */
+
+static void
+m68hc11_frame_this_id (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ struct frame_id *this_id)
+{
+ struct m68hc11_unwind_cache *info
+ = m68hc11_frame_unwind_cache (next_frame, this_prologue_cache);
+ CORE_ADDR base;
+ CORE_ADDR func;
+ struct frame_id id;
+
+ /* The FUNC is easy. */
+ func = frame_func_unwind (next_frame);
+
+ /* This is meant to halt the backtrace at "_start". Make sure we
+ don't halt it at a generic dummy frame. */
+ if (inside_entry_file (func))
+ return;
+
+ /* Hopefully the prologue analysis either correctly determined the
+ frame's base (which is the SP from the previous frame), or set
+ that base to "NULL". */
+ base = info->prev_sp;
+ if (base == 0)
+ return;
+
+ id = frame_id_build (base, func);
+#if 0
+ /* Check that we're not going round in circles with the same frame
+ ID (but avoid applying the test to sentinel frames which do go
+ round in circles). Can't use frame_id_eq() as that doesn't yet
+ compare the frame's PC value. */
+ if (frame_relative_level (next_frame) >= 0
+ && get_frame_type (next_frame) != DUMMY_FRAME
+ && frame_id_eq (get_frame_id (next_frame), id))
+ return;
+#endif
+ (*this_id) = id;
+}
+
+static void
+m68hc11_frame_prev_register (struct frame_info *next_frame,
+ void **this_prologue_cache,
+ int regnum, int *optimizedp,
+ enum lval_type *lvalp, CORE_ADDR *addrp,
+ int *realnump, void *bufferp)
+{
+ struct m68hc11_unwind_cache *info
+ = m68hc11_frame_unwind_cache (next_frame, this_prologue_cache);
+
+ trad_frame_prev_register (next_frame, info->saved_regs, regnum,
+ optimizedp, lvalp, addrp, realnump, bufferp);
+
+ if (regnum == HARD_PC_REGNUM)
+ {
+ /* Take into account the 68HC12 specific call (PC + page). */
+ if (info->return_kind == RETURN_RTC
+ && *addrp >= 0x08000 && *addrp < 0x0c000
+ && USE_PAGE_REGISTER)
+ {
+ int page_optimized;
+
+ CORE_ADDR page;
+
+ trad_frame_prev_register (next_frame, info->saved_regs,
+ HARD_PAGE_REGNUM, &page_optimized,
+ 0, &page, 0, 0);
+ *addrp -= 0x08000;
+ *addrp += ((page & 0x0ff) << 14);
+ *addrp += 0x1000000;
+ }
+ }
+}
+
+static const struct frame_unwind m68hc11_frame_unwind = {
+ NORMAL_FRAME,
+ m68hc11_frame_this_id,
+ m68hc11_frame_prev_register
+};
+
+const struct frame_unwind *
+m68hc11_frame_p (CORE_ADDR pc)
+{
+ return &m68hc11_frame_unwind;
+}
+
+static CORE_ADDR
+m68hc11_frame_base_address (struct frame_info *next_frame, void **this_cache)
+{
+ struct m68hc11_unwind_cache *info
+ = m68hc11_frame_unwind_cache (next_frame, this_cache);
+
+ return info->base;
+}
+
+static CORE_ADDR
+m68hc11_frame_args_address (struct frame_info *next_frame, void **this_cache)
+{
+ CORE_ADDR addr;
+ struct m68hc11_unwind_cache *info
+ = m68hc11_frame_unwind_cache (next_frame, this_cache);
+
+ addr = info->base + info->size;
+ if (info->return_kind == RETURN_RTC)
+ addr += 1;
+ else if (info->return_kind == RETURN_RTI)
+ addr += 7;
+
+ return addr;
+}
+
+static const struct frame_base m68hc11_frame_base = {
+ &m68hc11_frame_unwind,
+ m68hc11_frame_base_address,
+ m68hc11_frame_base_address,
+ m68hc11_frame_args_address
+};
+
+static CORE_ADDR
+m68hc11_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST sp;
+ frame_unwind_unsigned_register (next_frame, HARD_SP_REGNUM, &sp);
+ return sp;
+}
+
+/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that
+ dummy frame. The frame ID's base needs to match the TOS value
+ saved by save_dummy_frame_tos(), and the PC match the dummy frame's
+ breakpoint. */
+
+static struct frame_id
+m68hc11_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+ ULONGEST tos;
+ CORE_ADDR pc = frame_pc_unwind (next_frame);
+
+ frame_unwind_unsigned_register (next_frame, SOFT_FP_REGNUM, &tos);
+ tos += 2;
+ return frame_id_build (tos, pc);
+}
/* Get and print the register from the given frame. */
@@ -1410,10 +1676,6 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
gdbarch = gdbarch_alloc (&info, tdep);
tdep->elf_flags = elf_flags;
- /* NOTE: cagney/2002-12-06: This can be deleted when this arch is
- ready to unwind the PC first (see frame.c:get_prev_frame()). */
- set_gdbarch_deprecated_init_frame_pc (gdbarch, init_frame_pc_default);
-
switch (info.bfd_arch_info->arch)
{
case bfd_arch_m68hc11:
@@ -1461,16 +1723,16 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
/* Characters are unsigned. */
set_gdbarch_char_signed (gdbarch, 0);
+ set_gdbarch_unwind_pc (gdbarch, m68hc11_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, m68hc11_unwind_sp);
+
/* Set register info. */
set_gdbarch_fp0_regnum (gdbarch, -1);
- set_gdbarch_deprecated_frame_init_saved_regs (gdbarch, m68hc11_frame_init_saved_regs);
set_gdbarch_frame_args_skip (gdbarch, 0);
set_gdbarch_write_pc (gdbarch, generic_target_write_pc);
- set_gdbarch_deprecated_dummy_write_sp (gdbarch, deprecated_write_sp);
set_gdbarch_sp_regnum (gdbarch, HARD_SP_REGNUM);
- set_gdbarch_deprecated_fp_regnum (gdbarch, SOFT_FP_REGNUM);
set_gdbarch_register_name (gdbarch, m68hc11_register_name);
set_gdbarch_register_type (gdbarch, m68hc11_register_type);
set_gdbarch_pseudo_register_read (gdbarch, m68hc11_pseudo_register_read);
@@ -1478,27 +1740,15 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
set_gdbarch_push_dummy_call (gdbarch, m68hc11_push_dummy_call);
- set_gdbarch_deprecated_get_saved_register (gdbarch, deprecated_generic_get_saved_register);
set_gdbarch_extract_return_value (gdbarch, m68hc11_extract_return_value);
set_gdbarch_return_value_on_stack (gdbarch, m68hc11_return_value_on_stack);
- set_gdbarch_deprecated_store_struct_return (gdbarch, m68hc11_store_struct_return);
set_gdbarch_store_return_value (gdbarch, m68hc11_store_return_value);
set_gdbarch_extract_struct_value_address (gdbarch, m68hc11_extract_struct_value_address);
- set_gdbarch_deprecated_frame_chain (gdbarch, m68hc11_frame_chain);
- set_gdbarch_deprecated_frame_saved_pc (gdbarch, m68hc11_frame_saved_pc);
- set_gdbarch_deprecated_frame_args_address (gdbarch, m68hc11_frame_args_address);
- set_gdbarch_deprecated_saved_pc_after_call (gdbarch, m68hc11_saved_pc_after_call);
-
- set_gdbarch_deprecated_get_saved_register (gdbarch, deprecated_generic_get_saved_register);
-
- set_gdbarch_deprecated_store_struct_return (gdbarch, m68hc11_store_struct_return);
set_gdbarch_store_return_value (gdbarch, m68hc11_store_return_value);
set_gdbarch_extract_struct_value_address (gdbarch, m68hc11_extract_struct_value_address);
set_gdbarch_use_struct_convention (gdbarch, m68hc11_use_struct_convention);
- set_gdbarch_deprecated_init_extra_frame_info (gdbarch, m68hc11_init_extra_frame_info);
- set_gdbarch_deprecated_pop_frame (gdbarch, m68hc11_pop_frame);
set_gdbarch_skip_prologue (gdbarch, m68hc11_skip_prologue);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_decr_pc_after_break (gdbarch, 0);
@@ -1511,6 +1761,21 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
set_gdbarch_register_reggroup_p (gdbarch, m68hc11_register_reggroup_p);
set_gdbarch_print_registers_info (gdbarch, m68hc11_print_registers_info);
+ /* Hook in the DWARF CFI frame unwinder. */
+ frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
+ set_gdbarch_dwarf2_build_frame_info (gdbarch, dwarf2_build_frame_info);
+
+ frame_unwind_append_predicate (gdbarch, m68hc11_frame_p);
+ frame_base_set_default (gdbarch, &m68hc11_frame_base);
+
+ /* Methods for saving / extracting a dummy frame's ID. The ID's
+ stack address must match the SP value returned by
+ PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos. */
+ set_gdbarch_unwind_dummy_id (gdbarch, m68hc11_unwind_dummy_id);
+
+ /* Return the unwound PC value. */
+ set_gdbarch_unwind_pc (gdbarch, m68hc11_unwind_pc);
+
/* Minsymbol frobbing. */
set_gdbarch_elf_make_msymbol_special (gdbarch,
m68hc11_elf_make_msymbol_special);