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

H.J. Lu
2012-05-28T21:18:29Z

On Mon, May 28, 2012 at 1:26 PM, Mark Kettenis <mark.kettenis@xs4all.nl> wrote:
>> 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.

X32 kernel interface are highly OS specific. Different OSes can implement
very different kernel interfaces. The only generic x32 bits are

struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

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);

They are the same for all x32 OSes since they are determined by
hardware, not OS.

> 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.

Linux x32 kernel interface shares > 90% of Linux amd64 kernel
interface (309 system calls out of 337 are the same). See
64-bit system call table in Linux kernel 3.4:

http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=blob;f=arch/x86/syscalls/syscall_64.tbl;h=dd29a9ea27c560a9d2fcb6e1c2983f8b8e9be407;hb=HEAD

I believe we should start with sharing everything between Linux/x32
and Linux/amd64. We can update x32 part as we go.

> 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);
> +}

These won't work for x32. We have to copy almost everything in
amd64_linux_init_abi.

> +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);
> +}
>

I think this is the wrong approach for x32 support. We should
break amd64_x32_init_abi into 2 parts:

1. OS specific part. It should be private to amd64-tdep.c.
2. Generic part. It should be exported from amd64-tdep.c and can
be used by all x32 OSes.

Thanks.

--
H.J.