/* Copyright (C) 2017-2025 Free Software Foundation, Inc. Contributed by ARM Ltd. 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 . */ #if !defined (AARCH64_UNWIND_H) && !defined (__ILP32__) #define AARCH64_UNWIND_H #include "config/aarch64/aarch64-unwind-def.h" #include "ansidecl.h" #include #define AARCH64_DWARF_REGNUM_RA_STATE 34 #define AARCH64_DWARF_RA_STATE_MASK 0x1 /* The diversifiers used to sign a function's return address. */ typedef enum { aarch64_ra_no_signing = 0x0, aarch64_ra_signing_sp = 0x1, } __attribute__((packed)) aarch64_ra_signing_method_t; #define MD_ARCH_EXTENSION_CIE_AUG_HANDLER(fs, aug) \ aarch64_cie_aug_handler (fs, aug) #define MD_ARCH_EXTENSION_FRAME_INIT(context, fs) \ aarch64_arch_extension_frame_init (context, fs) #define MD_DEMANGLE_RETURN_ADDR(context, fs, addr) \ aarch64_demangle_return_addr (context, fs, addr) #define MD_FRAME_LOCAL_REGISTER_P(reg) \ aarch64_frame_local_register (reg) static inline aarch64_ra_signing_method_t aarch64_context_ra_state_get (struct _Unwind_Context *context) { const int index = AARCH64_DWARF_REGNUM_RA_STATE; return _Unwind_GetGR (context, index) & AARCH64_DWARF_RA_STATE_MASK; } static inline aarch64_ra_signing_method_t aarch64_fs_ra_state_get (_Unwind_FrameState const *fs) { const int index = AARCH64_DWARF_REGNUM_RA_STATE; return fs->regs.reg[index].loc.offset & AARCH64_DWARF_RA_STATE_MASK; } static inline void aarch64_fs_ra_state_set (_Unwind_FrameState *fs, aarch64_ra_signing_method_t signing_method) { fs->regs.reg[AARCH64_DWARF_REGNUM_RA_STATE].loc.offset = signing_method; } static inline void aarch64_fs_ra_state_toggle (_Unwind_FrameState *fs) { /* /!\ Mixing DW_CFA_val_expression with DW_CFA_AARCH64_negate_ra_state will result in undefined behavior (likely an unwinding failure), as the chronology of the DWARF directives will be broken. */ gcc_assert (fs->regs.how[AARCH64_DWARF_REGNUM_RA_STATE] == REG_ARCHEXT); aarch64_ra_signing_method_t signing_method = aarch64_fs_ra_state_get (fs); gcc_assert (signing_method == aarch64_ra_no_signing || signing_method == aarch64_ra_signing_sp); aarch64_fs_ra_state_set (fs, (signing_method == aarch64_ra_no_signing) ? aarch64_ra_signing_sp : aarch64_ra_no_signing); } /* CIE handler for custom augmentation string. */ static inline bool aarch64_cie_aug_handler (_Unwind_FrameState *fs, unsigned char aug) { /* AArch64 B-key pointer authentication. */ if (aug == 'B') { fs->regs.arch_fs.signing_key = AARCH64_PAUTH_KEY_B; return true; } return false; } /* At the entrance of a new frame, some cached information from the CIE/FDE, and registers values related to architectural extensions require a default initialization. If any of those values related to architecture extensions had to be saved for the next frame, it should be done via the architecture extensions handler MD_FROB_UPDATE_CONTEXT in uw_update_context_1 (libgcc/unwind-dw2.c). */ static inline void aarch64_arch_extension_frame_init (struct _Unwind_Context *context ATTRIBUTE_UNUSED, _Unwind_FrameState *fs) { /* By default, DW_CFA_AARCH64_negate_ra_state assumes key A is being used for signing. This can be overridden by adding 'B' to the augmentation string. */ fs->regs.arch_fs.signing_key = AARCH64_PAUTH_KEY_A; /* All registers are initially in state REG_UNSAVED, which indicates that they inherit register values from the previous frame. However, the return address starts every frame in the "unsigned" state. It also starts every frame in a state that supports the original toggle-based DW_CFA_AARCH64_negate_ra_state method of controlling RA signing. */ fs->regs.how[AARCH64_DWARF_REGNUM_RA_STATE] = REG_ARCHEXT; aarch64_fs_ra_state_set (fs, aarch64_ra_no_signing); } /* Before copying the current context to the target context, check whether the register is local to this context and should not be forwarded. */ static inline bool aarch64_frame_local_register(long reg) { return (reg == AARCH64_DWARF_REGNUM_RA_STATE); } /* Do AArch64 private extraction on ADDR_WORD based on context info CONTEXT and unwind frame info FS. If ADDR_WORD is signed, we do address authentication on it using CFA of current frame. Note: when DW_CFA_val_expression is used, FS only records the location of the associated CFI program, rather than the value of the expression itself. The CFI program is executed by uw_update_context when updating the context, so the value of the expression must be taken from CONTEXT rather than FS. */ static inline void * aarch64_demangle_return_addr (struct _Unwind_Context *context, _Unwind_FrameState *fs, _Unwind_Word addr_word) { void *addr = (void *)addr_word; const int reg = AARCH64_DWARF_REGNUM_RA_STATE; /* In libgcc, REG_ARCHEXT means that the RA state register was set by an AArch64 DWARF instruction and contains a valid value, or is used to describe the initial state set in aarch64_arch_extension_frame_init. Return-address signing state is normally toggled by DW_CFA_AARCH64_negate _ra_state (also knwon by its alias as DW_CFA_GNU_window_save). However, RA state register can be set directly via DW_CFA_val_expression too. GCC does not generate such CFI but some other compilers reportedly do (see PR104689 for more details). Any other value than REG_ARCHEXT should be interpreted as if the RA state register is set by another DWARF instruction, and the value is fetchable via _Unwind_GetGR. */ aarch64_ra_signing_method_t signing_method = aarch64_ra_no_signing; if (fs->regs.how[reg] == REG_ARCHEXT) signing_method = aarch64_fs_ra_state_get (fs); else if (fs->regs.how[reg] != REG_UNSAVED) signing_method = aarch64_context_ra_state_get (context); if (signing_method == aarch64_ra_signing_sp) { _Unwind_Word salt = (_Unwind_Word) context->cfa; if (fs->regs.arch_fs.signing_key == AARCH64_PAUTH_KEY_B) return __builtin_aarch64_autib1716 (addr, salt); return __builtin_aarch64_autia1716 (addr, salt); } return addr; } /* GCS enable flag for chkfeat instruction. */ #define _CHKFEAT_GCS 1 /* SME runtime function local to libgcc, streaming compatible and preserves more registers than the base PCS requires, but we don't rely on that here. */ __attribute__ ((visibility ("hidden"))) void __libgcc_arm_za_disable (void); /* Disable the SME ZA state in case an unwound frame used the ZA lazy saving scheme. And unwind the GCS for EH. */ #undef _Unwind_Frames_Extra #define _Unwind_Frames_Extra(x) \ do \ { \ __libgcc_arm_za_disable (); \ if (__builtin_aarch64_chkfeat (_CHKFEAT_GCS) == 0) \ { \ for (_Unwind_Word n = (x); n != 0; n--) \ __builtin_aarch64_gcspopm (); \ } \ } \ while (0) /* On signal entry the OS places a token on the GCS that can be used to verify the integrity of the GCS pointer on signal return. It also places the signal handler return address (the restorer that calls the signal return syscall) on the GCS so the handler can return. Because of this token, each stack frame visited during unwinding has exactly one corresponding entry on the GCS, so the frame count is the number of entries that will have to be popped at EH return time. Note: This depends on the GCS signal ABI of the OS. When unwinding across a stack frame for each frame the corresponding entry is checked on the GCS against the computed return address from the normal stack. If they don't match then _URC_FATAL_PHASE2_ERROR is returned. This check is omitted if 1. GCS is disabled. Note: asynchronous GCS disable is supported here if GCSPR and the GCS remains readable. 2. Non-catchable exception where exception_class == 0. Note: the pthread cancellation implementation in glibc sets exception_class to 0 when the unwinder is used for cancellation cleanup handling, so this allows the GCS to get out of sync during cancellation. This weakens security but avoids an ABI break in glibc. 3. Zero return address which marks the outermost stack frame. 4. Signal stack frame, the GCS entry is an OS specific token then with the top bit set. */ #undef _Unwind_Frames_Increment #define _Unwind_Frames_Increment(exc, context, frames) \ do \ { \ frames++; \ if (__builtin_aarch64_chkfeat (_CHKFEAT_GCS) != 0 \ || exc->exception_class == 0 \ || _Unwind_GetIP (context) == 0) \ break; \ const _Unwind_Word *gcs = __builtin_aarch64_gcspr (); \ if (_Unwind_IsSignalFrame (context)) \ { \ if (gcs[frames] >> 63 == 0) \ return _URC_FATAL_PHASE2_ERROR; \ } \ else \ { \ if (gcs[frames] != _Unwind_GetIP (context)) \ return _URC_FATAL_PHASE2_ERROR; \ } \ } \ while (0) #endif /* defined AARCH64_UNWIND_H && defined __ILP32__ */