소스 검색

gcc: backport the microblaze signal frame unwind fix to 15.3.0

The big endian microblaze target fails 12 tests, all of them the exception
based variants -- tst-cancelx4, x16, x18, x20, x21 and tst-oncex4 -- which
are the ones that unwind through a signal frame.

The cause is the toolchain, not uClibc-ng.  Asked for the macro that
carries the fix, the three toolchains answer:

  gcc-12.5.0 (this patch, as 0010)  __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__ 36
  gcc-15.3.0 (shipped)              not defined
  gcc-16.2.0 (upstream has it)      __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__ 36

  echo | microblaze-...-gcc -fbuilding-libgcc -E -dM - | grep ALT_FRAME

Without the column microblaze.h declares, init_dwarf_reg_size_table leaves
it unsized and _Unwind_GetGR aborts as soon as it unwinds through a signal
frame.  gcc got the fix upstream as 4ef64ad1a on 2026-06-12, in master and
releases/gcc-15; 15.3.0 predates it, and openadk carried the backport for
12.5.0 only.  microblazeel had 12.5.0 and 16.2.0, so the gap only showed
when the big endian target was built on the one version in between.

Same patch as 12.5.0/0010, numbered 0012 here because 0010 and 0011 are
taken by the csky and m68k patches.  It applies with one 58 line offset in
libgcc/config.host and touches nothing else: microblaze.h, config.host,
and the new libgcc/config/microblaze/linux-unwind.h.

Built with it, gcc 15.3.0 defines the macro.  Whether the 12 tests pass is
for the CI to say once the toolchain is packed.

Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>
ramin 1 주 전
부모
커밋
5c6fb71cd9
1개의 변경된 파일158개의 추가작업 그리고 0개의 파일을 삭제
  1. 158 0
      toolchain/gcc/patches/15.3.0/0012-microblaze-linux-signal-frame-unwind.patch

+ 158 - 0
toolchain/gcc/patches/15.3.0/0012-microblaze-linux-signal-frame-unwind.patch

