/* 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 . */ #include #include #include #include #include #include /* 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);