x32 ABI Support (was Re: Three weeks to branching (gdb 7.5 release))

Mark Kettenis
2012-05-28T20:26:28Z

> Date: Sun, 20 May 2012 15:48:54 -0700
> From: "H.J. Lu" <hjl.tools@gmail.com>
>
> Does this one look OK. I extracted x32_init_abi from amd64_x32_init_abi
> since amd64_x32_linux_init_abi can't call amd64_init_abi after
> calling amd64_linux_init_abi.

I guess multiple-inheritance is a bad idea, even when implemented in C ;)

I really do think that amd64_x32_linux_init_abi() should call
amd64_x32_init_abi(). That way it is immediately obvious that the OS-specific ABI inherits everything from the generic ABI.

In order too avoid too much code duplication, the common bits should
be split out from amd64_linux_init_abi() into a seperate function that
gets called from both amd64_linux_init_abi() and
amd64_x32_linux_init_abi(). As I wrote earlier, it isn't entirely
obvious that everything in amd64_linix_init_abi() applies to the x32
ABI. So we should be conservative in moving stuff into the common
function. In fact it might be a good idea to start out with something
like the attached diff, and gradually move things over.

Cheers,

Mark

Index: amd64-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/amd64-linux-tdep.c,v
retrieving revision 1.50
diff -u -p -r1.50 amd64-linux-tdep.c
--- amd64-linux-tdep.c 12 May 2012 08:54:03 -0000 1.50
+++ amd64-linux-tdep.c 28 May 2012 20:24:28 -0000
@@ -1298,8 +1298,6 @@ amd64_linux_init_abi (struct gdbarch_inf

gdb_assert (tdesc_data);

- linux_init_abi (info, gdbarch);
-
tdep->gregset_reg_offset = amd64_linux_gregset_reg_offset;
tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
tdep->sizeof_gregset = 27 * 8;
@@ -1323,6 +1321,8 @@ amd64_linux_init_abi (struct gdbarch_inf
if (!valid_p)
return;

+ linux_init_abi (info, gdbarch);
+
tdep->sigtramp_p = amd64_linux_sigtramp_p;
tdep->sigcontext_addr = amd64_linux_sigcontext_addr;
tdep->sc_reg_offset = amd64_linux_sc_reg_offset;
@@ -1543,6 +1543,44 @@ amd64_linux_init_abi (struct gdbarch_inf

tdep->i386_syscall_record = amd64_linux_syscall_record;
}
+
+static void
+amd64_x32_linux_init_abi(struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ const struct target_desc *tdesc = info.target_desc;
+
+ gdb_assert (tdesc_data);
+
+ tdep->gregset_reg_offset = amd64_linux_gregset_reg_offset;
+ tdep->gregset_num_regs = ARRAY_SIZE (amd64_linux_gregset_reg_offset);
+ tdep->sizeof_gregset = 27 * 8;
+
+ amd64_x32_init_abi (info, gdbarch);
+
+ /* Reserve a number for orig_rax. */
+ set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
+
+ if (! tdesc_has_registers (tdesc))
+ tdesc = tdesc_x32_linux;
+ tdep->tdesc = tdesc;
+
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
+ if (feature == NULL)
+ return;
+
+ valid_p = tdesc_numbered_register (feature, tdesc_data,
+ AMD64_LINUX_ORIG_RAX_REGNUM,
+ "orig_rax");
+ if (!valid_p)
+ return;
+
+ linux_init_abi (info, gdbarch);
+
+ /* GNU/Linux uses SVR4-style shared libraries. */
+ set_solib_svr4_fetch_link_map_offsets
+ (gdbarch, svr4_ilp32_fetch_link_map_offsets);
+}
\f

/* Provide a prototype to silence -Wmissing-prototypes. */
@@ -1553,6 +1591,8 @@ _initialize_amd64_linux_tdep (void)
{
gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64,
GDB_OSABI_LINUX, amd64_linux_init_abi);
+ gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x64_32,
+ GDB_OSABI_LINUX, amd64_x32_linux_init_abi);

/* Initialize the Linux target description. */
initialize_tdesc_amd64_linux ();
Index: amd64-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/amd64-tdep.c,v
retrieving revision 1.104
diff -u -p -r1.104 amd64-tdep.c
--- amd64-tdep.c 14 May 2012 18:56:40 -0000 1.104
+++ amd64-tdep.c 28 May 2012 20:24:29 -0000
@@ -258,7 +258,8 @@ static const char *amd64_word_names[] =
static const char *amd64_dword_names[] =
{
"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
- "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
+ "eip"
};

/* Return the name of register REGNUM. */
@@ -2729,6 +2730,43 @@ amd64_init_abi (struct gdbarch_info info
set_gdbarch_stap_parse_special_token (gdbarch,
i386_stap_parse_special_token);
}
+\f
+
+static struct type *
+amd64_x32_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ switch (regnum - tdep->eax_regnum)
+ {
+ case AMD64_RBP_REGNUM: /* %ebp */
+ case AMD64_RSP_REGNUM: /* %esp */
+ return builtin_type (gdbarch)->builtin_data_ptr;
+ case AMD64_RIP_REGNUM: /* %eip */
+ return builtin_type (gdbarch)->builtin_func_ptr;
+ }
+
+ return i386_pseudo_register_type (gdbarch, regnum);
+}
+
+void
+amd64_x32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ const struct target_desc *tdesc = info.target_desc;
+
+ amd64_init_abi (info, gdbarch);
+
+ if (! tdesc_has_registers (tdesc))
+ tdesc = tdesc_x32;
+ tdep->tdesc = tdesc;
+
+ tdep->num_dword_regs = 17;
+ set_tdesc_pseudo_register_type (gdbarch, amd64_x32_pseudo_register_type);
+
+ set_gdbarch_long_bit (gdbarch, 32);
+ set_gdbarch_ptr_bit (gdbarch, 32);
+}

/* Provide a prototype to silence -Wmissing-prototypes. */
void _initialize_amd64_tdep (void);
Index: amd64-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/amd64-tdep.h,v
retrieving revision 1.21
diff -u -p -r1.21 amd64-tdep.h
--- amd64-tdep.h 4 Jan 2012 08:16:56 -0000 1.21
+++ amd64-tdep.h 28 May 2012 20:24:29 -0000
@@ -80,6 +80,8 @@ extern void amd64_displaced_step_fixup (
struct regcache *regs);

extern void amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch);
+extern void amd64_x32_init_abi (struct gdbarch_info info,
+ struct gdbarch *gdbarch);

/* Fill register REGNUM in REGCACHE with the appropriate
floating-point or SSE register value from *FXSAVE. If REGNUM is