[SFrame-V3] bfd: ld: sframe: avoid unnecessary decoding of SFrame FREs at link time

At link time, in _bfd_elf_merge_section_sframe (), it suffices to bring
over the all per-function stack trace metadata (all FREs) as a blob into
the SFrame encoder object.  There is no need to "decode" each SFrame
FRE, only to add them in a serial fashion.

This is an optimization, and not directly related to any SFrame V3
related changes to the specification.  This should also bring us a step
closer to supporting SFrame for targets which use linker relaxations.
Removing the need to decode the input FREs can allow the linker to
simply use the available set of FREs from (relocated) contents.  To
support targets using linker relaxations in SFrame, other changes in the
SFrame parse functionality time may also be necessary, but this brings
us just a step closer.

Add two new APIs to accomplish this:
 - sframe_decoder_get_fres_buf, and
 - sframe_encoder_add_fres_buf

bfd/
	* elf-sframe.c (_bfd_elf_merge_section_sframe): Get all FRE data
	and add it all in bulk.
libsframe/
	* libsframe.ver: Add new APIs.
	* sframe.c (sframe_buf_fre_entry_size): New internal API to get
	size of one SFrame FRE at the indicated buffer location, without
	fully "decoding" the SFrame FRE.
	(sframe_decoder_get_fres_buf): New definition.
	(sframe_encoder_add_fres_buf): New definition.
include/
	* sframe-api.h (sframe_decoder_get_fres_buf): New declaration.
	(sframe_encoder_add_fres_buf): New declaration.
This commit is contained in:
Indu Bhagat
2026-01-15 16:43:20 -08:00
parent 77a0bf4928
commit 9d1500135a
4 changed files with 180 additions and 12 deletions

View File

@@ -475,7 +475,7 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
/* Iterate over the function descriptor entries and the FREs of the
function from the decoder context. Add each of them to the encoder
context, if suitable. */
uint32_t i = 0, j = 0, cur_fidx = 0;
uint32_t i = 0, cur_fidx = 0;
uint32_t num_fidx = sframe_decoder_get_num_fidx (sfd_ctx);
uint32_t num_enc_fidx = sframe_encoder_get_num_fidx (sfe_ctx);
@@ -569,18 +569,18 @@ _bfd_elf_merge_section_sframe (bfd *abfd,
BFD_ASSERT (!err);
}
for (j = 0; j < num_fres; j++)
{
sframe_frame_row_entry fre;
if (!sframe_decoder_get_fre (sfd_ctx, i, j, &fre))
{
int err = sframe_encoder_add_fre (sfe_ctx,
cur_fidx-1+num_enc_fidx,
&fre);
BFD_ASSERT (!err);
}
}
uint32_t fde_num_fres = 0;
char *fres_buf = NULL;
size_t fres_buf_size = 0;
int err = sframe_decoder_get_fres_buf (sfd_ctx, i, &fres_buf,
&fres_buf_size, &fde_num_fres);
BFD_ASSERT (!err && fde_num_fres == num_fres);
err = sframe_encoder_add_fres_buf (sfe_ctx, cur_fidx - 1 + num_enc_fidx,
num_fres, fres_buf, fres_buf_size);
BFD_ASSERT (!err);
}
sfd_info->sfd_state = SFRAME_SEC_MERGED;
/* Free the SFrame decoder context. */
sframe_decoder_free (&sfd_ctx);

View File

@@ -169,6 +169,19 @@ sframe_decoder_get_fre (const sframe_decoder_ctx *ctx,
unsigned int fre_idx,
sframe_frame_row_entry *fre);
/* Get the SFrame FRE data of the function at FUNC_IDX'th function index entry
in the SFrame decoder DCTX. The reference to the buffer is returned in
FRES_BUF with FRES_BUF_SIZE indicating the size of the buffer. The number
of FREs in the buffer are NUM_FRES. In SFrame V3, this buffer also contains
the FDE attr data before the actual SFrame FREs. Returns SFRAME_ERR in case
of error. */
extern int
sframe_decoder_get_fres_buf (const sframe_decoder_ctx *dctx,
uint32_t func_idx,
char **fres_buf,
size_t *fres_buf_size,
uint32_t *num_fres);
/* Get the data (NUM_FRES, FUNC_SIZE, FUNC_START_ADDRESS, FUNC_INFO,
REP_BLOCK_SIZE) from the function descriptor entry at index I'th
in the decoder CTX. If failed, return error code.
@@ -300,6 +313,16 @@ sframe_encoder_add_fre (sframe_encoder_ctx *ectx,
unsigned int func_idx,
sframe_frame_row_entry *frep);
/* Add SFrame FRE data given in the buffer FRES_BUF of size FRES_BUF_SIZE (for
function at index FUNC_IDX) to the encoder context ECTX. The number of FREs
in the buffer are NUM_FRES. */
int
sframe_encoder_add_fres_buf (sframe_encoder_ctx *ectx,
unsigned int func_idx,
uint32_t num_fres,
const char *fres_buf,
size_t fres_buf_size);
/* Add a new SFrame function descriptor entry with START_ADDR and FUNC_SIZE to
the encoder context ECTX. */
extern int

View File

