This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Improve windows amd64 call command


Currently, 

mingw64 GDB does a poor job when it comes to 
handling call command.

  This is because it uses the SYS V ABI,
while windows OS uses a separate ABI called Microsoft x64 calling convention
in

http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventi
ons

  This patch removes all failures in gdb.base/call*.exp
when using x86_6'-w64-mingw32-gcc version 4.7.3,
but some of those results might be GCC version dependent...
It would also be nice to  know what happens if a Microsoft compiler
is used...

  The only regression I found in gdb.base is in  varargs.exp:
< FAIL: gdb.base/varargs.exp: print find_max_float_real(4, fc1, fc2, fc3,
fc4)
< FAIL: gdb.base/varargs.exp: print find_max_double_real(4, dc1, dc2, dc3,
dc4)
< FAIL: gdb.base/varargs.exp: print find_max_long_double_real(4, ldc1, ldc2,
ldc3, ldc4)
---
> FAIL: gdb.base/varargs.exp: print find_max_double(5,1.0,17.0,2.0,3.0,4.0)
It's isn't a true regression, as there is only one failure instead of three,
but the one failure seemed to pass without the change...
I didn't investigate that yet.

 The overall change for gdb.base testsuite runs is 253 failures instead of
327
without the patch.

 Comments welcome,


Pierre Muller
GDB pascal language maintainer

2013-09-03  Pierre Muller  <muller@sourceware.org>

	* i386-tdep.h (struct gdbarch_tdep): Add new fields,
	neede to handle amd64 windows specfic calling conventions.
	* amd64-tdep.c (amd64_dummy_call_fp_regs): New array.
	(amd64_return_value): Add debug infrun information.
	(amd64_push_arguments): handle new fields of gdbarch_tdep
	structure.
	(amd64_push_dummy_call): Likewise.
	(amd64_init_abi): Initialize added fields.
	* amd64-windows-tdep.c (amd64_windows_dummy_call_fp_regs): New
array.
	(amd64_windows_classify): Try to comply with Windows ABI.
	(amd64_windows_return_value): Do not use $xmm0 return register
	for "long double" return type.
	(amd64_windows_init_abi): Initialize new fields of gdbarch_tdep.
	

Index: src/gdb/i386-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/i386-tdep.h,v
retrieving revision 1.82
diff -u -p -r1.82 i386-tdep.h
--- src/gdb/i386-tdep.h	1 Jan 2013 06:32:45 -0000	1.82
+++ src/gdb/i386-tdep.h	4 Sep 2013 21:21:34 -0000
@@ -80,6 +80,10 @@ struct gdbarch_tdep
      are passed through the stack on x86.  */
   int call_dummy_num_integer_regs;
   int *call_dummy_integer_regs;
+  /* Used on amd64 only.  The floating point registers used to pass
+     reals when making function calls.  */
+  int call_dummy_num_fp_regs;
+  int *call_dummy_fp_regs;
 
   /* Used on amd64 only.  Classify TYPE according to calling conventions,
      and store the result in CLASS.  */
@@ -104,6 +108,14 @@ struct gdbarch_tdep
      above).  */
   int integer_param_regs_saved_in_caller_frame;
 
+  /* Used on amd64 only.
+
+     If non-zero, integer and floating point registers are coupled:
+     Either ECX or XMM0 are used as first register argument,
+     but second will be EDX or XMM1, never ECX nor XMM0, this
+     coupled behavior is used in Microsoft x64 ABI.  */
+  int integer_and_fp_regs_coupled;
+
   /* Floating-point registers.  */
   struct regset *fpregset;
   size_t sizeof_fpregset;
Index: src/gdb/amd64-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/amd64-tdep.c,v
retrieving revision 1.117
diff -u -p -r1.117 amd64-tdep.c
--- src/gdb/amd64-tdep.c	1 Jan 2013 06:32:37 -0000	1.117
+++ src/gdb/amd64-tdep.c	4 Sep 2013 21:21:36 -0000
@@ -103,6 +103,16 @@ static int amd64_dummy_call_integer_regs
   9				/* %r9 */
 };
 
+/* The registers used to pass floating point arguments in function call.
*/
+static int amd64_dummy_call_fp_regs[] =
+{
+  /* %xmm0 ... %xmm7 */
+  AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM,
+  AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3,
+  AMD64_XMM0_REGNUM + 4, AMD64_XMM0_REGNUM + 5,
+  AMD64_XMM0_REGNUM + 6, AMD64_XMM0_REGNUM + 7,
+};
+
 /* DWARF Register Number Mapping as defined in the System V psABI,
    section 3.6.  */
 
