2005-07-29 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / gdb / hppa-linux-tdep.c
index 1d7d047ae6a670e6c13b0628aa58b2047ea90248..7a10aec84d3584eea95322355aa95c0596d59b90 100644 (file)
@@ -1,4 +1,4 @@
-/* Target-dependent code for Linux running on PA-RISC, for GDB.
+/* Target-dependent code for GNU/Linux running on PA-RISC, for GDB.
 
    Copyright 2004 Free Software Foundation, Inc.
 
@@ -28,8 +28,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "frame-unwind.h"
 #include "trad-frame.h"
 #include "dwarf2-frame.h"
+#include "value.h"
 #include "hppa-tdep.h"
 
+#include "elf/common.h"
+
 #if 0
 /* Convert DWARF register number REG to the appropriate register
    number used by GDB.  */
@@ -44,7 +47,7 @@ hppa_dwarf_reg_to_regnum (int reg)
   if (reg >= 32 && reg <= 85)
     return HPPA_FP4_REGNUM + (reg - 32);
 
-  warning ("Unmapped DWARF Register #%d encountered\n", reg);
+  warning (_("Unmapped DWARF Register #%d encountered."), reg);
   return -1;
 }
 #endif
@@ -146,7 +149,10 @@ insns_match_pattern (CORE_ADDR pc,
 
   for (i = 0; pattern[i].mask; i++)
     {
-      insn[i] = read_memory_unsigned_integer (npc, 4);
+      char buf[4];
+
+      deprecated_read_memory_nobpt (npc, buf, 4);
+      insn[i] = extract_unsigned_integer (buf, 4);
       if ((insn[i] & pattern[i].mask) == pattern[i].data)
         npc += 4;
       else
@@ -155,24 +161,39 @@ insns_match_pattern (CORE_ADDR pc,
   return 1;
 }
 
+/* The relaxed version of the insn matcher allows us to match from somewhere
+   inside the pattern, by looking backwards in the instruction scheme.  */
 static int
-hppa_linux_in_dyncall (CORE_ADDR pc)
+insns_match_pattern_relaxed (CORE_ADDR pc,
+                            struct insn_pattern *pattern,
+                            unsigned int *insn)
 {
-  static CORE_ADDR dyncall = 0;
+  int pat_len = 0;
+  int offset;
 
-  /* FIXME: if we switch exec files, dyncall should be reinitialized */
-  if (!dyncall)
-    {
-      struct minimal_symbol *minsym;
+  while (pattern[pat_len].mask)
+    pat_len++;
 
-      minsym = lookup_minimal_symbol ("$$dyncall", NULL, NULL);
-      if (minsym)
-       dyncall = SYMBOL_VALUE_ADDRESS (minsym);
-      else
-       dyncall = -1;
+  for (offset = 0; offset < pat_len; offset++)
+    {
+      if (insns_match_pattern (pc - offset * 4,
+                              pattern, insn))
+       return 1;
     }
 
-  return pc == dyncall;
+  return 0;
+}
+
+static int
+hppa_linux_in_dyncall (CORE_ADDR pc)
+{
+  struct unwind_table_entry *u;
+  u = find_unwind_entry (hppa_symbol_address ("$$dyncall"));
+
+  if (!u)
+    return 0;
+       
+  return pc >= u->region_start && pc <= u->region_end;
 }
 
 /* There are several kinds of "trampolines" that we need to deal with:
@@ -190,13 +211,20 @@ hppa_linux_in_solib_call_trampoline (CORE_ADDR pc, char *name)
 {
   unsigned int insn[HPPA_MAX_INSN_PATTERN_LEN];
   int r;
+  struct unwind_table_entry *u;
+
+  /* on hppa-linux, linker stubs have no unwind information.  Since the pattern
+     matching for linker stubs can be quite slow, we try to avoid it if
+     we can.  */
+  u = find_unwind_entry (pc);
 
   r = in_plt_section (pc, name)
       || hppa_linux_in_dyncall (pc)
-      || insns_match_pattern (pc, hppa_import_stub, insn)
-      || insns_match_pattern (pc, hppa_import_pic_stub, insn)
-      || insns_match_pattern (pc, hppa_long_branch_stub, insn)
-      || insns_match_pattern (pc, hppa_long_branch_pic_stub, insn);
+      || (u == NULL
+         && (insns_match_pattern_relaxed (pc, hppa_import_stub, insn)
+             || insns_match_pattern_relaxed (pc, hppa_import_pic_stub, insn)
+             || insns_match_pattern_relaxed (pc, hppa_long_branch_stub, insn)
+             || insns_match_pattern_relaxed (pc, hppa_long_branch_pic_stub, insn)));
 
   return r;
 }
@@ -250,7 +278,7 @@ hppa_linux_skip_trampoline_code (CORE_ADDR pc)
            }
          else
            {
-             error ("Cannot resolve plt stub at 0x%s\n",
+             error (_("Cannot resolve plt stub at 0x%s."),
                     paddr_nz (pc));
              pc = 0;
            }
@@ -282,7 +310,7 @@ hppa_linux_skip_trampoline_code (CORE_ADDR pc)
    Note that with a 2.4 64-bit kernel, the signal context is not properly
    passed back to userspace so the unwind will not work correctly.  */
 static CORE_ADDR
-hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp)
+hppa_linux_sigtramp_find_sigcontext (CORE_ADDR pc)
 {
   unsigned int dummy[HPPA_MAX_INSN_PATTERN_LEN];
   int offs = 0;
@@ -291,6 +319,12 @@ hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp)
   static int pcoffs[] = { 0, 4*4, 5*4 };
   /* offsets to the rt_sigframe structure */
   static int sfoffs[] = { 4*4, 10*4, 10*4 };
+  CORE_ADDR sp;
+
+  /* Most of the time, this will be correct.  The one case when this will
+     fail is if the user defined an alternate stack, in which case the
+     beginning of the stack will not be align_down (pc, 64).  */
+  sp = align_down (pc, 64);
 
   /* rt_sigreturn trampoline:
      3419000x ldi 0, %r25 or ldi 1, %r25   (x = 0 or 2)
@@ -308,7 +342,20 @@ hppa_linux_sigtramp_find_sigcontext (CORE_ADDR sp)
     }
 
   if (offs == 0)
-    return 0;
+    {
+      if (insns_match_pattern (pc, hppa_sigtramp, dummy))
+       {
+         /* sigaltstack case: we have no way of knowing which offset to 
+            use in this case; default to new kernel handling. If this is
+            wrong the unwinding will fail.  */
+         try = 2;
+         sp = pc - pcoffs[try];
+       }
+      else
+      {
+        return 0;
+      }
+    }
 
   /* sp + sfoffs[try] points to a struct rt_sigframe, which contains
      a struct siginfo and a struct ucontext.  struct ucontext contains
@@ -331,7 +378,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame,
 {
   struct gdbarch *gdbarch = get_frame_arch (next_frame);
   struct hppa_linux_sigtramp_unwind_cache *info;
-  CORE_ADDR sp, pc, scptr;
+  CORE_ADDR pc, scptr;
   int i;
 
   if (*this_cache)
@@ -342,8 +389,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame,
   info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
 
   pc = frame_pc_unwind (next_frame);
-  sp = (pc & ~63);
-  scptr = hppa_linux_sigtramp_find_sigcontext (sp);
+  scptr = hppa_linux_sigtramp_find_sigcontext (pc);
 
   /* structure of struct sigcontext:
    
@@ -393,8 +439,7 @@ hppa_linux_sigtramp_frame_unwind_cache (struct frame_info *next_frame,
   info->saved_regs[HPPA_PCOQ_TAIL_REGNUM].addr = scptr;
   scptr += 4;
 
-  info->base = read_memory_unsigned_integer (
-                 info->saved_regs[HPPA_SP_REGNUM].addr, 4);
+  info->base = frame_unwind_register_unsigned (next_frame, HPPA_SP_REGNUM);
 
   return info;
 }
@@ -415,21 +460,12 @@ hppa_linux_sigtramp_frame_prev_register (struct frame_info *next_frame,
                                         int regnum, int *optimizedp,
                                         enum lval_type *lvalp, 
                                         CORE_ADDR *addrp,
-                                        int *realnump, void *bufferp)
+                                        int *realnump, void *valuep)
 {
   struct hppa_linux_sigtramp_unwind_cache *info
     = hppa_linux_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache);
-  int pcoqt = (regnum == HPPA_PCOQ_TAIL_REGNUM);
-
-  if (pcoqt)
-    regnum = HPPA_PCOQ_HEAD_REGNUM;
-
-  trad_frame_prev_register (next_frame, info->saved_regs, regnum,
-                            optimizedp, lvalp, addrp, realnump, bufferp);
-
-  if (pcoqt)
-    store_unsigned_integer (bufferp, 4, 
-                           extract_unsigned_integer (bufferp, 4) + 4);
+  hppa_frame_prev_register_helper (next_frame, info->saved_regs, regnum,
+                                  optimizedp, lvalp, addrp, realnump, valuep);
 }
 
 static const struct frame_unwind hppa_linux_sigtramp_frame_unwind = {
@@ -448,14 +484,100 @@ static const struct frame_unwind *
 hppa_linux_sigtramp_unwind_sniffer (struct frame_info *next_frame)
 {
   CORE_ADDR pc = frame_pc_unwind (next_frame);
-  CORE_ADDR sp = (pc & ~63);
 
-  if (hppa_linux_sigtramp_find_sigcontext (sp))
+  if (hppa_linux_sigtramp_find_sigcontext (pc))
     return &hppa_linux_sigtramp_frame_unwind;
 
   return NULL;
 }
 
+/* Attempt to find (and return) the global pointer for the given
+   function.
+
+   This is a rather nasty bit of code searchs for the .dynamic section
+   in the objfile corresponding to the pc of the function we're trying
+   to call.  Once it finds the addresses at which the .dynamic section
+   lives in the child process, it scans the Elf32_Dyn entries for a
+   DT_PLTGOT tag.  If it finds one of these, the corresponding
+   d_un.d_ptr value is the global pointer.  */
+
+static CORE_ADDR
+hppa_linux_find_global_pointer (struct value *function)
+{
+  struct obj_section *faddr_sect;
+  CORE_ADDR faddr;
+  
+  faddr = value_as_address (function);
+
+  /* Is this a plabel? If so, dereference it to get the gp value.  */
+  if (faddr & 2)
+    {
+      int status;
+      char buf[4];
+
+      faddr &= ~3;
+
+      status = target_read_memory (faddr + 4, buf, sizeof (buf));
+      if (status == 0)
+       return extract_unsigned_integer (buf, sizeof (buf));
+    }
+
+  /* If the address is in the plt section, then the real function hasn't 
+     yet been fixed up by the linker so we cannot determine the gp of 
+     that function.  */
+  if (in_plt_section (faddr, NULL))
+    return 0;
+
+  faddr_sect = find_pc_section (faddr);
+  if (faddr_sect != NULL)
+    {
+      struct obj_section *osect;
+
+      ALL_OBJFILE_OSECTIONS (faddr_sect->objfile, osect)
+       {
+         if (strcmp (osect->the_bfd_section->name, ".dynamic") == 0)
+           break;
+       }
+
+      if (osect < faddr_sect->objfile->sections_end)
+       {
+         CORE_ADDR addr;
+
+         addr = osect->addr;
+         while (addr < osect->endaddr)
+           {
+             int status;
+             LONGEST tag;
+             char buf[4];
+
+             status = target_read_memory (addr, buf, sizeof (buf));
+             if (status != 0)
+               break;
+             tag = extract_signed_integer (buf, sizeof (buf));
+
+             if (tag == DT_PLTGOT)
+               {
+                 CORE_ADDR global_pointer;
+
+                 status = target_read_memory (addr + 4, buf, sizeof (buf));
+                 if (status != 0)
+                   break;
+                 global_pointer = extract_unsigned_integer (buf, sizeof (buf));
+
+                 /* The payoff... */
+                 return global_pointer;
+               }
+
+             if (tag == DT_NULL)
+               break;
+
+             addr += 8;
+           }
+       }
+    }
+  return 0;
+}
+
 /* Forward declarations.  */
 extern initialize_file_ftype _initialize_hppa_linux_tdep;
 
