This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[wip/rfc] signal trampoline frames


Hello,

This is a work-in-progress diff which I'm about to commit to my cagney_tramp-20040309-branch (it's actually created by taking a diff against the branchpoint). I've omitted the trad-frame patches as they are not immediatly relevant.

First this modifies the frame-unwind so that:

-- A new static (in the OO sense) method called sniffer is added to "struct frame_unwind" and that is called to sniff out the frame. That sniffer (unlike the other) returns the initial value of the frame cache.

-- A new data field (unwind_data) is added to "struct frame_unwind" so that more generic unwinders can be parameterised with [const] data

-- A mechanism for registering the "struct frame_unwind" (complete with sniffer) is added (the old mechanism remains).

-- All the unwind methods are parameterized with "struct frame_unwind"

With this in place a new object "tramp_frame", that can handle signal trampolines, is added. A tramp_frame both describes the instruction sequence used by the trampoline and provides a method for populating a trad_frame cache describing the registers saved by that signal trampoline. Note that this tramp_frame:

-- Matches the entire instruction sequence and hence determines the address of the trampolines first instruction (aka the FUNC addr). At present most trampolines (i.e., all the ones I looked at) incorrectly call frame_func_unwind (for these frames that function will return zero which the frame ID code treats as a wild card).

-- Does the unwind in two stages: the sniffer matches the trampoline and computes FUNC; if needed (i.e., called), this_id and/or prev_register, populate the trad_frame cache (using that computed FUNC).

I've included a sample tramp-frame. I think it is far simpler, and far more robust (I've got evil testcases pending), and more correct, than the old way of doing this.

comments?

My next tasks are to review the changes to frame-unwind and trad-frame (seeing if they can be trimmed back further) and then push them out to the mainline.

Andrew
Index: frame-unwind.h
===================================================================
RCS file: /cvs/src/src/gdb/frame-unwind.h,v
retrieving revision 1.9
diff -u -r1.9 frame-unwind.h
--- frame-unwind.h	16 Jul 2003 22:29:13 -0000	1.9
+++ frame-unwind.h	16 Mar 2004 15:12:15 -0000
@@ -1,6 +1,6 @@
 /* Definitions for a frame unwinder, for GDB, the GNU debugger.
 
-   Copyright 2003 Free Software Foundation, Inc.
+   Copyright 2003, 2004 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -22,6 +22,7 @@
 #if !defined (FRAME_UNWIND_H)
 #define FRAME_UNWIND_H 1
 
+struct frame_data;
 struct frame_info;
 struct frame_id;
 struct frame_unwind;
@@ -30,6 +31,13 @@
 
 #include "frame.h"		/* For enum frame_type.  */
 
+/* Given the NEXT frame, take a wiff of THIS frame's registers (namely
+   the PC and attributes) and if it is the applicable unwinder, return
+   an unwind cache (allocated using frame_obstack_zalloc).  */
+
+typedef void *(frame_sniffer_ftype) (const struct frame_unwind *self,
+				     struct frame_info *next_frame);
+
 /* The following unwind functions assume a chain of frames forming the
    sequence: (outer) prev <-> this <-> next (inner).  All the
    functions are called with called with the next frame's `struct
@@ -65,7 +73,8 @@
    with the other unwind methods.  Memory for that cache should be
    allocated using frame_obstack_zalloc().  */
 
