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]

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


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