2004-02-12 Andrew Cagney <cagney@redhat.com>
[deliverable/binutils-gdb.git] / gdb / i386-linux-tdep.c
index 0d6ab40747a15c0827a5b8ea8f26b7dd3ae22010..768a5b6a78bdf033fa4343663b9d504797464a42 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux running on i386's, for GDB.
 
-   Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "value.h"
 #include "regcache.h"
 #include "inferior.h"
+#include "osabi.h"
+#include "reggroups.h"
+#include "solib-svr4.h"
 
-/* For i386_linux_skip_solib_resolver.  */
-#include "symtab.h"
-#include "symfile.h"
-#include "objfiles.h"
-
-#include "solib-svr4.h"                /* For struct link_map_offsets.  */
+#include "gdb_string.h"
 
 #include "i386-tdep.h"
 #include "i386-linux-tdep.h"
+#include "glibc-tdep.h"
 
 /* Return the name of register REG.  */
 
@@ -47,6 +46,20 @@ i386_linux_register_name (int reg)
 
   return i386_register_name (reg);
 }
+
+/* Return non-zero, when the register is in the corresponding register
+   group.  Put the LINUX_ORIG_EAX register in the system group.  */
+static int
+i386_linux_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+                               struct reggroup *group)
+{
+  if (regnum == I386_LINUX_ORIG_EAX_REGNUM)
+    return (group == system_reggroup
+           || group == save_reggroup
+           || group == restore_reggroup);
+  return i386_register_reggroup_p (gdbarch, regnum, group);
+}
+
 \f
 /* Recognizing signal handler frames.  */
 
@@ -64,7 +77,7 @@ i386_linux_register_name (int reg)
 
    The instruction sequence for normal signals is
        pop    %eax
-       mov    $0x77,%eax
+       mov    $0x77, %eax
        int    $0x80
    or 0x58 0xb8 0x77 0x00 0x00 0x00 0xcd 0x80.
 
@@ -86,17 +99,17 @@ i386_linux_register_name (int reg)
    to the ones used by the kernel.  Therefore, these trampolines are
    supported too.  */
 
-#define LINUX_SIGTRAMP_INSN0 (0x58)    /* pop %eax */
-#define LINUX_SIGTRAMP_OFFSET0 (0)
-#define LINUX_SIGTRAMP_INSN1 (0xb8)    /* mov $NNNN,%eax */
-#define LINUX_SIGTRAMP_OFFSET1 (1)
-#define LINUX_SIGTRAMP_INSN2 (0xcd)    /* int */
-#define LINUX_SIGTRAMP_OFFSET2 (6)
+#define LINUX_SIGTRAMP_INSN0   0x58    /* pop %eax */
+#define LINUX_SIGTRAMP_OFFSET0 0
+#define LINUX_SIGTRAMP_INSN1   0xb8    /* mov $NNNN, %eax */
+#define LINUX_SIGTRAMP_OFFSET1 1
+#define LINUX_SIGTRAMP_INSN2   0xcd    /* int */
+#define LINUX_SIGTRAMP_OFFSET2 6
 
 static const unsigned char linux_sigtramp_code[] =
 {
   LINUX_SIGTRAMP_INSN0,                                        /* pop %eax */
-  LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00,                /* mov $0x77,%eax */
+  LINUX_SIGTRAMP_INSN1, 0x77, 0x00, 0x00, 0x00,                /* mov $0x77, %eax */
   LINUX_SIGTRAMP_INSN2, 0x80                           /* int $0x80 */
 };
 
@@ -150,20 +163,20 @@ i386_linux_sigtramp_start (CORE_ADDR pc)
 
 /* This function does the same for RT signals.  Here the instruction
    sequence is
-       mov    $0xad,%eax
+       mov    $0xad, %eax
        int    $0x80
    or 0xb8 0xad 0x00 0x00 0x00 0xcd 0x80.
 
    The effect is to call the system call rt_sigreturn.  */
 
