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)
commit2fc742f8d64c68b4a175a1dcb28351b112d63315
treed49512af5019d3ecdfddd96f9f2a0398184f352b
parent5a0ab35e43a6e3c69893c0091fe6a78ea8b3e443
sh: Improve unwind info for signals

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