2005-06-08 Jeff Johnston <jjohnstn@redhat.com>
authorJeff Johnston <jjohnstn@redhat.com>
Wed, 8 Jun 2005 21:54:23 +0000 (21:54 +0000)
committerJeff Johnston <jjohnstn@redhat.com>
Wed, 8 Jun 2005 21:54:23 +0000 (21:54 +0000)
            David Mosberger <davidm@hpl.hp.com>

        * ia64-tdep.c (KERNEL_START): New macro.
        (ia64_pseudo_register_read): Fix code to use libunwind to properly
        get the rse registers.
        (ia64_frame_this_id): Mark outermost frame with null frame id.
        (ia64_sigtramp_frame_prev_register): Fix up debug output.
        (ia64_access_rse_reg): New accessor function.
        (ia64_access_mem): Add special logic for accessing the
        kernel's unwind table.
        (getunwind_table): Fix for corefiles.
        (get_kernel_table): Fix to handle vDSO.
        (ia64_libunwind_frame_this_id): Check for null frame id and
        don't unwind past pc value of 0.  Fix debugging output.
        (ia64_libunwind_sigtramp_frame_this_id): New function.
        (ia64_libunwind_sigtramp_frame_prev_register): Ditto.
        (ia64_libunwind_sigtramp_frame_sniffer): Ditto.
        (ia64_libunwind_sigtramp_frame_unwind): New unwinder.
        (ia64_unw_rse_accessors): New libunwind accessors.
        (ia64_libunwind_descr): Add ia64_unw_rse_accessors.
        (ia64_gdbarch_init)[HAVE_LIBUNWIND_IA64_H]: Use
        ia64_libunwind_sigtramp_frame_sniffer instead of
        ia64_sigtramp_frame_sniffer.
        * libunwind-frame.c (libunwind_frame_set_descr): Add
        special register accessors.
        (libunwind_frame_cache): Add special logic to check for
        0 pc value.  Check for a signal trampoline frame.
        (libunwind_frame_this_id): Check if libunwind_frame_cache
        returns NULL.
        (libunwind_frame_prev_register): Check for NULL cache.
        (libunwind_frame_base_address): Ditto.
        (libunwind_sigtramp_frame_sniffer): New function.
        (libunwind_get_reg_special): Ditto.
        (libunwind_load): Add unw_is_signal_frame support.
        * libunwind-frame.h (struct libunwind_descr): Add special_accessors
        field.
        (libunwind_sigtramp_frame_sniffer): New prototype.
        (libunwind_get_reg_special): Ditto.

gdb/ChangeLog
gdb/ia64-tdep.c
gdb/libunwind-frame.c
gdb/libunwind-frame.h

index d6f43e2..f948314 100644 (file)
@@ -1,3 +1,43 @@
+2005-06-08  Jeff Johnston  <jjohnstn@redhat.com>
+           David Mosberger <davidm@hpl.hp.com>
+
+       * ia64-tdep.c (KERNEL_START): New macro.
+       (ia64_pseudo_register_read): Fix code to use libunwind to properly
+       get the rse registers.
+       (ia64_frame_this_id): Mark outermost frame with null frame id.
+       (ia64_sigtramp_frame_prev_register): Fix up debug output.
+       (ia64_access_rse_reg): New accessor function.
+       (ia64_access_mem): Add special logic for accessing the
+       kernel's unwind table.
+       (getunwind_table): Fix for corefiles.
+       (get_kernel_table): Fix to handle vDSO.
+       (ia64_libunwind_frame_this_id): Check for null frame id and
+       don't unwind past pc value of 0.  Fix debugging output.
+       (ia64_libunwind_sigtramp_frame_this_id): New function.
+       (ia64_libunwind_sigtramp_frame_prev_register): Ditto.
+       (ia64_libunwind_sigtramp_frame_sniffer): Ditto.
+       (ia64_libunwind_sigtramp_frame_unwind): New unwinder.
+       (ia64_unw_rse_accessors): New libunwind accessors.
+       (ia64_libunwind_descr): Add ia64_unw_rse_accessors.
+       (ia64_gdbarch_init)[HAVE_LIBUNWIND_IA64_H]: Use 
+       ia64_libunwind_sigtramp_frame_sniffer instead of 
+       ia64_sigtramp_frame_sniffer.
+       * libunwind-frame.c (libunwind_frame_set_descr): Add
+       special register accessors.
+       (libunwind_frame_cache): Add special logic to check for
+       0 pc value.  Check for a signal trampoline frame.
+       (libunwind_frame_this_id): Check if libunwind_frame_cache
+       returns NULL.
+       (libunwind_frame_prev_register): Check for NULL cache.
+       (libunwind_frame_base_address): Ditto.
+       (libunwind_sigtramp_frame_sniffer): New function.
+       (libunwind_get_reg_special): Ditto.
+       (libunwind_load): Add unw_is_signal_frame support.
+       * libunwind-frame.h (struct libunwind_descr): Add special_accessors
+       field.
+       (libunwind_sigtramp_frame_sniffer): New prototype.
+       (libunwind_get_reg_special): Ditto.
+       
 2005-06-08  Wu Zhou  <woodzltc@cn.ibm.com>
 
        * expression.h (enum exp_opcode): Fix a comment typo.
