This is the mail archive of the gdb@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]

get_frame_func() VS get_frame_id().code


Hello,

The current get_frame_func() is implemented as roughly:

fi->prev_func.addr = get_pc_function_start (addr_in_block);

Unfortunatly this isn't valid for a signal trampoline (or at least the evil ones that consist of random bytes in a random memory location). For such trampolines, get_pc_function_start [rightly] fails and "func" ends up as zero -- not good -- a properly constructed frame ID requires non-zero code and stack addresses.

Fortunatly, with a bit of extra instruction pattern matching, it is possible to identify the first instruction of a signal trampoline and hence correctly compute the trampolines "func" address. Similarly, more normal frames can determine the function start using the symbol table's get_pc_function_start.

Consequently, I think there should be mechanism for obtaining both the symbol table and frame's idea of a function's start address. This would mean introducing:

- get_frame_func_by_symtab
Returns the function start according to the symbol table. Much of the existing code (especially unwinders) would need to be updated to use this.


- get_frame_func_by_id
Returns the function start based on the frame ID. With the first change made, this could even be called get_frame_func.


I've attached a proof-of-concept and as such the patch points to a number of additional changes (the most obvious being the need for a "tramp-frame" that generalizes the technique).

Andrew
2004-03-03  Andrew Cagney  <cagney@redhat.com>

	* ppcnbsd-tdep.c: Include "objfiles.h", "trad-frame.h", and
	"frame-unwind.h".
	(struct ppcnbsd_sigtramp_cache, ppcnbsd_sigtramp_this_id)
	(ppcnbsd_sigtramp_prev_register, ppcnbsd_sigtramp_cache)
	(ppcnbsd_sigtramp_sniffer, ppcnbsd_sigtramp_unwind)
	(ppcnbsd_sigtramp_start, ppcnbsd_init_abi): Implement a NetBSD/PPC
	signal trampline unwinder, register.

Index: ppcnbsd-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/ppcnbsd-tdep.c,v
retrieving revision 1.11
diff -u -r1.11 ppcnbsd-tdep.c
--- ppcnbsd-tdep.c	10 Nov 2003 22:47:28 -0000	1.11
+++ ppcnbsd-tdep.c	5 Mar 2004 15:39:35 -0000
@@ -26,6 +26,9 @@
 #include "breakpoint.h"
 #include "value.h"
 #include "osabi.h"
+#include "trad-frame.h"
+#include "frame-unwind.h"
+#include "objfiles.h"
 
 #include "ppc-tdep.h"
 #include "ppcnbsd-tdep.h"
@@ -227,6 +230,165 @@
 					     readbuf, writebuf);
 }
 
