Detect the arm/thumb mode of code SIGRETURN or RT_SIGRETURN returns to
authorYao Qi <yao.qi@linaro.org>
Thu, 21 Jan 2016 07:48:50 +0000 (07:48 +0000)
committerYao Qi <yao.qi@linaro.org>
Thu, 21 Jan 2016 07:48:50 +0000 (07:48 +0000)
This patch fixes the following regression introduced by commit d0e59a68

step^M
39      } /* handler */^M
1: x/i $pc^M
=> 0x8740 <handler+80>: sub     sp, r11, #0^M
(gdb) step^M
^M
Program received signal SIGSEGV, Segmentation fault.^M
setitimer () at ../sysdeps/unix/syscall-template.S:81^M
81      ../sysdeps/unix/syscall-template.S: No such file or directory.^M
1: x/i $pc^M
=> 0xb6eff9c0 <setitimer>:      push    {r7}^M
(gdb) FAIL: gdb.base/sigstep.exp: continue to handler, si+advance in handler, step from handler: leave handler

in my test setting, program is compiled in arm mode, but the glibc
is built in thumb mode, so when we do 'step' to step over syscall
instruction svc for SIGRETURN, GDB should set breakpoint for arm mode
in the program, even though the current program in glibc is in thumb
mode.  Current GDB doesn't consider the case that the mode of program
SIGRETURN goes to can be different from current program mode.

In fact, GDB has taken care of this arm/thumb mode changes already,
see

/* Copy the value of next pc of sigreturn and rt_sigrturn into PC,
   return 1.  In addition, set IS_THUMB depending on whether we
   will return to ARM or Thumb code.  Return 0 if it is not a
   rt_sigreturn/sigreturn syscall.  */
static int
arm_linux_sigreturn_return_addr (struct frame_info *frame,
 unsigned long svc_number,
 CORE_ADDR *pc, int *is_thumb)

but in the commit d0e59a68

> -  arm_linux_sigreturn_return_addr (frame, svc_number, &return_addr, &is_thumb);
> +  if (svc_number == ARM_SIGRETURN || svc_number == ARM_RT_SIGRETURN)
> +    next_pc = arm_linux_sigreturn_next_pc (regcache, svc_number);

the IS_THUMB setting is lost, so it is a regression.

gdb:

2016-01-21  Yao Qi  <yao.qi@linaro.org>

* arm-linux-tdep.c (arm_linux_sigreturn_next_pc): Add parameter
is_thumb and set it according to CPSR saved on the stack.
(arm_linux_get_next_pcs_syscall_next_pc): Pass is_thumb to
arm_linux_sigreturn_next_pc.

gdb/gdbserver:

2016-01-21  Yao Qi  <yao.qi@linaro.org>

* linux-arm-low.c (arm_sigreturn_next_pc): Add parameter
is_thumb and set it according to CPSR saved on the stack.
(get_next_pcs_syscall_next_pc): Pass is_thumb to
arm_sigreturn_next_pc.

gdb/ChangeLog
gdb/arm-linux-tdep.c
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-arm-low.c

index 705c2e146f0b05025ccd2cb6aff5ec0a520461a4..0f0078ba0aaaeb1108fe30d312c6f065f885b6de 100644 (file)
@@ -1,3 +1,10 @@
+2016-01-21  Yao Qi  <yao.qi@linaro.org>
+
+       * arm-linux-tdep.c (arm_linux_sigreturn_next_pc): Add parameter
+       is_thumb and set it according to CPSR saved on the stack.
+       (arm_linux_get_next_pcs_syscall_next_pc): Pass is_thumb to
+       arm_linux_sigreturn_next_pc.
+
 2016-01-20  Simon Marchi  <simon.marchi@polymtl.ca>
 
        * python/lib/gdb/printing.py (FlagEnumerationPrinter.__call__):
