[PATCH] Unwinding CFI for x86_64 signal frame (__restore_rt)

Jan Kratochvil jan.kratochvil@redhat.com
Sat Nov 25 18:06:00 GMT 2006


Hi,

Frysk debugger in development would like to properly unwind signal frames
without any hacks as has been done in the gdb case. Provided patch sets proper
CFI unwind information for `__restore_rt'.

`__restore_rt' CFI is fixed only for x86_64 (tested on Fedora Core
kernel-xen-2.6.18-1.2849.fc6.x86_64) as on i386 (kernel-2.6.18-1.2747.el5.i686)
is in use VDSOed `__kernel_sigreturn' instead (with proper CFI already).
Still i386 should get fixed a similiar way but I do not have an easy testcase.

Currently gdb identifies signal frames using strcmp ("__restore_rt", ...)
(`amd64_linux_sigtramp_p') and gdb has also hardcoded arch-dependent unwind
info (register locations) for the signal frames.

I was told gcc is using `MD_FALLBACK_FRAME_STATE_FOR' (for exceptions
unwinding) but it is not suitable for ptrace(2)ing remote debuggers.

Testcase in glibc is not provided as the whole DWARF/CFI unwinding requires
framework IMO out of the glibc testcases' scope.


Regards,
Jan


Original Bug:
	https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217087

Testcase provided in libunwind testsuite forked in the Frysk repository:
	CVSROOT=:pserver:anoncvs@sources.redhat.com:/cvs/frysk
	CVS Repository=frysk-imports/libunwind
	testcase: tests/run-ptrace-stepper
	Requires patch: http://sourceware.org/bugzilla/show_bug.cgi?id=3590
-------------- next part --------------
2006-11-25  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* glibc-20061120T1000-orig/configure.in: Test whether gas supports
	`.cfi_signal_frame' and `rflags'.
	* glibc-20061120T1000-orig/sysdeps/unix/sysv/linux/x86_64/sigaction.c
	(__restore_rt): Set CFI as signal-frame and provide the CFI unwind info.


https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217087


diff -u -rup glibc-20061120T1000-orig/config.h.in glibc-20061120T1000/config.h.in
--- glibc-20061120T1000-orig/config.h.in	2006-10-28 08:44:04.000000000 +0200
+++ glibc-20061120T1000/config.h.in	2006-11-25 16:59:17.000000000 +0100
@@ -35,6 +35,12 @@
 /* Define if CFI directives are available.  */
 #undef	HAVE_ASM_CFI_DIRECTIVES
 
+/* Define if CFI .cfi_signal_frame directive is available.  */
+#undef	HAVE_ASM_CFI_SIGNAL_FRAME_DIRECTIVE
+
+/* Define if CFI x86_64 rflags is available.  */
+#undef	HAVE_ASM_CFI_RFLAGS
+
 /* Define to the assembler line separator character for multiple
    assembler instructions per line.  Default is `;'  */
 #undef ASM_LINE_SEP
diff -u -rup glibc-20061120T1000-orig/configure.in glibc-20061120T1000/configure.in
--- glibc-20061120T1000-orig/configure.in	2006-11-02 19:34:30.000000000 +0100
+++ glibc-20061120T1000/configure.in	2006-11-25 18:09:45.000000000 +0100
@@ -1793,6 +1793,48 @@ if test $libc_cv_asm_cfi_directives = ye
   AC_DEFINE(HAVE_ASM_CFI_DIRECTIVES)
 fi
 
