aboutsummaryrefslogtreecommitdiff
path: root/libgcc
diff options
context:
space:
mode:
Diffstat (limited to 'libgcc')
-rw-r--r--libgcc/ChangeLog11
-rw-r--r--libgcc/Makefile.in2
-rw-r--r--libgcc/config/aarch64/linux-unwind.h108
3 files changed, 118 insertions, 3 deletions
diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog
index f05492e..055e7ca 100644
--- a/libgcc/ChangeLog
+++ b/libgcc/ChangeLog
@@ -1,3 +1,14 @@
+2025-07-17 Richard Sandiford <richard.sandiford@arm.com>
+ Yury Khrustalev <yury.khrustalev@arm.com>
+
+ * config/aarch64/linux-unwind.h (aarch64_fallback_frame_state):
+ If a signal was raised while there was an uncommitted lazy save,
+ commit the save as part of the unwind process.
+
+2025-07-16 John Ericson <git@JohnEricson.me>
+
+ * Makefile.in: Delete dead `MACHMODE_H` variable
+
2025-07-10 Jan Dubiec <jdx@o2.pl>
PR target/116363
diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in
index 0719fd0..f7b48dc 100644
--- a/libgcc/Makefile.in
+++ b/libgcc/Makefile.in
@@ -193,7 +193,6 @@ AWK = @AWK@
GCC_FOR_TARGET = $(CC)
LIPO = @LIPO@
LIPO_FOR_TARGET = $(LIPO)
-MACHMODE_H = machmode.h mode-classes.def insn-modes.h
NM = @NM@
NM_FOR_TARGET = $(NM)
RANLIB_FOR_TARGET = $(RANLIB)
@@ -220,7 +219,6 @@ export INSTALL_DATA
export LIB1ASMSRC
export LIBGCC2_CFLAGS
export LIPO_FOR_TARGET
-export MACHMODE_H
export NM_FOR_TARGET
export STRIP_FOR_TARGET
export RANLIB_FOR_TARGET
diff --git a/libgcc/config/aarch64/linux-unwind.h b/libgcc/config/aarch64/linux-unwind.h
index e41ca6a..f5b73a0 100644
--- a/libgcc/config/aarch64/linux-unwind.h
+++ b/libgcc/config/aarch64/linux-unwind.h
@@ -27,7 +27,7 @@
#include <signal.h>
#include <sys/ucontext.h>
-
+#include <stdint.h>
/* Since insns are always stored LE, on a BE system the opcodes will
be loaded byte-reversed. Therefore, define two sets of opcodes,
@@ -43,6 +43,22 @@
#define MD_FALLBACK_FRAME_STATE_FOR aarch64_fallback_frame_state
+#ifndef FPSIMD_MAGIC
+#define FPSIMD_MAGIC 0x46508001
+#endif
+
+#ifndef TPIDR2_MAGIC
+#define TPIDR2_MAGIC 0x54504902
+#endif
+
+#ifndef ZA_MAGIC
+#define ZA_MAGIC 0x54366345
+#endif
+
+#ifndef EXTRA_MAGIC
+#define EXTRA_MAGIC 0x45585401
+#endif
+
static _Unwind_Reason_Code
aarch64_fallback_frame_state (struct _Unwind_Context *context,
_Unwind_FrameState * fs)
@@ -58,6 +74,21 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
ucontext_t uc;
};
+ struct tpidr2_block
+ {
+ uint64_t za_save_buffer;
+ uint16_t num_za_save_slices;
+ uint8_t reserved[6];
+ };
+
+ struct za_block
+ {
+ struct _aarch64_ctx head;
+ uint16_t vl;
+ uint16_t reserved[3];
+ uint64_t data;
+ };
+
struct rt_sigframe *rt_;
_Unwind_Ptr new_cfa;
unsigned *pc = context->ra;
@@ -103,11 +134,15 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
field can be used to skip over unrecognized context extensions.
The end of the context sequence is marked by a context with magic
0 or size 0. */
+ struct tpidr2_block *tpidr2 = 0;
+ struct za_block *za_ctx = 0;
+
for (extension_marker = (struct _aarch64_ctx *) &sc->__reserved;
extension_marker->magic;
extension_marker = (struct _aarch64_ctx *)
((unsigned char *) extension_marker + extension_marker->size))
{
+ restart:
if (extension_marker->magic == FPSIMD_MAGIC)
{
struct fpsimd_context *ctx =
@@ -139,12 +174,83 @@ aarch64_fallback_frame_state (struct _Unwind_Context *context,
fs->regs.reg[AARCH64_DWARF_V0 + i].loc.offset = offset;
}
}
+ else if (extension_marker->magic == TPIDR2_MAGIC)
+ {
+ /* A TPIDR2 context.
+
+ All the casting is to support big-endian ILP32. We could read
+ directly into TPIDR2 otherwise. */
+ struct { struct _aarch64_ctx h; uint64_t tpidr2; } *ctx
+ = (void *)extension_marker;
+#if defined (__ILP32__)
+ tpidr2 = (struct tpidr2_block *) (uintptr_t) ctx->tpidr2;
+#else
+ tpidr2 = (struct tpidr2_block *) ctx->tpidr2;
+#endif
+ }
+ else if (extension_marker->magic == ZA_MAGIC)
+ /* A ZA context. We interpret this later. */
+ za_ctx = (void *)extension_marker;
+ else if (extension_marker->magic == EXTRA_MAGIC)
+ {
+ /* Extra context. The ABI guarantees that the next _aarch64_ctx
+ in the current list will be the zero terminator, so we can simply
+ switch to the new list and continue from there. The new list is
+ also zero-terminated.
+
+ As above, the casting is to support big-endian ILP32. */
+ struct { struct _aarch64_ctx h; uint64_t next; } *ctx
+ = (void *)extension_marker;
+#if defined (__ILP32__)
+ extension_marker = (struct _aarch64_ctx *) (uintptr_t) ctx->next;
+#else
+ extension_marker = (struct _aarch64_ctx *) ctx->next;
+#endif
+ goto restart;
+ }
else
{
/* There is context provided that we do not recognize! */
}
}
+ /* Signal handlers are entered with ZA in the off state (TPIDR2_ELO==0 and
+ PSTATE.ZA==0). The normal process when transitioning from ZA being
+ dormant to ZA being off is to commit the lazy save; see the AAPCS64
+ for details. However, this is not done when entering a signal handler.
+ Instead, linux saves the old contents of ZA and TPIDR2_EL0 to the
+ sigcontext without interpreting them further.
+
+ Therefore, if a signal handler throws an exception to code outside the
+ signal handler, the unwinder must commit the lazy save after the fact.
+ Committing a lazy save means:
+
+ (1) Storing the contents of ZA into the buffer provided by TPIDR2_EL0.
+ (2) Setting TPIDR2_EL0 to zero.
+ (3) Turning ZA off.
+
+ (2) and (3) have already been done by the call to __libgcc_arm_za_disable.
+ (1) involves copying data from the ZA sigcontext entry to the
+ corresponding lazy save buffer. */
+ if (tpidr2 && za_ctx && tpidr2->za_save_buffer)
+ {
+ /* There is a 16-bit vector length (measured in bytes) at ZA_CTX + 8.
+ The data itself starts at ZA_CTX + 16.
+ As above, the casting is to support big-endian ILP32. */
+ uint16_t vl = za_ctx->vl;
+#if defined (__ILP32__)
+ void *save_buffer = (void *) (uintptr_t) tpidr2->za_save_buffer;
+ const void *za_buffer = (void *) (uintptr_t) &za_ctx->data;
+#else
+ void *save_buffer = (void *) tpidr2->za_save_buffer;
+ const void *za_buffer = (void *) &za_ctx->data;
+#endif
+ uint64_t num_slices = tpidr2->num_za_save_slices;
+ if (num_slices > vl)
+ num_slices = vl;
+ memcpy (save_buffer, za_buffer, num_slices * vl);
+ }
+
fs->regs.how[31] = REG_SAVED_OFFSET;
fs->regs.reg[31].loc.offset = (_Unwind_Ptr) & (sc->sp) - new_cfa;