This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] ARM/Linux: Unwind restart_syscall(2) frames


Hello,

 A backtrace like this may be seen if a syscall has been interrupted:

(gdb) bt
#0  0xbed5c83c in ?? ()
#1  0x401c0f80 in __libc_enable_asynccancel () at libc-cancellation.c:67
#2  0x00000000 in ?? ()
(gdb)

 The problem is GDB does not seem to support the ARM syscall restart 
mechanism.  In this case the restart trampoline on the stack looks like 
this:

	svc	0x00900000
	ldr	pc, [sp], #12

and the PC points to the second instruction -- the syscall was previously 
interrupted by a delivery of a signal, the signal was discarded and the 
syscall restarted with restart_syscall(2) via this trampoline built by the 
kernel on the user stack, and now it has returned again.

 Linux selects this way of handling syscall restarts whenever OABI support 
is enabled in the kernel -- regardless of whether EABI support is on too 
or not.  This is because the kernel does not know if the program being run 
is OABI or EABI and conservatively assumes R7 must not be clobbered.

 In kernels which support EABI only a different approach is taken -- in 
this case __NR_restart_syscall is placed in R7 and the original syscall 
instruction is executed, so no trampoline is used.

 The fix below makes stack unwinding work for me.  The backtrace from the 
same point looks like this now:

(gdb) bt
#0  0xbea3c83c in ?? ()
#1  0x4017fc14 in nanosleep ()
   from /lib/libc.so.6
#2  0x401c0f80 in __libc_enable_asynccancel () at libc-cancellation.c:67
#3  0x00000000 in ?? ()
(gdb)

Now that certainly isn't perfect, but the thread cancellation path taken 
around the original syscall messes with the stack without providing 
corresponding debug information at the same time (the code in question is 
handwritten assembly cooked up with C preprocessor magic), so these outer 
frames are unreachable regardless of the trampoline frame, that is e.g. 
when the original syscall instruction is reached.  Which means the 
incapability to reach frames beyond the original syscall is a separate 
problem.  Either debug information would have to be added to syscall 
wrappers or heuristic unwinders improved to handle these frames.

2009-08-25  Maciej W. Rozycki  <macro@codesourcery.com>

	* arm-linux-tdep.c (ARM_OABI_SYSCALL_RESTART_SYSCALL): New macro.
	(ARM_LDR_PC_SP_12): Likewise.
	(arm_linux_restart_syscall_init): New function.
	(arm_linux_restart_syscall_tramp_frame): New variable.
	(arm_linux_init_abi): Install the
	arm_linux_restart_syscall_tramp_frame unwinder.

 Regression-tested successfully for arm-none-linux-gnueabi, OK to apply?

  Maciej

gdb-6.8.50-arm-restart_syscall-2.patch
Index: gdb/arm-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/arm-linux-tdep.c,v
retrieving revision 1.70
diff -u -p -r1.70 arm-linux-tdep.c
--- gdb/arm-linux-tdep.c	30 Jul 2009 23:05:03 -0000	1.70
+++ gdb/arm-linux-tdep.c	25 Aug 2009 20:48:24 -0000
@@ -216,6 +216,11 @@ static const char arm_linux_thumb_le_bre
 #define ARM_SET_R7_RT_SIGRETURN		0xe3a070ad
 #define ARM_EABI_SYSCALL		0xef000000
 
+/* OABI syscall restart trampoline, used for EABI executables too
+   whenever OABI support has been enabled in the kernel.  */
+#define ARM_OABI_SYSCALL_RESTART_SYSCALL 0xef900000
+#define ARM_LDR_PC_SP_12		0xe49df00c
+
 static void
 arm_linux_sigtramp_cache (struct frame_info *this_frame,
 			  struct trad_frame_cache *this_cache,
@@ -325,6 +330,21 @@ arm_linux_rt_sigreturn_init (const struc
 			      + ARM_SIGCONTEXT_R0);
 }
 
+static void
+arm_linux_restart_syscall_init (const struct tramp_frame *self,
+				struct frame_info *this_frame,
+				struct trad_frame_cache *this_cache,
+				CORE_ADDR func)
+{
+  CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+
+  trad_frame_set_reg_addr (this_cache, ARM_PC_REGNUM, sp);
+  trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + 12);
+
+  /* Save a frame ID.  */
+  trad_frame_set_id (this_cache, frame_id_build (sp, func));
+}
+
 static struct tramp_frame arm_linux_sigreturn_tramp_frame = {
   SIGTRAMP_FRAME,
   4,
@@ -367,6 +387,17 @@ static struct tramp_frame arm_eabi_linux
   arm_linux_rt_sigreturn_init
 };
 
+static struct tramp_frame arm_linux_restart_syscall_tramp_frame = {
+  NORMAL_FRAME,
+  4,
+  {
+    { ARM_OABI_SYSCALL_RESTART_SYSCALL, -1 },
+    { ARM_LDR_PC_SP_12, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_restart_syscall_init
+};
+
 /* Core file and register set support.  */
 
 #define ARM_LINUX_SIZEOF_GREGSET (18 * INT_REGISTER_SIZE)
@@ -859,6 +890,8 @@ arm_linux_init_abi (struct gdbarch_info 
 				&arm_eabi_linux_sigreturn_tramp_frame);
   tramp_frame_prepend_unwinder (gdbarch,
 				&arm_eabi_linux_rt_sigreturn_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+				&arm_linux_restart_syscall_tramp_frame);
 
   /* Core file support.  */
   set_gdbarch_regset_from_core_section (gdbarch,


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]