+AC_CACHE_CHECK(whether CFI signal frame directive is supported, libc_cv_asm_cfi_signal_frame_directive, [dnl
+cat > conftest.s <<EOF
+        .text
+        .type   func,%function
+func:
+        .cfi_startproc
+        .cfi_signal_frame
+        .cfi_endproc
+EOF
+if AC_TRY_COMMAND(${CC-cc} $ASFLAGS -c conftest.s 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_asm_cfi_signal_frame_directive=yes
+else
+  libc_cv_asm_cfi_signal_frame_directive=no
+fi
+rm -f conftest*])
+if test $libc_cv_asm_cfi_signal_frame_directive = yes; then
+  AC_DEFINE(HAVE_ASM_CFI_SIGNAL_FRAME_DIRECTIVE)
+fi
+
+case $machine in
+  x86_64)
+    AC_CACHE_CHECK(whether CFI x86_64 rflags is supported, libc_cv_asm_cfi_rflags, [dnl
+    cat > conftest.s <<EOF
+        .text
+        .type   func,%function
+func:
+        .cfi_startproc
+        .cfi_offset rflags, 0
+        .cfi_endproc
+EOF
+    if AC_TRY_COMMAND(${CC-cc} $ASFLAGS -c conftest.s 1>&AS_MESSAGE_LOG_FD); then
+      libc_cv_asm_cfi_rflags=yes
+    else
+      libc_cv_asm_cfi_rflags=no
+    fi
+    rm -f conftest*])
+    if test $libc_cv_asm_cfi_rflags = yes; then
+      AC_DEFINE(HAVE_ASM_CFI_RFLAGS)
+    fi
+    ;;
+esac
+
 AC_CACHE_CHECK(for ld --no-whole-archive, libc_cv_ld_no_whole_archive, [dnl
 cat > conftest.c <<\EOF
 _start () {}
diff -u -rup glibc-20061120T1000-orig/sysdeps/generic/sysdep.h glibc-20061120T1000/sysdeps/generic/sysdep.h
--- glibc-20061120T1000-orig/sysdeps/generic/sysdep.h	2006-01-07 04:46:40.000000000 +0100
+++ glibc-20061120T1000/sysdeps/generic/sysdep.h	2006-11-25 16:59:17.000000000 +0100
@@ -84,6 +84,20 @@
 #  define cfi_window_save
 # endif
 
+/* `.cfi_signal_frame' was introduced later.  */
+# ifdef HAVE_ASM_CFI_SIGNAL_FRAME_DIRECTIVE
+#  define cfi_signal_frame		.cfi_signal_frame
+# else
+#  define cfi_signal_frame
+# endif
+
+/* To be used as: cfi_if_rflags (cfi_offset (rflags, xxx * 8))  */
+# ifdef HAVE_ASM_CFI_RFLAGS
+#  define cfi_if_rflags(x) x
+# else
+#  define cfi_if_rflags(x)
+# endif
+
 #else /* ! ASSEMBLER */
 # ifdef HAVE_ASM_CFI_DIRECTIVES
 #  define CFI_STRINGIFY(Name) CFI_STRINGIFY2 (Name)
@@ -134,4 +148,18 @@
 #  define CFI_WINDOW_SAVE
 # endif
 
+# ifdef HAVE_ASM_CFI_SIGNAL_FRAME_DIRECTIVE
+#  define CFI_SIGNAL_FRAME \
+   ".cfi_signal_frame"
+# else
+#  define CFI_SIGNAL_FRAME
+# endif
+
+/* To be used as: CFI_IF_RFLAGS (CFI_OFFSET (rflags, xxx * 8))  */
+# ifdef HAVE_ASM_CFI_RFLAGS
+#  define CFI_IF_RFLAGS(x) x
+# else
+#  define CFI_IF_RFLAGS(x)
+# endif
+
 #endif /* __ASSEMBLER__ */
diff -u -rup glibc-20061120T1000-orig/sysdeps/unix/sysv/linux/x86_64/sigaction.c glibc-20061120T1000/sysdeps/unix/sysv/linux/x86_64/sigaction.c
--- glibc-20061120T1000-orig/sysdeps/unix/sysv/linux/x86_64/sigaction.c	2006-10-28 08:44:03.000000000 +0200
+++ glibc-20061120T1000/sysdeps/unix/sysv/linux/x86_64/sigaction.c	2006-11-25 16:59:17.000000000 +0100
@@ -92,16 +92,49 @@ weak_alias (__libc_sigaction, sigaction)
    If you ever feel the need to make any changes, please notify the
    appropriate GDB maintainer.  */
 
+/* sizeof (struct rt_sigframe)  */
+#define FRAME_SIZE 0x238
+/* offsetof (struct rt_sigframe, uc.uc_mcontext)  */
+#define SIGCONTEXT_BASE (-FRAME_SIZE + 0x28)
+
 #define RESTORE(name, syscall) RESTORE2 (name, syscall)
 # define RESTORE2(name, syscall) \
-asm						\
-  (						\
-   ".align 16\n"				\
-   CFI_STARTPROC "\n"				\
-   "__" #name ":\n"				\
-   "	movq $" #syscall ", %rax\n"		\
-   "	syscall\n"				\
-   CFI_ENDPROC "\n"				\
+asm									\
+  (									\
+   ".align 16\n"							\
+   CFI_STARTPROC "\n"							\
+   CFI_SIGNAL_FRAME "\n"						\
+   CFI_DEF_CFA (rsp, +FRAME_SIZE) "\n"					\
+   /* Offsets from: <asm-x86_64/sigcontext.h>  */			\
+   CFI_OFFSET (r8, SIGCONTEXT_BASE + 0*8) "\n"				\
+   CFI_OFFSET (r9, SIGCONTEXT_BASE + 1*8) "\n"				\
+   CFI_OFFSET (r10, SIGCONTEXT_BASE + 2*8) "\n"				\
+   CFI_OFFSET (r11, SIGCONTEXT_BASE + 3*8) "\n"				\
+   CFI_OFFSET (r12, SIGCONTEXT_BASE + 4*8) "\n"				\
+   CFI_OFFSET (r13, SIGCONTEXT_BASE + 5*8) "\n"				\
+   CFI_OFFSET (r14, SIGCONTEXT_BASE + 6*8) "\n"				\
+   CFI_OFFSET (r15, SIGCONTEXT_BASE + 7*8) "\n"				\
+   CFI_OFFSET (rdi, SIGCONTEXT_BASE + 8*8) "\n"				\
+   CFI_OFFSET (rsi, SIGCONTEXT_BASE + 9*8) "\n"				\
+   CFI_OFFSET (rbp, SIGCONTEXT_BASE + 10*8) "\n"			\
+   CFI_OFFSET (rbx, SIGCONTEXT_BASE + 11*8) "\n"			\
+   CFI_OFFSET (rdx, SIGCONTEXT_BASE + 12*8) "\n"			\
+   CFI_OFFSET (rax, SIGCONTEXT_BASE + 13*8) "\n"			\
+   CFI_OFFSET (rcx, SIGCONTEXT_BASE + 14*8) "\n"			\
+   CFI_OFFSET (rsp, SIGCONTEXT_BASE + 15*8) "\n"			\
+   CFI_OFFSET (rip, SIGCONTEXT_BASE + 16*8) "\n"			\
+   CFI_IF_RFLAGS (CFI_OFFSET (rflags, SIGCONTEXT_BASE + 17*8)) "\n"	\
+   CFI_OFFSET (cs, SIGCONTEXT_BASE + 18*8 + 0) "\n"			\
+   /* gas: Error: register save offset not a multiple of 8  */		\
+   /* CFI_OFFSET (gs, SIGCONTEXT_BASE + 18*8 + 2) "\n" */		\
+   /* CFI_OFFSET (fs, SIGCONTEXT_BASE + 18*8 + 4) "\n" */		\
+   /* Debugger assumes `call' and automatically lookups `ip - 1'.  */	\
+   "	nop\n"								\
+   "	.type __" #name ",@function\n"					\
+   "__" #name ":\n"							\
+   "	movq $" #syscall ", %rax\n"					\
+   "	syscall\n"							\
+   CFI_ENDPROC "\n"							\
    );
 /* The return code for realtime-signals.  */
 RESTORE (restore_rt, __NR_rt_sigreturn)


More information about the Libc-alpha mailing list