Partial fix for PR backtrace/1718.
[deliverable/binutils-gdb.git] / gdb / i386-tdep.c
index 8542ef9f763fbdf661fc06210e8f0b660903e467..d51e162dcc4e55a5d513901239052a2b4050e562 100644 (file)
@@ -503,20 +503,28 @@ i386_analyze_frame_setup (CORE_ADDR pc, CORE_ADDR current_pc,
 
       op = read_memory_unsigned_integer (pc + 1, 1);
 
-      /* Check for some special instructions that might be migrated
-        by GCC into the prologue.  We check for
+      /* Check for some special instructions that might be migrated by
+        GCC into the prologue.  At this point in the prologue, code
+        should only touch the scratch registers %eax, %ecx and %edx,
+        so we check for
 
-           xorl %ebx, %ebx
+           movl $XXX, %eax
+           movl $XXX, %ecx
+           movl $XXX, %edx
+
+        These instructions have opcodes 0xb8, 0xb9 and 0xba.
+
+        We also check for
+
+           xorl %eax, %eax
            xorl %ecx, %ecx
            xorl %edx, %edx
-           xorl %eax, %eax
 
         and the equivalent
 
-           subl %ebx, %ebx
+           subl %eax, %eax
            subl %ecx, %ecx
            subl %edx, %edx
-           subl %eax, %eax
 
         Because of the symmetry, there are actually two ways to
         encode these instructions; with opcode bytes 0x29 and 0x2b
@@ -524,20 +532,34 @@ i386_analyze_frame_setup (CORE_ADDR pc, CORE_ADDR current_pc,
 
         Make sure we only skip these instructions if we later see the
         `movl %esp, %ebp' that actually sets up the frame.  */
-      while (op == 0x29 || op == 0x2b || op == 0x31 || op == 0x33)
+      while ((op >= 0xb8 && op <= 0xba)
+            || op == 0x29 || op == 0x2b
+            || op == 0x31 || op == 0x33)
        {
-         op = read_memory_unsigned_integer (pc + skip + 2, 1);
-         switch (op)
+         if (op >= 0xb8 && op <= 0xba)
            {
-           case 0xdb:  /* %ebx */
-           case 0xc9:  /* %ecx */
-           case 0xd2:  /* %edx */
-           case 0xc0:  /* %eax */
-             skip += 2;
-             break;
-           default:
-             return pc + 1;
+             /* Skip the `movl' instructions cited above.  */
+             skip += 5;
            }
+         else
+           {
+             /* Skip the `subl' and `xorl' instructions cited above.  */
+             op = read_memory_unsigned_integer (pc + skip + 2, 1);
+             switch (op)
+               {
+               case 0xc0:      /* %eax */
+               case 0xc9:      /* %ecx */
+               case 0xd2:      /* %edx */
+                 skip += 2;
+                 break;
+               default:
+                 return pc + 1;
+               }
+           }
+
+         /* If that's all, return now.  */
+         if (current_pc <= pc + skip + 1)
+           return current_pc;
 
          op = read_memory_unsigned_integer (pc + skip + 1, 1);
        }
@@ -1119,7 +1141,7 @@ i386_get_longjmp_target (CORE_ADDR *pc)
 \f
 
 static CORE_ADDR
-i386_push_dummy_call (struct gdbarch *gdbarch, CORE_ADDR func_addr,
+i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
                      struct regcache *regcache, CORE_ADDR bp_addr, int nargs,
                      struct value **args, CORE_ADDR sp, int struct_return,
                      CORE_ADDR struct_addr)
@@ -1352,7 +1374,28 @@ i386_return_value (struct gdbarch *gdbarch, struct type *type,
 
   if ((code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION)
       && !i386_reg_struct_return_p (gdbarch, type))
-    return RETURN_VALUE_STRUCT_CONVENTION;
+    {
+      /* The System V ABI says that:
+
+        "A function that returns a structure or union also sets %eax
+        to the value of the original address of the caller's area
+        before it returns.  Thus when the caller receives control
+        again, the address of the returned object resides in register
+        %eax and can be used to access the object."
+
+        So the ABI guarantees that we can always find the return
+        value just after the function has returned.  */
+
+      if (readbuf)
+       {
+         ULONGEST addr;
+
+         regcache_raw_read_unsigned (regcache, I386_EAX_REGNUM, &addr);
+         read_memory (addr, readbuf, TYPE_LENGTH (type));
+       }
+
+      return RETURN_VALUE_ABI_RETURNS_ADDRESS;
+    }
 
   /* This special case is for structures consisting of a single
      `float' or `double' member.  These structures are returned in
@@ -1587,15 +1630,15 @@ i386_value_to_register (struct frame_info *frame, int regnum,
     }
 }
 \f
-/* Supply register REGNUM from the general-purpose register set REGSET
-   to register cache REGCACHE.  If REGNUM is -1, do this for all
-   registers in REGSET.  */
+/* Supply register REGNUM from the buffer specified by GREGS and LEN
+   in the general-purpose register set REGSET to register cache
+   REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
 
 void
 i386_supply_gregset (const struct regset *regset, struct regcache *regcache,
                     int regnum, const void *gregs, size_t len)
 {
-  const struct gdbarch_tdep *tdep = regset->descr;
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
   const char *regs = gregs;
   int i;
 
@@ -1609,15 +1652,39 @@ i386_supply_gregset (const struct regset *regset, struct regcache *regcache,
     }
 }
 
-/* Supply register REGNUM from the floating-point register set REGSET
-   to register cache REGCACHE.  If REGNUM is -1, do this for all
-   registers in REGSET.  */
+/* Collect register REGNUM from the register cache REGCACHE and store
+   it in the buffer specified by GREGS and LEN as described by the
+   general-purpose register set REGSET.  If REGNUM is -1, do this for
+   all registers in REGSET.  */
+
+void
+i386_collect_gregset (const struct regset *regset,
+                     const struct regcache *regcache,
+                     int regnum, void *gregs, size_t len)
+{
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+  char *regs = gregs;
+  int i;
+
+  gdb_assert (len == tdep->sizeof_gregset);
+
+  for (i = 0; i < tdep->gregset_num_regs; i++)
+    {
+      if ((regnum == i || regnum == -1)
+         && tdep->gregset_reg_offset[i] != -1)
+       regcache_raw_collect (regcache, i, regs + tdep->gregset_reg_offset[i]);
+    }
+}
+
+/* Supply register REGNUM from the buffer specified by FPREGS and LEN
+   in the floating-point register set REGSET to register cache
+   REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
 
 static void
 i386_supply_fpregset (const struct regset *regset, struct regcache *regcache,
                      int regnum, const void *fpregs, size_t len)
 {
-  const struct gdbarch_tdep *tdep = regset->descr;
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
 
   if (len == I387_SIZEOF_FXSAVE)
     {
@@ -1629,6 +1696,28 @@ i386_supply_fpregset (const struct regset *regset, struct regcache *regcache,
   i387_supply_fsave (regcache, regnum, fpregs);
 }
 
+/* Collect register REGNUM from the register cache REGCACHE and store
+   it in the buffer specified by FPREGS and LEN as described by the
+   floating-point register set REGSET.  If REGNUM is -1, do this for
+   all registers in REGSET.  */
+
+static void
+i386_collect_fpregset (const struct regset *regset,
+                      const struct regcache *regcache,
+                      int regnum, void *fpregs, size_t len)
+{
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
+
+  if (len == I387_SIZEOF_FXSAVE)
+    {
+      i387_collect_fxsave (regcache, regnum, fpregs);
+      return;
+    }
+
+  gdb_assert (len == tdep->sizeof_fpregset);
+  i387_collect_fsave (regcache, regnum, fpregs);
+}
+
 /* Return the appropriate register set for the core section identified
    by SECT_NAME and SECT_SIZE.  */
 
@@ -1641,11 +1730,8 @@ i386_regset_from_core_section (struct gdbarch *gdbarch,
   if (strcmp (sect_name, ".reg") == 0 && sect_size == tdep->sizeof_gregset)
     {
       if (tdep->gregset == NULL)
-       {
-         tdep->gregset = XMALLOC (struct regset);
-         tdep->gregset->descr = tdep;
-         tdep->gregset->supply_regset = i386_supply_gregset;
-       }
+       tdep->gregset = regset_alloc (gdbarch, i386_supply_gregset,
+                                     i386_collect_gregset);
       return tdep->gregset;
     }
 
@@ -1654,11 +1740,8 @@ i386_regset_from_core_section (struct gdbarch *gdbarch,
          && sect_size == I387_SIZEOF_FXSAVE))
     {
       if (tdep->fpregset == NULL)
-       {
-         tdep->fpregset = XMALLOC (struct regset);
-         tdep->fpregset->descr = tdep;
-         tdep->fpregset->supply_regset = i386_supply_fpregset;
-       }
+       tdep->fpregset = regset_alloc (gdbarch, i386_supply_fpregset,
+                                      i386_collect_fpregset);
       return tdep->fpregset;
     }
 
This page took 0.026333 seconds and 4 git commands to generate.