diff options
Diffstat (limited to 'sysdeps/generic')
-rw-r--r-- | sysdeps/generic/Makefile | 3 | ||||
-rw-r--r-- | sysdeps/generic/getrandom-internal.h | 2 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 35 | ||||
-rw-r--r-- | sysdeps/generic/libc-tsd.h | 60 | ||||
-rw-r--r-- | sysdeps/generic/math-type-macros-double.h | 2 | ||||
-rw-r--r-- | sysdeps/generic/math-type-macros-float.h | 2 | ||||
-rw-r--r-- | sysdeps/generic/math-type-macros-float128.h | 9 | ||||
-rw-r--r-- | sysdeps/generic/math-type-macros-ldouble.h | 2 | ||||
-rw-r--r-- | sysdeps/generic/math-type-macros.h | 1 | ||||
-rw-r--r-- | sysdeps/generic/sframe-read.c | 636 | ||||
-rw-r--r-- | sysdeps/generic/sframe-read.h | 112 | ||||
-rw-r--r-- | sysdeps/generic/sframe.c | 187 | ||||
-rw-r--r-- | sysdeps/generic/sframe.h | 378 | ||||
-rw-r--r-- | sysdeps/generic/sysdep.h | 3 | ||||
-rw-r--r-- | sysdeps/generic/uw-sigframe.h | 31 |
15 files changed, 1397 insertions, 66 deletions
diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile index 3ed75dd..1be63b7 100644 --- a/sysdeps/generic/Makefile +++ b/sysdeps/generic/Makefile @@ -21,6 +21,9 @@ CFLAGS-wordcopy.c += -Wno-uninitialized endif ifeq ($(subdir),elf) +ifeq ($(enable-gsframe),yes) +sysdep_routines += sframe-read sframe +endif ifeq (yes:yes,$(build-shared):$(unwind-find-fde)) # This is needed to support g++ v2 and v3. sysdep_routines += framestate unwind-pe diff --git a/sysdeps/generic/getrandom-internal.h b/sysdeps/generic/getrandom-internal.h index 7c54194..4872598 100644 --- a/sysdeps/generic/getrandom-internal.h +++ b/sysdeps/generic/getrandom-internal.h @@ -19,7 +19,7 @@ #ifndef _GETRANDOM_INTERNAL_H #define _GETRANDOM_INTERNAL_H -static inline void __getrandom_early_init (_Bool) +static inline void __getrandom_early_init (_Bool initial) { } diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 5b12a41..74025f1 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -368,8 +368,6 @@ struct rtld_global size_t n_elements; void (*free) (void *); } _ns_unique_sym_table; - /* Keep track of changes to each namespace' list. */ - struct r_debug_extended _ns_debug; } _dl_ns[DL_NNS]; /* One higher than index of last used namespace. */ EXTERN size_t _dl_nns; @@ -717,10 +715,23 @@ extern const ElfW(Phdr) *_dl_phdr; extern size_t _dl_phnum; #endif +/* Possible values for the glibc.rtld.execstack tunable. */ +enum stack_tunable_mode + { + /* Do not allow executable stacks, even if program requires it. */ + stack_tunable_mode_disable = 0, + /* Follows either ABI requirement, or the PT_GNU_STACK value. */ + stack_tunable_mode_enable = 1, + /* Always enable an executable stack. */ + stack_tunable_mode_force = 2 + }; + +void _dl_handle_execstack_tunable (void) attribute_hidden; + /* This function changes the permission of the memory region pointed by STACK_ENDP to executable and update the internal memory protection flags for future thread stack creation. */ -int _dl_make_stack_executable (void **stack_endp) attribute_hidden; +int _dl_make_stack_executable (const void *stack_endp) attribute_hidden; /* Variable pointing to the end of the stack (or close to it). This value must be constant over the runtime of the application. Some programs @@ -1076,15 +1087,29 @@ extern void _dl_debug_state (void); rtld_hidden_proto (_dl_debug_state) /* Initialize `struct r_debug_extended' for the namespace NS. LDBASE - is the run-time load address of the dynamic linker, to be put in the - `r_ldbase' member. Return the address of the structure. */ + is the run-time load address of the dynamic linker, to be put in + the `r_ldbase' member. + + Return the address of the r_debug structure for the namespace. + This is not merely a convenience or optimization, but it is + necessary for the LIBC_PROBE Systemtap/debugger probes to work + reliably: direct variable access can create probes that tools + cannot consume. */ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) attribute_hidden; +/* This is called after relocation processing to handle a potential + copy relocation for _r_debug. */ +void _dl_debug_post_relocate (struct link_map *main_map) attribute_hidden; + /* Update the `r_map' member and return the address of `struct r_debug' of the namespace NS. */ extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden; +/* Update R->r_state to STATE and notify the debugger by calling + _dl_debug_state. */ +void _dl_debug_change_state (struct r_debug *r, int state) attribute_hidden; + /* Initialize the basic data structure for the search paths. SOURCE is either "LD_LIBRARY_PATH" or "--library-path". GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to diff --git a/sysdeps/generic/libc-tsd.h b/sysdeps/generic/libc-tsd.h deleted file mode 100644 index b95e409..0000000 --- a/sysdeps/generic/libc-tsd.h +++ /dev/null @@ -1,60 +0,0 @@ -/* libc-internal interface for thread-specific data. Stub or TLS version. - Copyright (C) 1998-2025 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - <https://www.gnu.org/licenses/>. */ - -#ifndef _GENERIC_LIBC_TSD_H -#define _GENERIC_LIBC_TSD_H 1 - -/* This file defines the following macros for accessing a small fixed - set of thread-specific `void *' data used only internally by libc. - - __libc_tsd_define(CLASS, TYPE, KEY) -- Define or declare a datum with TYPE - for KEY. CLASS can be `static' for - keys used in only one source file, - empty for global definitions, or - `extern' for global declarations. - __libc_tsd_address(TYPE, KEY) -- Return the `TYPE *' pointing to - the current thread's datum for KEY. - __libc_tsd_get(TYPE, KEY) -- Return the `TYPE' datum for KEY. - __libc_tsd_set(TYPE, KEY, VALUE) -- Set the datum for KEY to VALUE. - - The set of available KEY's will usually be provided as an enum, - and contains (at least): - _LIBC_TSD_KEY_MALLOC - _LIBC_TSD_KEY_DL_ERROR - _LIBC_TSD_KEY_RPC_VARS - All uses must be the literal _LIBC_TSD_* name in the __libc_tsd_* macros. - Some implementations may not provide any enum at all and instead - using string pasting in the macros. */ - -#include <tls.h> - -/* When full support for __thread variables is available, this interface is - just a trivial wrapper for it. Without TLS, this is the generic/stub - implementation for wholly single-threaded systems. - - We don't define an enum for the possible key values, because the KEYs - translate directly into variables by macro magic. */ - -#define __libc_tsd_define(CLASS, TYPE, KEY) \ - CLASS __thread TYPE __libc_tsd_##KEY attribute_tls_model_ie; - -#define __libc_tsd_address(TYPE, KEY) (&__libc_tsd_##KEY) -#define __libc_tsd_get(TYPE, KEY) (__libc_tsd_##KEY) -#define __libc_tsd_set(TYPE, KEY, VALUE) (__libc_tsd_##KEY = (VALUE)) - -#endif /* libc-tsd.h */ diff --git a/sysdeps/generic/math-type-macros-double.h b/sysdeps/generic/math-type-macros-double.h index 1e84d3f..f03aea1 100644 --- a/sysdeps/generic/math-type-macros-double.h +++ b/sysdeps/generic/math-type-macros-double.h @@ -28,6 +28,8 @@ #define M_STRTO_NAN __strtod_nan #define M_USE_BUILTIN(c) USE_ ##c ##_BUILTIN +#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUND (RM) + #include <libm-alias-double.h> #include <math-nan-payload-double.h> diff --git a/sysdeps/generic/math-type-macros-float.h b/sysdeps/generic/math-type-macros-float.h index 4aac524..445535a 100644 --- a/sysdeps/generic/math-type-macros-float.h +++ b/sysdeps/generic/math-type-macros-float.h @@ -30,6 +30,8 @@ /* GNU extension float constant macros. */ #define M_MLIT(c) c ## f +#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDF (RM) + #include <libm-alias-float.h> #include <math-nan-payload-float.h> diff --git a/sysdeps/generic/math-type-macros-float128.h b/sysdeps/generic/math-type-macros-float128.h index ad2310b..f64c1d3 100644 --- a/sysdeps/generic/math-type-macros-float128.h +++ b/sysdeps/generic/math-type-macros-float128.h @@ -19,6 +19,8 @@ #ifndef _MATH_TYPE_MACROS_FLOAT128 #define _MATH_TYPE_MACROS_FLOAT128 +#include <fenv_private.h> + #define M_LIT(c) __f128 (c) #define M_PFX FLT128 #define M_SUF(c) c ## f128 @@ -30,6 +32,13 @@ #define M_MLIT(c) c ## f128 +/* fenv_private.h may not define SET_RESTORE_ROUNDF128. */ +#ifdef SET_RESTORE_ROUNDF128 +# define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDF128 (RM) +#else +# define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDL (RM) +#endif + #include <libm-alias-float128.h> #include <math-nan-payload-float128.h> diff --git a/sysdeps/generic/math-type-macros-ldouble.h b/sysdeps/generic/math-type-macros-ldouble.h index 931d5ec..00309cb 100644 --- a/sysdeps/generic/math-type-macros-ldouble.h +++ b/sysdeps/generic/math-type-macros-ldouble.h @@ -28,6 +28,8 @@ #define M_STRTO_NAN __strtold_nan #define M_USE_BUILTIN(c) USE_ ##c ##L_BUILTIN +#define M_SET_RESTORE_ROUND(RM) SET_RESTORE_ROUNDL (RM) + #include <libm-alias-ldouble.h> #include <math-nan-payload-ldouble.h> diff --git a/sysdeps/generic/math-type-macros.h b/sysdeps/generic/math-type-macros.h index e3224a0..21c5fee 100644 --- a/sysdeps/generic/math-type-macros.h +++ b/sysdeps/generic/math-type-macros.h @@ -33,6 +33,7 @@ M_STRTO_NAN - Resolves to the internal libc function which converts a string into the appropriate FLOAT nan value. + M_SET_RESTORE_ROUND - Resolves to a SET_RESTORE_ROUND call for M_TYPE. declare_mgen_alias(from,to) This exposes the appropriate symbol(s) for a diff --git a/sysdeps/generic/sframe-read.c b/sysdeps/generic/sframe-read.c new file mode 100644 index 0000000..a6ebc42 --- /dev/null +++ b/sysdeps/generic/sframe-read.c @@ -0,0 +1,636 @@ +/* Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include <sframe-read.h> + +/* Get the SFrame header size. */ + +static inline uint32_t +sframe_get_hdr_size (sframe_header *sfh) +{ + return SFRAME_V1_HDR_SIZE (*sfh); +} + +/* Access functions for frame row entry data. */ + +static inline uint8_t +sframe_fre_get_offset_count (uint8_t fre_info) +{ + return SFRAME_V1_FRE_OFFSET_COUNT (fre_info); +} + +static inline uint8_t +sframe_fre_get_offset_size (uint8_t fre_info) +{ + return SFRAME_V1_FRE_OFFSET_SIZE (fre_info); +} + +static inline bool +sframe_get_fre_ra_mangled_p (uint8_t fre_info) +{ + return SFRAME_V1_FRE_MANGLED_RA_P (fre_info); +} + +/* Access functions for info from function descriptor entry. */ + +static uint32_t +sframe_get_fre_type (sframe_func_desc_entry *fdep) +{ + uint32_t fre_type = 0; + if (fdep != NULL) + fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info); + return fre_type; +} + +static uint32_t +sframe_get_fde_type (sframe_func_desc_entry *fdep) +{ + uint32_t fde_type = 0; + if (fdep != NULL) + fde_type = SFRAME_V1_FUNC_FDE_TYPE (fdep->sfde_func_info); + return fde_type; +} + +/* Check if SFrame header has valid data. Only consider SFrame type + 2. */ + +static bool +sframe_header_sanity_check_p (sframe_header *hp) +{ + /* Check preamble is valid. */ + if ((hp->sfh_preamble.sfp_magic != SFRAME_MAGIC) + || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_2) + || (hp->sfh_preamble.sfp_flags & ~SFRAME_V2_F_ALL_FLAGS)) + return false; + + /* Check offsets are valid. */ + if (hp->sfh_fdeoff > hp->sfh_freoff) + return false; + + return true; +} + +/* Get the FRE start address size. */ + +static size_t +sframe_fre_start_addr_size (uint32_t fre_type) +{ + size_t addr_size = 0; + switch (fre_type) + { + case SFRAME_FRE_TYPE_ADDR1: + addr_size = 1; + break; + case SFRAME_FRE_TYPE_ADDR2: + addr_size = 2; + break; + case SFRAME_FRE_TYPE_ADDR4: + addr_size = 4; + break; + default: + break; + } + return addr_size; +} + +/* Check if the FREP has valid data. */ + +static bool +sframe_fre_sanity_check_p (sframe_frame_row_entry *frep) +{ + uint8_t offset_size, offset_cnt; + uint8_t fre_info; + + if (frep == NULL) + return false; + + fre_info = frep->fre_info; + offset_size = sframe_fre_get_offset_size (fre_info); + + if (offset_size != SFRAME_FRE_OFFSET_1B + && offset_size != SFRAME_FRE_OFFSET_2B + && offset_size != SFRAME_FRE_OFFSET_4B) + return false; + + offset_cnt = sframe_fre_get_offset_count (fre_info); + if (offset_cnt > MAX_NUM_STACK_OFFSETS) + return false; + + return true; +} + +/* Get FRE_INFO's offset size in bytes. */ + +static size_t +sframe_fre_offset_bytes_size (uint8_t fre_info) +{ + uint8_t offset_size, offset_cnt; + + offset_size = sframe_fre_get_offset_size (fre_info); + + offset_cnt = sframe_fre_get_offset_count (fre_info); + + if (offset_size == SFRAME_FRE_OFFSET_2B + || offset_size == SFRAME_FRE_OFFSET_4B) /* 2 or 4 bytes. */ + return (offset_cnt * (offset_size * 2)); + + return (offset_cnt); +} + +/* Get total size in bytes to represent FREP in the binary format. This + includes the starting address, FRE info, and all the offsets. */ + +static size_t +sframe_fre_entry_size (sframe_frame_row_entry *frep, size_t addr_size) +{ + if (frep == NULL) + return 0; + + uint8_t fre_info = frep->fre_info; + + return (addr_size + sizeof (frep->fre_info) + + sframe_fre_offset_bytes_size (fre_info)); +} + +/* Get SFrame header from the given decoder context DCTX. */ + +static inline sframe_header * +sframe_decoder_get_header (sframe_decoder_ctx *dctx) +{ + sframe_header *hp = NULL; + if (dctx != NULL) + hp = &dctx->sfd_header; + return hp; +} + +/* Get the offset of the sfde_func_start_address field (from the start of the + on-disk layout of the SFrame section) of the FDE at FUNC_IDX in the decoder + context DCTX. */ + +static uint32_t +sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx, + uint32_t func_idx, + _Unwind_Reason_Code *errp) +{ + sframe_header *dhp; + + dhp = sframe_decoder_get_header (dctx); + if (dhp == NULL) + { + if (errp != NULL) + *errp = _URC_END_OF_STACK; + return 0; + } + + if (func_idx >= dhp->sfh_num_fdes) + { + if (errp != NULL) + *errp = _URC_END_OF_STACK; + return 0; + } + else if (errp != NULL) + *errp = _URC_NO_REASON; + + return (sframe_get_hdr_size (dhp) + + func_idx * sizeof (sframe_func_desc_entry) + + offsetof (sframe_func_desc_entry, sfde_func_start_address)); +} + + +/* Get the offset of the start PC of the SFrame FDE at FUNC_IDX from + the start of the SFrame section. If the flag + SFRAME_F_FDE_FUNC_START_PCREL is set, sfde_func_start_address is + the offset of the start PC of the function from the field itself. + + If FUNC_IDX is not a valid index in the given decoder object, returns 0. */ + +static int32_t +sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx, + uint32_t func_idx) +{ + int32_t func_start_addr; + _Unwind_Reason_Code err = 0; + int32_t offsetof_fde_in_sec = 0; + + /* Check if we have SFRAME_F_FDE_FUNC_START_PCREL. */ + sframe_header *sh = &dctx->sfd_header; + if ((sh->sfh_preamble.sfp_flags & SFRAME_F_FDE_FUNC_START_PCREL)) + { + offsetof_fde_in_sec = + sframe_decoder_get_offsetof_fde_start_addr (dctx, func_idx, &err); + /* If func_idx is not a valid index, return 0. */ + if (err == _URC_END_OF_STACK) + return 0; + } + + func_start_addr = dctx->sfd_funcdesc[func_idx].sfde_func_start_address; + + return func_start_addr + offsetof_fde_in_sec; +} + +/* Check if the SFrame Frame Row Entry identified via the + START_IP_OFFSET and the END_IP_OFFSET (for SFrame FDE at + FUNC_IDX). */ + +static bool +sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx, + uint32_t start_ip_offset, uint32_t end_ip_offset, + int32_t pc) +{ + sframe_func_desc_entry *fdep; + int32_t func_start_addr; + uint8_t rep_block_size; + uint32_t fde_type; + uint32_t pc_offset; + bool mask_p; + + fdep = &dctx->sfd_funcdesc[func_idx]; + if (fdep == NULL) + return false; + + func_start_addr = sframe_decoder_get_secrel_func_start_addr (dctx, func_idx); + fde_type = sframe_get_fde_type (fdep); + mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK); + rep_block_size = fdep->sfde_func_rep_size; + + if (func_start_addr > pc) + return false; + + /* Given func_start_addr <= pc, pc - func_start_addr must be positive. */ + pc_offset = pc - func_start_addr; + /* For SFrame FDEs encoding information for repetitive pattern of insns, + masking with the rep_block_size is necessary to find the matching FRE. */ + if (mask_p) + pc_offset = pc_offset % rep_block_size; + + return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset); +} + +/* Get IDX'th offset from FRE. Set ERRP as applicable. */ + +static int32_t +sframe_get_fre_offset (sframe_frame_row_entry *fre, + int idx, + _Unwind_Reason_Code *errp) +{ + uint8_t offset_cnt, offset_size; + + if (!sframe_fre_sanity_check_p (fre)) + { + *errp = _URC_END_OF_STACK; + return 0; + } + + offset_cnt = sframe_fre_get_offset_count (fre->fre_info); + offset_size = sframe_fre_get_offset_size (fre->fre_info); + + if (offset_cnt < (idx + 1)) + { + *errp = _URC_END_OF_STACK; + return 0; + } + *errp = _URC_NO_REASON; + + if (offset_size == SFRAME_FRE_OFFSET_1B) + { + int8_t *sp = (int8_t *)fre->fre_offsets; + return sp[idx]; + } + else if (offset_size == SFRAME_FRE_OFFSET_2B) + { + int16_t *sp = (int16_t *)fre->fre_offsets; + return sp[idx]; + } + else + { + int32_t *ip = (int32_t *)fre->fre_offsets; + return ip[idx]; + } +} + +/* Decode the SFrame FRE start address offset value from FRE_BUF in on-disk + binary format, given the FRE_TYPE. Updates the FRE_START_ADDR. */ + +static void +sframe_decode_fre_start_address (const char *fre_buf, + uint32_t *fre_start_addr, + uint32_t fre_type) +{ + uint32_t saddr = 0; + + if (fre_type == SFRAME_FRE_TYPE_ADDR1) + { + uint8_t *uc = (uint8_t *)fre_buf; + saddr = (uint32_t)*uc; + } + else if (fre_type == SFRAME_FRE_TYPE_ADDR2) + { + uint16_t *ust = (uint16_t *)fre_buf; + saddr = (uint32_t)*ust; + } + else if (fre_type == SFRAME_FRE_TYPE_ADDR4) + { + uint32_t *uit = (uint32_t *)fre_buf; + saddr = (uint32_t)*uit; + } + else + return; + + *fre_start_addr = saddr; +} + +/* Find the function descriptor entry starting which contains the specified + address ADDR. */ + +static sframe_func_desc_entry * +sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr, + int *errp, uint32_t *func_idx) +{ + sframe_header *dhp; + sframe_func_desc_entry *fdp; + int low, high; + + if (ctx == NULL) + return NULL; + + dhp = sframe_decoder_get_header (ctx); + + if (dhp == NULL || dhp->sfh_num_fdes == 0 || ctx->sfd_funcdesc == NULL) + return NULL; + /* If the FDE sub-section is not sorted on PCs, skip the lookup because + binary search cannot be used. */ + if ((dhp->sfh_preamble.sfp_flags & SFRAME_F_FDE_SORTED) == 0) + return NULL; + + /* Do the binary search. */ + fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc; + low = 0; + high = dhp->sfh_num_fdes - 1; + while (low <= high) + { + int mid = low + (high - low) / 2; + + /* Given sfde_func_start_address <= addr, + addr - sfde_func_start_address must be positive. */ + if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) <= addr + && ((uint32_t)(addr - sframe_decoder_get_secrel_func_start_addr (ctx, + mid)) + < fdp[mid].sfde_func_size)) + { + *func_idx = mid; + return fdp + mid; + } + + if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr) + low = mid + 1; + else + high = mid - 1; + } + + return NULL; +} + +/* Get the end IP offset for the FRE at index i in the FDEP. The buffer FRES + is the starting location for the FRE. */ + +static uint32_t +sframe_fre_get_end_ip_offset (sframe_func_desc_entry *fdep, unsigned int i, + const char *fres) +{ + uint32_t end_ip_offset = 0; + uint32_t fre_type; + + fre_type = sframe_get_fre_type (fdep); + + /* Get the start address of the next FRE in sequence. */ + if (i < fdep->sfde_func_num_fres - 1) + { + sframe_decode_fre_start_address (fres, &end_ip_offset, fre_type); + end_ip_offset -= 1; + } + else + /* The end IP offset for the FRE needs to be deduced from the function + size. */ + end_ip_offset = fdep->sfde_func_size - 1; + + return end_ip_offset; +} + +/* Get the SFrame's fixed FP offset given the decoder context CTX. */ + +static int8_t +sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx) +{ + sframe_header *dhp; + dhp = sframe_decoder_get_header (ctx); + return dhp->sfh_cfa_fixed_fp_offset; +} + +/* Get the SFrame's fixed RA offset given the decoder context CTX. */ + +static int8_t +sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *ctx) +{ + sframe_header *dhp; + dhp = sframe_decoder_get_header (ctx); + return dhp->sfh_cfa_fixed_ra_offset; +} + +/* Get the base reg id from the FRE info. Set errp if failure. */ + +uint8_t +__sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre) +{ + uint8_t fre_info = fre->fre_info; + return SFRAME_V1_FRE_CFA_BASE_REG_ID (fre_info); +} + +/* Get the CFA offset from the FRE. If the offset is unavailable, + sets errp. */ + +int32_t +__sframe_fre_get_cfa_offset (sframe_decoder_ctx *dctx __attribute__ ((__unused__)), + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp) +{ + return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp); +} + +/* Get the FP offset from the FRE. If the offset is unavailable, sets + errp. */ + +int32_t +__sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp) +{ + uint32_t fp_offset_idx = 0; + int8_t fp_offset = sframe_decoder_get_fixed_fp_offset (dctx); + + *errp = _URC_NO_REASON; + /* If the FP offset is not being tracked, return the fixed FP offset + from the SFrame header. */ + if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID) + return fp_offset; + + /* In some ABIs, the stack offset to recover RA (using the CFA) from is + fixed (like AMD64). In such cases, the stack offset to recover FP will + appear at the second index. */ + fp_offset_idx = ((sframe_decoder_get_fixed_ra_offset (dctx) + != SFRAME_CFA_FIXED_RA_INVALID) + ? SFRAME_FRE_RA_OFFSET_IDX + : SFRAME_FRE_FP_OFFSET_IDX); + return sframe_get_fre_offset (fre, fp_offset_idx, errp); +} + +/* Get the RA offset from the FRE. If the offset is unavailable, sets + errp. */ + +int32_t +__sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx, + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp) +{ + int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx); + *errp = _URC_NO_REASON; + + /* If the RA offset was not being tracked, return the fixed RA offset + from the SFrame header. */ + if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID) + return ra_offset; + + /* Otherwise, get the RA offset from the FRE. */ + return sframe_get_fre_offset (fre, SFRAME_FRE_RA_OFFSET_IDX, errp); +} + +/* Decode the specified SFrame buffer SF_BUF and return the new SFrame + decoder context. */ + +_Unwind_Reason_Code +__sframe_decode (sframe_decoder_ctx *dctx, const char *sf_buf) +{ + const sframe_preamble *sfp; + size_t hdrsz; + sframe_header *sfheaderp; + char *frame_buf; + + int fidx_size; + uint32_t fre_bytes; + + if (sf_buf == NULL) + return _URC_END_OF_STACK; + + sfp = (const sframe_preamble *) sf_buf; + + /* Check for foreign endianness. */ + if (sfp->sfp_magic != SFRAME_MAGIC) + return _URC_END_OF_STACK; + + frame_buf = (char *)sf_buf; + + /* Handle the SFrame header. */ + dctx->sfd_header = *(sframe_header *) frame_buf; + + /* Validate the contents of SFrame header. */ + sfheaderp = &dctx->sfd_header; + if (!sframe_header_sanity_check_p (sfheaderp)) + return _URC_END_OF_STACK; + + hdrsz = sframe_get_hdr_size (sfheaderp); + frame_buf += hdrsz; + + /* Handle the SFrame Function Descriptor Entry section. */ + fidx_size + = sfheaderp->sfh_num_fdes * sizeof (sframe_func_desc_entry); + dctx->sfd_funcdesc = (sframe_func_desc_entry *)frame_buf; + frame_buf += (fidx_size); + + dctx->sfd_fres = frame_buf; + fre_bytes = sfheaderp->sfh_fre_len; + dctx->sfd_fre_nbytes = fre_bytes; + + return _URC_NO_REASON; +} + +/* Find the SFrame Row Entry which contains the PC. Returns + _URC_END_OF_STACK if failure. */ + +_Unwind_Reason_Code +__sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc, + sframe_frame_row_entry *frep) +{ + sframe_func_desc_entry *fdep; + uint32_t func_idx; + uint32_t fre_type, i; + uint32_t start_ip_offset; + int32_t func_start_addr; + uint32_t end_ip_offset; + const char *fres; + size_t size = 0; + int err = 0; + + if ((ctx == NULL) || (frep == NULL)) + return _URC_END_OF_STACK; + + /* Find the FDE which contains the PC, then scan its fre entries. */ + fdep = sframe_get_funcdesc_with_addr_internal (ctx, pc, &err, &func_idx); + if (fdep == NULL || ctx->sfd_fres == NULL) + return _URC_END_OF_STACK; + + fre_type = sframe_get_fre_type (fdep); + + fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off; + func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx); + + for (i = 0; i < fdep->sfde_func_num_fres; i++) + { + size_t addr_size; + + /* Partially decode the FRE. */ + sframe_decode_fre_start_address (fres, &frep->fre_start_addr, fre_type); + + addr_size = sframe_fre_start_addr_size (fre_type); + if (addr_size == 0) + return _URC_END_OF_STACK; + + frep->fre_info = *(uint8_t *)(fres + addr_size); + size = sframe_fre_entry_size (frep, addr_size); + + start_ip_offset = frep->fre_start_addr; + end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size); + + /* Stop search if FRE's start_ip is greater than pc. Given + func_start_addr <= pc, pc - func_start_addr must be positive. */ + if (start_ip_offset > (uint32_t) (pc - func_start_addr)) + return _URC_END_OF_STACK; + + if (sframe_fre_check_range_p (ctx, func_idx, start_ip_offset, + end_ip_offset, pc)) + { + /* Decode last FRE bits: offsets size. */ + frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info); + return _URC_NO_REASON; + } + + fres += size; + } + return _URC_END_OF_STACK; +} diff --git a/sysdeps/generic/sframe-read.h b/sysdeps/generic/sframe-read.h new file mode 100644 index 0000000..1461421 --- /dev/null +++ b/sysdeps/generic/sframe-read.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _SFRAME_API_H +#define _SFRAME_API_H + +#include <sframe.h> +#include <stdbool.h> +#include <unwind.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct sframe_decoder_ctx +{ + + sframe_header sfd_header; + + sframe_func_desc_entry *sfd_funcdesc; + /* SFrame FRE table. */ + char *sfd_fres; + /* Number of bytes needed for SFrame FREs. */ + int sfd_fre_nbytes; +} sframe_decoder_ctx; + +#define MAX_NUM_STACK_OFFSETS 3 + +/* User interfacing SFrame Row Entry. + An abstraction provided by libsframe so the consumer is decoupled from + the binary format representation of the same. + + The members are best ordered such that they are aligned at their natural + boundaries. This helps avoid usage of undesirable misaligned memory + accesses. See PR libsframe/29856. */ + +typedef struct sframe_frame_row_entry +{ + uint32_t fre_start_addr; + const char *fre_offsets; + unsigned char fre_info; +} sframe_frame_row_entry; + +/* The SFrame Decoder. */ + +/* Decode the specified SFrame buffer CF_BUF and return the new SFrame + decoder context. */ + +extern _Unwind_Reason_Code +__sframe_decode (sframe_decoder_ctx *dctx, const char *cf_buf); + +/* Find the SFrame Frame Row Entry which contains the PC. Returns + _URC_END_OF_STACK if failure. */ + +extern _Unwind_Reason_Code +__sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc, + sframe_frame_row_entry *frep); + +/* Get the base reg id from the FRE info. */ + +extern uint8_t +__sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre); + +/* Get the CFA offset from the FRE. Sets ERRP if an error is + detected. */ + +extern int32_t +__sframe_fre_get_cfa_offset (sframe_decoder_ctx *dtcx, + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp); + +/* Get the FP offset from the FRE. If the offset is unavailable, sets + ERRP. */ + +extern int32_t +__sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp); + +/* Get the RA offset from the FRE. Sets ERRP if ra offset is + unavailable. */ + +extern int32_t +__sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx, + sframe_frame_row_entry *fre, + _Unwind_Reason_Code *errp); + +/* Get the offset of the sfde_func_start_address field. */ + +extern uint32_t +__sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx, + uint32_t func_idx, + _Unwind_Reason_Code *errp); +#ifdef __cplusplus +} +#endif + +#endif /* _SFRAME_API_H */ diff --git a/sysdeps/generic/sframe.c b/sysdeps/generic/sframe.c new file mode 100644 index 0000000..ba0830d --- /dev/null +++ b/sysdeps/generic/sframe.c @@ -0,0 +1,187 @@ +/* Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <sframe-read.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <unwind.h> +#include <uw-sigframe.h> +#include <ldsodefs.h> + +/* Some arches like s390x needs an offset to correct the value where + SP is located in relation to CFA. */ +#ifndef SFRAME_SP_VAL_OFFSET +#define SFRAME_SP_VAL_OFFSET 0 +#endif + +static inline _Unwind_Ptr +read_stack_value (_Unwind_Ptr loc) +{ + _Unwind_Ptr value = *((_Unwind_Ptr *) loc); + return value; +} + +/* Helper to avoid PLT call in libc. Fixes elf/check-localplt + errors. */ + +static int +_dl_find_object_helper (void *address, struct dl_find_object *result) +{ + return GLRO (dl_find_object) (address, result); +} + +/* Backtrace the stack and collect the stacktrace given SFrame info. + If successful, store the return addresses in RA_LST. The SIZE + argument specifies the maximum number of return addresses that can + be stored in RA_LST and contains the number of the addresses + collected. */ + +int +__stacktrace_sframe (void **ra_lst, int count, frame *frame) +{ + _Unwind_Ptr sframe_vma, cfa, return_addr, ra_stack_loc, fp_stack_loc, pc, + frame_ptr; + int cfa_offset, fp_offset, ra_offset, i; + sframe_frame_row_entry fred, *frep = &fred; + + if (!ra_lst || !count) + return 0; + + for (i = 0; i < count; i++) + { + _Unwind_Reason_Code err; + struct dl_find_object data; + sframe_decoder_ctx decoder_context, *dctx = &decoder_context; + + /* Clean decoder context. */ + memset (dctx, 0, sizeof (sframe_decoder_ctx)); + + /* Load and set up the SFrame stack trace info for pc. */ + if (_dl_find_object_helper ((void *) frame->pc, &data) < 0) + /* Force fallback to DWARF stacktracer. */ + return 0; + + sframe_vma = (_Unwind_Ptr) data.dlfo_sframe; + if (!sframe_vma || !(data.dlfo_flags & DLFO_FLAG_SFRAME)) + { +#ifdef MD_DECODE_SIGNAL_FRAME + /* If there is no valid SFrame section or SFrame section is + corrupted then check if it is a signal frame. */ + if (MD_DECODE_SIGNAL_FRAME (frame) == _URC_NO_REASON) + { + ra_lst[i] = (void *) frame->pc; + continue; + } +#endif + /* Force fallback to DWARF stacktracer. */ + return 0; + } + + /* Decode the specified SFrame buffer populate sframe's decoder + context. */ + if (__sframe_decode (dctx, (char *) data.dlfo_sframe) != _URC_NO_REASON) + /* Force fallback to DWARF stacktracer. */ + return 0; + + pc = frame->pc - sframe_vma; + /* Find the SFrame Row Entry which contains the PC. */ + if (__sframe_find_fre (dctx, pc, frep) == _URC_END_OF_STACK) + { +#ifdef MD_DECODE_SIGNAL_FRAME + /* If there are no valid FREs, check if it is a signal + frame, and if so decode it. */ + if (MD_DECODE_SIGNAL_FRAME (frame) == _URC_NO_REASON) + { + ra_lst[i] = (void *) frame->pc; + continue; + } +#endif +#ifdef MD_DETECT_OUTERMOST_FRAME + if (MD_DETECT_OUTERMOST_FRAME (frame) == _URC_END_OF_STACK) + return i; +#endif + /* Force fallback to DWARF stacktracer. */ + return 0; + } + + /* Get the CFA offset from the FRE. If offset is unavailable, + sets err. */ + cfa_offset = __sframe_fre_get_cfa_offset (dctx, frep, &err); + if (err != _URC_NO_REASON) + /* Force fallback to DWARF stacktracer. */ + return 0; + + /* Get CFA using base reg id from the FRE info. */ + cfa = ((__sframe_fre_get_base_reg_id (frep) + == SFRAME_BASE_REG_SP) ? frame->sp : frame->fp) + cfa_offset; + + /* Get the RA offset from the FRE. If the offset is + unavailable, sets err. */ + ra_offset = __sframe_fre_get_ra_offset (dctx, frep, &err); + if (err != _URC_NO_REASON) + /* Force fallback to DWARF stacktracer. */ + return 0; + + /* RA offset is available, get the value stored in the stack + location. */ + ra_stack_loc = cfa + ra_offset; + return_addr = read_stack_value (ra_stack_loc); + + ra_lst[i] = (void *) return_addr; + + /* Get the FP offset from the FRE. If the offset is + unavailable, sets err. */ + fp_offset = __sframe_fre_get_fp_offset (dctx, frep, &err); + frame_ptr = frame->fp; + if (err == _URC_NO_REASON) + { + /* FP offset is available, get the value stored in the stack + location. */ + fp_stack_loc = cfa + fp_offset; + frame_ptr = read_stack_value (fp_stack_loc); + } + + /* Set up for the next frame. */ + frame->fp = frame_ptr; + frame->sp = cfa + SFRAME_SP_VAL_OFFSET; + frame->pc = return_addr; + } + return i; +} + +libc_hidden_def (__stacktrace_sframe); + +/* A noinline helper used to obtain the caller's current PC. */ + +_Unwind_Ptr __attribute__ ((noinline)) +__getPC (void) +{ + return (_Unwind_Ptr) + __builtin_extract_return_addr (__builtin_return_address (0)); +} + +libc_hidden_def (__getPC); + +/* A noinline helper used to obtain the caller's current SP. It + mimics gcc14's __builtin_stack_address() functionality. */ + +_Unwind_Ptr __attribute__ ((noinline)) +__getSP (void) +{ + return (_Unwind_Ptr) __builtin_dwarf_cfa() + SFRAME_SP_VAL_OFFSET; +} + +libc_hidden_def (__getSP); diff --git a/sysdeps/generic/sframe.h b/sysdeps/generic/sframe.h new file mode 100644 index 0000000..e38adcf --- /dev/null +++ b/sysdeps/generic/sframe.h @@ -0,0 +1,378 @@ +/* SFrame format description. + Copyright (C) 2022-2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. */ + +#ifndef _SFRAME_H +#define _SFRAME_H + +#include <sys/types.h> +#include <limits.h> +#include <stdint.h> +#include <unwind.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* SFrame format. + + SFrame format is a simple format to represent the information needed + for generating vanilla backtraces. SFrame format keeps track of the + minimal necessary information needed for stack tracing: + - Canonical Frame Address (CFA) + - Frame Pointer (FP) + - Return Address (RA) + + The SFrame section itself has the following structure: + + +--------+------------+---------+ + | file | function | frame | + | header | descriptor | row | + | | entries | entries | + +--------+------------+---------+ + + The file header stores a magic number and version information, flags, and + the byte offset of each of the sections relative to the end of the header + itself. The file header also specifies the total number of Function + Descriptor Entries, Frame Row Entries and length of the FRE sub-section. + + Following the header is a list of Function Descriptor Entries (FDEs). + This list may be sorted if the flags in the file header indicate it to be + so. The sort order, if applicable, is the order of functions in the + .text.* sections in the resulting binary artifact. Each Function + Descriptor Entry specifies the start PC of a function, the size in bytes + of the function and an offset to its first Frame Row Entry (FRE). Each FDE + additionally also specifies the type of FRE it uses to encode the stack + trace information. + + Next, the SFrame Frame Row Entry sub-section is a list of variable size + records. Each entry represents stack trace information for a set of PCs + of the function. A singular Frame Row Entry is a self-sufficient record + which contains information on how to generate stack trace from the + applicable set of PCs. + + */ + + +/* SFrame format versions. */ +#define SFRAME_VERSION_1 1 +#define SFRAME_VERSION_2 2 +/* SFrame magic number. */ +#define SFRAME_MAGIC 0xdee2 +/* Current version of SFrame format. */ +#define SFRAME_VERSION SFRAME_VERSION_2 + +/* Various flags for SFrame. */ + +/* Function Descriptor Entries are sorted on PC. */ +#define SFRAME_F_FDE_SORTED 0x1 +/* Functions preserve frame pointer. */ +#define SFRAME_F_FRAME_POINTER 0x2 +/* Function start address in SFrame FDE is encoded as the distance from the + location of the sfde_func_start_address to the start PC of the function. + If absent, the function start address in SFrame FDE is encoded as the + distance from the start of the SFrame FDE section to the start PC of the + function. */ +#define SFRAME_F_FDE_FUNC_START_PCREL 0x4 + +/* Set of all defined flags in SFrame V2. */ +#define SFRAME_V2_F_ALL_FLAGS \ + (SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER \ + | SFRAME_F_FDE_FUNC_START_PCREL) + +#define SFRAME_CFA_FIXED_FP_INVALID 0 +#define SFRAME_CFA_FIXED_RA_INVALID 0 + +/* Supported ABIs/Arch. */ +#define SFRAME_ABI_AARCH64_ENDIAN_BIG 1 /* AARCH64 big endian. */ +#define SFRAME_ABI_AARCH64_ENDIAN_LITTLE 2 /* AARCH64 little endian. */ +#define SFRAME_ABI_AMD64_ENDIAN_LITTLE 3 /* AMD64 little endian. */ + +/* SFrame FRE types. */ +#define SFRAME_FRE_TYPE_ADDR1 0 +#define SFRAME_FRE_TYPE_ADDR2 1 +#define SFRAME_FRE_TYPE_ADDR4 2 + +/* SFrame Function Descriptor Entry types. + + The SFrame format has two possible representations for functions. The + choice of which type to use is made according to the instruction patterns + in the relevant program stub. + + An SFrame FDE of type SFRAME_FDE_TYPE_PCINC is an indication + that the PCs in the FREs should be treated as increments in bytes. This is + used for a bulk of the executable code of a program, which contains + instructions with no specific pattern. + + An SFrame FDE of type SFRAME_FDE_TYPE_PCMASK is an indication + that the PCs in the FREs should be treated as masks. This type is useful + for the cases when a small pattern of instructions in a program stub is + repeatedly to cover a specific functionality. Typical usescases are pltN + entries, trampolines etc. */ + +/* Unwinders perform a (PC >= FRE_START_ADDR) to look up a matching FRE. */ +#define SFRAME_FDE_TYPE_PCINC 0 +/* Unwinders perform a (PC % REP_BLOCK_SIZE >= FRE_START_ADDR) to look up a + matching FRE. */ +#define SFRAME_FDE_TYPE_PCMASK 1 + +typedef struct sframe_preamble +{ + uint16_t sfp_magic; /* Magic number (SFRAME_MAGIC). */ + uint8_t sfp_version; /* Data format version number (SFRAME_VERSION). */ + uint8_t sfp_flags; /* Flags. */ +} __attribute__ ((packed)) sframe_preamble; + +typedef struct sframe_header +{ + sframe_preamble sfh_preamble; + /* Information about the arch (endianness) and ABI. */ + uint8_t sfh_abi_arch; + /* Offset for the Frame Pointer (FP) from CFA may be fixed for some + ABIs (e.g, in AMD64 when -fno-omit-frame-pointer is used). When fixed, + this field specifies the fixed stack frame offset and the individual + FREs do not need to track it. When not fixed, it is set to + SFRAME_CFA_FIXED_FP_INVALID, and the individual FREs may provide + the applicable stack frame offset, if any. */ + int8_t sfh_cfa_fixed_fp_offset; + /* Offset for the Return Address from CFA is fixed for some ABIs + (e.g., AMD64 has it as CFA-8). When fixed, the header specifies the + fixed stack frame offset and the individual FREs do not track it. When + not fixed, it is set to SFRAME_CFA_FIXED_RA_INVALID, and individual + FREs provide the applicable stack frame offset, if any. */ + int8_t sfh_cfa_fixed_ra_offset; + /* Number of bytes making up the auxiliary header, if any. + Some ABI/arch, in the future, may use this space for extending the + information in SFrame header. Auxiliary header is contained in + bytes sequentially following the sframe_header. */ + uint8_t sfh_auxhdr_len; + /* Number of SFrame FDEs in this SFrame section. */ + uint32_t sfh_num_fdes; + /* Number of SFrame Frame Row Entries. */ + uint32_t sfh_num_fres; + /* Number of bytes in the SFrame Frame Row Entry section. */ + uint32_t sfh_fre_len; + /* Offset of SFrame Function Descriptor Entry section. */ + uint32_t sfh_fdeoff; + /* Offset of SFrame Frame Row Entry section. */ + uint32_t sfh_freoff; +} __attribute__ ((packed)) sframe_header; + +#define SFRAME_V1_HDR_SIZE(sframe_hdr) \ + ((sizeof (sframe_header) + (sframe_hdr).sfh_auxhdr_len)) + +/* Two possible keys for executable (instruction) pointers signing. */ +#define SFRAME_AARCH64_PAUTH_KEY_A 0 /* Key A. */ +#define SFRAME_AARCH64_PAUTH_KEY_B 1 /* Key B. */ + +typedef struct sframe_func_desc_entry +{ + /* Function start address. Encoded as a signed offset, relative to the + beginning of the current FDE. */ + int32_t sfde_func_start_address; + /* Size of the function in bytes. */ + uint32_t sfde_func_size; + /* Offset of the first SFrame Frame Row Entry of the function, relative to the + beginning of the SFrame Frame Row Entry sub-section. */ + uint32_t sfde_func_start_fre_off; + /* Number of frame row entries for the function. */ + uint32_t sfde_func_num_fres; + /* Additional information for stack tracing from the function: + - 4-bits: Identify the FRE type used for the function. + - 1-bit: Identify the FDE type of the function - mask or inc. + - 1-bit: PAC authorization A/B key (aarch64). + - 2-bits: Unused. + ------------------------------------------------------------------------ + | Unused | PAC auth A/B key (aarch64) | FDE type | FRE type | + | | Unused (amd64) | | | + ------------------------------------------------------------------------ + 8 6 5 4 0 */ + uint8_t sfde_func_info; + /* Size of the block of repeating insns. Used for SFrame FDEs of type + SFRAME_FDE_TYPE_PCMASK. */ + uint8_t sfde_func_rep_size; + uint16_t sfde_func_padding2; +} __attribute__ ((packed)) sframe_func_desc_entry; + +/* Macros to compose and decompose function info in FDE. */ + +/* Note: Set PAC auth key to SFRAME_AARCH64_PAUTH_KEY_A by default. */ +#define SFRAME_V1_FUNC_INFO(fde_type, fre_enc_type) \ + (((SFRAME_AARCH64_PAUTH_KEY_A & 0x1) << 5) | \ + (((fde_type) & 0x1) << 4) | ((fre_enc_type) & 0xf)) + +#define SFRAME_V1_FUNC_FRE_TYPE(data) ((data) & 0xf) +#define SFRAME_V1_FUNC_FDE_TYPE(data) (((data) >> 4) & 0x1) +#define SFRAME_V1_FUNC_PAUTH_KEY(data) (((data) >> 5) & 0x1) + +/* Set the pauth key as indicated. */ +#define SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY(pauth_key, fde_info) \ + ((((pauth_key) & 0x1) << 5) | ((fde_info) & 0xdf)) + +/* Size of stack frame offsets in an SFrame Frame Row Entry. A single + SFrame FRE has all offsets of the same size. Offset size may vary + across frame row entries. */ +#define SFRAME_FRE_OFFSET_1B 0 +#define SFRAME_FRE_OFFSET_2B 1 +#define SFRAME_FRE_OFFSET_4B 2 + +/* An SFrame Frame Row Entry can be SP or FP based. */ +#define SFRAME_BASE_REG_FP 0 +#define SFRAME_BASE_REG_SP 1 + +/* The index at which a specific offset is presented in the variable length + bytes of an FRE. */ +#define SFRAME_FRE_CFA_OFFSET_IDX 0 +/* The RA stack offset, if present, will always be at index 1 in the variable + length bytes of the FRE. */ +#define SFRAME_FRE_RA_OFFSET_IDX 1 +/* The FP stack offset may appear at offset 1 or 2, depending on the ABI as RA + may or may not be tracked. */ +#define SFRAME_FRE_FP_OFFSET_IDX 2 + +typedef struct sframe_fre_info +{ + /* Information about + - 1 bit: base reg for CFA + - 4 bits: Number of offsets (N). A value of upto 3 is allowed to track + all three of CFA, FP and RA (fixed implicit order). + - 2 bits: information about size of the offsets (S) in bytes. + Valid values are SFRAME_FRE_OFFSET_1B, SFRAME_FRE_OFFSET_2B, + SFRAME_FRE_OFFSET_4B + - 1 bit: Mangled RA state bit (aarch64 only). + ---------------------------------------------------------------------------------- + | Mangled-RA (aarch64) | Size of offsets | Number of offsets | base_reg | + | Unused (amd64) | | | | + ---------------------------------------------------------------------------------- + 8 7 5 1 0 + + */ + uint8_t fre_info; +} sframe_fre_info; + +/* Macros to compose and decompose FRE info. */ + +/* Note: Set mangled_ra_p to zero by default. */ +#define SFRAME_V1_FRE_INFO(base_reg_id, offset_num, offset_size) \ + (((0 & 0x1) << 7) | (((offset_size) & 0x3) << 5) | \ + (((offset_num) & 0xf) << 1) | ((base_reg_id) & 0x1)) + +/* Set the mangled_ra_p bit as indicated. */ +#define SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P(mangled_ra_p, fre_info) \ + ((((mangled_ra_p) & 0x1) << 7) | ((fre_info) & 0x7f)) + +#define SFRAME_V1_FRE_CFA_BASE_REG_ID(data) ((data) & 0x1) +#define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf) +#define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3) +#define SFRAME_V1_FRE_MANGLED_RA_P(data) (((data) >> 7) & 0x1) + +/* SFrame Frame Row Entry definitions. + + Used for both AMD64 and AARCH64. + + An SFrame Frame Row Entry is a self-sufficient record which contains + information on how to generate the stack trace for the specified range of + PCs. Each SFrame Frame Row Entry is followed by S*N bytes, where: + S is the size of the stack frame offset for the FRE, and + N is the number of stack frame offsets in the FRE + + The interpretation of FRE stack offsets is ABI-specific: + + AMD64: + offset1 (interpreted as CFA = BASE_REG + offset1) + if FP is being tracked + offset2 (intrepreted as FP = CFA + offset2) + fi + + AARCH64: + offset1 (interpreted as CFA = BASE_REG + offset1) + if FP is being tracked (in other words, if frame record created) + offset2 (interpreted as RA = CFA + offset2) + offset3 (intrepreted as FP = CFA + offset3) + fi + Note that in AAPCS64, a frame record, if created, will save both FP and + LR on stack. +*/ + +/* Used when SFRAME_FRE_TYPE_ADDR1 is specified as FRE type. */ +typedef struct sframe_frame_row_entry_addr1 +{ + /* Start address of the frame row entry. Encoded as an 1-byte unsigned + offset, relative to the start address of the function. */ + uint8_t sfre_start_address; + sframe_fre_info sfre_info; +} __attribute__ ((packed)) sframe_frame_row_entry_addr1; + +/* Upper limit of start address in sframe_frame_row_entry_addr1 + is 0x100 (not inclusive). */ +#define SFRAME_FRE_TYPE_ADDR1_LIMIT \ + (1ULL << ((SFRAME_FRE_TYPE_ADDR1 + 1) * 8)) + +/* Used when SFRAME_FRE_TYPE_ADDR2 is specified as FRE type. */ +typedef struct sframe_frame_row_entry_addr2 +{ + /* Start address of the frame row entry. Encoded as an 2-byte unsigned + offset, relative to the start address of the function. */ + uint16_t sfre_start_address; + sframe_fre_info sfre_info; +} __attribute__ ((packed)) sframe_frame_row_entry_addr2; + +/* Upper limit of start address in sframe_frame_row_entry_addr2 + is 0x10000 (not inclusive). */ +#define SFRAME_FRE_TYPE_ADDR2_LIMIT \ + (1ULL << ((SFRAME_FRE_TYPE_ADDR2 * 2) * 8)) + +/* Used when SFRAME_FRE_TYPE_ADDR4 is specified as FRE type. */ +typedef struct sframe_frame_row_entry_addr4 +{ + /* Start address of the frame row entry. Encoded as a 4-byte unsigned + offset, relative to the start address of the function. */ + uint32_t sfre_start_address; + sframe_fre_info sfre_info; +} __attribute__ ((packed)) sframe_frame_row_entry_addr4; + +/* Upper limit of start address in sframe_frame_row_entry_addr2 + is 0x100000000 (not inclusive). */ +#define SFRAME_FRE_TYPE_ADDR4_LIMIT \ + (1ULL << ((SFRAME_FRE_TYPE_ADDR4 * 2) * 8)) + +/* Used to pass frame information to stack trace routine. */ +typedef struct cframe +{ + _Unwind_Ptr pc; + _Unwind_Ptr sp; + _Unwind_Ptr fp; +} frame; + +/* SFrame stack tracing support. */ +int __stacktrace_sframe (void **, int, frame *); +libc_hidden_proto (__stacktrace_sframe); + +/* Helper used by SFrame tracing algorithm. */ +_Unwind_Ptr __getPC (void); +libc_hidden_proto (__getPC); + +/* Helper used by SFrame tracing algorithm. */ +_Unwind_Ptr __getSP (void); +libc_hidden_proto (__getSP); + +#ifdef __cplusplus +} +#endif + +#endif /* _SFRAME_H */ diff --git a/sysdeps/generic/sysdep.h b/sysdeps/generic/sysdep.h index 4c0dda4..ef5eba2 100644 --- a/sysdeps/generic/sysdep.h +++ b/sysdeps/generic/sysdep.h @@ -45,6 +45,7 @@ # define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off # define cfi_offset(reg, off) .cfi_offset reg, off # define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off +# define cfi_val_offset(reg, off) .cfi_val_offset reg, off # define cfi_register(r1, r2) .cfi_register r1, r2 # define cfi_return_column(reg) .cfi_return_column reg # define cfi_restore(reg) .cfi_restore reg @@ -74,6 +75,8 @@ ".cfi_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off) # define CFI_REL_OFFSET(reg, off) \ ".cfi_rel_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off) +# define CFI_VAL_OFFSET(reg, off) \ + ".cfi_val_offset " CFI_STRINGIFY(reg) "," CFI_STRINGIFY(off) # define CFI_REGISTER(r1, r2) \ ".cfi_register " CFI_STRINGIFY(r1) "," CFI_STRINGIFY(r2) # define CFI_RETURN_COLUMN(reg) \ diff --git a/sysdeps/generic/uw-sigframe.h b/sysdeps/generic/uw-sigframe.h new file mode 100644 index 0000000..b357f8a --- /dev/null +++ b/sysdeps/generic/uw-sigframe.h @@ -0,0 +1,31 @@ +/* Internal header file for handling signal frames. Generic version. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +/* Each architecture that supports SFrame may need to define several + macros to handle exceptional cases during stack backtracing. + + MD_DECODE_SIGNAL_FRAME(frame) should recover frame information when + a signal-related exception occurs. The input frame must contain a + valid program counter (PC) field. On success, the macro should + return _URC_NO_REASON. + + MD_DETECT_OUTERMOST_FRAME(frame) is used to detect the outermost + stack frame. It returns _URC_NO_REASON upon successful + detection. + + The FRAME structure is defined in sysdeps/generic/sframe.h */ |