index 2306bda1a9c9ab3460c15d3885fe4cc61b6479e1..f6a831ad31eb72c4bad52671b5d68304f157165f 100644 (file)
@@ -782,10 +782,12 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame,
 }
 
 /* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
-   based on current processor state.  */
+   based on current processor state.  In addition, set IS_THUMB depending
+   on whether we will return to ARM or Thumb code.  */
+
 static CORE_ADDR
 arm_linux_sigreturn_next_pc (struct regcache *regcache,
-                            unsigned long svc_number)
+                            unsigned long svc_number, int *is_thumb)
 {
   ULONGEST sp;
   unsigned long sp_data;
@@ -794,6 +796,7 @@ arm_linux_sigreturn_next_pc (struct regcache *regcache,
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   int pc_offset = 0;
   int is_sigreturn = 0;
+  CORE_ADDR cpsr;
 
   gdb_assert (svc_number == ARM_SIGRETURN
              || svc_number == ARM_RT_SIGRETURN);
@@ -807,6 +810,10 @@ arm_linux_sigreturn_next_pc (struct regcache *regcache,
 
   next_pc = read_memory_unsigned_integer (sp + pc_offset, 4, byte_order);
 
+  /* Set IS_THUMB according the CPSR saved on the stack.  */
+  cpsr = read_memory_unsigned_integer (sp + pc_offset + 4, 4, byte_order);
+  *is_thumb = ((cpsr & arm_psr_thumb_bit (gdbarch)) != 0);
+
   return next_pc;
 }
 
@@ -899,7 +906,12 @@ arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
     }
 
   if (svc_number == ARM_SIGRETURN || svc_number == ARM_RT_SIGRETURN)
-    next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number);
+    {
+      /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+        update IS_THUMB.   */
+      next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number,
+                                            &is_thumb);
+    }
 
   /* Addresses for calling Thumb functions have the bit 0 set.  */
   if (is_thumb)
index 4eb92dbf9d695db275853ba1f0d0ce8b0d0dff33..c33c5d33d25819258d335b244d54a932f791f172 100644 (file)
@@ -1,3 +1,10 @@
+2016-01-21  Yao Qi  <yao.qi@linaro.org>
+
+       * linux-arm-low.c (arm_sigreturn_next_pc): Add parameter
+       is_thumb and set it according to CPSR saved on the stack.
+       (get_next_pcs_syscall_next_pc): Pass is_thumb to
+       arm_sigreturn_next_pc.
+
 2016-01-18  Yao Qi  <yao.qi@linaro.org>
 
        * linux-low.c (linux_set_pc_64bit): New function.
index 57cb8551e365e3e9eaba5012f31d5f19a7726121..c4f43bb247b45bc537fd49d91cd8f16449c82ed3 100644 (file)
@@ -750,16 +750,20 @@ arm_prepare_to_resume (struct lwp_info *lwp)
       }
 }
 
-/* Find the next pc for a sigreturn or rt_sigreturn syscall.
+/* Find the next pc for a sigreturn or rt_sigreturn syscall.  In
+   addition, set IS_THUMB depending on whether we will return to ARM
+   or Thumb code.
    See arm-linux.h for stack layout details.  */
 static CORE_ADDR
-arm_sigreturn_next_pc (struct regcache *regcache, int svc_number)
+arm_sigreturn_next_pc (struct regcache *regcache, int svc_number,
+                      int *is_thumb)
 {
   unsigned long sp;
   unsigned long sp_data;
   /* Offset of PC register.  */
   int pc_offset = 0;
   CORE_ADDR next_pc = 0;
+  CORE_ADDR cpsr;
 
   gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
 
@@ -771,6 +775,10 @@ arm_sigreturn_next_pc (struct regcache *regcache, int svc_number)
 
   (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
 
+  /* Set IS_THUMB according the CPSR saved on the stack.  */
+  (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4);
+  *is_thumb = ((cpsr & CPSR_T) != 0);
+
   return next_pc;
 }
 
@@ -812,7 +820,9 @@ get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self, CORE_ADDR pc)
   /* This is a sigreturn or sigreturn_rt syscall.  */
   if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
     {
-      next_pc = arm_sigreturn_next_pc (regcache, svc_number);
+      /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+        update IS_THUMB.   */
+      next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb);
     }
 
   /* Addresses for calling Thumb functions have the bit 0 set.  */