1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 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);
|