index a69343a..ab9a340 100644 (file)
 #include "elf/ia64.h"           /* for PT_IA_64_UNWIND value */
 #include "libunwind-frame.h"
 #include "libunwind-ia64.h"
+
+/* Note: KERNEL_START is supposed to be an address which is not going
+         to ever contain any valid unwind info.  For ia64 linux, the choice
+         of 0xc000000000000000 is fairly safe since that's uncached space.
+         We use KERNEL_START as follows: after obtaining the kernel's
+         unwind table via getunwind(), we project its unwind data into
+         address-range KERNEL_START-(KERNEL_START+ktab_size) and then
+         when ia64_access_mem() sees a memory access to this
+         address-range, we redirect it to ktab instead.
+
+         None of this hackery is needed with a modern kernel/libcs
+         which uses the kernel virtual DSO to provide access to the
+         kernel's unwind info.  In that case, ktab_size remains 0 and
+         hence the value of KERNEL_START doesn't matter.  */
+
+#define KERNEL_START 0xc000000000000000ULL
+
+static size_t ktab_size = 0;
+struct ia64_table_entry
+  {
+    uint64_t start_offset;
+    uint64_t end_offset;
+    uint64_t info_offset;
+  };
+
+static struct ia64_table_entry *ktab = NULL;
+
 #endif
 
 /* An enumeration of the different IA-64 instruction types.  */