@@ -650,6 +660,8 @@ amd64_return_value (struct gdbarch *gdba
 	  read_memory (addr, readbuf, TYPE_LENGTH (type));
 	}
 
+      if (debug_infrun)
+	printf_filtered (_("Return value as memory address in RAX\n"));
       return RETURN_VALUE_ABI_RETURNS_ADDRESS;
     }
 
@@ -674,6 +686,8 @@ amd64_return_value (struct gdbarch *gdba
 	  regcache_raw_write_unsigned (regcache, AMD64_FTAG_REGNUM, 0xfff);
 	}
 
+      if (debug_infrun)
+	printf_filtered (_("Complex X87 return value in ST0/ST1\n"));
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
 
@@ -739,6 +753,9 @@ amd64_return_value (struct gdbarch *gdba
       if (writebuf)
 	regcache_raw_write_part (regcache, regnum, offset, min (len, 8),
 				 writebuf + i * 8);
+      if (debug_infrun)
+	printf_filtered (_("Return value in register %s\n"),
+			 gdbarch_register_name (gdbarch, regnum));
     }
 
   return RETURN_VALUE_REGISTER_CONVENTION;
@@ -753,15 +770,10 @@ amd64_push_arguments (struct regcache *r
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   int *integer_regs = tdep->call_dummy_integer_regs;
   int num_integer_regs = tdep->call_dummy_num_integer_regs;
+  int *sse_regs = tdep->call_dummy_fp_regs;
+  int num_sse_regs = tdep->call_dummy_num_fp_regs;
+  int regs_coupled = tdep->integer_and_fp_regs_coupled;
 
-  static int sse_regnum[] =
-  {
-    /* %xmm0 ... %xmm7 */
-    AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM,
-    AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3,
-    AMD64_XMM0_REGNUM + 4, AMD64_XMM0_REGNUM + 5,
-    AMD64_XMM0_REGNUM + 6, AMD64_XMM0_REGNUM + 7,
-  };
   struct value **stack_args = alloca (nargs * sizeof (struct value *));
   /* An array that mirrors the stack_args array.  For all arguments
      that are passed by MEMORY, if that argument's address also needs
@@ -769,8 +781,10 @@ amd64_push_arguments (struct regcache *r
      that register number (or a negative value otherwise).  */
   int *arg_addr_regno = alloca (nargs * sizeof (int));
   int num_stack_args = 0;
-  int num_elements = 0;
-  int element = 0;
+  int total_64bit_elements = 0;
+  int stack_64bit_elements = 0;
+  int indirect_element = 0;
+  int addr_element = 0;
   int integer_reg = 0;
   int sse_reg = 0;
   int i;
@@ -798,19 +812,27 @@ amd64_push_arguments (struct regcache *r
       for (j = 0; j < 2; j++)
 	{
 	  if (class[j] == AMD64_INTEGER)
-	    needed_integer_regs++;
+	    {
+	      needed_integer_regs++;
+	      if (regs_coupled)
+		needed_sse_regs++;
+	    }
 	  else if (class[j] == AMD64_SSE)
-	    needed_sse_regs++;
+	    {
+	      needed_sse_regs++;
+	      if (regs_coupled)
+		needed_integer_regs++;
+	    }
 	}
 
       /* Check whether enough registers are available, and if the
          argument should be passed in registers at all.  */
       if (integer_reg + needed_integer_regs > num_integer_regs
-	  || sse_reg + needed_sse_regs > ARRAY_SIZE (sse_regnum)
+	  || sse_reg + needed_sse_regs > num_sse_regs
 	  || (needed_integer_regs == 0 && needed_sse_regs == 0))
 	{
 	  /* The argument will be passed on the stack.  */
-	  num_elements += ((len + 7) / 8);
+	  total_64bit_elements += ((len + 7) / 8);
 	  stack_args[num_stack_args] = args[i];
           /* If this is an AMD64_MEMORY argument whose address must also
              be passed in one of the integer registers, reserve that
@@ -818,11 +840,29 @@ amd64_push_arguments (struct regcache *r
              we can store the argument address as soon as we know it.  */
           if (class[0] == AMD64_MEMORY
               && tdep->memory_args_by_pointer
-              && integer_reg < tdep->call_dummy_num_integer_regs)
-            arg_addr_regno[num_stack_args] =
-              tdep->call_dummy_integer_regs[integer_reg++];
+              && integer_reg < num_integer_regs)
+            {
+	      arg_addr_regno[num_stack_args] =
+		tdep->call_dummy_integer_regs[integer_reg++];
+	      if (debug_infrun)
+		printf_filtered (_("Arg. %d (len=%d on stack),"
+				   " addr. in register %s\n"),
+		  i, len, gdbarch_register_name (gdbarch,
+			    arg_addr_regno[num_stack_args]));
+	      if (regs_coupled)
+		sse_reg++;
+	    }
           else
-            arg_addr_regno[num_stack_args] = -1;
+            {
+	      arg_addr_regno[num_stack_args] = -1;
+	      if (tdep->memory_args_by_pointer
+		  && len != 1 && len != 2 && len != 4 && len != 8)
+		total_64bit_elements += 1;
+	      stack_64bit_elements += 1;
+	      if (debug_infrun)
+		printf_filtered (_("Arg. %d (len=%d on stack)\n"),
+		  i, len);
+	    }
           num_stack_args++;
 	}
       else
@@ -842,15 +882,28 @@ amd64_push_arguments (struct regcache *r
 		{
 		case AMD64_INTEGER:
 		  regnum = integer_regs[integer_reg++];
+		  if (debug_infrun)
+		    printf_filtered (_("Arg. %d in register %s\n"),
+		      i, gdbarch_register_name (gdbarch, regnum));
+		  if (regs_coupled)
+		    sse_reg++;
 		  break;
 
 		case AMD64_SSE:
-		  regnum = sse_regnum[sse_reg++];
+		  regnum = sse_regs[sse_reg++];
+		  if (debug_infrun)
+		    printf_filtered (_("Arg. %d in register %s\n"),
+		      i, gdbarch_register_name (gdbarch, regnum));
+		  if (regs_coupled)
+		    integer_reg++;
 		  break;
 
 		case AMD64_SSEUP:
 		  gdb_assert (sse_reg > 0);
-		  regnum = sse_regnum[sse_reg - 1];
+		  if (debug_infrun)
+		    printf_filtered (_("Arg. %d in register %s at offset
8\n"),
+		      i, gdbarch_register_name (gdbarch, regnum));
+		  regnum = sse_regs[sse_reg - 1];
 		  offset = 8;
 		  break;
 
@@ -867,7 +920,7 @@ amd64_push_arguments (struct regcache *r
     }
 
   /* Allocate space for the arguments on the stack.  */
-  sp -= num_elements * 8;
+  sp -= total_64bit_elements * 8;
 
   /* The psABI says that "The end of the input argument area shall be
      aligned on a 16 byte boundary."  */
@@ -878,9 +931,20 @@ amd64_push_arguments (struct regcache *r
     {
       struct type *type = value_type (stack_args[i]);
       const gdb_byte *valbuf = value_contents (stack_args[i]);
-      CORE_ADDR arg_addr = sp + element * 8;
+      int len = TYPE_LENGTH (type);
+      CORE_ADDR arg_addr;
+      int indirect = (arg_addr_regno[i] > 0)
+		     || (tdep->memory_args_by_pointer
+			 && len != 1 && len != 2 && len != 4 && len != 8);
+      if (indirect)
+	arg_addr = sp + (stack_64bit_elements + indirect_element) * 8;
+      else
+	arg_addr = sp + addr_element * 8;
 
-      write_memory (arg_addr, valbuf, TYPE_LENGTH (type));
+      write_memory (arg_addr, valbuf, len);
+      if (debug_infrun)
+	printf_unfiltered (_("Stack arg. %d (len=%d) at addr. %s\n"),
+			   i, len, paddress (gdbarch, arg_addr));
       if (arg_addr_regno[i] >= 0)
         {
           /* We also need to store the address of that argument in
@@ -890,8 +954,34 @@ amd64_push_arguments (struct regcache *r
 
           store_unsigned_integer (buf, 8, byte_order, arg_addr);
           regcache_cooked_write (regcache, arg_addr_regno[i], buf);
+	  if (debug_infrun)
+	    printf_unfiltered (_("Stack arg. %d addr. stored in reg. %s\n"),
+			       i, gdbarch_register_name (gdbarch,
+				    arg_addr_regno[i]));
         }
-      element += ((TYPE_LENGTH (type) + 7) / 8);
+      else if (indirect)
+	{
+          /* We also need to store the address of that argument in
+             the given register.  */
+          gdb_byte buf[8];
+          enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+	  CORE_ADDR arg_addrp = sp + addr_element * 8;
+
+          store_unsigned_integer (buf, 8, byte_order, arg_addr);
+	  write_memory (arg_addrp, buf, 8);
+
+	  if (debug_infrun)
+	    printf_unfiltered (_("Stack arg. %d addr. stored at addr.
%s\n"),
+			       i, paddress (gdbarch, arg_addrp));
+	}
+      if (indirect)
+	{
+	  indirect_element += ((len + 7) / 8);
+	  if (arg_addr_regno[i] < 0)
+	    addr_element += 1;
+	}
+      else
+	addr_element += ((len + 7) / 8);
     }
 
   /* The psABI says that "For calls that may call functions that use
@@ -899,7 +989,7 @@ amd64_push_arguments (struct regcache *r
      containing ellipsis (...) in the declaration) %al is used as
      hidden argument to specify the number of SSE registers used.  */
   regcache_raw_write_unsigned (regcache, AMD64_RAX_REGNUM, sse_reg);
-  return sp; 
+  return sp;
 }
 
 static CORE_ADDR
@@ -913,7 +1003,11 @@ amd64_push_dummy_call (struct gdbarch *g
   gdb_byte buf[8];
 
   /* Pass arguments.  */
+  if (debug_infrun)
+    printf_unfiltered (_("Start sp=%s\n"), paddress (gdbarch, sp));
   sp = amd64_push_arguments (regcache, nargs, args, sp, struct_return);
+  if (debug_infrun)
+    printf_unfiltered (_("sp after push args=%s\n"), paddress (gdbarch,
sp));
 
   /* Pass "hidden" argument".  */
   if (struct_return)
@@ -929,19 +1023,33 @@ amd64_push_dummy_call (struct gdbarch *g
   /* Reserve some memory on the stack for the integer-parameter registers,
      if required by the ABI.  */
   if (tdep->integer_param_regs_saved_in_caller_frame)
-    sp -= tdep->call_dummy_num_integer_regs * 8;
+    {
+      sp -= tdep->call_dummy_num_integer_regs * 8;
+      if (debug_infrun)
+	printf_unfiltered (_("sp after integer reg. saved area=%s\n"),
+			   paddress (gdbarch, sp));
+
+    }
 
   /* Store return address.  */
   sp -= 8;
   store_unsigned_integer (buf, 8, byte_order, bp_addr);
   write_memory (sp, buf, 8);
+  if (debug_infrun)
+    printf_unfiltered (_("Return addr. %s stored at %s\n"),
+		       paddress (gdbarch, bp_addr), paddress (gdbarch, sp));
+
 
   /* Finally, update the stack pointer...  */
   store_unsigned_integer (buf, 8, byte_order, sp);
   regcache_cooked_write (regcache, AMD64_RSP_REGNUM, buf);
+  if (debug_infrun)
+    printf_unfiltered (_("Setting RSP to %s\n"), paddress (gdbarch, sp));
 
   /* ...and fake a frame pointer.  */
   regcache_cooked_write (regcache, AMD64_RBP_REGNUM, buf);
+  if (debug_infrun)
+    printf_unfiltered (_("Setting RBP to %s\n"), paddress (gdbarch, sp));
 
   return sp + 16;
 }
@@ -2924,7 +3032,11 @@ amd64_init_abi (struct gdbarch_info info
   tdep->call_dummy_num_integer_regs =
     ARRAY_SIZE (amd64_dummy_call_integer_regs);
   tdep->call_dummy_integer_regs = amd64_dummy_call_integer_regs;
+  tdep->call_dummy_num_fp_regs =
+    ARRAY_SIZE (amd64_dummy_call_fp_regs);
+  tdep->call_dummy_fp_regs = amd64_dummy_call_fp_regs;
   tdep->classify = amd64_classify;
+  tdep->integer_and_fp_regs_coupled = 0;
 
   set_gdbarch_convert_register_p (gdbarch, i387_convert_register_p);
   set_gdbarch_register_to_value (gdbarch, i387_register_to_value);
Index: src/gdb/amd64-windows-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/amd64-windows-tdep.c,v
retrieving revision 1.17
diff -u -p -r1.17 amd64-windows-tdep.c
--- src/gdb/amd64-windows-tdep.c	2 Sep 2013 09:28:02 -0000	1.17
+++ src/gdb/amd64-windows-tdep.c	4 Sep 2013 21:21:36 -0000
@@ -31,6 +31,7 @@
 #include "coff/i386.h"
 #include "coff/pe.h"
 #include "libcoff.h"
+#include "buildsym.h"
 
 /* The registers used to pass integer arguments during a function call.  */
 static int amd64_windows_dummy_call_integer_regs[] =
@@ -41,6 +42,14 @@ static int amd64_windows_dummy_call_inte
   9                          /* %r9 */
 };
 
+/* The registers used to pass floating point arguments in function call.
*/
+static int amd64_windows_dummy_call_fp_regs[] =
+{
+  /* %xmm0 ... %xmm7 */
+  AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM,
+  AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3,
+};
+
 /* Implement the "classify" method in the gdbarch_tdep structure
    for amd64-windows.  */
 
@@ -64,13 +73,45 @@ amd64_windows_classify (struct type *typ
 	    || TYPE_LENGTH (type) == 4
 	    || TYPE_LENGTH (type) == 8)
 	  {
-	    class[0] = AMD64_INTEGER;
-	    class[1] = AMD64_NO_CLASS;
+	    if (TYPE_NFIELDS (type) == 1)
+	      {
+		struct type *subtype = check_typedef (
+					 TYPE_FIELD_TYPE (type, 0));
+
+		amd64_windows_classify (subtype, class);
+	      }
+	    else
+	      {
+		class[0] = AMD64_INTEGER;
+		class[1] = AMD64_NO_CLASS;
+	      }
 	  }
 	else
 	  class[0] = class[1] = AMD64_MEMORY;
 	break;
 
+      /* Pass all complex types in memory.  */
+      case TYPE_CODE_COMPLEX:
+	  if (TYPE_LENGTH (type) == 8)
+	    {
+	      class[0] = AMD64_INTEGER;
+	      class[1] = AMD64_NO_CLASS;
+	    }
+	  else
+	    class[0] = class[1] = AMD64_MEMORY;
+	break;
+
+      case TYPE_CODE_FLT:
+	/* For "long double" type,
+           GNU C compiler still uses x87 coprocessor.
+	   Parameters are passed by memory address.  */
+	if ((TYPE_CODE (type) == TYPE_CODE_FLT)
+	    && (processing_gcc_compilation != 0)
+	    && ((TYPE_LENGTH(type) == 12) || (TYPE_LENGTH(type) == 16)))
+	  {
+	    class[0] = class[1] = AMD64_MEMORY;
+	    break;
+	  }
       default:
 	/* For all the other types, the conventions are the same as
 	   with the System V ABI.  */
@@ -94,6 +135,11 @@ amd64_windows_return_value (struct gdbar
     {
       case TYPE_CODE_FLT:
       case TYPE_CODE_DECFLOAT:
+	/* GNU C compiler still uses coprocessor by default.  */
+	if ((TYPE_CODE (type) == TYPE_CODE_FLT)
+	    && (processing_gcc_compilation != 0)
+	    && ((len == 12) || (len == 16)))
+	  break;
         /* __m128, __m128i, __m128d, floats, and doubles are returned
            via XMM0.  */
         if (len == 4 || len == 8 || len == 16)
@@ -979,9 +1025,13 @@ amd64_windows_init_abi (struct gdbarch_i
   tdep->call_dummy_num_integer_regs =
     ARRAY_SIZE (amd64_windows_dummy_call_integer_regs);
   tdep->call_dummy_integer_regs = amd64_windows_dummy_call_integer_regs;
+  tdep->call_dummy_num_fp_regs =
+    ARRAY_SIZE (amd64_windows_dummy_call_fp_regs);
+  tdep->call_dummy_fp_regs = amd64_windows_dummy_call_fp_regs;
   tdep->classify = amd64_windows_classify;
   tdep->memory_args_by_pointer = 1;
   tdep->integer_param_regs_saved_in_caller_frame = 1;
+  tdep->integer_and_fp_regs_coupled = 1;
   set_gdbarch_return_value (gdbarch, amd64_windows_return_value);
   set_gdbarch_skip_main_prologue (gdbarch, amd64_skip_main_prologue);
   set_gdbarch_skip_trampoline_code (gdbarch,


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]