[Buildroot] [PATCH v1 1/1] package/gcc: add microblaze frame unwinding fix to v15 and v16
Neal Frager via buildroot <[email protected]>
| Newsgroups | net.busybox.buildroot |
|---|---|
| Message-ID | <[email protected]> |
Add microblaze signal-frame unwinding fix back port to gcc 15.3.0 and 16.2.0. Upstream: https://github.com/gcc-mirror/gcc/commit/4ef64ad1a Signed-off-by: Neal Frager <[email protected]> --- ...Linux-signal-frame-unwinding-support.patch | 193 ++++++++++++++++++ ...Linux-signal-frame-unwinding-support.patch | 193 ++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 package/gcc/15.3.0/0004-microblaze-add-Linux-signal-frame-unwinding-support.patch create mode 100644 package/gcc/16.2.0/0003-microblaze-add-Linux-signal-frame-unwinding-support.patch diff --git a/package/gcc/15.3.0/0004-microblaze-add-Linux-signal-frame-unwinding-support.patch b/package/gcc/15.3.0/0004-microblaze-add-Linux-signal-frame-unwinding-support.patch new file mode 100644 index 0000000000..4ec8fe8868 --- /dev/null +++ b/package/gcc/15.3.0/0004-microblaze-add-Linux-signal-frame-unwinding-support.patch @@ -0,0 +1,193 @@ +From 4ef64ad1aa9bbbe9471610e2de6ef0c8afed8b1f Mon Sep 17 00:00:00 2001 +From: Ramin Moussavi <[email protected]> +Date: Wed, 3 Jun 2026 00:00:00 +0200 +Subject: [PATCH] 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 <[email protected]> +--- + gcc/config/microblaze/microblaze.h | 8 ++ + libgcc/config.host | 1 + + libgcc/config/microblaze/linux-unwind.h | 102 ++++++++++++++++++++++++ + 3 files changed, 111 insertions(+) + create mode 100644 libgcc/config/microblaze/linux-unwind.h + +diff --git a/gcc/config/microblaze/microblaze.h b/gcc/config/microblaze/microblaze.h +index 2b5bd52040f7f..fc8c4b8717545 100644 +--- a/gcc/config/microblaze/microblaze.h ++++ b/gcc/config/microblaze/microblaze.h +@@ -176,6 +176,14 @@ extern enum pipeline_type microblaze_pipe; + #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, such as during 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. +diff --git a/libgcc/config.host b/libgcc/config.host +index 046156fa5e786..a685d5da7a107 100644 +--- a/libgcc/config.host ++++ b/libgcc/config.host +@@ -1050,6 +1050,7 @@ mcore-*-elf) + ;; + 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" +diff --git a/libgcc/config/microblaze/linux-unwind.h b/libgcc/config/microblaze/linux-unwind.h +new file mode 100644 +index 0000000000000..b54704a065198 +--- /dev/null ++++ b/libgcc/config/microblaze/linux-unwind.h +@@ -0,0 +1,102 @@ ++/* 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 *) ((_Unwind_Ptr) 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.how[i] = 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 the alternate return column ++ (DWARF_ALT_FRAME_RETURN_COLUMN, defined as 36 in ++ gcc/config/microblaze/microblaze.h), one past the hard registers; ++ column 15 above keeps the interrupted r15. */ ++ fs->regs.how[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__] = REG_SAVED_OFFSET; ++ fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset ++ = (_Unwind_Ptr) &sc->regs.pc - new_cfa; ++ fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__; ++ fs->signal_frame = 1; ++ ++ return _URC_NO_REASON; ++} ++ ++#endif diff --git a/package/gcc/16.2.0/0003-microblaze-add-Linux-signal-frame-unwinding-support.patch b/package/gcc/16.2.0/0003-microblaze-add-Linux-signal-frame-unwinding-support.patch new file mode 100644 index 0000000000..4ec8fe8868 --- /dev/null +++ b/package/gcc/16.2.0/0003-microblaze-add-Linux-signal-frame-unwinding-support.patch @@ -0,0 +1,193 @@ +From 4ef64ad1aa9bbbe9471610e2de6ef0c8afed8b1f Mon Sep 17 00:00:00 2001 +From: Ramin Moussavi <[email protected]> +Date: Wed, 3 Jun 2026 00:00:00 +0200 +Subject: [PATCH] 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 <[email protected]> +--- + gcc/config/microblaze/microblaze.h | 8 ++ + libgcc/config.host | 1 + + libgcc/config/microblaze/linux-unwind.h | 102 ++++++++++++++++++++++++ + 3 files changed, 111 insertions(+) + create mode 100644 libgcc/config/microblaze/linux-unwind.h + +diff --git a/gcc/config/microblaze/microblaze.h b/gcc/config/microblaze/microblaze.h +index 2b5bd52040f7f..fc8c4b8717545 100644 +--- a/gcc/config/microblaze/microblaze.h ++++ b/gcc/config/microblaze/microblaze.h +@@ -176,6 +176,14 @@ extern enum pipeline_type microblaze_pipe; + #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, such as during 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. +diff --git a/libgcc/config.host b/libgcc/config.host +index 046156fa5e786..a685d5da7a107 100644 +--- a/libgcc/config.host ++++ b/libgcc/config.host +@@ -1050,6 +1050,7 @@ mcore-*-elf) + ;; + 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" +diff --git a/libgcc/config/microblaze/linux-unwind.h b/libgcc/config/microblaze/linux-unwind.h +new file mode 100644 +index 0000000000000..b54704a065198 +--- /dev/null ++++ b/libgcc/config/microblaze/linux-unwind.h +@@ -0,0 +1,102 @@ ++/* 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 *) ((_Unwind_Ptr) 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.how[i] = 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 the alternate return column ++ (DWARF_ALT_FRAME_RETURN_COLUMN, defined as 36 in ++ gcc/config/microblaze/microblaze.h), one past the hard registers; ++ column 15 above keeps the interrupted r15. */ ++ fs->regs.how[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__] = REG_SAVED_OFFSET; ++ fs->regs.reg[__LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__].loc.offset ++ = (_Unwind_Ptr) &sc->regs.pc - new_cfa; ++ fs->retaddr_column = __LIBGCC_DWARF_ALT_FRAME_RETURN_COLUMN__; ++ fs->signal_frame = 1; ++ ++ return _URC_NO_REASON; ++} ++ ++#endif -- 2.43.0 _______________________________________________ buildroot mailing list [email protected] https://lists.buildroot.org/mailman/listinfo/buildroot