@@ -0,0 +1,158 @@
+From: Ramin Moussavi <ramin.moussavi@yacoub.de>
+Subject: [PATCH] microblaze: add DWARF unwind support for Linux signal frames
+
+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.
+On uClibc-ng this fails ~17 NPTL cancellation tests of the test suite.
+
+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 interrupted PC is recorded in DWARF column 36 (one past the hard
+registers): column 15 must keep the interrupted r15, which is 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 (same
+issue the C-SKY port had).
+
+Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
+
+--- a/gcc/config/microblaze/microblaze.h
++++ b/gcc/config/microblaze/microblaze.h
+@@ -176,6 +176,14 @@
+ #define DWARF_FRAME_RETURN_COLUMN \
+ 	(GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM)
+
++/* Signal frames (config/microblaze/linux-unwind.h) record the
++   interrupted PC in DWARF column 36, one past the hard registers,
++   because column 15 must keep the interrupted r15.  Declaring it as
++   the alternate return column makes init_dwarf_reg_size_table size
++   it; otherwise _Unwind_GetGR aborts when unwinding through a signal
++   frame (e.g. pthread cancellation).  */
++#define DWARF_ALT_FRAME_RETURN_COLUMN 36
++
+ /* Initial state of return address on entry to func = R15.
+    Actually, the RA is at R15+8, but gcc doesn't know how
+    to generate this.
+--- a/libgcc/config.host
++++ b/libgcc/config.host
+@@ -994,6 +994,7 @@
+ 	;;
+ microblaze*-linux*)
+ 	tmake_file="${tmake_file} microblaze/t-microblaze t-fdpbit t-slibgcc-libgcc"
++	md_unwind_header=microblaze/linux-unwind.h
+ 	;;
+ microblaze*-*-elf)
+ 	tmake_file="${tmake_file} microblaze/t-microblaze t-fdpbit"
+--- /dev/null
++++ b/libgcc/config/microblaze/linux-unwind.h
+@@ -0,0 +1,100 @@
++/* DWARF2 EH unwinding support for MicroBlaze Linux.
++   Copyright (C) 2026 Free Software Foundation, Inc.
++
++   This file is part of GCC.
++
++   GCC is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 3, or (at your option)
++   any later version.
++
++   GCC is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   Under Section 7 of GPL version 3, you are granted additional
++   permissions described in the GCC Runtime Library Exception, version
++   3.1, as published by the Free Software Foundation.
++
++   You should have received a copy of the GNU General Public License and
++   a copy of the GCC Runtime Library Exception along with this program;
++   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
++   <http://www.gnu.org/licenses/>.  */
++
++#ifndef inhibit_libc
++
++/* Do code reading to identify a signal frame, and set the frame state
++   data appropriately.  See unwind-dw2.c for the structs.  */
++
++#include <signal.h>
++#include <sys/ucontext.h>
++#include <asm/unistd.h>
++
++#define MD_FALLBACK_FRAME_STATE_FOR microblaze_fallback_frame_state
++
++static _Unwind_Reason_Code
++microblaze_fallback_frame_state (struct _Unwind_Context *context,
++				 _Unwind_FrameState *fs)
++{
++  const unsigned int *pc = (const unsigned int *) context->ra;
++  struct sigcontext *sc;
++  _Unwind_Ptr new_cfa;
++  int i;
++
++  /* The outermost frame of a thread may leave a null or near-null
++     return address; do not dereference it looking for the
++     trampoline.  */
++  if ((unsigned long) pc < 4096)
++    return _URC_END_OF_STACK;
++
++  /* The kernel writes the signal trampoline onto the stack
++     (struct rt_sigframe.tramp):
++
++	addik r12, r0, __NR_rt_sigreturn
++	brki  r14, 0x8
++
++     and sets the saved r15 to the trampoline address minus 8 (the
++     handler returns with "rtsd r15, 8"), so the unwound return
++     address points 8 bytes before the trampoline.  */
++  if (pc[0] == (0x31800000 | __NR_rt_sigreturn) && pc[1] == 0xb9cc0008)
++    ;
++  else if (pc[2] == (0x31800000 | __NR_rt_sigreturn) && pc[3] == 0xb9cc0008)
++    pc += 2;
++  else
++    return _URC_END_OF_STACK;
++
++  /* The trampoline is the last member of the kernel's rt_sigframe and
++     the ucontext sits directly in front of it.  Anchor there rather
++     than at the CFA so the layout of the frame head does not matter
++     (the kernel may insert an ABI argument-home gap at the front).
++     uClibc's ucontext_t matches the kernel's struct ucontext.  */
++  ucontext_t *uc = (ucontext_t *) ((char *) pc - sizeof (ucontext_t));
++
++  sc = (struct sigcontext *) &uc->uc_mcontext;
++
++  new_cfa = sc->regs.r1;
++  fs->regs.cfa_how = CFA_REG_OFFSET;
++  fs->regs.cfa_reg = 1;		/* r1, the stack pointer.  */
++  fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;
++
++  /* pt_regs holds r0..r31 consecutively.  */
++  for (i = 0; i < 32; i++)
++    {
++      fs->regs.reg[i].how = REG_SAVED_OFFSET;
++      fs->regs.reg[i].loc.offset
++	= (_Unwind_Ptr) &sc->regs.r0 + i * sizeof (unsigned long) - new_cfa;
++    }
++
++  /* The interrupted PC goes into column 36, the alternate return
++     column (DWARF_ALT_FRAME_RETURN_COLUMN in gcc proper); column 15
++     above keeps the interrupted r15.  */
++  fs->regs.reg[36].how = REG_SAVED_OFFSET;
++  fs->regs.reg[36].loc.offset = (_Unwind_Ptr) &sc->regs.pc - new_cfa;
++  fs->retaddr_column = 36;
++  fs->signal_frame = 1;
++
++  return _URC_NO_REASON;
++}
++
++#endif /* ifdef inhibit_libc  */