@@ -649,24 +677,33 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
 {
   if (regnum >= V32_REGNUM && regnum <= V127_REGNUM)
     {
-      ULONGEST bsp;
-      ULONGEST cfm;
-      CORE_ADDR reg;
-      regcache_cooked_read_unsigned (regcache, IA64_BSP_REGNUM, &bsp);
-      regcache_cooked_read_unsigned (regcache, IA64_CFM_REGNUM, &cfm);
-
-      /* The bsp points at the end of the register frame so we
-        subtract the size of frame from it to get start of register frame.  */
-      bsp = rse_address_add (bsp, -(cfm & 0x7f));
-      if ((cfm & 0x7f) > regnum - V32_REGNUM) 
+      /* First try and use the libunwind special reg accessor, otherwise fallback to
+        standard logic.  */
+      if (!libunwind_is_initialized ()
+         || libunwind_get_reg_special (gdbarch, regnum, buf) != 0)
        {
-         ULONGEST reg_addr = rse_address_add (bsp, (regnum - V32_REGNUM));
-         reg = read_memory_integer ((CORE_ADDR)reg_addr, 8);
-         store_unsigned_integer (buf, register_size (current_gdbarch, regnum), reg);
+         /* The fallback position is to assume that r32-r127 are found sequentially
+            in memory starting at $bof.  This isn't always true, but without libunwind,
+            this is the best we can do.  */
+         ULONGEST cfm;
+         ULONGEST bsp;
+         CORE_ADDR reg;
+         regcache_cooked_read_unsigned (regcache, IA64_BSP_REGNUM, &bsp);
+         regcache_cooked_read_unsigned (regcache, IA64_CFM_REGNUM, &cfm);
+         
+         /* The bsp points at the end of the register frame so we
+            subtract the size of frame from it to get start of register frame.  */
+         bsp = rse_address_add (bsp, -(cfm & 0x7f));
+         
+         if ((cfm & 0x7f) > regnum - V32_REGNUM) 
+           {
+             ULONGEST reg_addr = rse_address_add (bsp, (regnum - V32_REGNUM));
+             reg = read_memory_integer ((CORE_ADDR)reg_addr, 8);
+             store_unsigned_integer (buf, register_size (current_gdbarch, regnum), reg);
+           }
+         else
+           store_unsigned_integer (buf, register_size (current_gdbarch, regnum), 0);
        }
-      else
-       store_unsigned_integer (buf, register_size (current_gdbarch, regnum), 0);
     }
   else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT31_REGNUM)
     {
@@ -1522,11 +1559,11 @@ ia64_frame_this_id (struct frame_info *next_frame, void **this_cache,
   struct ia64_frame_cache *cache =
     ia64_frame_cache (next_frame, this_cache);
 
-  /* This marks the outermost frame.  */
+  /* If outermost frame, mark with null frame id.  */
   if (cache->base == 0)
-    return;
-
-  (*this_id) = frame_id_build_special (cache->base, cache->pc, cache->bsp);
+    (*this_id) = null_frame_id;
+  else
+    (*this_id) = frame_id_build_special (cache->base, cache->pc, cache->bsp);
   if (gdbarch_debug >= 1)
     fprintf_unfiltered (gdb_stdlog,
                        "regular frame id: code 0x%s, stack 0x%s, special 0x%s, next_frame %p\n",
@@ -2008,8 +2045,13 @@ ia64_sigtramp_frame_prev_register (struct frame_info *next_frame,
   if (gdbarch_debug >= 1)
     fprintf_unfiltered (gdb_stdlog,
                        "sigtramp prev register <%s> is 0x%s\n",
-                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
-                        ? ia64_register_names[regnum] : "r??"), 
+                       (regnum < IA64_GR32_REGNUM
+                        || (regnum > IA64_GR127_REGNUM
+                            && regnum < LAST_PSEUDO_REGNUM))
+                        ? ia64_register_names[regnum]
+                        : (regnum < LAST_PSEUDO_REGNUM
+                           ? ia64_register_names[regnum-IA64_GR32_REGNUM+V32_REGNUM]
+                           : "OUT_OF_RANGE"),
                        paddr_nz (extract_unsigned_integer (valuep, 8)));
 }
 
@@ -2278,12 +2320,131 @@ ia64_access_fpreg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_fpreg_t *val
   return 0;
 }
 
+/* Libunwind callback accessor function for top-level rse registers.  */
+static int
+ia64_access_rse_reg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_word_t *val, 
+                    int write, void *arg)
+{
+  int regnum = ia64_uw2gdb_regnum (uw_regnum);
+  unw_word_t bsp, sof, sol, cfm, psr, ip;
+  long new_sof, old_sof;
+  
+  if (write)
+    {
+      if (regnum < 0)
+       /* ignore writes to pseudo-registers such as UNW_IA64_PROC_STARTI.  */
+       return 0;
+  
+      switch (uw_regnum)
+       {
+       case UNW_REG_IP:
+         ia64_write_pc (*val, inferior_ptid);
+         break;
+
+       case UNW_IA64_AR_BSPSTORE:
+         write_register (IA64_BSP_REGNUM, *val);
+         break;
+         
+       case UNW_IA64_AR_BSP:
+       case UNW_IA64_BSP:
+         /* Account for the fact that ptrace() expects bsp to point
+            after the current register frame.  */
+         cfm = read_register (IA64_CFM_REGNUM);
+         sof = (cfm & 0x7f);
+         bsp = ia64_rse_skip_regs (*val, sof);
+         write_register (IA64_BSP_REGNUM, bsp);
+         break;
+         
+       case UNW_IA64_CFM:
+         /* If we change CFM, we need to adjust ptrace's notion of
+            bsp accordingly, so that the real bsp remains
+            unchanged.  */
+         bsp = read_register (IA64_BSP_REGNUM);
+         cfm = read_register (IA64_CFM_REGNUM);
+         old_sof = (cfm & 0x7f);
+         new_sof = (*val & 0x7f);
+         if (old_sof != new_sof)
+           {
+             bsp = ia64_rse_skip_regs (bsp, -old_sof + new_sof);
+             write_register (IA64_BSP_REGNUM, bsp);
+           }
+         write_register (IA64_CFM_REGNUM, *val);
+         break;
+         
+       default:
+         write_register (regnum, *val);
+         break;
+       }
+      if (gdbarch_debug >= 1)
+       fprintf_unfiltered (gdb_stdlog, 
+                           "  access_rse_reg: to cache: %4s=0x%s\n",
+                           (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                            ? ia64_register_names[regnum] : "r??"), 
+                           paddr_nz (*val));
+    }
+  else
+    {
+      switch (uw_regnum)
+       {
+       case UNW_REG_IP:
+         /* Libunwind expects to see the pc value which means the slot number
+            from the psr must be merged with the ip word address.  */
+         ip = read_register (IA64_IP_REGNUM); 
+         psr = read_register (IA64_PSR_REGNUM);
+         *val = ip | ((psr >> 41) & 0x3);
+         break;
+         
+       case UNW_IA64_AR_BSP:
+         /* Libunwind expects to see the beginning of the current register
+            frame so we must account for the fact that ptrace() will return a value
+            for bsp that points *after* the current register frame.  */
+         bsp = read_register (IA64_BSP_REGNUM);
+         cfm = read_register (IA64_CFM_REGNUM);
+         sof = (cfm & 0x7f);
+         *val = ia64_rse_skip_regs (bsp, -sof);
+         break;
+         
+       case UNW_IA64_AR_BSPSTORE:
+         /* Libunwind wants bspstore to be after the current register frame.
+            This is what ptrace() and gdb treats as the regular bsp value.  */
+         *val = read_register (IA64_BSP_REGNUM);
+         break;
+
+       default:
+         /* For all other registers, just read the value directly.  */
+         *val = read_register (regnum);
+         break;
+       }
+    }
+      
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog, 
+                       "  access_rse_reg: from cache: %4s=0x%s\n",
+                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                        ? ia64_register_names[regnum] : "r??"), 
+                       paddr_nz (*val));
+
+  return 0;
+}
+
 /* Libunwind callback accessor function for accessing memory.  */
 static int
 ia64_access_mem (unw_addr_space_t as,
                 unw_word_t addr, unw_word_t *val,
                 int write, void *arg)
 {
+  if (addr - KERNEL_START < ktab_size)
+    {
+      unw_word_t *laddr = (unw_word_t*) ((char *) ktab
+                          + (addr - KERNEL_START));
+               
+      if (write)
+        *laddr = *val; 
+      else 
+        *val = *laddr;
+      return 0;
+    }
+
   /* XXX do we need to normalize byte-order here?  */
   if (write)
     return target_write_memory (addr, (char *) val, sizeof (unw_word_t));
@@ -2296,8 +2457,14 @@ static int
 getunwind_table (void *buf, size_t len)
 {
   LONGEST x;
-  x = target_read_partial (&current_target, TARGET_OBJECT_UNWIND_TABLE, NULL,
-                          buf, 0, len);
+
+  /* FIXME: This is a temporary solution to backtracing syscalls in corefiles.
+            To do this properly, the AUXV section should be used.  This
+           fix will work as long as the kernel used to generate the corefile
+           is equivalent to the kernel used to debug the corefile.  */
+  x = ia64_linux_xfer_unwind_table (&current_target, 
+                                   TARGET_OBJECT_UNWIND_TABLE, NULL,
+                                   buf, NULL, 0, len);
 
   return (int)x;
 }
@@ -2306,27 +2473,20 @@ getunwind_table (void *buf, size_t len)
 static int
 get_kernel_table (unw_word_t ip, unw_dyn_info_t *di)
 {
-  size_t size;
-  struct ia64_table_entry
-  {
-    uint64_t start_offset;
-    uint64_t end_offset;
-    uint64_t info_offset;
-  };
-  static struct ia64_table_entry *ktab = NULL, *etab;
+  static struct ia64_table_entry *etab;
 
-  if (!ktab)
+  if (!ktab) 
     {
+      size_t size;
       size = getunwind_table (NULL, 0);
       if ((int)size < 0)
-       return -UNW_ENOINFO;
-      ktab = xmalloc (size);
-      getunwind_table (ktab, size);
-  
-      /* Determine length of kernel's unwind table and relocate
-        it's entries.  */
+        return -UNW_ENOINFO;
+      ktab_size = size;
+      ktab = xmalloc (ktab_size);
+      getunwind_table (ktab, ktab_size);
+                         
       for (etab = ktab; etab->start_offset; ++etab)
-       etab->info_offset += (uint64_t) ktab;
+        etab->info_offset += KERNEL_START;
     }
   
   if (ip < ktab[0].start_offset || ip >= etab[-1].end_offset)
@@ -2389,16 +2549,35 @@ ia64_find_unwind_table (struct objfile *objfile, unw_word_t ip,
        }
     }
 
-  if (!p_text || !p_unwind
-      /* Verify that the segment that contains the IP also contains
-        the static unwind table.  If not, we are dealing with
-        runtime-generated code, for which we have no info here.  */
-      || (p_unwind->p_vaddr - p_text->p_vaddr) >= p_text->p_memsz)
+  if (!p_text || !p_unwind)
     return -UNW_ENOINFO;
 
+  /* Verify that the segment that contains the IP also contains
+     the static unwind table.  If not, we may be in the Linux kernel's
+     DSO gate page in which case the unwind table is another segment. 
+     Otherwise, we are dealing with runtime-generated code, for which we 
+     have no info here.  */
   segbase = p_text->p_vaddr + load_base;
 
-  dip->start_ip = segbase;
+  if ((p_unwind->p_vaddr - p_text->p_vaddr) >= p_text->p_memsz)
+    {
+      int ok = 0;
+      for (i = 0; i < ehdr->e_phnum; ++i)
+        {
+          if (phdr[i].p_type == PT_LOAD
+             && (p_unwind->p_vaddr - phdr[i].p_vaddr) < phdr[i].p_memsz)
+           {
+              ok = 1;
+             /* Get the segbase from the section containing the
+                libunwind table.  */
+             segbase = phdr[i].p_vaddr + load_base;
+           }
+       }
+      if (!ok)
+        return -UNW_ENOINFO;
+    }
+
+  dip->start_ip = p_text->p_vaddr + load_base;
   dip->end_ip = dip->start_ip + p_text->p_memsz;
   dip->gp = ia64_find_global_pointer (ip);
   dip->format = UNW_INFO_FORMAT_REMOTE_TABLE;
@@ -2528,14 +2707,33 @@ ia64_libunwind_frame_this_id (struct frame_info *next_frame, void **this_cache,
   char buf[8];
   CORE_ADDR bsp;
   struct frame_id id;
+  CORE_ADDR prev_ip, addr;
+  int realnum, optimized;
+  enum lval_type lval;
+
 
   libunwind_frame_this_id (next_frame, this_cache, &id);
+  if (frame_id_eq (id, null_frame_id))
+    {
+      (*this_id) = null_frame_id;
+      return;
+    }
 
-  /* We must add the bsp as the special address for frame comparison purposes.  */
+  /* We must add the bsp as the special address for frame comparison 
+     purposes.  */
   frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
   bsp = extract_unsigned_integer (buf, 8);
 
-  (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp);
+  /* If the previous frame pc value is 0, then we are at the end of the stack
+     and don't want to unwind past this frame.  We return a null frame_id to
+     indicate this.  */
+  libunwind_frame_prev_register (next_frame, this_cache, IA64_IP_REGNUM, 
+                                &optimized, &lval, &addr, &realnum, &prev_ip);
+
+  if (prev_ip != 0)
+    (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp);
+  else
+    (*this_id) = null_frame_id;
 
   if (gdbarch_debug >= 1)
     fprintf_unfiltered (gdb_stdlog,
@@ -2625,8 +2823,13 @@ ia64_libunwind_frame_prev_register (struct frame_info *next_frame,
   if (gdbarch_debug >= 1)
     fprintf_unfiltered (gdb_stdlog,
                        "libunwind prev register <%s> is 0x%s\n",
-                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
-                        ? ia64_register_names[regnum] : "r??"), 
+                       (regnum < IA64_GR32_REGNUM
+                        || (regnum > IA64_GR127_REGNUM
+                            && regnum < LAST_PSEUDO_REGNUM))
+                        ? ia64_register_names[regnum]
+                        : (regnum < LAST_PSEUDO_REGNUM
+                           ? ia64_register_names[regnum-IA64_GR32_REGNUM+V32_REGNUM]
+                           : "OUT_OF_RANGE"),
                        paddr_nz (extract_unsigned_integer (valuep, 8)));
 }
 
@@ -2646,6 +2849,86 @@ ia64_libunwind_frame_sniffer (struct frame_info *next_frame)
   return NULL;
 }
 
+static void
+ia64_libunwind_sigtramp_frame_this_id (struct frame_info *next_frame, void **this_cache,
+                                      struct frame_id *this_id)
+{
+  char buf[8];
+  CORE_ADDR bsp;
+  struct frame_id id;
+  CORE_ADDR prev_ip;
+
+  libunwind_frame_this_id (next_frame, this_cache, &id);
+  if (frame_id_eq (id, null_frame_id))
+    {
+      (*this_id) = null_frame_id;
+      return;
+    }
+
+  /* We must add the bsp as the special address for frame comparison 
+     purposes.  */
+  frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
+  bsp = extract_unsigned_integer (buf, 8);
+
+  /* For a sigtramp frame, we don't make the check for previous ip being 0.  */
+  (*this_id) = frame_id_build_special (id.stack_addr, id.code_addr, bsp);
+
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "libunwind sigtramp frame id: code 0x%s, stack 0x%s, special 0x%s, next_frame %p\n",
+                       paddr_nz (id.code_addr), paddr_nz (id.stack_addr), 
+                       paddr_nz (bsp), next_frame);
+}
+
+static void
+ia64_libunwind_sigtramp_frame_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)
+
+{
+  CORE_ADDR prev_ip, addr;
+  int realnum, optimized;
+  enum lval_type lval;
+
+
+  /* If the previous frame pc value is 0, then we want to use the SIGCONTEXT
+     method of getting previous registers.  */
+  libunwind_frame_prev_register (next_frame, this_cache, IA64_IP_REGNUM, 
+                                &optimized, &lval, &addr, &realnum, &prev_ip);
+
+  if (prev_ip == 0)
+    {
+      void *tmp_cache = NULL;
+      ia64_sigtramp_frame_prev_register (next_frame, &tmp_cache, regnum, optimizedp, lvalp,
+                                        addrp, realnump, valuep);
+    }
+  else
+    ia64_libunwind_frame_prev_register (next_frame, this_cache, regnum, optimizedp, lvalp,
+                                       addrp, realnump, valuep);
+}
+
+static const struct frame_unwind ia64_libunwind_sigtramp_frame_unwind =
+{
+  SIGTRAMP_FRAME,
+  ia64_libunwind_sigtramp_frame_this_id,
+  ia64_libunwind_sigtramp_frame_prev_register
+};
+
+static const struct frame_unwind *
+ia64_libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame)
+{
+  if (libunwind_is_initialized ())
+    {
+      if (libunwind_sigtramp_frame_sniffer (next_frame))
+        return &ia64_libunwind_sigtramp_frame_unwind;
+      return NULL;
+    }
+  else
+    return ia64_sigtramp_frame_sniffer (next_frame);
+}
+
 /* Set of libunwind callback acccessor functions.  */
 static unw_accessors_t ia64_unw_accessors =
 {
@@ -2659,6 +2942,22 @@ static unw_accessors_t ia64_unw_accessors =
   /* get_proc_name */
 };
 