@@ -25,6 +25,7 @@ LIBSFRAME_3.0 {
sframe_decoder_get_funcdesc_v2;
sframe_decoder_get_funcdesc_v3;
sframe_decoder_get_fre;
sframe_decoder_get_fres_buf;
sframe_encode;
sframe_encoder_free;
sframe_encoder_get_flags;
@@ -37,6 +38,7 @@ LIBSFRAME_3.0 {
sframe_encoder_add_funcdesc;
sframe_encoder_add_funcdesc_v2;
sframe_encoder_add_funcdesc_v3;
sframe_encoder_add_fres_buf;
sframe_encoder_write;
dump_sframe;
sframe_errmsg;

View File

@@ -491,6 +491,21 @@ sframe_fre_entry_size (sframe_frame_row_entry *frep, uint32_t fre_type)
+ sframe_fre_offset_bytes_size (fre_info));
}
/* Get total size in bytes in the SFrame FRE at FRE_BUF location, given the
type of FRE as FRE_TYPE. */
static size_t
sframe_buf_fre_entry_size (const char *fre_buf, uint32_t fre_type)
{
if (fre_buf == NULL)
return 0;
size_t addr_size = sframe_fre_start_addr_size (fre_type);
uint8_t fre_info = *(uint8_t *)(fre_buf + addr_size);
return (addr_size + sizeof (fre_info)
+ sframe_fre_offset_bytes_size (fre_info));
}
/* Get the function descriptor entry at index FUNC_IDX in the decoder
context CTX. */
@@ -1700,6 +1715,52 @@ sframe_decoder_get_fre (const sframe_decoder_ctx *ctx,
return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
}
/* Get the SFrame FRE data of the function at FUNC_IDX'th function index entry
in the SFrame decoder DCTX. The reference to the buffer is returned in
FRES_BUF with FRES_BUF_SIZE indicating the size of the buffer. The number
of FREs in the buffer are NUM_FRES. In SFrame V3, this buffer also contains
the FDE attr data before the actual SFrame FREs. Returns SFRAME_ERR in case
of error. */
int
sframe_decoder_get_fres_buf (const sframe_decoder_ctx *dctx,
const uint32_t func_idx,
char **fres_buf,
size_t *fres_buf_size,
uint32_t *num_fres)
{
sframe_func_desc_entry_int *fdep;
uint32_t i = 0;
uint32_t fre_type;
size_t esz;
int err = 0;
if (dctx == NULL || fres_buf == NULL)
return sframe_set_errno (&err, SFRAME_ERR_INVAL);
/* Get function descriptor entry at index func_idx. */
fdep = sframe_decoder_get_funcdesc_at_index (dctx, func_idx);
*num_fres = fdep->func_num_fres;
if (fdep == NULL)
return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
fre_type = sframe_get_fre_type (fdep);
/* Update the pointer to (and total size of) the FRE entries. */
*fres_buf = dctx->sfd_fres + fdep->func_start_fre_off;
const char *tmp_buf = *fres_buf;
while (i < *num_fres)
{
/* Avoid cost of full decoding at this time. */
esz = sframe_buf_fre_entry_size (tmp_buf, fre_type);
tmp_buf += esz;
*fres_buf_size += esz;
i++;
}
return 0;
}
/* SFrame Encoder. */
@@ -1950,6 +2011,88 @@ bad:
return -1;
}
/* Add SFrame FRE data given in the buffer FRES_BUF of size FRES_BUF_SIZE (for
function at index FUNC_IDX) to the encoder context ECTX. The number of FREs
in the buffer are NUM_FRES. Returns SFRAME_ERR if failure. */
int
sframe_encoder_add_fres_buf (sframe_encoder_ctx *ectx,
unsigned int func_idx,
uint32_t num_fres,
const char *fres_buf,
size_t fres_buf_size)
{
sframe_frame_row_entry *ectx_frep;
size_t esz = 0;
int err = 0;
if (ectx == NULL || ((fres_buf == NULL) != (fres_buf_size == 0)))
return sframe_set_errno (&err, SFRAME_ERR_INVAL);
/* Use func_idx to gather the function descriptor entry. */
sframe_func_desc_entry_int *fdep
= sframe_encoder_get_funcdesc_at_index (ectx, func_idx);
if (fdep == NULL)
return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
sf_fre_tbl *fre_tbl = ectx->sfe_fres;
if (fre_tbl == NULL || fre_tbl->count + num_fres >= fre_tbl->alloced)
{
sf_fre_tbl *tmp = sframe_grow_fre_tbl (fre_tbl, num_fres, &err);
if (err)
{
sframe_set_errno (&err, SFRAME_ERR_NOMEM);
goto bad; /* OOM. */
}
fre_tbl = tmp;
}
uint32_t fre_type = sframe_get_fre_type (fdep);
uint32_t remaining = num_fres;
size_t buf_size = 0;
while (remaining)
{
ectx_frep = &fre_tbl->entry[fre_tbl->count];
/* Copy the SFrame FRE data over to the encoder object's fre_tbl. */
sframe_decode_fre (fres_buf, ectx_frep, fre_type, &esz);
if (!sframe_fre_sanity_check_p (ectx_frep))
return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
/* Although a stricter sanity check on fre_start_addr like:
if (fdep->func_size)
sframe_assert (frep->fre_start_addr < fdep->func_size);
is more suitable, some code has been seen to not abide by it. See PR
libsframe/33131. */
sframe_assert (ectx_frep->fre_start_addr <= fdep->func_size);
ectx->sfe_fre_nbytes += esz;
fre_tbl->count++;
fres_buf += esz;
buf_size += esz;
remaining--;
}
sframe_assert (fres_buf_size == buf_size);
ectx->sfe_fres = fre_tbl;
sframe_header *ehp = sframe_encoder_get_header (ectx);
ehp->sfh_num_fres = fre_tbl->count;
/* Update the value of the number of FREs for the function. */
fdep->func_num_fres = num_fres;
return 0;
bad:
if (fre_tbl != NULL)
free (fre_tbl);
ectx->sfe_fres = NULL;
ectx->sfe_fre_nbytes = 0;
return -1;
}
/* Add a new SFrame function descriptor entry with START_ADDR, FUNC_SIZE and
FUNC_INFO to the encoder context ECTX. Caller must make sure that ECTX
exists. */