[v2,1/2] Enable ILP32 mode in gdb on aarch64

Message ID 1488837762.2866.268.camel@caviumnetworks.com
State New, archived
Headers

Commit Message

Steve Ellcey March 6, 2017, 10:02 p.m. UTC
  This is the first of two new patches for ILP32 support on aarch64.
This one is the gdb changes, the next will be the bfd changes.

The standard gdb testsuite run on 64 bit aarch64 executables results
in 65 failures (30627 passes), running it on 32 bit aarch64 executables
results in 213 failures (30354 passes).

I believe most of the new failures are due to two problems.  One is
unwinding through signal handlers and the other is accessing thread
local data.  I think the thread problem may be due to a version
mismatch between the thread_db library that gdb (a 64 bit process)
is using and a newer thread_db library that the 32 bit executables
being debugged are using and not a problem with the code.

I am working on the signal unwinding problem but I was hoping that
the part that is already working is sufficiently useful to check in
while I work on fixing this problem.  The current code handles the
usual gdb functionality of breakpoints, stepping, printing variables,
etc.

Steve Ellcey
sellcey@cavium.com


gdb ChangeLog:

2017-03-06  Andrew Pinski  <apinski@cavium.com>
	    Steve Ellcey  <sellcey@cavium.com>
	    Yao Qi  <yao.qi@linaro.org>

	* aarch64-linux-nat.c (IS_ARM32): New macro.
	(fetch_gregs_from_thread): Use IS_ARM32 macro.
	(store_gregs_to_thread): Ditto.
	(fetch_fpregs_from_thread): Ditto.
	(store_fpregs_to_thread): Ditto.
	(ps_get_thread_area): Ditto.
	(aarch64_linux_siginfo_fixup): Ditto.
	* aarch64-linux-tdep.c (aarch64_linux_init_abi): Set link
	map offsets to 32 or 64 bits.
	* aarch64-tdep.c (aarch64_ilp32_register_type): New function.
	(aarch64_gdbarch_init): Setup ILP32 support.
	Make sure the gdbarches have compatible ilp32 flags.
	Set long and ptr sizes correctly for ilp32.
	* aarch64-tdep.h (gdbarch_tdep) <ilp32>: New field.

gdbserver ChangeLog:

2017-03-06  Andrew Pinski  <apinski@cavium.com>
	    Steve Ellcey  <sellcey@cavium.com>

	* linux-aarch64-low.c (aarch64_linux_read_description): Use
	machine instead of is_elf64 to determine architecture.  Give an
	error when using 32 bit gdbserver on 64 bit program.
  

Comments

Yao Qi March 7, 2017, 10:20 a.m. UTC | #1
Steve Ellcey <sellcey@caviumnetworks.com> writes:

> I believe most of the new failures are due to two problems.  One is
> unwinding through signal handlers and the other is accessing thread

Do the answers in this thread help?
https://sourceware.org/ml/gdb/2017-02/msg00056.html

> local data.  I think the thread problem may be due to a version
> mismatch between the thread_db library that gdb (a 64 bit process)
> is using and a newer thread_db library that the 32 bit executables

Do you mean libpthread?  thread_db is only used in with gdb.  It can be
the issue you described but could you provide more information?  Just
make sure ptrace NT_ARM_TLS is right on ILP32 between user space and
kernel.

> I am working on the signal unwinding problem but I was hoping that
> the part that is already working is sufficiently useful to check in
> while I work on fixing this problem.  The current code handles the
> usual gdb functionality of breakpoints, stepping, printing variables,
> etc.

The patch can be pushed in after all questions/issues are resolved (I still
have one question asked above) and after ILP32 kernel patches are merged.

> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index 0d472e2..0caac7f 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -49,6 +49,9 @@
>  #define TRAP_HWBKPT 0x0004
>  #endif
>  
> +/* Check if we are on arm (as opposed to aarch64).  */
> +#define IS_ARM32(gdbarch) (gdbarch_bfd_arch_info(gdbarch)->arch == bfd_arch_arm)
> +

This line is too long.

