[PATCH] ARM/Linux: Unwind restart_syscall(2) frames
Maciej W. Rozycki
macro@codesourcery.com
Wed Aug 26 01:34:00 GMT 2009
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,
More information about the Gdb-patches
mailing list