2004-01-05 Andrew Cagney <cagney@redhat.com>
[deliverable/binutils-gdb.git] / gdb / ia64-tdep.c
index 79c645d2ced0d097c66c4d7ba9189521dbcffc6a..760a17d1a4f97d1ac7b4af559ebc319721ef056a 100644 (file)
 #include "objfiles.h"
 #include "elf/common.h"                /* for DT_PLTGOT value */
 #include "elf-bfd.h"
+#include "elf.h"                /* for PT_IA64_UNWIND value */
 #include "dis-asm.h"
 
+#ifdef HAVE_LIBUNWIND_IA64_H
+#include "libunwind-frame.h"
+#include "libunwind-ia64.h"
+#endif
+
 /* Hook for determining the global pointer when calling functions in
    the inferior under AIX.  The initialization code in ia64-aix-nat.c
    sets this hook to the address of a function which will find the
@@ -87,6 +93,7 @@ typedef enum instruction_type
 /* FIXME: These extern declarations should go in ia64-tdep.h.  */
 extern CORE_ADDR ia64_linux_sigcontext_register_address (CORE_ADDR, int);
 extern CORE_ADDR ia64_aix_sigcontext_register_address (CORE_ADDR, int);
+extern unsigned long ia64_linux_getunwind_table (void *, size_t);
 
 static gdbarch_init_ftype ia64_gdbarch_init;
 
@@ -108,7 +115,7 @@ static int fp_regnum = IA64_VFP_REGNUM;
 static int lr_regnum = IA64_VRAP_REGNUM;
 
 /* NOTE: we treat the register stack registers r32-r127 as pseudo-registers because
-   they are in memory and must be calculated via the bsp register.  */
+   they may not be accessible via the ptrace register get/set interfaces.  */
 enum pseudo_regs { FIRST_PSEUDO_REGNUM = NUM_IA64_RAW_REGS, VBOF_REGNUM = IA64_NAT127_REGNUM + 1, V32_REGNUM, 
                   V127_REGNUM = V32_REGNUM + 95, 
                   VP0_REGNUM, VP16_REGNUM = VP0_REGNUM + 16, VP63_REGNUM = VP0_REGNUM + 63, LAST_PSEUDO_REGNUM };
@@ -232,6 +239,7 @@ struct ia64_frame_cache
   CORE_ADDR saved_sp;  /* stack pointer for frame */
   CORE_ADDR bsp;       /* points at r32 for the current frame */
   CORE_ADDR cfm;       /* cfm value for current frame */
+  CORE_ADDR prev_cfm;   /* cfm value for previous frame */
   int   frameless;
   int   sof;           /* Size of frame  (decoded from cfm value) */
   int  sol;            /* Size of locals (decoded from cfm value) */
@@ -255,9 +263,6 @@ struct ia64_frame_cache
 
 struct gdbarch_tdep
   {
-    int os_ident;      /* From the ELF header, one of the ELFOSABI_
-                           constants: ELFOSABI_LINUX, ELFOSABI_AIX,
-                          etc. */
     CORE_ADDR (*sigcontext_register_address) (CORE_ADDR, int);
                        /* OS specific function which, given a frame address
                           and register number, returns the offset to the
@@ -316,10 +321,16 @@ ia64_dwarf_reg_to_regnum (int reg)
   return reg;
 }
 
+static int
+floatformat_valid (const struct floatformat *fmt, const char *from)
+{
+  return 1;
+}
+
 const struct floatformat floatformat_ia64_ext =
 {
   floatformat_little, 82, 0, 1, 17, 65535, 0x1ffff, 18, 64,
-  floatformat_intbit_yes
+  floatformat_intbit_yes, "floatformat_ia64_ext", floatformat_valid
 };
 
 
@@ -343,7 +354,7 @@ read_sigcontext_register (struct frame_info *frame, int regnum)
 
   regaddr = SIGCONTEXT_REGISTER_ADDRESS (get_frame_base (frame), regnum);
   if (regaddr)
-    return read_memory_integer (regaddr, REGISTER_RAW_SIZE (regnum));
+    return read_memory_integer (regaddr, register_size (current_gdbarch, regnum));
   else
     internal_error (__FILE__, __LINE__,
                    "read_sigcontext_register: Register %d not in struct sigcontext", regnum);
@@ -716,10 +727,10 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
        {
          ULONGEST reg_addr = rse_address_add (bsp, (regnum - V32_REGNUM));
          reg = read_memory_integer ((CORE_ADDR)reg_addr, 8);
-         store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), reg);
+         store_unsigned_integer (buf, register_size (current_gdbarch, regnum), reg);
        }
       else
-       store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), 0);
+       store_unsigned_integer (buf, register_size (current_gdbarch, regnum), 0);
     }
   else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT31_REGNUM)
     {
@@ -727,7 +738,7 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
       ULONGEST unat;
       regcache_cooked_read_unsigned (regcache, IA64_UNAT_REGNUM, &unat);
       unatN_val = (unat & (1LL << (regnum - IA64_NAT0_REGNUM))) != 0;
-      store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), unatN_val);
+      store_unsigned_integer (buf, register_size (current_gdbarch, regnum), unatN_val);
     }
   else if (IA64_NAT32_REGNUM <= regnum && regnum <= IA64_NAT127_REGNUM)
     {
@@ -762,7 +773,7 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
          natN_val = (nat_collection >> nat_bit) & 1;
        }
       
-      store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), natN_val);
+      store_unsigned_integer (buf, register_size (current_gdbarch, regnum), natN_val);
     }
   else if (regnum == VBOF_REGNUM)
     {
@@ -777,7 +788,7 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
       /* The bsp points at the end of the register frame so we
         subtract the size of frame from it to get beginning of frame.  */
       vbsp = rse_address_add (bsp, -(cfm & 0x7f));
-      store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), vbsp);
+      store_unsigned_integer (buf, register_size (current_gdbarch, regnum), vbsp);
     }
   else if (VP0_REGNUM <= regnum && regnum <= VP63_REGNUM)
     {
@@ -799,10 +810,10 @@ ia64_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
                 + ((regnum - VP16_REGNUM) + rrb_pr) % 48;
        }
       prN_val = (pr & (1LL << (regnum - VP0_REGNUM))) != 0;