-#define LINUX_RT_SIGTRAMP_INSN0 (0xb8) /* mov $NNNN,%eax */
-#define LINUX_RT_SIGTRAMP_OFFSET0 (0)
-#define LINUX_RT_SIGTRAMP_INSN1 (0xcd) /* int */
-#define LINUX_RT_SIGTRAMP_OFFSET1 (5)
+#define LINUX_RT_SIGTRAMP_INSN0                0xb8 /* mov $NNNN, %eax */
+#define LINUX_RT_SIGTRAMP_OFFSET0      0
+#define LINUX_RT_SIGTRAMP_INSN1                0xcd /* int */
+#define LINUX_RT_SIGTRAMP_OFFSET1      5
 
 static const unsigned char linux_rt_sigtramp_code[] =
 {
-  LINUX_RT_SIGTRAMP_INSN0, 0xad, 0x00, 0x00, 0x00,     /* mov $0xad,%eax */
+  LINUX_RT_SIGTRAMP_INSN0, 0xad, 0x00, 0x00, 0x00,     /* mov $0xad, %eax */
   LINUX_RT_SIGTRAMP_INSN1, 0x80                                /* int $0x80 */
 };
 
@@ -222,49 +235,47 @@ i386_linux_pc_in_sigtramp (CORE_ADDR pc, char *name)
          || strcmp ("__restore_rt", name) == 0);
 }
 
-/* Assuming FRAME is for a GNU/Linux sigtramp routine, return the
-   address of the associated sigcontext structure.  */
+/* Offset to struct sigcontext in ucontext, from <asm/ucontext.h>.  */
+#define I386_LINUX_UCONTEXT_SIGCONTEXT_OFFSET 20
+
+/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp
+   routine, return the address of the associated sigcontext structure.  */
 
 static CORE_ADDR
