diff --git a/bfd/elf-sframe.c b/bfd/elf-sframe.c index 1ef53120543..af6356f49fe 100644 --- a/bfd/elf-sframe.c +++ b/bfd/elf-sframe.c @@ -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); diff --git a/include/sframe-api.h b/include/sframe-api.h index 4d5c9dccadb..455738b5ef1 100644 --- a/include/sframe-api.h +++ b/include/sframe-api.h @@ -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 diff --git a/libsframe/libsframe.ver b/libsframe/libsframe.ver index 726c2ea9524..df88a2b28b1 100644 --- a/libsframe/libsframe.ver +++ b/libsframe/libsframe.ver @@ -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; diff --git a/libsframe/sframe.c b/libsframe/sframe.c index 38bdd55ac96..a32144d221b 100644 --- a/libsframe/sframe.c +++ b/libsframe/sframe.c @@ -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. */