-typedef void (frame_this_id_ftype) (struct frame_info *next_frame,
+typedef void (frame_this_id_ftype) (const struct frame_unwind *self,
+				    struct frame_info *next_frame,
 				    void **this_prologue_cache,
 				    struct frame_id *this_id);
 
@@ -101,7 +110,8 @@
    with the other unwind methods.  Memory for that cache should be
    allocated using frame_obstack_zalloc().  */
 
-typedef void (frame_prev_register_ftype) (struct frame_info *next_frame,
+typedef void (frame_prev_register_ftype) (const struct frame_unwind *self,
+					  struct frame_info *next_frame,
 					  void **this_prologue_cache,
 					  int prev_regnum,
 					  int *optimized,
@@ -118,8 +128,16 @@
      here?  */
   frame_this_id_ftype *this_id;
   frame_prev_register_ftype *prev_register;
+  const struct frame_data *unwind_data;
+  frame_sniffer_ftype *sniffer;
 };
 
+/* Register a frame unwinder, appending it to the end of the search
+   list.  */
+extern void frame_unwind_append (struct gdbarch *gdbarch,
+				 const struct frame_unwind *unwinder);
+
+
 /* Given the NEXT frame, take a wiff of THIS frame's registers (namely
    the PC and attributes) and if it is the applicable unwinder return
    the unwind methods, or NULL if it is not.  */
@@ -134,8 +152,9 @@
 					 frame_unwind_sniffer_ftype *sniffer);
 
 /* Iterate through the next frame's sniffers until one returns with an
-   unwinder implementation.  */
+   unwinder implementation.  Possibly initialize THIS_CACHE.  */
 
-extern const struct frame_unwind *frame_unwind_find_by_frame (struct frame_info *next_frame);
+extern const struct frame_unwind *frame_unwind_find_by_frame (struct frame_info *next_frame,
+							      void **this_cache);
 
 #endif
Index: tramp-frame.h
===================================================================
RCS file: tramp-frame.h
diff -N tramp-frame.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tramp-frame.h	11 Mar 2004 21:59:53 -0000	1.1.2.1
@@ -0,0 +1,64 @@
+/* Signal trampoline unwinder, for GDB the GNU Debugger.
+
+   Copyright 2004 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#ifndef TRAMP_FRAME_H
+#define TRAMP_FRAME_H
+
+struct trad_frame;
+struct frame_info;
+struct trad_frame_cache;
+
+/* A trampline consists of a small sequence of instructions placed at
+   an unspecified location in the inferior's address space.  The only
+   identifying attribute of the trampoline's address is that it does
+   not fall inside an object file's section.
+
+   The only way to identify a trampoline is to perform a brute force
+   examination of the instructions at and around the PC.
+
+   This module provides a convient interface for performing that
+   operation.  */
+
+/* A trampoline descriptor.  */
+
+struct tramp_frame
+{
+  /* The trampoline's entire instruction sequence.  Search for this in
+     the inferior at or around the frame's PC.  It is assumed that the
+     PC is INSN_SIZE aligned, and that each element of TRAMP containts
+     one INSN_SIZE instruction.  It is also assumed that TRAMP[0]
+     contains the first instruction of the trampoline and hence the
+     address of the instruction matching TRAMP[0] is the trampolines
+     "func" address.  */
+  int insn_size;
+  ULONGEST insn[8];
+  /* Initialize a trad-frame cache corresponding to the tramp-frame.
+     FUNC is the address of the instruction TRAMP[0] in memory.  */
+  void (*init) (const struct tramp_frame *self,
+		struct frame_info *next_frame,
+		struct trad_frame_cache *this_cache,
+		CORE_ADDR func);
+};
+
+void tramp_frame_append (struct gdbarch *gdbarch,
+			 const struct tramp_frame *tramp);
+
+#endif
Index: ppcnbsd-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppcnbsd-tdep.c,v
retrieving revision 1.11
retrieving revision 1.11.10.1
diff -u -r1.11 -r1.11.10.1
--- ppcnbsd-tdep.c	10 Nov 2003 22:47:28 -0000	1.11
+++ ppcnbsd-tdep.c	9 Mar 2004 22:59:00 -0000	1.11.10.1
@@ -30,6 +30,8 @@
 #include "ppc-tdep.h"
 #include "ppcnbsd-tdep.h"
 #include "nbsd-tdep.h"
+#include "tramp-frame.h"
+#include "trad-frame.h"
 
 #include "solib-svr4.h"
 
@@ -228,6 +230,57 @@
 }
 
 static void