-i386_linux_sigcontext_addr (struct frame_info *frame)
+i386_linux_sigcontext_addr (struct frame_info *next_frame)
 {
   CORE_ADDR pc;
+  CORE_ADDR sp;
+  char buf[4];
+
+  frame_unwind_register (next_frame, I386_ESP_REGNUM, buf);
+  sp = extract_unsigned_integer (buf, 4);
 
-  pc = i386_linux_sigtramp_start (frame->pc);
+  pc = i386_linux_sigtramp_start (frame_pc_unwind (next_frame));
   if (pc)
     {
-      CORE_ADDR sp;
-
-      if (frame->next)
-       /* If this isn't the top frame, the next frame must be for the
-          signal handler itself.  The sigcontext structure lives on
-          the stack, right after the signum argument.  */
-       return frame->next->frame + 12;
-
-      /* This is the top frame.  We'll have to find the address of the
-        sigcontext structure by looking at the stack pointer.  Keep
-        in mind that the first instruction of the sigtramp code is
-        "pop %eax".  If the PC is at this instruction, adjust the
-        returned value accordingly.  */
-      sp = read_register (SP_REGNUM);
-      if (pc == frame->pc)
+      /* The sigcontext structure lives on the stack, right after
+        the signum argument.  We determine the address of the
+        sigcontext structure by looking at the frame's stack
+        pointer.  Keep in mind that the first instruction of the
+        sigtramp code is "pop %eax".  If the PC is after this
+        instruction, adjust the returned value accordingly.  */
+      if (pc == frame_pc_unwind (next_frame))
        return sp + 4;
       return sp;
     }
 
-  pc = i386_linux_rt_sigtramp_start (frame->pc);
+  pc = i386_linux_rt_sigtramp_start (frame_pc_unwind (next_frame));
   if (pc)
     {
-      if (frame->next)
-       /* If this isn't the top frame, the next frame must be for the
-          signal handler itself.  The sigcontext structure is part of
-          the user context.  A pointer to the user context is passed
-          as the third argument to the signal handler.  */
-       return read_memory_integer (frame->next->frame + 16, 4) + 20;
-
-      /* This is the top frame.  Again, use the stack pointer to find
-        the address of the sigcontext structure.  */
-      return read_memory_integer (read_register (SP_REGNUM) + 8, 4) + 20;
+      CORE_ADDR ucontext_addr;
+
+      /* The sigcontext structure is part of the user context.  A
+        pointer to the user context is passed as the third argument
+        to the signal handler.  */
+      read_memory (sp + 8, buf, 4);
+      ucontext_addr = extract_unsigned_integer (buf, 4);
+      return ucontext_addr + I386_LINUX_UCONTEXT_SIGCONTEXT_OFFSET;
     }
 
   error ("Couldn't recognize signal trampoline.");
@@ -276,7 +287,7 @@ i386_linux_sigcontext_addr (struct frame_info *frame)
 static void
 i386_linux_write_pc (CORE_ADDR pc, ptid_t ptid)
 {
-  write_register_pid (PC_REGNUM, pc, ptid);
+  write_register_pid (I386_EIP_REGNUM, pc, ptid);
 
   /* We must be careful with modifying the program counter.  If we
      just interrupted a system call, the kernel might try to restart
@@ -295,93 +306,6 @@ i386_linux_write_pc (CORE_ADDR pc, ptid_t ptid)
   write_register_pid (I386_LINUX_ORIG_EAX_REGNUM, -1, ptid);
 }
 \f
-/* Calling functions in shared libraries.  */
-
-/* Find the minimal symbol named NAME, and return both the minsym
-   struct and its objfile.  This probably ought to be in minsym.c, but
-   everything there is trying to deal with things like C++ and
-   SOFUN_ADDRESS_MAYBE_TURQUOISE, ...  Since this is so simple, it may
-   be considered too special-purpose for general consumption.  */
-
-static struct minimal_symbol *
-find_minsym_and_objfile (char *name, struct objfile **objfile_p)
-{
-  struct objfile *objfile;
-
-  ALL_OBJFILES (objfile)
-    {
-      struct minimal_symbol *msym;
-
-      ALL_OBJFILE_MSYMBOLS (objfile, msym)
-       {
-         if (SYMBOL_NAME (msym)
-             && STREQ (SYMBOL_NAME (msym), name))
-           {
-             *objfile_p = objfile;
-             return msym;
-           }
-       }
-    }
-
-  return 0;
-}
-
-static CORE_ADDR
-skip_hurd_resolver (CORE_ADDR pc)
-{
-  /* The HURD dynamic linker is part of the GNU C library, so many
-     GNU/Linux distributions use it.  (All ELF versions, as far as I
-     know.)  An unresolved PLT entry points to "_dl_runtime_resolve",
-     which calls "fixup" to patch the PLT, and then passes control to
-     the function.
-
-     We look for the symbol `_dl_runtime_resolve', and find `fixup' in
-     the same objfile.  If we are at the entry point of `fixup', then
-     we set a breakpoint at the return address (at the top of the
-     stack), and continue.
-  
-     It's kind of gross to do all these checks every time we're
-     called, since they don't change once the executable has gotten
-     started.  But this is only a temporary hack --- upcoming versions
-     of GNU/Linux will provide a portable, efficient interface for
-     debugging programs that use shared libraries.  */
-
-  struct objfile *objfile;
-  struct minimal_symbol *resolver 
-    = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile);
-
-  if (resolver)
-    {
-      struct minimal_symbol *fixup
-       = lookup_minimal_symbol ("fixup", NULL, objfile);
-
-      if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc)
-       return (SAVED_PC_AFTER_CALL (get_current_frame ()));
-    }
-
-  return 0;
-}      
-
-/* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c.
-   This function:
-   1) decides whether a PLT has sent us into the linker to resolve
-      a function reference, and 
-   2) if so, tells us where to set a temporary breakpoint that will
-      trigger when the dynamic linker is done.  */
-
-CORE_ADDR
-i386_linux_skip_solib_resolver (CORE_ADDR pc)
-{
-  CORE_ADDR result;
-
-  /* Plug in functions for other kinds of resolvers here.  */
-  result = skip_hurd_resolver (pc);
-  if (result)
-    return result;
-
-  return 0;
-}
-
 /* Fetch (and possibly build) an appropriate link_map_offsets
    structure for native GNU/Linux x86 targets using the struct offsets
    defined in link.h (but without actual reference to that file).
@@ -424,6 +348,71 @@ i386_linux_svr4_fetch_link_map_offsets (void)
 }
 \f
 
+/* The register sets used in GNU/Linux ELF core-dumps are identical to
+   the register sets in `struct user' that are used for a.out
+   core-dumps.  These are also used by ptrace(2).  The corresponding
+   types are `elf_gregset_t' for the general-purpose registers (with
+   `elf_greg_t' the type of a single GP register) and `elf_fpregset_t'
+   for the floating-point registers.
+
+   Those types used to be available under the names `gregset_t' and
+   `fpregset_t' too, and GDB used those names in the past.  But those
+   names are now used for the register sets used in the `mcontext_t'
+   type, which have a different size and layout.  */
+
+/* Mapping between the general-purpose registers in `struct user'
+   format and GDB's register cache layout.  */
+
+/* From <sys/reg.h>.  */
+static int i386_linux_gregset_reg_offset[] =
+{
+  6 * 4,                       /* %eax */
+  1 * 4,                       /* %ecx */
+  2 * 4,                       /* %edx */
+  0 * 4,                       /* %ebx */
+  15 * 4,                      /* %esp */
+  5 * 4,                       /* %ebp */
+  3 * 4,                       /* %esi */
+  4 * 4,                       /* %edi */
+  12 * 4,                      /* %eip */
+  14 * 4,                      /* %eflags */
+  13 * 4,                      /* %cs */
+  16 * 4,                      /* %ss */
+  7 * 4,                       /* %ds */
+  8 * 4,                       /* %es */
+  9 * 4,                       /* %fs */
+  10 * 4,                      /* %gs */
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1,
+  11 * 4                       /* "orig_eax" */
+};
+
+/* Mapping between the general-purpose registers in `struct
+   sigcontext' format and GDB's register cache layout.  */
+
+/* From <asm/sigcontext.h>.  */
+static int i386_linux_sc_reg_offset[] =
+{
+  11 * 4,                      /* %eax */
+  10 * 4,                      /* %ecx */
+  9 * 4,                       /* %edx */
+  8 * 4,                       /* %ebx */
+  7 * 4,                       /* %esp */
+  6 * 4,                       /* %ebp */
+  5 * 4,                       /* %esi */
+  4 * 4,                       /* %edi */
+  14 * 4,                      /* %eip */
+  16 * 4,                      /* %eflags */
+  15 * 4,                      /* %cs */
+  18 * 4,                      /* %ss */
+  3 * 4,                       /* %ds */
+  2 * 4,                       /* %es */
+  1 * 4,                       /* %fs */
+  0 * 4                                /* %gs */
+};
+
 static void
 i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -432,23 +421,23 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* GNU/Linux uses ELF.  */
   i386_elf_init_abi (info, gdbarch);
 
