sh: Improve unwind info for signals
authorCarl Shaw <carl.shaw@st.com>
Mon, 24 Aug 2009 06:07:08 +0000 (15:07 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Mon, 24 Aug 2009 06:07:08 +0000 (15:07 +0900)
GCC does not issue unwind information for function epilogues.
Unfortunately we can catch a signal during an epilogue.  The signal
handler writes the current context and signal return code onto the stack
overwriting previous contents.  During unwinding, libgcc can try to
restore registers from the stack and restores corrupted ones. This can
lead to segmentation, misaligned access and sigbus faults.

For example, consider the following code:

    mov.l   r12,@-r15
    mov.l   r14,@-r15
    sts.l   pr,@-r15
    mov     r15,r14

    <do stuff>

    mov r14, r15
    lds.l @r15+, pr
<<< SIGNAL HERE
    mov.l @r15+, r14
    mov.l @r15+, r12
    rts

Unwind is aware that pr was pushed to stack in prolog, so tries to
restore it.  Unfortunately it restores the last word of the signal
handler code placed on the stack by the kernel.

This patch tries to avoid the problem by adding a guard region on the
stack between where the function pushes data and where the signal handler
pushes its return code.  We probably don't see this problem often because
exception handling unwinding in an epilogue only occurs due to a pthread
cancel signal.  Also the kernel signal stack handler alignment of 8 bytes
could hide the occurance of this problem sometimes as the stack may not
be trampled at a particular required word.

This is not guaranteed to always work.  It relies on a frame pointer
existing for the function (so it can get the correct sp value) which is
not always the case for the SH4.

Modifications will also be made to libgcc for the case where there is no
fp.

Signed-off-by: Carl Shaw <carl.shaw@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/kernel/signal_32.c

index b5afbec..6010750 100644 (file)
@@ -41,6 +41,16 @@ struct fdpic_func_descriptor {
 };
 
 /*
+ * The following define adds a 64 byte gap between the signal
+ * stack frame and previous contents of the stack.  This allows
+ * frame unwinding in a function epilogue but only if a frame
+ * pointer is used in the function.  This is necessary because
+ * current gcc compilers (<4.3) do not generate unwind info on
+ * SH for function epilogues.
+ */
+#define UNWINDGUARD 64
+
+/*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
 asmlinkage int
@@ -327,7 +337,7 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
                        sp = current->sas_ss_sp + current->sas_ss_size;
        }
 
-       return (void __user *)((sp - frame_size) & -8ul);
+       return (void __user *)((sp - (frame_size+UNWINDGUARD)) & -8ul);
 }
 
 /* These symbols are defined with the addresses in the vsyscall page.