+/* Set of special libunwind callback acccessor functions specific for accessing
+   the rse registers.  At the top of the stack, we want libunwind to figure out
+   how to read r32 - r127.  Though usually they are found sequentially in memory
+   starting from $bof, this is not always true.  */
+static unw_accessors_t ia64_unw_rse_accessors =
+{
+  ia64_find_proc_info_x,
+  ia64_put_unwind_info,
+  ia64_get_dyn_info_list,
+  ia64_access_mem,
+  ia64_access_rse_reg,
+  ia64_access_fpreg,
+  /* resume */
+  /* get_proc_name */
+};
+
 /* Set of ia64 gdb libunwind-frame callbacks and data for generic libunwind-frame code to use.  */
 static struct libunwind_descr ia64_libunwind_descr =
 {
@@ -2666,6 +2965,7 @@ static struct libunwind_descr ia64_libunwind_descr =
   ia64_uw2gdb_regnum, 
   ia64_is_fpreg, 
   &ia64_unw_accessors,
+  &ia64_unw_rse_accessors,
 };
 
 #endif /* HAVE_LIBUNWIND_IA64_H  */
@@ -3307,10 +3607,12 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_unwind_dummy_id (gdbarch, ia64_unwind_dummy_id);
 
   set_gdbarch_unwind_pc (gdbarch, ia64_unwind_pc);