-  /* We support the SSE registers on GNU/Linux.  */
-  tdep->num_xmm_regs = I386_NUM_XREGS - 1;
-  /* set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS); */
-
   /* Since we have the extra "orig_eax" register on GNU/Linux, we have
      to adjust a few things.  */
 
   set_gdbarch_write_pc (gdbarch, i386_linux_write_pc);
-  set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS + 1);
+  set_gdbarch_num_regs (gdbarch, I386_LINUX_NUM_REGS);
   set_gdbarch_register_name (gdbarch, i386_linux_register_name);
-  set_gdbarch_register_bytes (gdbarch, I386_SSE_SIZEOF_REGS + 4);
+  set_gdbarch_register_reggroup_p (gdbarch, i386_linux_register_reggroup_p);
+
+  tdep->gregset_reg_offset = i386_linux_gregset_reg_offset;
+  tdep->gregset_num_regs = ARRAY_SIZE (i386_linux_gregset_reg_offset);
+  tdep->sizeof_gregset = 17 * 4;
 
   tdep->jb_pc_offset = 20;     /* From <bits/setjmp.h>.  */
 
   tdep->sigcontext_addr = i386_linux_sigcontext_addr;
-  tdep->sc_pc_offset = 14 * 4; /* From <asm/sigcontext.h>.  */
-  tdep->sc_sp_offset = 7 * 4;
+  tdep->sc_reg_offset = i386_linux_sc_reg_offset;
+  tdep->sc_num_regs = ARRAY_SIZE (i386_linux_sc_reg_offset);
 
   /* When the i386 Linux kernel calls a signal handler, the return
      address points to a bit of code on the stack.  This function is
@@ -456,6 +445,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
      to support backtracing through calls to signal handlers.  */
   set_gdbarch_pc_in_sigtramp (gdbarch, i386_linux_pc_in_sigtramp);
 
+  set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
   set_solib_svr4_fetch_link_map_offsets (gdbarch,
                                       i386_linux_svr4_fetch_link_map_offsets);
 }
@@ -466,6 +456,6 @@ extern void _initialize_i386_linux_tdep (void);
 void
 _initialize_i386_linux_tdep (void)
 {
-  gdbarch_register_osabi (bfd_arch_i386, GDB_OSABI_LINUX,
+  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_LINUX,
                          i386_linux_init_abi);
 }
This page took 0.028785 seconds and 4 git commands to generate.