-      store_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum), prN_val);
+      store_unsigned_integer (buf, register_size (current_gdbarch, regnum), prN_val);
     }
   else
-    memset (buf, 0, REGISTER_RAW_SIZE (regnum));
+    memset (buf, 0, register_size (current_gdbarch, regnum));
 }
 
 static void
@@ -829,7 +840,7 @@ ia64_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
     {
       ULONGEST unatN_val, unat, unatN_mask;
       regcache_cooked_read_unsigned (regcache, IA64_UNAT_REGNUM, &unat);
-      unatN_val = extract_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum)); 
+      unatN_val = extract_unsigned_integer (buf, register_size (current_gdbarch, regnum)); 
       unatN_mask = (1LL << (regnum - IA64_NAT0_REGNUM));
       if (unatN_val == 0)
        unat &= ~unatN_mask;
@@ -853,7 +864,7 @@ ia64_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
       if ((cfm & 0x7f) > regnum - V32_REGNUM) 
        gr_addr = rse_address_add (bsp, (regnum - V32_REGNUM));
       
-      natN_val = extract_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum)); 
+      natN_val = extract_unsigned_integer (buf, register_size (current_gdbarch, regnum)); 
 
       if (gr_addr != 0 && (natN_val == 0 || natN_val == 1))
        {
@@ -882,7 +893,7 @@ ia64_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
                nat_collection |= natN_mask;
              else
                nat_collection &= ~natN_mask;
-             store_unsigned_integer (nat_buf, REGISTER_RAW_SIZE (regnum), nat_collection);
+             store_unsigned_integer (nat_buf, register_size (current_gdbarch, regnum), nat_collection);
              write_memory (nat_addr, nat_buf, 8);
            }
        }
@@ -907,7 +918,7 @@ ia64_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
          regnum = VP16_REGNUM 
                 + ((regnum - VP16_REGNUM) + rrb_pr) % 48;
        }
-      prN_val = extract_unsigned_integer (buf, REGISTER_RAW_SIZE (regnum)); 
+      prN_val = extract_unsigned_integer (buf, register_size (current_gdbarch, regnum)); 
       prN_mask = (1LL << (regnum - VP0_REGNUM));
       if (prN_val == 0)
        pr &= ~prN_mask;
@@ -1030,6 +1041,7 @@ ia64_alloc_frame_cache (void)
   cache->base = 0;
   cache->pc = 0;
   cache->cfm = 0;
+  cache->prev_cfm = 0;
   cache->sof = 0;
   cache->sol = 0;
   cache->sor = 0;
@@ -1450,9 +1462,20 @@ examine_prologue (CORE_ADDR pc, CORE_ADDR lim_pc, struct frame_info *next_frame,
 
       /* For the previous argument registers we require the previous bof.  
         If we can't find the previous cfm, then we can do nothing.  */
+      cfm = 0;
       if (cache->saved_regs[IA64_CFM_REGNUM] != 0)
        {
          cfm = read_memory_integer (cache->saved_regs[IA64_CFM_REGNUM], 8);
+       }
+      else if (cfm_reg != 0)
+       {
+         frame_unwind_register (next_frame, cfm_reg, buf);
+         cfm = extract_unsigned_integer (buf, 8);
+       }
+      cache->prev_cfm = cfm;
+      
+      if (cfm != 0)
+       {
          sor = ((cfm >> 14) & 0xf) * 8;
          sof = (cfm & 0x7f);
          sol = (cfm >> 7) & 0x7f;
@@ -1564,7 +1587,11 @@ ia64_frame_this_id (struct frame_info *next_frame, void **this_cache,
   if (cache->base == 0)
     return;
 
-  (*this_id) = frame_id_build (cache->base, cache->pc);
+  (*this_id) = frame_id_build_special (cache->base, cache->pc, cache->bsp);
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "regular frame id: code %lx, stack %lx, special %lx, next_frame %p\n",
+                       this_id->code_addr, this_id->stack_addr, cache->bsp, next_frame);
 }
 
 static void
@@ -1593,12 +1620,12 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
   if (!valuep)
     valuep = dummy_valp;
   
-  memset (valuep, 0, REGISTER_RAW_SIZE (regnum));
+  memset (valuep, 0, register_size (current_gdbarch, regnum));
  
   if (regnum == SP_REGNUM)
     {
       /* Handle SP values for all frames but the topmost. */
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (regnum),
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum),
                              cache->base);
     }
   else if (regnum == IA64_BSP_REGNUM)
@@ -1613,7 +1640,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
       /* We want to calculate the previous bsp as the end of the previous register stack frame.
         This corresponds to what the hardware bsp register will be if we pop the frame
         back which is why we might have been called.  We know the beginning of the current
-         frame is cache->bsp - cache->sof.  This value in the previous frame points to
+        frame is cache->bsp - cache->sof.  This value in the previous frame points to
         the start of the output registers.  We can calculate the end of that frame by adding
         the size of output (sof (size of frame) - sol (size of locals)).  */
       ia64_frame_prev_register (next_frame, this_cache, IA64_CFM_REGNUM,
@@ -1623,23 +1650,25 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
       bsp = rse_address_add (cache->bsp, -(cache->sof));
       prev_bsp = rse_address_add (bsp, (prev_cfm & 0x7f) - ((prev_cfm >> 7) & 0x7f));
 
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (regnum), 
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), 
                              prev_bsp);
     }
   else if (regnum == IA64_CFM_REGNUM)
     {
-      CORE_ADDR addr = 0;
-
-      if (cache->frameless)
+      CORE_ADDR addr = cache->saved_regs[IA64_CFM_REGNUM];
+      
+      if (addr != 0)
        {
-         CORE_ADDR cfm = 0;
-         frame_unwind_register (next_frame, IA64_PFS_REGNUM, valuep);
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
        }
-      else
+      else if (cache->prev_cfm)
+       store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), cache->prev_cfm);
+      else if (cache->frameless)
        {
-         addr = cache->saved_regs[IA64_CFM_REGNUM];
-         if (addr != 0)
-           read_memory (addr, valuep, REGISTER_RAW_SIZE (regnum));
+         CORE_ADDR cfm = 0;
+         frame_unwind_register (next_frame, IA64_PFS_REGNUM, valuep);
        }
     }
   else if (regnum == IA64_VFP_REGNUM)