-  frame_unwind_append_sniffer (gdbarch, ia64_sigtramp_frame_sniffer);
 #ifdef HAVE_LIBUNWIND_IA64_H
+  frame_unwind_append_sniffer (gdbarch, ia64_libunwind_sigtramp_frame_sniffer);
   frame_unwind_append_sniffer (gdbarch, ia64_libunwind_frame_sniffer);
   libunwind_frame_set_descr (gdbarch, &ia64_libunwind_descr);
+#else
+  frame_unwind_append_sniffer (gdbarch, ia64_sigtramp_frame_sniffer);
 #endif
   frame_unwind_append_sniffer (gdbarch, ia64_frame_sniffer);
   frame_base_set_default (gdbarch, &ia64_frame_base);
index 4e5d6b5..9f5a31e 100644 (file)
@@ -49,6 +49,7 @@ static struct gdbarch_data *libunwind_descr_handle;
 static int (*unw_get_reg_p) (unw_cursor_t *, unw_regnum_t, unw_word_t *);
 static int (*unw_get_fpreg_p) (unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
 static int (*unw_get_saveloc_p) (unw_cursor_t *, unw_regnum_t, unw_save_loc_t *);
+static int (*unw_is_signal_frame_p) (unw_cursor_t *);
 static int (*unw_step_p) (unw_cursor_t *);
 static int (*unw_init_remote_p) (unw_cursor_t *, unw_addr_space_t, void *);
 static unw_addr_space_t (*unw_create_addr_space_p) (unw_accessors_t *, int);
@@ -78,6 +79,7 @@ struct libunwind_frame_cache
 static char *get_reg_name = STRINGIFY(UNW_OBJ(get_reg));
 static char *get_fpreg_name = STRINGIFY(UNW_OBJ(get_fpreg));
 static char *get_saveloc_name = STRINGIFY(UNW_OBJ(get_save_loc));
+static char *is_signal_frame_name = STRINGIFY(UNW_OBJ(is_signal_frame));
 static char *step_name = STRINGIFY(UNW_OBJ(step));
 static char *init_remote_name = STRINGIFY(UNW_OBJ(init_remote));
 static char *create_addr_space_name = STRINGIFY(UNW_OBJ(create_addr_space));
@@ -119,6 +121,7 @@ libunwind_frame_set_descr (struct gdbarch *gdbarch, struct libunwind_descr *desc
   arch_descr->uw2gdb = descr->uw2gdb;
   arch_descr->is_fpreg = descr->is_fpreg;
   arch_descr->accessors = descr->accessors;
+  arch_descr->special_accessors = descr->special_accessors;
 }
 
 static struct libunwind_frame_cache *
@@ -139,6 +142,10 @@ libunwind_frame_cache (struct frame_info *next_frame, void **this_cache)
   cache = FRAME_OBSTACK_ZALLOC (struct libunwind_frame_cache);
 
   cache->func_addr = frame_func_unwind (next_frame);
+  if (cache->func_addr == 0
+      && frame_relative_level (next_frame) > 0
+      && get_frame_type (next_frame) != SIGTRAMP_FRAME)
+    return NULL;
 
   /* Get a libunwind cursor to the previous frame.  We do this by initializing
      a cursor.  Libunwind treats a new cursor as the top of stack and will get
@@ -156,7 +163,8 @@ libunwind_frame_cache (struct frame_info *next_frame, void **this_cache)
                                 : __LITTLE_ENDIAN);
 
   unw_init_remote_p (&cache->cursor, as, next_frame);
-  unw_step_p (&cache->cursor);
+  if (unw_step_p (&cache->cursor) < 0)
+    return NULL;
 
   /* To get base address, get sp from previous frame.  */
   uw_sp_regnum = descr->gdb2uw (SP_REGNUM);
@@ -208,8 +216,14 @@ libunwind_frame_sniffer (struct frame_info *next_frame)
 
   ret = unw_init_remote_p (&cursor, as, next_frame);
 
-  if (ret >= 0)
-    ret = unw_step_p (&cursor);
+  if (ret < 0)
+    return NULL;
+
+  /* Check to see if we have libunwind info by checking if we are in a 
+     signal frame.  If it doesn't return an error, we have libunwind info
+     and can use libunwind.  */
+  ret = unw_is_signal_frame_p (&cursor);
 
   if (ret < 0)
     return NULL;
@@ -224,7 +238,10 @@ libunwind_frame_this_id (struct frame_info *next_frame, void **this_cache,
   struct libunwind_frame_cache *cache =
     libunwind_frame_cache (next_frame, this_cache);
 
-  (*this_id) = frame_id_build (cache->base, cache->func_addr);
+  if (cache != NULL)
+    (*this_id) = frame_id_build (cache->base, cache->func_addr);
+  else
+    (*this_id) = null_frame_id;
 }
 
 void
@@ -245,6 +262,9 @@ libunwind_frame_prev_register (struct frame_info *next_frame, void **this_cache,
   unw_regnum_t uw_regnum;
   struct libunwind_descr *descr;
 
+  if (cache == NULL)
+    return;
+  
   /* Convert from gdb register number to libunwind register number.  */
   descr = libunwind_descr (get_frame_arch (next_frame));
   uw_regnum = descr->gdb2uw (regnum);
@@ -312,6 +332,8 @@ libunwind_frame_base_address (struct frame_info *next_frame, void **this_cache)
   struct libunwind_frame_cache *cache =
     libunwind_frame_cache (next_frame, this_cache);
 
+  if (cache == NULL)
+    return (CORE_ADDR)NULL;
   return cache->base;
 }
 
@@ -325,6 +347,95 @@ libunwind_search_unwind_table (void *as, long ip, void *di,
                                    di, pi, need_unwind_info, args);
 }
 