+/* 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 CORE_ADDR
+ppcnbsd_sigtramp_start (struct frame_info *next_frame)
+{
+  static const long sigtramp[] = {
+    0x38610018, /* addi r3,r1,24 */
+    0x38000127, /* li r0,295 */
+    0x44000002, /* sc */
+    0x38000001, /* li r0,1 */
+    0x44000002, /* sc */
+  };
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  long pcinsn;
+  int pci;
+
+  /* Fetch the instruction at PC and try to find it in the
+     sigtramp.  */
+  pcinsn = read_memory_unsigned_integer (pc, 4);
+  for (pci = 0; pci < ARRAY_SIZE (sigtramp); pci++)
+    {
+      if (sigtramp[pci] == pcinsn)
+	{
+	  /* With the PC's instruction found, check that the remaining
+	     instructions also match.  Because there are two SC's in
+	     the above, this part may need to be executed more than
+	     once.  */
+	  int i;
+	  long insn;
+	  for (i = 0; i < ARRAY_SIZE (sigtramp); i++)
+	    {
+	      if (i == pci) continue;
+	      insn = read_memory_unsigned_integer (pc + 4 * (i - pci), 4);
+	      if (sigtramp[i] != insn)
+		break;
+	    }
+	  /* All matched, return the start of the signal
+	     trampoline.  */
+	  if (i >= ARRAY_SIZE (sigtramp))
+	    return pc - 4 * pci;
+	}
+    }
+  /* Not a sigtramp.  */
+  return 0;
+}
+
+struct ppcnbsd_sigtramp_cache
+{
+  struct frame_id this_id;
+  CORE_ADDR this_base;
+  struct trad_frame_saved_reg *saved_regs;
+};
+
+static struct ppcnbsd_sigtramp_cache *
+ppcnbsd_sigtramp_cache (struct frame_info *next_frame, void **this_cache)
+{
+  CORE_ADDR offset;
+  int i;
+  struct ppcnbsd_sigtramp_cache *cache;
+  struct gdbarch *gdbarch = get_frame_arch (next_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if ((*this_cache) != NULL)
+    return (*this_cache);
+  cache = FRAME_OBSTACK_ZALLOC (struct ppcnbsd_sigtramp_cache);
+  (*this_cache) = cache;
+  cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+
+  cache->this_base = frame_unwind_register_unsigned (next_frame, SP_REGNUM);
+  offset = cache->this_base + 0x18 + 2 * tdep->wordsize;
+  for (i = 0; i < 32; i++)
+    {
+      int regnum = i + tdep->ppc_gp0_regnum;
+      cache->saved_regs[regnum].addr = offset;
+      offset += tdep->wordsize;
+    }
+  cache->saved_regs[tdep->ppc_lr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  cache->saved_regs[tdep->ppc_cr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  cache->saved_regs[tdep->ppc_xer_regnum].addr = offset;
+  offset += tdep->wordsize;
+  cache->saved_regs[tdep->ppc_ctr_regnum].addr = offset;
+  offset += tdep->wordsize;
+  cache->saved_regs[PC_REGNUM].addr = offset; /* SRR0? */
+  offset += tdep->wordsize;
+
+  /* Construct the frame ID using the function start.  */
+  {
+    CORE_ADDR func = frame_func_unwind (next_frame);
+    if (func == 0)
+      cache->this_id = frame_id_build (cache->this_base,
+				       ppcnbsd_sigtramp_start (next_frame));
+    else
+      cache->this_id = frame_id_build (cache->this_base, func);
+  }
+
+  return cache;
+}
+
+static void
+ppcnbsd_sigtramp_this_id (struct frame_info *next_frame, void **this_cache,
+			  struct frame_id *this_id)
+{
+  struct ppcnbsd_sigtramp_cache *info = ppcnbsd_sigtramp_cache (next_frame,
+								this_cache);
+  (*this_id) = info->this_id;
+}
+
+static void
+ppcnbsd_sigtramp_prev_register (struct frame_info *next_frame,
+				void **this_cache,
+				int regnum, int *optimizedp,
+				enum lval_type *lvalp, CORE_ADDR *addrp,
+				int *realnump, void *valuep)
+{
+  struct ppcnbsd_sigtramp_cache *info = ppcnbsd_sigtramp_cache (next_frame,
+								this_cache);
+  trad_frame_prev_register (next_frame, info->saved_regs, regnum,
+			    optimizedp, lvalp, addrp, realnump, valuep);
+}
+
+static const struct frame_unwind ppcnbsd_sigtramp_unwind =
+{
+  SIGTRAMP_FRAME,
+  ppcnbsd_sigtramp_this_id,
+  ppcnbsd_sigtramp_prev_register
+};
+
+static const struct frame_unwind *
+ppcnbsd_sigtramp_sniffer (struct frame_info *next_frame)
+{
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  char *name;
+  find_pc_partial_function (pc, &name, NULL, NULL);
+  if (name != NULL)
+    {
+      /* A modern unwinder (post 1.6.x) that lives in libc.  It can be
+	 identified by its name.  */
+      if (nbsd_pc_in_sigtramp (pc, name))
+	return &ppcnbsd_sigtramp_unwind;
+      else
+	return NULL;
+    }
+  if (find_pc_section (pc) != NULL)
+    /* Old style signal trampolines (which lived on the stack) never
+       had a section, so if one is found, it can't be a sigtramp.  */
+    return NULL;
+  /* Examine instructions around PC to see if they match the sigtramp,
+     if they do ok.  */
+  if (ppcnbsd_sigtramp_start (next_frame) != 0)
+    return &ppcnbsd_sigtramp_unwind;
+  else
+    return NULL;
+}
+
 static void
 ppcnbsd_init_abi (struct gdbarch_info info,
                   struct gdbarch *gdbarch)
@@ -237,6 +399,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);
+  frame_unwind_append_sniffer (gdbarch, ppcnbsd_sigtramp_sniffer);
 }
 
 void

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