This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Borland fastcall support (i386)
- From: Jonas Maebe <jonas dot maebe at elis dot ugent dot be>
- To: gdb-patches at sourceware dot org
- Date: Fri, 3 Apr 2009 15:38:13 +0200
- Subject: Borland fastcall support (i386)
Hello,
I've got a first version ready of a patch to add support for the
Borland fastcall calling convention. It is the default calling
convention of Borland/CodeGear/Embarcadero Delphi, and because of that
also of the Free Pascal Compiler (FPC) on i386 platforms.
This probably cannot (yet) be committed as is, because
a) my FSF copyright assignment is not yet finalized (waiting for the
contracts department of my employer to approve)
b) one of the modified headers (include/elf/dwarf2.h) appears to be
imported from gcc, so I guess I should submit that change to gcc first
or instead?
c) the patch is against Archer rather than against CVS head (although
I don't know how different they are as far as the modified files are
concerned)
I've verified its basic operation on Mac OS X/i386 by modifying FPC to
add the appropriate calling convention attribute to the DWARF info and
calling a bunch of different procedures and functions in a small test
program (with/without structured function result, with/without
parameters on the stack).
I'm mainly posting it now already to get comments on whether this the
correct approach, and to see whether I didn't make any obvious
mistakes against the coding guidelines or other conventions. The
processing of the stack paramaters is basically a copy/paste from
i386_push_dummy_call, except that it goes through the parameters in
the opposite order and skips register parameters.
Regarding the constant chosen for the calling convention: as I
mentioned on the gdb list, there is no procedure to reserve such new
identifiers, so I've simply picked the next one available. My mail to
the DWARF list on this topic did result in a call to document all
extensions at http://wiki.dwarfstd.org/index.php?
title=Vendor_Extensions though, so it is easier to avoid multiple
vendors creating overlapping/conflicting extensions.
Thanks,
Jonas
PS: I'm not subscribed to this list, so please CC me in replies.
PS2: I'm not sure if the check_typedef() I used is necessary. It's
used in some places and not in others, and I'm not really sure when it
is required and when not.
2009-04-03 Jonas Maebe <jonas.maebe@elis.ugent.be>
Add support for the "Borland fastcall" calling convention.
* elf/dwarf2.h: Add DW_CC_GNU_borland_fastcall_i386 constant.
* i386-tdep.c: #include elf/dwarf2.h
(i386_push_dummy_borland_fast_call): New.
(i386_push_dummy_generic_call): New dispatch function that calls
i386_push_dummy_call or i386_push_dummy_borland_fast_call depending
on the calling convention.
(i386_gdbarch_init): Install i386_push_dummy_generic_call as
push_dummy_call handler.
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index b74270a..11e83c1 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -50,6 +50,8 @@
#include "i386-tdep.h"
#include "i387-tdep.h"
+#include "elf/dwarf2.h"
+
/* Register names. */
static char *i386_register_names[] =
@@ -1753,6 +1755,151 @@ i386_push_dummy_call (struct gdbarch *gdbarch,
struct value *function,
return sp + 8;
}
+/* Borland fastcall: register parameters are passed from left to
+ right, stack parameters from right to left. The first three
unstructured
+ parameters <= 32 bits are passed in eax, edx and ecx. The others
are passed
+ on the stack. Furthermore, in case of a struct return by
reference, the
+ address of this struct is passed as the last parameter. */
+static CORE_ADDR
+i386_push_dummy_borland_fast_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)
+{
+ static const int para_regs[3] = {I386_EAX_REGNUM, I386_EDX_REGNUM,
I386_ECX_REGNUM};
+
+ gdb_byte buf[4];
+ int reg_paras[3] = {-1, -1, -1};
+ int i, j;
+ int write_pass;
+ int para_regnum = 0;
+
+ /* First assign the register parameters (left to right). */
+ for (i = 0; i < nargs && para_regnum < 3; i++)
+ {
+ struct type *type = check_typedef(value_enclosing_type
(args[i]));
+ int len = TYPE_LENGTH (type);
+
+ if (len <= 4
+ && TYPE_CODE(type) != TYPE_CODE_ARRAY
+ && TYPE_CODE(type) != TYPE_CODE_STRUCT
+ && TYPE_CODE(type) != TYPE_CODE_FLT)
+ {
+ regcache_cooked_write (regcache, para_regs[para_regnum],
+ value_contents_all (args[i]));
+ reg_paras[para_regnum] = i;
+ para_regnum++;
+ }
+ }
+ if (struct_return)
+ {
+ if (para_regnum < 3)
+ {
+ store_unsigned_integer (buf, 4, struct_addr);
+ regcache_cooked_write (regcache, para_regs[para_regnum],
+ buf);
+ reg_paras[para_regnum] = nargs;
+ para_regnum++;
+ }
+ }
+
+ /* Now process the stack parameters from right to left. */
+ for (write_pass = 0; write_pass < 2; write_pass++)
+ {
+ int args_space = 0;
+ int have_16_byte_aligned_arg = 0;
+
+ for (i = nargs - 1; i >= 0; i--)
+ {
+ struct type *type = check_typedef(value_enclosing_type (args[i]));
+ int len = TYPE_LENGTH (type);
+ int processed = 0;
+
+ /* Skip parameters already assigned to registers. */
+ for (j = 0; j < para_regnum; j++)
+ if (reg_paras[j] == i)
+ {
+ processed = 1;
+ break;
+ }
+ if (processed)
+ continue;
+
+ if (i386_16_byte_align_p (value_enclosing_type (args[i])))
+ {
+ args_space = align_up (args_space, 16);
+ have_16_byte_aligned_arg = 1;
+ }
+ if (write_pass)
+ {
+ write_memory (sp + args_space,
+ value_contents_all (args[i]), len);
+ }
+ args_space += align_up (len, 4);
+ }
+
+ /* If we have a struct_return then para_regnum cannot be 0,
since at
+ least this return struct would have been passed in a register.
+ Additionally, if it is passed via a register, it will always be in
+ last used position of the reg_paras array. */
+ if (struct_return
+ && reg_paras[para_regnum-1] != nargs)
+ {
+ if (write_pass)
+ {
+ /* Push value address. */
+ store_unsigned_integer (buf, 4, struct_addr);
+ write_memory (sp + args_space, buf, 4);
+ }
+ args_space += 4;
+ }
+
+ if (!write_pass)
+ {
+ /* Early exit if nothing to do. */
+ if (!args_space)
+ break;
+ if (have_16_byte_aligned_arg)
+ args_space = align_up (args_space, 16);
+ sp -= args_space;
+
+ }
+ }
+
+ /* Store return address. */
+ sp -= 4;
+ store_unsigned_integer (buf, 4, bp_addr);
+ write_memory (sp, buf, 4);
+
+ /* Finally, update the stack pointer... */
+ store_unsigned_integer (buf, 4, sp);
+ regcache_cooked_write (regcache, I386_ESP_REGNUM, buf);
+
+ /* ...and fake a frame pointer. */
+ regcache_cooked_write (regcache, I386_EBP_REGNUM, buf);
+
+ /* See the end of i386_push_dummy_call. */
+ return sp + 8;
+}
+
+static CORE_ADDR
+i386_push_dummy_generic_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)
+{
+ struct type *type = check_typedef (value_type (function));
+ if (type
+ && TYPE_CALLING_CONVENTION (type) ==
DW_CC_GNU_borland_fastcall_i386)
+ return i386_push_dummy_borland_fast_call (gdbarch, function,
regcache,
+ bp_addr, nargs, args, sp,
+ struct_return, struct_addr);
+ else
+ return i386_push_dummy_call (gdbarch, function, regcache,
bp_addr, nargs,
+ args, sp, struct_return, struct_addr);
+}
+
/* These registers are used for returning integers (and on some
targets also for returning `struct' and `union' values when their
size and alignment match an integer type). */
@@ -2799,7 +2946,7 @@ i386_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
set_gdbarch_get_longjmp_target (gdbarch, i386_get_longjmp_target);
/* Call dummy code. */
- set_gdbarch_push_dummy_call (gdbarch, i386_push_dummy_call);
+ set_gdbarch_push_dummy_call (gdbarch, i386_push_dummy_generic_call);
set_gdbarch_convert_register_p (gdbarch, i386_convert_register_p);
set_gdbarch_register_to_value (gdbarch, i386_register_to_value);
diff --git a/include/elf/dwarf2.h b/include/elf/dwarf2.h
index f8d010b..915d40d 100644
--- a/include/elf/dwarf2.h
+++ b/include/elf/dwarf2.h
@@ -662,7 +662,8 @@ enum dwarf_calling_convention
DW_CC_normal = 0x1,
DW_CC_program = 0x2,
DW_CC_nocall = 0x3,
- DW_CC_GNU_renesas_sh = 0x40
+ DW_CC_GNU_renesas_sh = 0x40,
+ DW_CC_GNU_borland_fastcall_i386 = 0x41
};
#define DW_CC_lo_user 0x40