> @@ -460,8 +463,7 @@ ps_err_e
>  ps_get_thread_area (struct ps_prochandle *ph,
>  		    lwpid_t lwpid, int idx, void **base)
>  {
> -  int is_64bit_p
> -    = (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 64);
> +  int is_64bit_p = !IS_ARM32 (target_gdbarch ());
>  
>    return aarch64_ps_get_thread_area (ph, lwpid, idx, base,
>    is_64bit_p);

Can you rename the variable to "is_aarch64_p"?
  
Steve Ellcey March 7, 2017, 7:55 p.m. UTC | #2
On Tue, 2017-03-07 at 10:20 +0000, Yao Qi wrote:

> > I believe most of the new failures are due to two problems.  One is
> > unwinding through signal handlers and the other is accessing thread
> Do the answers in this thread help?
> https://sourceware.org/ml/gdb/2017-02/msg00056.html

I have been looking at the code and offsets used in aarch64_linux_sigframe_init
but I haven't had much luck in understanding the layout of the frames or the
changes needed between the 32 and 64 bit ABIs.

The code has these magic numbers:
/* These magic numbers need to reflect the layout of the kernel
   defined struct rt_sigframe and ucontext.  */
#define AARCH64_SIGCONTEXT_REG_SIZE             8
#define AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET     128
#define AARCH64_UCONTEXT_SIGCONTEXT_OFFSET      176
#define AARCH64_SIGCONTEXT_XO_OFFSET            8

I think AARCH64_SIGCONTEXT_REG_SIZE is OK, registers are still 8 bytes long
in ILP32 mode.

I am not sure what AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET is supposed to
represent.  Is it the size of the rt_sigframe structure?  Or the
offset from rt_sigframe to the sigframe structure inside of rt_sigframe?
(i.e. the size of sigframe).  I have the same problem with the other
magic numbers, I am just not sure what they represent.  I am also not
sure if just changing the offset numbers is all that is needed or if
there is some copying that needs to be done to massage the ILP32 formats
into the expected layout.  I have never done any kernel work and was not
around when the original aarch64 ILP32 work was done so I am not very
familiar with the history of all this.

arch/arm64/kernel/signal.c has:

struct rt_sigframe {
        struct siginfo info;
        struct sigframe sig;
};

but arch/arm64/kernel/signal_ilp32.c (this is part of the kernel
patch set that has been submitted but not yet checked in) has:

struct ilp32_rt_sigframe {
        struct compat_siginfo info;
        struct ilp32_sigframe sig;
};


I think the definitions of siginfo (in LP64 mode from 
include/uapi/asm-generic/siginfo.h and compat_siginfo
from arch/arm64/include/asm/compat.h match up.  So that
should not be different.

sigframe (LP64) is defined in signal.c as:

struct sigframe {
        struct ucontext uc;
        u64 fp;
        u64 lr;
};

struct ucontext {
        unsigned long     uc_flags;
        struct ucontext  *uc_link;
        stack_t           uc_stack;
        struct sigcontext uc_mcontext;
        sigset_t          uc_sigmask;   /* mask last for extensibility */
};

and ilp32_sigframe is:

struct ilp32_sigframe {
        struct ilp32_ucontext uc;
        u64 fp;
        u64 lr;
};
struct ilp32_ucontext {
        u32             uc_flags;
        u32             uc_link;
        compat_stack_t  uc_stack;
        compat_sigset_t uc_sigmask;
        /* glibc uses a 1024-bit sigset_t */
        __u8            __unused[1024 / 8 - sizeof(compat_sigset_t)];
        /* last for future expansion */
        struct sigcontext uc_mcontext;
};

These context structures have differences but I don't know what to do with
them.

Steve Ellcey
sellcey@cavium.com
  
Yao Qi March 9, 2017, 1:24 p.m. UTC | #3
On Tue, Mar 7, 2017 at 7:55 PM, Steve Ellcey <sellcey@caviumnetworks.com> wrote:
> I have been looking at the code and offsets used in aarch64_linux_sigframe_init
> but I haven't had much luck in understanding the layout of the frames or the
> changes needed between the 32 and 64 bit ABIs.
>
> The code has these magic numbers:
> /* These magic numbers need to reflect the layout of the kernel
>    defined struct rt_sigframe and ucontext.  */
> #define AARCH64_SIGCONTEXT_REG_SIZE             8
> #define AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET     128
> #define AARCH64_UCONTEXT_SIGCONTEXT_OFFSET      176
> #define AARCH64_SIGCONTEXT_XO_OFFSET            8
>
> I think AARCH64_SIGCONTEXT_REG_SIZE is OK, registers are still 8 bytes long
> in ILP32 mode.
>
> I am not sure what AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET is supposed to
> represent.  Is it the size of the rt_sigframe structure?  Or the
> offset from rt_sigframe to the sigframe structure inside of rt_sigframe?
> (i.e. the size of sigframe).  I have the same problem with the other

probably because they are magic, :)  the comments above these
macros are

  The handler then needs to recover the saved register set from
  ucontext.uc_mcontext.

to your question, they are about offsets.  That is how I get the
offsets (I am not the author of this piece of code), load vmlinux
into gdb, and examine the offsets,

(gdb) ptype struct ucontext
type = struct ucontext {
    unsigned long uc_flags;
    struct ucontext *uc_link;
    stack_t uc_stack;
    sigset_t uc_sigmask;
    __u8 __unused[120];
    struct sigcontext uc_mcontext;
}

(gdb) p/d  &((struct rt_sigframe *) 0)->uc
$8 = 128

it is AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET.

(gdb) p/d  &((struct rt_sigframe *) 0)->uc->uc_mcontext
$9 = 304

it is AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET
+ AARCH64_UCONTEXT_SIGCONTEXT_OFFSET,

> magic numbers, I am just not sure what they represent.  I am also not
> sure if just changing the offset numbers is all that is needed or if
> there is some copying that needs to be done to massage the ILP32 formats
> into the expected layout.  I have never done any kernel work and was not
> around when the original aarch64 ILP32 work was done so I am not very
> familiar with the history of all this.
>

Assuming ILP32 kernel patches is correct, you can get these
magic number by loading ILP32 kernel vmlinux, and doing these
steps I do above.
  
Steve Ellcey March 9, 2017, 5:33 p.m. UTC | #4
On Thu, 2017-03-09 at 13:24 +0000, Yao Qi wrote:

> Assuming ILP32 kernel patches is correct, you can get these
> magic number by loading ILP32 kernel vmlinux, and doing these
> steps I do above.

The problem is that it looks like this isn't just a case of the offsets
changing, some of the kernel types have changed too and I think that is
the bigger problem.  The problem may be related to this GCC patch
involving exception handling:

	https://gcc.gnu.org/ml/gcc-patches/2017-02/msg00581.html

This patch sets REG_VALUE_IN_UNWIND_CONTEXT, something that is only done
on one other platform, ILP32 mode on x86_64.  I tried looking at the ILP32
support for x86_64 in gdb but it doesn't seem to have any type of
sigframe_init routine.


In the unpatched kernel:

(gdb) ptype struct rt_sigframe
type = struct rt_sigframe {
    struct siginfo info;
    struct ucontext uc;
    u64 fp;
    u64 lr;
}

In the new kernel:

(gdb) ptype struct rt_sigframe
type = struct rt_sigframe {
    struct siginfo info;
    struct sigframe sig;
}

there is no ucontext.  There is one inside sigframe though:

(gdb) ptype struct sigframe
type = struct sigframe {
    struct ucontext uc;
    u64 fp;
    u64 lr;
}

But if I change the print to use the new structure I get the same offsets
with both kernels:

Old kernel:

(gdb) p/d  &((struct rt_sigframe *) 0)->uc
$2 = 128
(gdb) p/d  &((struct rt_sigframe *) 0)->uc->uc_mcontext
$1 = 304

New kernel:

(gdb) p/d &((struct rt_sigframe *) 0)->sig->uc
$2 = 128
(gdb) p/d &((struct rt_sigframe *) 0)->sig->uc->uc_mcontext
$1 = 304


Steve Ellcey
sellcey@cavium.com
  

Patch

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 0d472e2..0caac7f 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -49,6 +49,9 @@ 
 #define TRAP_HWBKPT 0x0004
 #endif
 
+/* Check if we are on arm (as opposed to aarch64).  */
+#define IS_ARM32(gdbarch) (gdbarch_bfd_arch_info(gdbarch)->arch == bfd_arch_arm)
+
 /* Per-process data.  We don't bind this to a per-inferior registry
    because of targets like x86 GNU/Linux that need to keep track of
    processes that aren't bound to any inferior (e.g., fork children,
@@ -166,7 +169,7 @@  fetch_gregs_from_thread (struct regcache *regcache)
   tid = ptid_get_lwp (inferior_ptid);
 
   iovec.iov_base = &regs;
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     iovec.iov_len = 18 * 4;
   else
     iovec.iov_len = sizeof (regs);
@@ -175,7 +178,7 @@  fetch_gregs_from_thread (struct regcache *regcache)
   if (ret < 0)
     perror_with_name (_("Unable to fetch general registers."));
 
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     aarch32_gp_regcache_supply (regcache, (uint32_t *) regs, 1);
   else
     {
@@ -203,7 +206,7 @@  store_gregs_to_thread (const struct regcache *regcache)
   tid = ptid_get_lwp (inferior_ptid);
 
   iovec.iov_base = &regs;
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     iovec.iov_len = 18 * 4;
   else
     iovec.iov_len = sizeof (regs);
@@ -212,7 +215,7 @@  store_gregs_to_thread (const struct regcache *regcache)
   if (ret < 0)
     perror_with_name (_("Unable to fetch general registers."));
 
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     aarch32_gp_regcache_collect (regcache, (uint32_t *) regs, 1);
   else
     {
@@ -248,7 +251,7 @@  fetch_fpregs_from_thread (struct regcache *regcache)
 
   iovec.iov_base = &regs;
 
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     {
       iovec.iov_len = VFP_REGS_SIZE;
 
@@ -295,7 +298,7 @@  store_fpregs_to_thread (const struct regcache *regcache)
 
   iovec.iov_base = &regs;
 
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     {
       iovec.iov_len = VFP_REGS_SIZE;
 
@@ -328,7 +331,7 @@  store_fpregs_to_thread (const struct regcache *regcache)
 			      (char *) &regs.fpcr);
     }
 
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     {
       ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_VFP, &iovec);
       if (ret < 0)
@@ -460,8 +463,7 @@  ps_err_e
 ps_get_thread_area (struct ps_prochandle *ph,
 		    lwpid_t lwpid, int idx, void **base)
 {
-  int is_64bit_p
-    = (gdbarch_bfd_arch_info (target_gdbarch ())->bits_per_word == 64);
+  int is_64bit_p = !IS_ARM32 (target_gdbarch ());
 
   return aarch64_ps_get_thread_area (ph, lwpid, idx, base, is_64bit_p);
 }
@@ -517,7 +519,7 @@  aarch64_linux_siginfo_fixup (siginfo_t *native, gdb_byte *inf, int direction)
 
   /* Is the inferior 32-bit?  If so, then do fixup the siginfo
      object.  */
-  if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+  if (IS_ARM32 (gdbarch))
     {
       if (direction == 0)
 	aarch64_compat_siginfo_from_siginfo ((struct compat_siginfo *) inf,
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index b94ccb2..6d0c3bd 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -1000,8 +1000,12 @@  aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   linux_init_abi (info, gdbarch);
 
-  set_solib_svr4_fetch_link_map_offsets (gdbarch,
-					 svr4_lp64_fetch_link_map_offsets);
+  if (tdep->ilp32)
+    set_solib_svr4_fetch_link_map_offsets (gdbarch,
+					   svr4_ilp32_fetch_link_map_offsets);
+  else
+    set_solib_svr4_fetch_link_map_offsets (gdbarch,
+					   svr4_lp64_fetch_link_map_offsets);
 
   /* Enable TLS support.  */
   set_gdbarch_fetch_tls_load_module_address (gdbarch,
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 801c03d..67f1a2e 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -2091,6 +2091,22 @@  aarch64_gen_return_address (struct gdbarch *gdbarch,
 }
 
 
+/* Implement the "register_type" gdbarch method.
+   Adjust the register type of $PC and $SP on ILP32.  */
+
+static struct type *
+aarch64_ilp32_register_type (struct gdbarch *gdbarch, int regnum)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  gdb_assert (tdep->ilp32);
+
+  if (regnum == AARCH64_SP_REGNUM || regnum == AARCH64_PC_REGNUM)
+    return builtin_type (gdbarch)->builtin_uint64;
+  else
+    return tdesc_register_type (gdbarch, regnum);
+}
+
 /* Return the pseudo register name corresponding to register regnum.  */
 
 static const char *
@@ -2851,6 +2867,10 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   const struct tdesc_feature *feature;
   int num_regs = 0;
   int num_pseudo_regs = 0;
+  bool ilp32 = false;
+
+  if (info.bfd_arch_info->mach == bfd_mach_aarch64_ilp32)
+    ilp32 = true;
 
   /* Ensure we always have a target descriptor.  */
   if (!tdesc_has_registers (tdesc))
@@ -2908,6 +2928,9 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
        best_arch != NULL;
        best_arch = gdbarch_list_lookup_by_info (best_arch->next, &info))
     {
+      /* ILP32 and LP64 are incompatible. */
+      if (gdbarch_tdep (arches->gdbarch)->ilp32 != ilp32)
+	continue;
       /* Found a match.  */
       break;
     }
@@ -2926,6 +2949,7 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->lowest_pc = 0x20;
   tdep->jb_pc = -1;		/* Longjump support not enabled by default.  */
   tdep->jb_elt_size = 8;
+  tdep->ilp32 = ilp32;
 
   set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
@@ -2968,9 +2992,9 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_float_bit (gdbarch, 32);
   set_gdbarch_double_bit (gdbarch, 64);
   set_gdbarch_long_double_bit (gdbarch, 128);
-  set_gdbarch_long_bit (gdbarch, 64);
+  set_gdbarch_long_bit (gdbarch, ilp32 ? 32 : 64);
   set_gdbarch_long_long_bit (gdbarch, 64);
-  set_gdbarch_ptr_bit (gdbarch, 64);
+  set_gdbarch_ptr_bit (gdbarch, ilp32 ? 32 : 64);
   set_gdbarch_char_signed (gdbarch, 0);
   set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
   set_gdbarch_double_format (gdbarch, floatformats_ieee_double);
@@ -3012,6 +3036,13 @@  aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   tdesc_use_registers (gdbarch, tdesc, tdesc_data);
 
+  if (ilp32)
+    {
+      /* Override tdesc_register_type to adjust the types of $PC and
+	 $SP in ILP32.  */
+      set_gdbarch_register_type (gdbarch, aarch64_ilp32_register_type);
+    }
+
   /* Add standard register aliases.  */
   for (i = 0; i < ARRAY_SIZE (aarch64_register_aliases); i++)
     user_reg_add (gdbarch, aarch64_register_aliases[i].name,
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index 85c6a97..87d36b6 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -97,6 +97,9 @@  struct gdbarch_tdep
 
   /* syscall record.  */
   int (*aarch64_syscall_record) (struct regcache *regcache, unsigned long svc_number);
+  /* If this is ILP32 or LP64.  */
+  bool ilp32;
+
 };
 
 extern struct target_desc *tdesc_aarch64;
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 334310b..e31d5c5 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -484,7 +484,12 @@  aarch64_linux_read_description (void)
 
   is_elf64 = linux_pid_exe_is_elf_64_file (tid, &machine);
 
-  if (is_elf64)
+  /* There are problems with ptrace when gdbserver is 32 bits and the
+     program being debugged is 64 bits.  */
+  if (sizeof (void *) == 4 && is_elf64)
+    error (_("Can't debug 64-bit process with 32-bit GDBserver"));
+
+  if (machine == EM_AARCH64)
     return tdesc_aarch64;
   else
     return tdesc_arm_with_neon;