1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-08-07 06:43:00 +03:00

sframe: Add support for SFRAME_F_FDE_FUNC_START_PCREL flag

The Sframe V2 has a new errata which introduces the
SFRAME_F_FDE_FUNC_START_PCREL flag. This flag indicates the encoding
of the SFrame FDE function start address field like this:

- if set, sfde_func_start_address field contains the offset in bytes
to the start PC of the associated function from the field itself.

- if unset, sfde_func_start_address field contains the offset in bytes
to the start PC of the associated function from the start of the
SFrame section.

Signed-off-by: Claudiu Zissulescu <claudiu.zissulescu-ianculescu@oracle.com>
Reviewed-by: Sam James <sam@gentoo.org>
This commit is contained in:
Claudiu Zissulescu
2025-07-22 12:55:51 -03:00
committed by Adhemerval Zanella
parent 20528165bd
commit 072b5a9922
3 changed files with 118 additions and 31 deletions

View File

@@ -75,11 +75,10 @@ sframe_get_fde_type (sframe_func_desc_entry *fdep)
static bool static bool
sframe_header_sanity_check_p (sframe_header *hp) sframe_header_sanity_check_p (sframe_header *hp)
{ {
uint8_t all_flags = SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER;
/* Check preamble is valid. */ /* Check preamble is valid. */
if ((hp->sfh_preamble.sfp_magic != SFRAME_MAGIC) if ((hp->sfh_preamble.sfp_magic != SFRAME_MAGIC)
|| (hp->sfh_preamble.sfp_version != SFRAME_VERSION_2) || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_2)
|| ((hp->sfh_preamble.sfp_flags | all_flags) != all_flags)) || (hp->sfh_preamble.sfp_flags & ~SFRAME_V2_F_ALL_FLAGS))
return false; return false;
/* Check offsets are valid. */ /* Check offsets are valid. */
@@ -171,25 +170,103 @@ sframe_fre_entry_size (sframe_frame_row_entry *frep, size_t addr_size)
+ sframe_fre_offset_bytes_size (fre_info)); + sframe_fre_offset_bytes_size (fre_info));
} }
/* Check whether for the given FDEP, the SFrame Frame Row Entry identified via /* Get SFrame header from the given decoder context DCTX. */
the START_IP_OFFSET and the END_IP_OFFSET, provides the stack trace
information for the PC. */ 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 static bool
sframe_fre_check_range_p (sframe_func_desc_entry *fdep, sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx,
uint32_t start_ip_offset, uint32_t end_ip_offset, uint32_t start_ip_offset, uint32_t end_ip_offset,
int32_t pc) int32_t pc)
{ {
sframe_func_desc_entry *fdep;
int32_t func_start_addr; int32_t func_start_addr;
uint8_t rep_block_size; uint8_t rep_block_size;
uint32_t fde_type; uint32_t fde_type;
uint32_t pc_offset; uint32_t pc_offset;
bool mask_p; bool mask_p;
fdep = &dctx->sfd_funcdesc[func_idx];
if (fdep == NULL) if (fdep == NULL)
return false; return false;
func_start_addr = fdep->sfde_func_start_address; func_start_addr = sframe_decoder_get_secrel_func_start_addr (dctx, func_idx);
fde_type = sframe_get_fde_type (fdep); fde_type = sframe_get_fde_type (fdep);
mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK); mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK);
rep_block_size = fdep->sfde_func_rep_size; rep_block_size = fdep->sfde_func_rep_size;
@@ -207,19 +284,6 @@ sframe_fre_check_range_p (sframe_func_desc_entry *fdep,
return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset); return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset);
} }
/* The SFrame Decoder. */
/* 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 IDX'th offset from FRE. Set ERRP as applicable. */ /* Get IDX'th offset from FRE. Set ERRP as applicable. */
static int32_t static int32_t
@@ -298,7 +362,7 @@ sframe_decode_fre_start_address (const char *fre_buf,
static sframe_func_desc_entry * static sframe_func_desc_entry *
sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr, sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
int *errp) int *errp, uint32_t *func_idx)
{ {
sframe_header *dhp; sframe_header *dhp;
sframe_func_desc_entry *fdp; sframe_func_desc_entry *fdp;
@@ -319,19 +383,23 @@ sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
/* Do the binary search. */ /* Do the binary search. */
fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc; fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc;
low = 0; low = 0;
high = dhp->sfh_num_fdes; high = dhp->sfh_num_fdes - 1;
while (low <= high) while (low <= high)
{ {
int mid = low + (high - low) / 2; int mid = low + (high - low) / 2;
/* Given sfde_func_start_address <= addr, /* Given sfde_func_start_address <= addr,
addr - sfde_func_start_address must be positive. */ addr - sfde_func_start_address must be positive. */
if (fdp[mid].sfde_func_start_address <= addr if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) <= addr
&& ((uint32_t)(addr - fdp[mid].sfde_func_start_address) && ((uint32_t)(addr - sframe_decoder_get_secrel_func_start_addr (ctx,
mid))
< fdp[mid].sfde_func_size)) < fdp[mid].sfde_func_size))
return fdp + mid; {
*func_idx = mid;
return fdp + mid;
}
if (fdp[mid].sfde_func_start_address < addr) if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr)
low = mid + 1; low = mid + 1;
else else
high = mid - 1; high = mid - 1;
@@ -510,6 +578,7 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
sframe_frame_row_entry *frep) sframe_frame_row_entry *frep)
{ {
sframe_func_desc_entry *fdep; sframe_func_desc_entry *fdep;
uint32_t func_idx;
uint32_t fre_type, i; uint32_t fre_type, i;
uint32_t start_ip_offset; uint32_t start_ip_offset;
int32_t func_start_addr; int32_t func_start_addr;
@@ -522,14 +591,14 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
/* Find the FDE which contains the PC, then scan its fre entries. */ /* Find the FDE which contains the PC, then scan its fre entries. */
fdep = sframe_get_funcdesc_with_addr_internal (ctx, pc, &err); fdep = sframe_get_funcdesc_with_addr_internal (ctx, pc, &err, &func_idx);
if (fdep == NULL || ctx->sfd_fres == NULL) if (fdep == NULL || ctx->sfd_fres == NULL)
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
fre_type = sframe_get_fre_type (fdep); fre_type = sframe_get_fre_type (fdep);
fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off; fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off;
func_start_addr = fdep->sfde_func_start_address; func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx);
for (i = 0; i < fdep->sfde_func_num_fres; i++) for (i = 0; i < fdep->sfde_func_num_fres; i++)
{ {
@@ -553,7 +622,8 @@ __sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
if (start_ip_offset > (uint32_t) (pc - func_start_addr)) if (start_ip_offset > (uint32_t) (pc - func_start_addr))
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
if (sframe_fre_check_range_p (fdep, start_ip_offset, end_ip_offset, pc)) if (sframe_fre_check_range_p (ctx, func_idx, start_ip_offset,
end_ip_offset, pc))
{ {
/* Decode last FRE bits: offsets size. */ /* Decode last FRE bits: offsets size. */
frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info); frep->fre_offsets = fres + addr_size + sizeof (frep->fre_info);

View File

@@ -99,6 +99,12 @@ __sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx,
sframe_frame_row_entry *fre, sframe_frame_row_entry *fre,
_Unwind_Reason_Code *errp); _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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -80,9 +80,20 @@ extern "C"
/* Various flags for SFrame. */ /* Various flags for SFrame. */
/* Function Descriptor Entries are sorted on PC. */ /* Function Descriptor Entries are sorted on PC. */
#define SFRAME_F_FDE_SORTED 0x1 #define SFRAME_F_FDE_SORTED 0x1
/* Functions preserve frame pointer. */ /* Functions preserve frame pointer. */
#define SFRAME_F_FRAME_POINTER 0x2 #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_FP_INVALID 0
#define SFRAME_CFA_FIXED_RA_INVALID 0 #define SFRAME_CFA_FIXED_RA_INVALID 0