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

H.J. Lu
2012-06-05T12:58:39Z

On Thu, May 31, 2012 at 11:18 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, May 28, 2012 at 2:18 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> 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.
>>
>
>
> Here is the updated patch.  I added amd64_x32_init for generic x32
> setting.  It can be used by all x32 init_abi functions.  Since
> tdep->tdesc can't be changed after being used, I renamed
> amd64_init_abi/amd64_init_abi to amd64_init/amd64_linux_init
> to take an  argument to set tdep->tdesc properly.  OK for trunk?
>
> Thanks.
>

Hi Mark,

Do you have a chance to take a look at this?

Thanks.

H.J.
> --
> H.J.
> ---
> 2012-05-31  Mark Kettenis  <kettenis@gnu.org>
>            H.J. Lu  <hongjiu.lu@intel.com>
>
>        * amd64-linux-tdep.c (amd64_linux_init_abi): Renamed to ...
>        (amd64_linux_init): This.  Add an argument, amd64_linux_tdesc.
>        (amd64_linux_init_abi): Call amd64_linux_init.
>        (amd64_x32_linux_init_abi): New function.
>        (_initialize_amd64_linux_tdep): Register bfd_mach_x64_32 with
>        amd64_x32_linux_init_abi.
>
>        * amd64-tdep.c (amd64_dword_names): Add "eip".
>        (amd64_x32_pseudo_register_type): New function.
>        (amd64_x32_init): Likewise.
>        (amd64_x32_init_abi): Likewise.
>        (amd64_init_abi): Renamed to ...
>        (amd64_init): This. Add an argument, amd64_tdesc.
>        (amd64_init_abi): Call amd64_init.
>
>        * amd64-tdep.h (amd64_x32_init_abi): New prototype.
>        (amd64_x32_init): Likewise.
>
>        * i386-tdep.c (i386_pseudo_register_type): Make it global.
>
>        * i386-tdep.h (i386_pseudo_register_type): New prototype.
>
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index 42dc89a..23bf318 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -1288,7 +1288,8 @@ amd64_linux_core_read_description (struct
> gdbarch *gdbarch,
>  }
>
>  static void
> -amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> +amd64_linux_init (struct gdbarch_info info, struct gdbarch *gdbarch,
> +                 const struct target_desc *amd64_linux_tdesc)
>  {
>   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
>   const struct target_desc *tdesc = info.target_desc;
> @@ -1310,9 +1311,10 @@ amd64_linux_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch)
>   set_gdbarch_num_regs (gdbarch, AMD64_LINUX_NUM_REGS);
>
>   if (! tdesc_has_registers (tdesc))
> -    tdesc = tdesc_amd64_linux;
> +    tdesc = amd64_linux_tdesc;
>   tdep->tdesc = tdesc;
>
> +  /* tdep->tdesc can't be changed after being used here.  */
>   feature = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.linux");
>   if (feature == NULL)
>     return;
> @@ -1543,6 +1545,24 @@ amd64_linux_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch)
>
>   tdep->i386_syscall_record = amd64_linux_syscall_record;
>  }
> +
> +static void
> +amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> +{
> +  amd64_linux_init (info, gdbarch, tdesc_amd64_linux);
> +}
> +
> +static void
> +amd64_x32_linux_init_abi (struct gdbarch_info info,
> +                         struct gdbarch *gdbarch)
> +{
> +  amd64_linux_init (info, gdbarch, tdesc_x32_linux);
> +  amd64_x32_init (gdbarch);
> +
> +   /* GNU/Linux uses SVR4-style shared libraries.  */
> +  set_solib_svr4_fetch_link_map_offsets
> +    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
> +}
>
>
>
>  /* Provide a prototype to silence -Wmissing-prototypes.  */
> @@ -1553,6 +1573,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 ();
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index df91a51..47f8019 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -258,9 +258,32 @@ 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 GDB type object for the "standard" data type of data in
> +   register REGNUM.  Only used for x32.  */
> +
> +static struct type *
> +amd64_x32_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
> +{
> +  /* Use pointer types for ebp, esp and eip registers in x32.  */
> +  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
> +  switch (regnum - tdep->eax_regnum)
> +    {
> +    default:
> +      break;
> +    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);
> +}
> +
>  /* Return the name of register REGNUM.  */
>
>  static const char *
> @@ -2606,8 +2629,9 @@ static const int amd64_record_regmap[] =
>   AMD64_DS_REGNUM, AMD64_ES_REGNUM, AMD64_FS_REGNUM, AMD64_GS_REGNUM
>  };
>
> -void
> -amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> +static void
> +amd64_init (struct gdbarch_info info, struct gdbarch *gdbarch,
> +           const struct target_desc *amd64_tdesc)
>  {
>   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
>   const struct target_desc *tdesc = info.target_desc;
> @@ -2617,12 +2641,13 @@ amd64_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch)
>   tdep->sizeof_fpregset = I387_SIZEOF_FXSAVE;
>
>   if (! tdesc_has_registers (tdesc))
> -    tdesc = tdesc_amd64;
> +    tdesc = amd64_tdesc;
>   tdep->tdesc = tdesc;
>
>   tdep->num_core_regs = AMD64_NUM_GREGS + I387_NUM_REGS;
>   tdep->register_names = amd64_register_names;
>
> +  /* tdep->tdesc can't be changed after being used here.  */
>   if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx") != NULL)
>     {
>       tdep->ymmh_register_names = amd64_ymmh_names;
> @@ -2730,6 +2755,32 @@ amd64_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch)
>                                        i386_stap_parse_special_token);
>  }
>
> +void
> +amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> +{
> +  amd64_init (info, gdbarch, tdesc_amd64);
> +}
> +
> +void
> +amd64_x32_init (struct gdbarch *gdbarch)
> +{
> +  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);
> +}
> +
> +void
> +amd64_x32_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> +{
> +  amd64_init (info, gdbarch, tdesc_x32);
> +  amd64_x32_init (gdbarch);
> +}
> +
>  /* Provide a prototype to silence -Wmissing-prototypes.  */
>  void _initialize_amd64_tdep (void);
>
> diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
> index 1ed109c..ce47ae7 100644
> --- a/gdb/amd64-tdep.h
> +++ b/gdb/amd64-tdep.h
> @@ -80,6 +80,9 @@ extern void amd64_displaced_step_fixup (struct
> gdbarch *gdbarch,
>                                        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);
> +extern void amd64_x32_init (struct gdbarch *gdbarch);
>
>  /* Fill register REGNUM in REGCACHE with the appropriate
>    floating-point or SSE register value from *FXSAVE.  If REGNUM is
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 5b04505..6333556 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -2780,7 +2780,7 @@ i386_mmx_type (struct gdbarch *gdbarch)
>  /* Return the GDB type object for the "standard" data type of data in
>    register REGNUM.  */
>
> -static struct type *
> +struct type *
>  i386_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
>  {
>   if (i386_mmx_regnum_p (gdbarch, regnum))
> diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
> index f297ae7..a12b1f5 100644
> --- a/gdb/i386-tdep.h
> +++ b/gdb/i386-tdep.h
> @@ -307,6 +307,7 @@ extern int i386_dword_regnum_p (struct gdbarch
> *gdbarch, int regnum);
>  extern int i386_xmm_regnum_p (struct gdbarch *gdbarch, int regnum);
>  extern int i386_ymm_regnum_p (struct gdbarch *gdbarch, int regnum);
>
> +extern struct type *i386_pseudo_register_type (struct gdbarch *, int);
>  extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch,
>                                              int regnum);