@@ -1649,7 +1678,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
         above.  If the function lacks one of these frame pointers, we can
         still provide a value since we know the size of the frame.  */
       CORE_ADDR vfp = cache->base;
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (IA64_VFP_REGNUM), vfp);
+      store_unsigned_integer (valuep, register_size (current_gdbarch, IA64_VFP_REGNUM), vfp);
     }
   else if (VP0_REGNUM <= regnum && regnum <= VP63_REGNUM)
     {
@@ -1673,7 +1702,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
        }
       prN_val = extract_bit_field ((unsigned char *) pr_valuep,
                                    regnum - VP0_REGNUM, 1);
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (regnum), prN_val);
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), prN_val);
     }
   else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT31_REGNUM)
     {
@@ -1687,7 +1716,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
                                &unat_optim, &unat_lval, &unat_addr, &unat_realnum, unat_valuep);
       unatN_val = extract_bit_field ((unsigned char *) unat_valuep,
                                    regnum - IA64_NAT0_REGNUM, 1);
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (regnum), 
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), 
                               unatN_val);
     }
   else if (IA64_NAT32_REGNUM <= regnum && regnum <= IA64_NAT127_REGNUM)
@@ -1722,58 +1751,73 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
          natval = (nat_collection >> nat_bit) & 1;
        }
 
-      store_unsigned_integer (valuep, REGISTER_RAW_SIZE (regnum), natval);
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), natval);
     }
   else if (regnum == IA64_IP_REGNUM)
     {
       CORE_ADDR pc = 0;
+      CORE_ADDR addr = cache->saved_regs[IA64_VRAP_REGNUM];
 
-      if (cache->frameless)
+      if (addr != 0)
        {
-         frame_unwind_register (next_frame, IA64_BR0_REGNUM, buf);
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, buf, register_size (current_gdbarch, IA64_IP_REGNUM));
          pc = extract_unsigned_integer (buf, 8);
        }
-      else
+      else if (cache->frameless)
        {
-         CORE_ADDR addr = cache->saved_regs[IA64_VRAP_REGNUM];
-         if (addr != 0)
-           {
-             read_memory (addr, buf, REGISTER_RAW_SIZE (IA64_IP_REGNUM));
-             pc = extract_unsigned_integer (buf, 8);
-           }
+         frame_unwind_register (next_frame, IA64_BR0_REGNUM, buf);
+         pc = extract_unsigned_integer (buf, 8);
        }
       pc &= ~0xf;
       store_unsigned_integer (valuep, 8, pc);
     }
   else if (regnum == IA64_PSR_REGNUM)
     {
+      /* We don't know how to get the complete previous PSR, but we need it for
+        the slot information when we unwind the pc (pc is formed of IP register
+        plus slot information from PSR).  To get the previous slot information, 
+        we mask it off the return address.  */
       ULONGEST slot_num = 0;
       CORE_ADDR pc= 0;
       CORE_ADDR psr = 0;
+      CORE_ADDR addr = cache->saved_regs[IA64_VRAP_REGNUM];
 
       frame_unwind_register (next_frame, IA64_PSR_REGNUM, buf);
       psr = extract_unsigned_integer (buf, 8);
 
-      if (cache->frameless)
+      if (addr != 0)
        {
-         CORE_ADDR pc;
-         frame_unwind_register (next_frame, IA64_BR0_REGNUM, buf);
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, buf, register_size (current_gdbarch, IA64_IP_REGNUM));
          pc = extract_unsigned_integer (buf, 8);
        }
-      else
+      else if (cache->frameless)
        {
-         CORE_ADDR addr = cache->saved_regs[IA64_VRAP_REGNUM];
-         if (addr != 0)
-           {
-             read_memory (addr, buf, REGISTER_RAW_SIZE (IA64_IP_REGNUM));
-             pc = extract_unsigned_integer (buf, 8);
-           }
+         CORE_ADDR pc;
+         frame_unwind_register (next_frame, IA64_BR0_REGNUM, buf);
+         pc = extract_unsigned_integer (buf, 8);
        }
       psr &= ~(3LL << 41);
       slot_num = pc & 0x3LL;
       psr |= (CORE_ADDR)slot_num << 41;
       store_unsigned_integer (valuep, 8, psr);
     }
+  else if (regnum == IA64_BR0_REGNUM)
+    {
+      CORE_ADDR br0 = 0;
+      CORE_ADDR addr = cache->saved_regs[IA64_BR0_REGNUM];
+      if (addr != 0)
+       {
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, buf, register_size (current_gdbarch, IA64_BR0_REGNUM));
+         br0 = extract_unsigned_integer (buf, 8);
+       }
+      store_unsigned_integer (valuep, 8, br0);
+    }
  else if ((regnum >= IA64_GR32_REGNUM && regnum <= IA64_GR127_REGNUM) ||
           (regnum >= V32_REGNUM && regnum <= V127_REGNUM))
     {
@@ -1785,7 +1829,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
        {
          *lvalp = lval_memory;
          *addrp = addr;
-         read_memory (addr, valuep, REGISTER_RAW_SIZE (regnum));
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
        }
       else if (cache->frameless)
         {
@@ -1809,7 +1853,7 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
          addr = rse_address_add (prev_bof, (regnum - IA64_GR32_REGNUM));
          *lvalp = lval_memory;
          *addrp = addr;
-         read_memory (addr, valuep, REGISTER_RAW_SIZE (regnum));
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
         }
     }
   else
@@ -1833,12 +1877,18 @@ ia64_frame_prev_register (struct frame_info *next_frame, void **this_cache,
        {
          *lvalp = lval_memory;
          *addrp = addr;
-         read_memory (addr, valuep, REGISTER_RAW_SIZE (regnum));
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
        }
       /* Otherwise, punt and get the current value of the register.  */
       else 
        frame_unwind_register (next_frame, regnum, valuep);
     }