+ppcnbsd_sigtramp_cache_init (const struct tramp_frame *self,
+			     struct frame_info *next_frame,
+			     struct trad_frame_cache *this_cache,
+			     CORE_ADDR func)
+{
+  CORE_ADDR offset;
+  int i;
+  struct gdbarch *gdbarch = get_frame_arch (next_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  this_cache->this_base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+  offset = this_cache->this_base + 0x18 + 2 * tdep->wordsize;
+  for (i = 0; i < 32; i++)
+    {
+      int regnum = i + tdep->ppc_gp0_regnum;
+      this_cache->prev_regs[regnum].addr = offset;
+      offset += tdep->wordsize;
+    }
+  this_cache->prev_regs[tdep->ppc_lr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  this_cache->prev_regs[tdep->ppc_cr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  this_cache->prev_regs[tdep->ppc_xer_regnum].addr = offset;
+  offset += tdep->wordsize;
+  this_cache->prev_regs[tdep->ppc_ctr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  this_cache->prev_regs[PC_REGNUM].addr = offset; /* SRR0? */
+  offset += tdep->wordsize;
+
+  /* Construct the frame ID using the function start.  */
+  this_cache->this_id = frame_id_build (this_cache->this_base, func);
+}
+
+/* Given the NEXTE frame, examine the instructions at and around this
+   frame's resume address (aka PC) to see of they look like a signal
+   trampoline.  Return the address of the trampolines first
+   instruction, or zero if it isn't a signal trampoline.  */
+
+static const struct tramp_frame ppcnbsd_sigtramp = {
+  4, /* insn size */
+  { /* insn */
+    0x38610018, /* addi r3,r1,24 */
+    0x38000127, /* li r0,295 */
+    0x44000002, /* sc */
+    0x38000001, /* li r0,1 */
+    0x44000002, /* sc */
+  },
+  ppcnbsd_sigtramp_cache_init
+};
+
+static void
 ppcnbsd_init_abi (struct gdbarch_info info,
                   struct gdbarch *gdbarch)
 {
@@ -237,6 +290,7 @@
   set_gdbarch_return_value (gdbarch, ppcnbsd_return_value);
   set_solib_svr4_fetch_link_map_offsets (gdbarch,
                                 nbsd_ilp32_solib_svr4_fetch_link_map_offsets);
+  tramp_frame_append (gdbarch, &ppcnbsd_sigtramp);
 }
 
 void
Index: tramp-frame.c
===================================================================
RCS file: tramp-frame.c
diff -N tramp-frame.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tramp-frame.c	16 Mar 2004 15:12:23 -0000
@@ -0,0 +1,161 @@
+/* Signal trampoline unwinder, for GDB the GNU Debugger.
+
+   Copyright 2004 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"
+#include "tramp-frame.h"
+#include "frame-unwind.h"
+#include "gdbcore.h"
+#include "symtab.h"
+#include "objfiles.h"
+#include "target.h"
+#include "trad-frame.h"
+
+struct frame_data
+{
+  const struct tramp_frame *tramp_frame;
+};
+
+struct tramp_frame_cache
+{
+  CORE_ADDR func;
+  struct trad_frame_cache *trad_cache;
+};
+
+static struct trad_frame_cache *
+tramp_frame_cache (const struct frame_unwind *self,
+		   struct frame_info *next_frame,
+		   void **this_cache)
+{
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  struct tramp_frame_cache *tramp_cache = (*this_cache);
+  if (tramp_cache->trad_cache == NULL)
+    {
+      tramp_cache->trad_cache = trad_frame_cache_zalloc (next_frame);
+      self->unwind_data->tramp_frame->init (self->unwind_data->tramp_frame,
+					    next_frame,
+					    tramp_cache->trad_cache,
+					    tramp_cache->func);
+    }
+  return tramp_cache->trad_cache;
+}
+
+static void
+tramp_frame_this_id (const struct frame_unwind *self,
+		     struct frame_info *next_frame,
+		     void **this_cache,
+		     struct frame_id *this_id)
+{
+  struct trad_frame_cache *trad_cache
+    = tramp_frame_cache (self, next_frame, this_cache);
+  trad_frame_this_id (trad_cache, next_frame, this_id);
+}
+
+static void
+tramp_frame_prev_register (const struct frame_unwind *self,
+			   struct frame_info *next_frame,
+			   void **this_cache,
+			   int prev_regnum,
+			   int *optimizedp,
+			   enum lval_type * lvalp,
+			   CORE_ADDR *addrp,
+			   int *realnump, void *valuep)
+{
+  struct trad_frame_cache *trad_cache
+    = tramp_frame_cache (self, next_frame, this_cache);
+  trad_frame_prev_register (trad_cache, next_frame, prev_regnum, optimizedp,
+			    lvalp, addrp, realnump, valuep);
+}
+
+static CORE_ADDR
+tramp_frame_start (CORE_ADDR pc, const struct tramp_frame *tramp)
+{
+  int ti;
+  /* Search through the trampoline for one that matches the
+     instruction sequence around PC.  */
+  for (ti = 0; tramp->insn[ti] != 0; ti++)
+    {
+      CORE_ADDR func = pc - tramp->insn_size * ti;
+      int i;
+      for (i = 0; 1; i++)
+	{
+	  bfd_byte buf[sizeof (LONGEST)];
+	  CORE_ADDR insn;
+	  if (tramp->insn[i] == 0)
+	    return func;
+	  if (target_read_memory (func + i * tramp->insn_size, buf,
+				  tramp->insn_size) != 0)
+	    break;
+	  insn = extract_unsigned_integer (buf, tramp->insn_size);
+	  if (tramp->insn[i] != insn)
+	    break;
+	}
+    }
+  /* Trampoline doesn't match.  */
+  return 0;
+}
+
+static void *
+tramp_frame_sniffer (const struct frame_unwind *self,
+		     struct frame_info *next_frame)
+{
+  const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  CORE_ADDR func;
+  char *name;
+  struct tramp_frame_cache *tramp_cache;
+
+  /* If the function has a valid symbol name, it isn't a
+     trampoline.  */
+  find_pc_partial_function (pc, &name, NULL, NULL);
+  if (name != NULL)
+    return NULL;
+  /* If the function lives in a valid section (even without a starting
+     point) it isn't a trampoline.  */
+  if (find_pc_section (pc) != NULL)
+    return NULL;
+  /* Finally, check that the trampoline matches at PC.  */
+  func = tramp_frame_start (pc, tramp);
+  if (func == 0)
+    return NULL;
+  tramp_cache = FRAME_OBSTACK_ZALLOC (struct tramp_frame_cache);
+  tramp_cache->func = func;
+  return tramp_cache;
+}
+
+void
+tramp_frame_append (struct gdbarch *gdbarch,
+		    const struct tramp_frame *tramp_frame)
+{
+  struct frame_data *data;
+  struct frame_unwind *unwinder;
+
+  data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data);
+  unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind);
+
+  data->tramp_frame = tramp_frame;
+  unwinder->type = SIGTRAMP_FRAME;
+  unwinder->unwind_data = data;
+  unwinder->sniffer = tramp_frame_sniffer;
+  unwinder->this_id = tramp_frame_this_id;
+  unwinder->prev_register = tramp_frame_prev_register;
+
+  frame_unwind_append (gdbarch, unwinder);
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]