mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
This patch adds the necessary bits to enable stack tracing using SFrame. In the case the new SFrame stack tracing procedure doesn't find SFrame related info, the stack tracing falls back on default Dwarf implementation. The new SFrame stack tracing procedure is added to debug/backtrace.c file, the support functions are added in sysdeps folder, namely sframe.h, read-sframe.c and read-sfame.h. Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com> Reviewed-by: DJ Delorie <dj@redhat.com>
188 lines
5.6 KiB
C
188 lines
5.6 KiB
C
/* 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);
|