+
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "regular prev register <%d> <%s> is %lx\n", regnum, 
+                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                        ? ia64_register_names[regnum] : "r??"), extract_unsigned_integer (valuep, 8));
 }
  
 static const struct frame_unwind ia64_frame_unwind =
@@ -1869,10 +1919,8 @@ ia64_sigtramp_frame_init_saved_regs (struct ia64_frame_cache *cache)
        SIGCONTEXT_REGISTER_ADDRESS (cache->base, IA64_CFM_REGNUM);
       cache->saved_regs[IA64_PSR_REGNUM] = 
        SIGCONTEXT_REGISTER_ADDRESS (cache->base, IA64_PSR_REGNUM);
-#if 0
       cache->saved_regs[IA64_BSP_REGNUM] = 
-       SIGCONTEXT_REGISTER_ADDRESS (frame->frame, IA64_BSP_REGNUM);
-#endif
+       SIGCONTEXT_REGISTER_ADDRESS (cache->base, IA64_BSP_REGNUM);
       cache->saved_regs[IA64_RNAT_REGNUM] = 
        SIGCONTEXT_REGISTER_ADDRESS (cache->base, IA64_RNAT_REGNUM);
       cache->saved_regs[IA64_CCV_REGNUM] = 
@@ -1886,9 +1934,8 @@ ia64_sigtramp_frame_init_saved_regs (struct ia64_frame_cache *cache)
       cache->saved_regs[IA64_LC_REGNUM] = 
        SIGCONTEXT_REGISTER_ADDRESS (cache->base, IA64_LC_REGNUM);
       for (regno = IA64_GR1_REGNUM; regno <= IA64_GR31_REGNUM; regno++)
-       if (regno != sp_regnum)
-         cache->saved_regs[regno] =
-           SIGCONTEXT_REGISTER_ADDRESS (cache->base, regno);
+       cache->saved_regs[regno] =
+         SIGCONTEXT_REGISTER_ADDRESS (cache->base, regno);
       for (regno = IA64_BR0_REGNUM; regno <= IA64_BR7_REGNUM; regno++)
        cache->saved_regs[regno] =
          SIGCONTEXT_REGISTER_ADDRESS (cache->base, regno);
@@ -1912,7 +1959,16 @@ ia64_sigtramp_frame_cache (struct frame_info *next_frame, void **this_cache)
   cache = ia64_alloc_frame_cache ();
 
   frame_unwind_register (next_frame, sp_regnum, buf);
-  cache->base = extract_unsigned_integer (buf, 8) + cache->mem_stack_frame_size;
+  /* Note that frame size is hard-coded below.  We cannot calculate it
+     via prologue examination.  */
+  cache->base = extract_unsigned_integer (buf, 8) + 16;
+
+  frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
+  cache->bsp = extract_unsigned_integer (buf, 8);
+
+  frame_unwind_register (next_frame, IA64_CFM_REGNUM, buf);
+  cache->cfm = extract_unsigned_integer (buf, 8);
+  cache->sof = cache->cfm & 0x7f;
 
   ia64_sigtramp_frame_init_saved_regs (cache);
 
@@ -1927,7 +1983,11 @@ ia64_sigtramp_frame_this_id (struct frame_info *next_frame,
   struct ia64_frame_cache *cache =
     ia64_sigtramp_frame_cache (next_frame, this_cache);
 
-  (*this_id) = frame_id_build (cache->base, frame_pc_unwind (next_frame));
+  (*this_id) = frame_id_build_special (cache->base, frame_pc_unwind (next_frame), cache->bsp);
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "sigtramp frame id: code %lx, stack %lx, special %lx, next_frame %p\n",
+                       this_id->code_addr, this_id->stack_addr, cache->bsp, next_frame);
 }
 
 static void
@@ -1937,11 +1997,75 @@ ia64_sigtramp_frame_prev_register (struct frame_info *next_frame,
                                   enum lval_type *lvalp, CORE_ADDR *addrp,
                                   int *realnump, void *valuep)
 {
-  /* Make sure we've initialized the cache.  */
-  ia64_sigtramp_frame_cache (next_frame, this_cache);
+  char dummy_valp[MAX_REGISTER_SIZE];
+  char buf[MAX_REGISTER_SIZE];
+
+  struct ia64_frame_cache *cache =
+    ia64_sigtramp_frame_cache (next_frame, this_cache);
+
+  gdb_assert (regnum >= 0);
+
+  if (!target_has_registers)
+    error ("No registers.");
+
+  *optimizedp = 0;
+  *addrp = 0;
+  *lvalp = not_lval;
+  *realnump = -1;
+
+  /* Rather than check each time if valuep is non-null, supply a dummy buffer
+     when valuep is not supplied.  */
+  if (!valuep)
+    valuep = dummy_valp;
+  
+  memset (valuep, 0, register_size (current_gdbarch, regnum));
+  if (regnum == IA64_IP_REGNUM)
+    {
+      CORE_ADDR pc = 0;
+      CORE_ADDR addr = cache->saved_regs[IA64_VRAP_REGNUM];
+
+      if (addr != 0)
+       {
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, buf, register_size (current_gdbarch, IA64_IP_REGNUM));
+         pc = extract_unsigned_integer (buf, 8);
+       }
+      pc &= ~0xf;
+      store_unsigned_integer (valuep, 8, pc);
+    }
+ else if ((regnum >= IA64_GR32_REGNUM && regnum <= IA64_GR127_REGNUM) ||
+          (regnum >= V32_REGNUM && regnum <= V127_REGNUM))
+    {
+      CORE_ADDR addr = 0;
+      if (regnum >= V32_REGNUM)
+       regnum = IA64_GR32_REGNUM + (regnum - V32_REGNUM);
+      addr = cache->saved_regs[regnum];
+      if (addr != 0)
+       {
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
+       }
+    }
+  else
+    {
+      /* All other registers not listed above.  */
+      CORE_ADDR addr = cache->saved_regs[regnum];
+      if (addr != 0)
+       {
+         *lvalp = lval_memory;
+         *addrp = addr;
+         read_memory (addr, valuep, register_size (current_gdbarch, regnum));
+       }
+    }
 
