Epilogue unwinder for PowerPC.
authorWei-cheng Wang <cole945@gmail.com>
Sat, 17 Jan 2015 06:29:16 +0000 (14:29 +0800)
committerCole Wang <cole945@gmail.com>
Sat, 17 Jan 2015 11:48:22 +0000 (19:48 +0800)
gdb/ChangeLog
gdb/rs6000-tdep.c

index b95abdcdecc22f9ae5d9ccb5bad1360a97129fc2..982e9aed9b404613d71417a5abff934180b0891b 100644 (file)
@@ -1,3 +1,14 @@
+2015-01-17  Wei-cheng Wang  <cole945@gmail.com>
+
+       * rs6000-tdep.c (rs6000_in_function_epilogue_p): Rename to
+       rs6000_in_function_epilogue_frame_p and add an argument
+       for frame_info.
+       (rs6000_epilogue_frame_cache, rs6000_epilogue_frame_this_id,
+       rs6000_epilogue_frame_prev_register, rs6000_epilogue_frame_sniffer):
+       New functions.
+       (rs6000_epilogue_frame_unwind): New.
+       (rs6000_gdbarch_init): Append epilogue unwinder.
+
 2015-01-16  Sergio Durigan Junior  <sergiodj@redhat.com>
 
        * nat/linux-personality.c: Replace "#ifndef
index 455318afb7f4c5608edbf3a2d59b4001b5655d0f..e6e2e60a81dc83909fb82157f789ecb81fdf8d4a 100644 (file)
@@ -872,14 +872,14 @@ insn_changes_sp_or_jumps (unsigned long insn)
            limit for the size of an epilogue.  */
 
 static int
-rs6000_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+rs6000_in_function_epilogue_frame_p (struct frame_info *curfrm,
+                                    struct gdbarch *gdbarch, CORE_ADDR pc)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   bfd_byte insn_buf[PPC_INSN_SIZE];
   CORE_ADDR scan_pc, func_start, func_end, epilogue_start, epilogue_end;
   unsigned long insn;
-  struct frame_info *curfrm;
 
   /* Find the search limits based on function boundaries and hard limit.  */
 
@@ -892,8 +892,6 @@ rs6000_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
   epilogue_end = pc + PPC_MAX_EPILOGUE_INSTRUCTIONS * PPC_INSN_SIZE;
   if (epilogue_end > func_end) epilogue_end = func_end;
 
-  curfrm = get_current_frame ();
-
   /* Scan forward until next 'blr'.  */
 
   for (scan_pc = pc; scan_pc < epilogue_end; scan_pc += PPC_INSN_SIZE)
@@ -934,6 +932,15 @@ rs6000_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
   return 0;
 }
 
+/* Implementation of gdbarch_in_function_epilogue_p.  */
+
+static int
+rs6000_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  return rs6000_in_function_epilogue_frame_p (get_current_frame (),
+                                             gdbarch, pc);
+}
+
 /* Get the ith function argument for the current function.  */
 static CORE_ADDR
 rs6000_fetch_pointer_argument (struct frame_info *frame, int argi, 
@@ -3339,6 +3346,89 @@ static const struct frame_unwind rs6000_frame_unwind =
   NULL,
   default_frame_sniffer
 };
+
+static struct rs6000_frame_cache *
+rs6000_epilogue_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  volatile struct gdb_exception ex;
+  struct rs6000_frame_cache *cache;
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  CORE_ADDR sp;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct rs6000_frame_cache);
+  (*this_cache) = cache;
+  cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* At this point the stack looks as if we just entered the
+        function, and the return address is stored in LR.  */
+      CORE_ADDR sp, lr;
+
+      sp = get_frame_register_unsigned (this_frame, gdbarch_sp_regnum (gdbarch));
+      lr = get_frame_register_unsigned (this_frame, tdep->ppc_lr_regnum);
+
+      cache->base = sp;
+      cache->initial_sp = sp;
+
+      trad_frame_set_value (cache->saved_regs,
+                           gdbarch_pc_regnum (gdbarch), lr);
+    }
+  if (ex.reason < 0 && ex.error != NOT_AVAILABLE_ERROR)
+    throw_exception (ex);
+
+  return cache;
+}
+
+static void
+rs6000_epilogue_frame_this_id (struct frame_info *this_frame,
+                              void **this_cache, struct frame_id *this_id)
+{
+  CORE_ADDR pc;
+  struct rs6000_frame_cache *info =
+    rs6000_epilogue_frame_cache (this_frame, this_cache);
+
+  pc = get_frame_func (this_frame);
+  if (info->base == 0)
+    (*this_id) = frame_id_build_unavailable_stack (pc);
+  else
+    (*this_id) = frame_id_build (info->base, pc);
+}
+
+static struct value *
+rs6000_epilogue_frame_prev_register (struct frame_info *this_frame,
+                                    void **this_cache, int regnum)
+{
+  struct rs6000_frame_cache *info =
+    rs6000_epilogue_frame_cache (this_frame, this_cache);
+  return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
+}
+
+static int
+rs6000_epilogue_frame_sniffer (const struct frame_unwind *self,
+                              struct frame_info *this_frame,
+                              void **this_prologue_cache)
+{
+  if (frame_relative_level (this_frame) == 0)
+    return rs6000_in_function_epilogue_frame_p (this_frame,
+                                               get_frame_arch (this_frame),
+                                               get_frame_pc (this_frame));
+  else
+    return 0;
+}
+
+static const struct frame_unwind rs6000_epilogue_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  rs6000_epilogue_frame_this_id, rs6000_epilogue_frame_prev_register,
+  NULL,
+  rs6000_epilogue_frame_sniffer
+};
 \f
 
 static CORE_ADDR
@@ -4154,6 +4244,7 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     case GDB_OSABI_NETBSD_ELF:
     case GDB_OSABI_UNKNOWN:
       set_gdbarch_unwind_pc (gdbarch, rs6000_unwind_pc);
+      frame_unwind_append_unwinder (gdbarch, &rs6000_epilogue_frame_unwind);
       frame_unwind_append_unwinder (gdbarch, &rs6000_frame_unwind);
       set_gdbarch_dummy_id (gdbarch, rs6000_dummy_id);
       frame_base_append_sniffer (gdbarch, rs6000_frame_base_sniffer);
@@ -4162,6 +4253,7 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       set_gdbarch_believe_pcc_promotion (gdbarch, 1);
 
       set_gdbarch_unwind_pc (gdbarch, rs6000_unwind_pc);
+      frame_unwind_append_unwinder (gdbarch, &rs6000_epilogue_frame_unwind);
       frame_unwind_append_unwinder (gdbarch, &rs6000_frame_unwind);
       set_gdbarch_dummy_id (gdbarch, rs6000_dummy_id);
       frame_base_append_sniffer (gdbarch, rs6000_frame_base_sniffer);