@@ -464,9 +586,11 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* Linux is always ELF.  */
+  /* GNU/Linux is always ELF.  */
   tdep->is_elf = 1;
 
+  tdep->find_global_pointer = hppa_linux_find_global_pointer;
+
   set_gdbarch_write_pc (gdbarch, hppa_linux_target_write_pc);
 
   frame_unwind_append_sniffer (gdbarch, hppa_linux_sigtramp_unwind_sniffer);
@@ -475,14 +599,18 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_solib_svr4_fetch_link_map_offsets
     (gdbarch, svr4_ilp32_fetch_link_map_offsets);
 
-  set_gdbarch_in_solib_call_trampoline
-        (gdbarch, hppa_linux_in_solib_call_trampoline);
+  tdep->in_solib_call_trampoline = hppa_linux_in_solib_call_trampoline;
   set_gdbarch_skip_trampoline_code
        (gdbarch, hppa_linux_skip_trampoline_code);
 
   /* GNU/Linux uses the dynamic linker included in the GNU C Library.  */
   set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
 
+  /* On hppa-linux, currently, sizeof(long double) == 8.  There has been
+     some discussions to support 128-bit long double, but it requires some
+     more work in gcc and glibc first.  */
+  set_gdbarch_long_double_bit (gdbarch, 64);
+
 #if 0
   /* Dwarf-2 unwinding support.  Not yet working.  */
   set_gdbarch_dwarf_reg_to_regnum (gdbarch, hppa_dwarf_reg_to_regnum);
@@ -490,6 +618,10 @@ hppa_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
   frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
 #endif
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+                                             svr4_fetch_objfile_link_map);
 }
 
 void
This page took 0.026997 seconds and 4 git commands to generate.