microblaze: add Linux signal frame unwinding support

libgcc has no MD_FALLBACK_FRAME_STATE_FOR for microblaze*-linux*, so the
DWARF unwinder cannot step through signal frames at all.  Anything that
unwinds out of a signal handler -- most prominently NPTL asynchronous
pthread cancellation (SIGCANCEL) -- either stops early with
_URC_END_OF_STACK (cleanup handlers below the signal frame never run) or
misinterprets the on-stack signal trampoline and crashes with SIGSEGV.

Add the standard fallback: recognize the two-instruction trampoline the
kernel writes into struct rt_sigframe on the stack

	addik r12, r0, __NR_rt_sigreturn
	brki  r14, 0x8

and rebuild the frame state from the sigcontext's pt_regs.  The ucontext
is anchored relative to the trampoline (its last member) rather than to
the CFA, so the layout of the frame head does not matter.

The interrupted PC is recorded in DWARF column 36, one past the hard
registers, because column 15 must keep the interrupted r15 (unrelated to
the resume address of a signal frame).  Declaring it as
DWARF_ALT_FRAME_RETURN_COLUMN makes init_dwarf_reg_size_table size the
column; without that _Unwind_GetGR reads a zero size and aborts.

Tested with a microblazeel-linux-uclibc cross compiler against uClibc-ng
git, running its NPTL test suite under qemu-system-microblazeel -M
petalogix-s3adsp1800.  Without the fix 17 tests fail (tst-cancel{1..5,7,
9,16,20,x4,x7}, tst-cleanup{1..3}, tst-cond{16,17}) by SIGSEGV or by
hanging in the unwinder; with it all 17 pass and the rest of the suite
is unchanged.  The implementation follows the mips/aarch64
linux-unwind.h pattern.

gcc/ChangeLog:

	* config/microblaze/microblaze.h (DWARF_ALT_FRAME_RETURN_COLUMN):
	Define.

libgcc/ChangeLog:

	* config.host (microblaze*-linux*): Set md_unwind_header.
	* config/microblaze/linux-unwind.h: New file.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
3 files changed