+/* Verify if we are in a sigtramp frame and we can use libunwind to unwind.  */
+const struct frame_unwind *
+libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame)
+{
+  unw_cursor_t cursor;
+  unw_accessors_t *acc;
+  unw_addr_space_t as;
+  struct libunwind_descr *descr;
+  int i, ret;
+
+  /* To test for libunwind unwind support, initialize a cursor to the
+     current frame and try to back up.  We use this same method when
+     setting up the frame cache (see libunwind_frame_cache()).  If
+     libunwind returns success for this operation, it means that it
+     has found sufficient libunwind unwinding information to do
+     so.  */
+
+  descr = libunwind_descr (get_frame_arch (next_frame));
+  acc = descr->accessors;
+  as =  unw_create_addr_space_p (acc,
+                                TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
+                                ? __BIG_ENDIAN
+                                : __LITTLE_ENDIAN);
+
+  ret = unw_init_remote_p (&cursor, as, next_frame);
+
+  if (ret < 0)
+    return NULL;
+
+  /* Check to see if we are in a signal frame.  */
+  ret = unw_is_signal_frame_p (&cursor);
+  if (ret > 0)
+    return &libunwind_frame_unwind;
+
+  return NULL;
+}
+
+/* The following routine is for accessing special registers of the top frame.
+   A special set of accessors must be given that work without frame info.
+   This is used by ia64 to access the rse registers r32-r127.  While they
+   are usually located at BOF, this is not always true and only the libunwind
+   info can decipher where they actually are.  */
+int
+libunwind_get_reg_special (struct gdbarch *gdbarch, int regnum, void *buf)
+{
+  unw_cursor_t cursor;
+  unw_accessors_t *acc;
+  unw_addr_space_t as;
+  struct libunwind_descr *descr;
+  int ret;
+  unw_regnum_t uw_regnum;
+  unw_word_t intval;
+  unw_fpreg_t fpval;
+  void *ptr;
+
+
+  descr = libunwind_descr (gdbarch);
+  acc = descr->special_accessors;
+  as =  unw_create_addr_space_p (acc,
+                                TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
+                                ? __BIG_ENDIAN
+                                : __LITTLE_ENDIAN);
+
+  ret = unw_init_remote_p (&cursor, as, NULL);
+  if (ret < 0)
+    return -1;
+
+  uw_regnum = descr->gdb2uw (regnum);
+
+  if (descr->is_fpreg (uw_regnum))
+    {
+      ret = unw_get_fpreg_p (&cursor, uw_regnum, &fpval);
+      ptr = &fpval;
+    }
+  else
+    {
+      ret = unw_get_reg_p (&cursor, uw_regnum, &intval);
+      ptr = &intval;
+    }
+
+  if (ret < 0)
+    return -1;
+
+  if (buf)
+    memcpy (buf, ptr, register_size (current_gdbarch, regnum));
+
+  return 0;
+}
+  
 static int
 libunwind_load (void)
 {
@@ -348,6 +459,10 @@ libunwind_load (void)
   if (unw_get_saveloc_p == NULL)
     return 0;
 
+  unw_is_signal_frame_p = dlsym (handle, is_signal_frame_name);
+  if (unw_is_signal_frame_p == NULL)
+    return 0;
+
   unw_step_p = dlsym (handle, step_name);
   if (unw_step_p == NULL)
     return 0;
index e47a792..5a90235 100644 (file)
@@ -25,6 +25,7 @@
 
 struct frame_info;
 struct frame_id;
+struct gdbarch;
 
 #ifndef LIBUNWIND_FRAME_H
 #define LIBUNWIND_FRAME_H 1
@@ -37,9 +38,11 @@ struct libunwind_descr
   int (*uw2gdb) (int);
   int (*is_fpreg) (int);
   void *accessors;
+  void *special_accessors;
 };
 
 const struct frame_unwind *libunwind_frame_sniffer (struct frame_info *next_frame);
+const struct frame_unwind *libunwind_sigtramp_frame_sniffer (struct frame_info *next_frame);
 
 void libunwind_frame_set_descr (struct gdbarch *arch, struct libunwind_descr *descr);
 
@@ -59,6 +62,8 @@ int libunwind_search_unwind_table (void *as, long ip, void *di,
 unw_word_t libunwind_find_dyn_list (unw_addr_space_t, unw_dyn_info_t *,
                                    void *);
 
+int libunwind_get_reg_special (struct gdbarch *gdbarch, int regnum, void *buf);
+
 #endif /* libunwind-frame.h */
 
 #endif /* HAVE_LIBUNWIND_H  */