/* Copyright (C) 2017-2024 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; } /* 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. */ #undef _Unwind_Frames_Extra #define _Unwind_Frames_Extra(x) \ do \ { \ __libgcc_arm_za_disable (); \ } \ while (0) #endif /* defined AARCH64_UNWIND_H && defined __ILP32__ */