mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
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>
637 lines
17 KiB
C
637 lines
17 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 <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;
|
|
}
|