2005-07-29 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / gdb / hppa-linux-tdep.c
index 8f3c632a152e7b8d8e2be1c6bf54425351894f25..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
@@ -148,7 +151,7 @@ insns_match_pattern (CORE_ADDR pc,
     {
       char buf[4];
 
-      read_memory_nobpt (npc, 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;
@@ -158,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:
@@ -193,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;
 }
@@ -253,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;
            }
@@ -466,6 +491,93 @@ hppa_linux_sigtramp_unwind_sniffer (struct frame_info *next_frame)
   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;
 
@@ -474,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);
@@ -485,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);
@@ -500,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.025487 seconds and 4 git commands to generate.