This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[RFC] Improve windows amd64 call command
- From: "Pierre Muller" <pierre dot muller at ics-cnrs dot unistra dot fr>
- To: <gdb-patches at sourceware dot org>
- Date: Wed, 4 Sep 2013 23:54:12 +0200
- Subject: [RFC] Improve windows amd64 call command
- Authentication-results: sourceware.org; auth=none
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,