-  ia64_frame_prev_register (next_frame, this_cache, regnum,
-                           optimizedp, lvalp, addrp, realnump, valuep);
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "sigtramp prev register <%s> is %lx\n",
+                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                        ? ia64_register_names[regnum] : "r??"), extract_unsigned_integer (valuep, 8));
 }
 
 static const struct frame_unwind ia64_sigtramp_frame_unwind =
@@ -1982,6 +2106,609 @@ static const struct frame_base ia64_frame_base =
   ia64_frame_base_address
 };
 
+#ifdef HAVE_LIBUNWIND_IA64_H
+
+struct ia64_unwind_table_entry
+  {
+    unw_word_t start_offset;
+    unw_word_t end_offset;
+    unw_word_t info_offset;
+  };
+
+static __inline__ uint64_t
+ia64_rse_slot_num (uint64_t addr)
+{
+  return (addr >> 3) & 0x3f;
+}
+
+/* Skip over a designated number of registers in the backing
+   store, remembering every 64th position is for NAT.  */
+static __inline__ uint64_t
+ia64_rse_skip_regs (uint64_t addr, long num_regs)
+{
+  long delta = ia64_rse_slot_num(addr) + num_regs;
+
+  if (num_regs < 0)
+    delta -= 0x3e;
+  return addr + ((num_regs + delta/0x3f) << 3);
+}
+  
+/* Gdb libunwind-frame callback function to convert from an ia64 gdb register 
+   number to a libunwind register number.  */
+static int
+ia64_gdb2uw_regnum (int regnum)
+{
+  if (regnum == sp_regnum)
+    return UNW_IA64_SP;
+  else if (regnum == IA64_BSP_REGNUM)
+    return UNW_IA64_BSP;
+  else if ((unsigned) (regnum - IA64_GR0_REGNUM) < 128)
+    return UNW_IA64_GR + (regnum - IA64_GR0_REGNUM);
+  else if ((unsigned) (regnum - V32_REGNUM) < 95)
+    return UNW_IA64_GR + 32 + (regnum - V32_REGNUM);
+  else if ((unsigned) (regnum - IA64_FR0_REGNUM) < 128)
+    return UNW_IA64_FR + (regnum - IA64_FR0_REGNUM);
+  else if ((unsigned) (regnum - IA64_PR0_REGNUM) < 64)
+    return -1;
+  else if ((unsigned) (regnum - IA64_BR0_REGNUM) < 8)
+    return UNW_IA64_BR + (regnum - IA64_BR0_REGNUM);
+  else if (regnum == IA64_PR_REGNUM)
+    return UNW_IA64_PR;
+  else if (regnum == IA64_IP_REGNUM)
+    return UNW_REG_IP;
+  else if (regnum == IA64_CFM_REGNUM)
+    return UNW_IA64_CFM;
+  else if ((unsigned) (regnum - IA64_AR0_REGNUM) < 128)
+    return UNW_IA64_AR + (regnum - IA64_AR0_REGNUM);
+  else if ((unsigned) (regnum - IA64_NAT0_REGNUM) < 128)
+    return UNW_IA64_NAT + (regnum - IA64_NAT0_REGNUM);
+  else
+    return -1;
+}
+  
+/* Gdb libunwind-frame callback function to convert from a libunwind register 
+   number to a ia64 gdb register number.  */
+static int
+ia64_uw2gdb_regnum (int uw_regnum)
+{
+  if (uw_regnum == UNW_IA64_SP)
+    return sp_regnum;
+  else if (uw_regnum == UNW_IA64_BSP)
+    return IA64_BSP_REGNUM;
+  else if ((unsigned) (uw_regnum - UNW_IA64_GR) < 32)
+    return IA64_GR0_REGNUM + (uw_regnum - UNW_IA64_GR);
+  else if ((unsigned) (uw_regnum - UNW_IA64_GR) < 128)
+    return V32_REGNUM + (uw_regnum - (IA64_GR0_REGNUM + 32));
+  else if ((unsigned) (uw_regnum - UNW_IA64_FR) < 128)
+    return IA64_FR0_REGNUM + (uw_regnum - UNW_IA64_FR);
+  else if ((unsigned) (uw_regnum - UNW_IA64_BR) < 8)
+    return IA64_BR0_REGNUM + (uw_regnum - UNW_IA64_BR);
+  else if (uw_regnum == UNW_IA64_PR)
+    return IA64_PR_REGNUM;
+  else if (uw_regnum == UNW_REG_IP)
+    return IA64_IP_REGNUM;
+  else if (uw_regnum == UNW_IA64_CFM)
+    return IA64_CFM_REGNUM;
+  else if ((unsigned) (uw_regnum - UNW_IA64_AR) < 128)
+    return IA64_AR0_REGNUM + (uw_regnum - UNW_IA64_AR);
+  else if ((unsigned) (uw_regnum - UNW_IA64_NAT) < 128)
+    return IA64_NAT0_REGNUM + (uw_regnum - UNW_IA64_NAT);
+  else
+    return -1;
+}
+
+/* Gdb libunwind-frame callback function to reveal if register is a float 
+   register or not.  */
+static int
+ia64_is_fpreg (int uw_regnum)
+{
+  return unw_is_fpreg (uw_regnum);
+}
+  
+/* Libunwind callback accessor function for general registers.  */
+static int
+ia64_access_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;
+  struct frame_info *next_frame = arg;
+  long new_sof, old_sof;
+  char buf[MAX_REGISTER_SIZE];
+  
+  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_reg: to cache: %4s=%016lx\n",
+                           (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                            ? ia64_register_names[regnum] : "r??"), *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.  */
+         frame_unwind_register (next_frame, IA64_IP_REGNUM, buf);
+         ip = extract_unsigned_integer (buf, 8); 
+         frame_unwind_register (next_frame, IA64_PSR_REGNUM, buf);
+         psr = extract_unsigned_integer (buf, 8); 
+         *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.  */
+         frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
+         bsp = extract_unsigned_integer (buf, 8);
+         frame_unwind_register (next_frame, IA64_CFM_REGNUM, buf);
+         cfm = extract_unsigned_integer (buf, 8); 
+         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.  */
+         frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
+         *val = extract_unsigned_integer (buf, 8);
+         break;
+
+       default:
+         /* For all other registers, just unwind the value directly.  */
+         frame_unwind_register (next_frame, regnum, buf);
+         *val = extract_unsigned_integer (buf, 8); 
+         break;
+       }
+      
+      if (gdbarch_debug >= 1)
+       fprintf_unfiltered (gdb_stdlog, 
+                           "  access_reg: from cache: %4s=%016lx\n",
+                           (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                            ? ia64_register_names[regnum] : "r??"), *val);
+    }
+  return 0;
+}
+
+/* Libunwind callback accessor function for floating-point registers.  */
+static int
+ia64_access_fpreg (unw_addr_space_t as, unw_regnum_t uw_regnum, unw_fpreg_t *val, 
+                  int write, void *arg)
+{
+  int regnum = ia64_uw2gdb_regnum (uw_regnum);
+  
+  if (write)
+    regcache_cooked_write (current_regcache, regnum, (char *) val);
+  else
+    regcache_cooked_read (current_regcache, regnum, (char *) 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)
+{
+  /* XXX do we need to normalize byte-order here?  */
+  if (write)
+    return target_write_memory (addr, (char *) val, sizeof (unw_word_t));
+  else
+    return target_read_memory (addr, (char *) val, sizeof (unw_word_t));
+}
+
+/* Call low-level function to access the kernel unwind table.  */
+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);
+
+  return (int)x;
+}
+       
+/* Get the kernel unwind table.  */                             
+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;
+
+  if (!ktab)
+    {
+      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.  */
+      for (etab = ktab; etab->start_offset; ++etab)
+       etab->info_offset += (uint64_t) ktab;
+    }
+  
+  if (ip < ktab[0].start_offset || ip >= etab[-1].end_offset)
+    return -UNW_ENOINFO;
+  
+  di->format = UNW_INFO_FORMAT_TABLE;
+  di->gp = 0;
+  di->start_ip = ktab[0].start_offset;
+  di->end_ip = etab[-1].end_offset;
+  di->u.ti.name_ptr = (unw_word_t) "<kernel>";
+  di->u.ti.segbase = 0;
+  di->u.ti.table_len = ((char *) etab - (char *) ktab) / sizeof (unw_word_t);
+  di->u.ti.table_data = (unw_word_t *) ktab;
+  
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog, "get_kernel_table: found table `%s': "
+                       "segbase=%lx, length=%lu, gp=%lx\n",
+                       (char *) di->u.ti.name_ptr, di->u.ti.segbase, 
+                       di->u.ti.table_len, di->gp);
+  return 0;
+}
+
+/* Find the unwind table entry for a specified address.  */
+static int
+ia64_find_unwind_table (struct objfile *objfile, unw_word_t ip,
+                       unw_dyn_info_t *dip, void **buf)
+{
+  Elf_Internal_Phdr *phdr, *p_text = NULL, *p_unwind = NULL;
+  Elf_Internal_Ehdr *ehdr;
+  unw_word_t segbase = 0;
+  CORE_ADDR load_base;
+  bfd *bfd;
+  int i;
+
+  bfd = objfile->obfd;
+  
+  ehdr = elf_tdata (bfd)->elf_header;
+  phdr = elf_tdata (bfd)->phdr;
+
+  load_base = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
+
+  for (i = 0; i < ehdr->e_phnum; ++i)
+    {
+      switch (phdr[i].p_type)
+       {
+       case PT_LOAD:
+         if ((unw_word_t) (ip - load_base - phdr[i].p_vaddr)
+             < phdr[i].p_memsz)
+           p_text = phdr + i;
+         break;
+
+       case PT_IA_64_UNWIND:
+         p_unwind = phdr + i;
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  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)
+    return -UNW_ENOINFO;
+
+  segbase = p_text->p_vaddr + load_base;
+
+  dip->start_ip = segbase;
+  dip->end_ip = dip->start_ip + p_text->p_memsz;
+  dip->gp = FIND_GLOBAL_POINTER (ip);
+  dip->format = UNW_INFO_FORMAT_REMOTE_TABLE;
+  dip->u.rti.name_ptr = (unw_word_t) bfd_get_filename (bfd);
+  dip->u.rti.segbase = segbase;
+  dip->u.rti.table_len = p_unwind->p_memsz / sizeof (unw_word_t);
+  dip->u.rti.table_data = p_unwind->p_vaddr + load_base;
+
+  return 0;
+}
+
+/* Libunwind callback accessor function to acquire procedure unwind-info.  */
+static int
+ia64_find_proc_info_x (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+                      int need_unwind_info, void *arg)
+{
+  struct obj_section *sec = find_pc_section (ip);
+  unw_dyn_info_t di;
+  int ret;
+  void *buf = NULL;
+
+  if (!sec)
+    {
+      /* XXX This only works if the host and the target architecture are
+        both ia64 and if the have (more or less) the same kernel
+        version.  */
+      if (get_kernel_table (ip, &di) < 0)
+       return -UNW_ENOINFO;
+
+      if (gdbarch_debug >= 1)
+       fprintf_unfiltered (gdb_stdlog, "ia64_find_proc_info_x: %lx -> "
+                           "(name=`%s',segbase=%lx,start=%lx,end=%lx,gp=%lx,"
+                           "length=%lu,data=%p)\n",
+                           ip, (char *)di.u.ti.name_ptr,
+                           di.u.ti.segbase, di.start_ip, di.end_ip,
+                           di.gp, di.u.ti.table_len, di.u.ti.table_data);
+    }
+  else
+    {
+      ret = ia64_find_unwind_table (sec->objfile, ip, &di, &buf);
+      if (ret < 0)
+       return ret;
+
+      if (gdbarch_debug >= 1)
+       fprintf_unfiltered (gdb_stdlog, "ia64_find_proc_info_x: %lx -> "
+                           "(name=`%s',segbase=%lx,start=%lx,end=%lx,gp=%lx,"
+                           "length=%lu,data=%lx)\n",
+                           ip, (char *)di.u.rti.name_ptr,
+                           di.u.rti.segbase, di.start_ip, di.end_ip,
+                           di.gp, di.u.rti.table_len, di.u.rti.table_data);
+    }
+
+  ret = libunwind_search_unwind_table (&as, ip, &di, pi, need_unwind_info,
+                                      arg);
+
+  /* We no longer need the dyn info storage so free it.  */
+  xfree (buf);
+
+  return ret;
+}
+
+/* Libunwind callback accessor function for cleanup.  */
+static void
+ia64_put_unwind_info (unw_addr_space_t as,
+                     unw_proc_info_t *pip, void *arg)
+{
+  /* Nothing required for now.  */
+}
+
+/* Libunwind callback accessor function to get head of the dynamic 
+   unwind-info registration list.  */ 
+static int
+ia64_get_dyn_info_list (unw_addr_space_t as,
+                       unw_word_t *dilap, void *arg)
+{
+  struct obj_section *text_sec;
+  struct objfile *objfile;
+  unw_word_t ip, addr;
+  unw_dyn_info_t di;
+  int ret;
+
+  if (!libunwind_is_initialized ())
+    return -UNW_ENOINFO;
+
+  for (objfile = object_files; objfile; objfile = objfile->next)
+    {
+      void *buf = NULL;
+
+      text_sec = objfile->sections + SECT_OFF_TEXT (objfile);
+      ip = text_sec->addr;
+      ret = ia64_find_unwind_table (objfile, ip, &di, &buf);
+      if (ret >= 0)
+       {
+         addr = libunwind_find_dyn_list (as, &di, arg);
+         /* We no longer need the dyn info storage so free it.  */
+         xfree (buf);
+
+         if (addr)
+           {
+             if (gdbarch_debug >= 1)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "dynamic unwind table in objfile %s "
+                                   "at %lx (gp=%lx)\n",
+                                   bfd_get_filename (objfile->obfd),
+                                   addr, di.gp);
+             *dilap = addr;
+             return 0;
+           }
+       }
+    }
+  return -UNW_ENOINFO;
+}
+
+
+/* Frame interface functions for libunwind.  */
+
+static void
+ia64_libunwind_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;
+
+  libunwind_frame_this_id (next_frame, this_cache, &id);
+
+  /* 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 (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "libunwind frame id: code %lx, stack %lx, special %lx, next_frame %p\n",
+                       id.code_addr, id.stack_addr, bsp, next_frame);
+}
+
+static void
+ia64_libunwind_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)
+{
+  int reg = regnum;
+
+  if (VP0_REGNUM <= regnum && regnum <= VP63_REGNUM)
+    reg = IA64_PR_REGNUM;
+  else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT127_REGNUM)
+    reg = IA64_UNAT_REGNUM;
+
+  /* Let libunwind do most of the work.  */
+  libunwind_frame_prev_register (next_frame, this_cache, reg,
+                                optimizedp, lvalp, addrp, realnump, valuep);
+
+  if (VP0_REGNUM <= regnum && regnum <= VP63_REGNUM)
+    {
+      ULONGEST prN_val;
+
+      if (VP16_REGNUM <= regnum && regnum <= VP63_REGNUM)
+       {
+         int rrb_pr = 0;
+         ULONGEST cfm;
+         unsigned char buf[MAX_REGISTER_SIZE];
+
+         /* Fetch predicate register rename base from current frame
+            marker for this frame.  */
+         frame_unwind_register (next_frame, IA64_CFM_REGNUM, buf);
+         cfm = extract_unsigned_integer (buf, 8); 
+         rrb_pr = (cfm >> 32) & 0x3f;
+         
+         /* Adjust the register number to account for register rotation.  */
+         regnum = VP16_REGNUM 
+           + ((regnum - VP16_REGNUM) + rrb_pr) % 48;
+       }
+      prN_val = extract_bit_field ((unsigned char *) valuep,
+                                  regnum - VP0_REGNUM, 1);
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), prN_val);
+    }
+  else if (IA64_NAT0_REGNUM <= regnum && regnum <= IA64_NAT127_REGNUM)
+    {
+      ULONGEST unatN_val;
+
+      unatN_val = extract_bit_field ((unsigned char *) valuep,
+                                   regnum - IA64_NAT0_REGNUM, 1);
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), 
+                              unatN_val);
+    }
+  else if (regnum == IA64_BSP_REGNUM)
+    {
+      char cfm_valuep[MAX_REGISTER_SIZE];
+      int  cfm_optim;
+      int  cfm_realnum;
+      enum lval_type cfm_lval;
+      CORE_ADDR cfm_addr;
+      CORE_ADDR bsp, prev_cfm, prev_bsp;
+
+      /* We want to calculate the previous bsp as the end of the previous register stack frame.
+        This corresponds to what the hardware bsp register will be if we pop the frame
+        back which is why we might have been called.  We know that libunwind will pass us back
+        the beginning of the current frame so we should just add sof to it. */
+      prev_bsp = extract_unsigned_integer (valuep, 8);
+      libunwind_frame_prev_register (next_frame, this_cache, IA64_CFM_REGNUM,
+                                    &cfm_optim, &cfm_lval, &cfm_addr, &cfm_realnum, cfm_valuep);
+      prev_cfm = extract_unsigned_integer (cfm_valuep, 8);
+      prev_bsp = rse_address_add (prev_bsp, (prev_cfm & 0x7f));
+
+      store_unsigned_integer (valuep, register_size (current_gdbarch, regnum), 
+                             prev_bsp);
+    }
+
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "libunwind prev register <%s> is %lx\n",
+                       (((unsigned) regnum <= IA64_NAT127_REGNUM)
+                        ? ia64_register_names[regnum] : "r??"), extract_unsigned_integer (valuep, 8));
+}
+
+static const struct frame_unwind ia64_libunwind_frame_unwind =
+{
+  NORMAL_FRAME,
+  ia64_libunwind_frame_this_id,
+  ia64_libunwind_frame_prev_register
+};
+
+static const struct frame_unwind *
+ia64_libunwind_frame_sniffer (struct frame_info *next_frame)
+{
+  if (libunwind_is_initialized () && libunwind_frame_sniffer (next_frame))
+    return &ia64_libunwind_frame_unwind;
+
+  return NULL;
+}
+
+/* Set of libunwind callback acccessor functions.  */
+static unw_accessors_t ia64_unw_accessors =
+{
+  ia64_find_proc_info_x,
+  ia64_put_unwind_info,
+  ia64_get_dyn_info_list,
+  ia64_access_mem,
+  ia64_access_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 =
+{
+  ia64_gdb2uw_regnum, 
+  ia64_uw2gdb_regnum, 
+  ia64_is_fpreg, 
+  &ia64_unw_accessors,
+};
+
+#endif /* HAVE_LIBUNWIND_IA64_H  */
+
 /* Should we use EXTRACT_STRUCT_VALUE_ADDRESS instead of
    EXTRACT_RETURN_VALUE?  GCC_P is true if compiled with gcc
    and TYPE is the type (which is known to be struct, union or array).  */
@@ -2293,7 +3020,8 @@ find_func_descr (CORE_ADDR faddr, CORE_ADDR *fdaptr)
    so the user can see the function address rather than just the
    function descriptor.  */
 static CORE_ADDR
-ia64_convert_from_func_ptr_addr (CORE_ADDR addr)
+ia64_convert_from_func_ptr_addr (struct gdbarch *gdbarch, CORE_ADDR addr,
+                                struct target_ops *targ)
 {
   struct obj_section *s;
 
@@ -2473,12 +3201,20 @@ static struct frame_id
 ia64_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
 {
   char buf[8];
-  CORE_ADDR sp;
+  CORE_ADDR sp, bsp;
 
   frame_unwind_register (next_frame, sp_regnum, buf);
   sp = extract_unsigned_integer (buf, 8);
 
-  return frame_id_build (sp, frame_pc_unwind (next_frame));
+  frame_unwind_register (next_frame, IA64_BSP_REGNUM, buf);
+  bsp = extract_unsigned_integer (buf, 8);
+
+  if (gdbarch_debug >= 1)
+    fprintf_unfiltered (gdb_stdlog,
+                       "dummy frame id: code %lx, stack %lx, special %lx\n",
+                       frame_pc_unwind (next_frame), sp, bsp);
+
+  return frame_id_build_special (sp, frame_pc_unwind (next_frame), bsp);
 }
 
 static CORE_ADDR 
@@ -2579,47 +3315,21 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 {
   struct gdbarch *gdbarch;
   struct gdbarch_tdep *tdep;
-  int os_ident;
 
-  if (info.abfd != NULL
-      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
-    {
-      os_ident = elf_elfheader (info.abfd)->e_ident[EI_OSABI];
-
-      /* If os_ident is 0, it is not necessarily the case that we're
-         on a SYSV system.  (ELFOSABI_NONE is defined to be 0.)
-         GNU/Linux uses a note section to record OS/ABI info, but
-         leaves e_ident[EI_OSABI] zero.  So we have to check for note
-         sections too.  */
-      if (os_ident == 0)
-       {
-         bfd_map_over_sections (info.abfd,
-                                process_note_abi_tag_sections,
-                                &os_ident);
-       }
-    }
-  else
-    os_ident = -1;
-
-  for (arches = gdbarch_list_lookup_by_info (arches, &info);
-       arches != NULL;
-       arches = gdbarch_list_lookup_by_info (arches->next, &info))
-    {
-      tdep = gdbarch_tdep (arches->gdbarch);
-      if (tdep &&tdep->os_ident == os_ident)
-       return arches->gdbarch;
-    }
+  /* If there is already a candidate, use it.  */
+  arches = gdbarch_list_lookup_by_info (arches, &info);
+  if (arches != NULL)
+    return arches->gdbarch;
 
   tdep = xmalloc (sizeof (struct gdbarch_tdep));
   gdbarch = gdbarch_alloc (&info, tdep);
-  tdep->os_ident = os_ident;
 
   /* Set the method of obtaining the sigcontext addresses at which
      registers are saved.  The method of checking to see if
      native_find_global_pointer is nonzero to indicate that we're
      on AIX is kind of hokey, but I can't think of a better way
      to do it.  */
-  if (os_ident == ELFOSABI_LINUX)
+  if (info.osabi == GDB_OSABI_LINUX)
     tdep->sigcontext_register_address = ia64_linux_sigcontext_register_address;
   else if (native_find_global_pointer != 0)
     tdep->sigcontext_register_address = ia64_aix_sigcontext_register_address;
@@ -2633,7 +3343,7 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
      generic_elf_find_global_pointer.  This arrangement should (in
      theory) allow us to cross debug GNU/Linux binaries from an AIX
      machine.  */
-  if (os_ident == ELFOSABI_LINUX)
+  if (info.osabi == GDB_OSABI_LINUX)
     tdep->find_global_pointer = generic_elf_find_global_pointer;
   else if (native_find_global_pointer != 0)
     tdep->find_global_pointer = native_find_global_pointer;
@@ -2646,6 +3356,16 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
                0, "builtin_type_ia64_ext", NULL);
   TYPE_FLOATFORMAT (builtin_type_ia64_ext) = &floatformat_ia64_ext;
 
+  /* According to the ia64 specs, instructions that store long double
+     floats in memory use a long-double format different than that
+     used in the floating registers.  The memory format matches the
+     x86 extended float format which is 80 bits.  An OS may choose to
+     use this format (e.g. GNU/Linux) or choose to use a different
+     format for storing long doubles (e.g. HPUX).  In the latter case,
+     the setting of the format may be moved/overridden in an
+     OS-specific tdep file.  */
+  set_gdbarch_long_double_format (gdbarch, &floatformat_i387_ext);
+
   set_gdbarch_short_bit (gdbarch, 16);
   set_gdbarch_int_bit (gdbarch, 32);
   set_gdbarch_long_bit (gdbarch, 64);
@@ -2695,6 +3415,10 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   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_frame_sniffer);
+  libunwind_frame_set_descr (gdbarch, &ia64_libunwind_descr);
+#endif
   frame_unwind_append_sniffer (gdbarch, ia64_frame_sniffer);
   frame_base_set_default (gdbarch, &ia64_frame_base);
 
This page took 0.038748 seconds and 4 git commands to generate.