diff options
Diffstat (limited to 'libsframe/sframe.c')
-rw-r--r-- | libsframe/sframe.c | 212 |
1 files changed, 153 insertions, 59 deletions
diff --git a/libsframe/sframe.c b/libsframe/sframe.c index c2693b9..ea0e1c7 100644 --- a/libsframe/sframe.c +++ b/libsframe/sframe.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <stdarg.h> #include <string.h> +#include <stddef.h> #include "sframe-impl.h" #include "swap.h" @@ -204,12 +205,11 @@ flip_fde (sframe_func_desc_entry *fdep) static bool sframe_header_sanity_check_p (sframe_header *hp) { - unsigned char all_flags = SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER; /* Check preamble is valid. */ if (hp->sfh_preamble.sfp_magic != SFRAME_MAGIC || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_1 && 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; /* Check offsets are valid. */ @@ -363,49 +363,62 @@ sframe_decoder_get_funcdesc_at_index (sframe_decoder_ctx *ctx, return fdep; } +/* Get the offset of the start PC of the SFrame FDE at FUNC_IDX from the start + of the SFrame section. This section-relative offset is used within + libsframe for sorting the SFrame FDEs, and also information lookup routines + like sframe_find_fre. + + 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) +{ + int err = 0; + int32_t 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) + return 0; + + int32_t func_start_addr = dctx->sfd_funcdesc[func_idx].sfde_func_start_address; + + return func_start_addr + offsetof_fde_in_sec; +} + /* Check whether for the given FDEP, the SFrame Frame Row Entry identified via the START_IP_OFFSET and the END_IP_OFFSET, provides the stack trace information for the PC. */ static bool -sframe_fre_check_range_p (sframe_func_desc_entry *fdep, - int32_t start_ip_offset, int32_t end_ip_offset, +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) { - int32_t start_ip, end_ip; + sframe_func_desc_entry *fdep; int32_t func_start_addr; uint8_t rep_block_size; uint32_t fde_type; - int32_t masked_pc; + uint32_t pc_offset; bool mask_p; - bool ret; - ret = false; - - if (!fdep) - return ret; - - func_start_addr = fdep->sfde_func_start_address; + fdep = &dctx->sfd_funcdesc[func_idx]; + 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 (!mask_p) - { - start_ip = start_ip_offset + func_start_addr; - end_ip = end_ip_offset + func_start_addr; - ret = ((start_ip <= pc) && (end_ip >= pc)); - } - else - { - /* For FDEs for repetitive pattern of insns, we need to return the FRE - where pc % rep_block_size is between start_ip_offset and - end_ip_offset. */ - masked_pc = pc % rep_block_size; - ret = ((start_ip_offset <= masked_pc) && (end_ip_offset >= masked_pc)); - } + 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 ret; + return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset); } static int @@ -922,7 +935,7 @@ sframe_decode (const char *sf_buf, size_t sf_size, int *errp) sfheaderp = &dctx->sfd_header; if (!sframe_header_sanity_check_p (sfheaderp)) { - sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); + sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); goto decode_fail_free; } hdrsz = sframe_get_hdr_size (sfheaderp); @@ -997,6 +1010,15 @@ sframe_decoder_get_version (sframe_decoder_ctx *dctx) return dhp->sfh_preamble.sfp_version; } +/* Get the section flags from the SFrame decoder context DCTX. */ + +uint8_t +sframe_decoder_get_flags (sframe_decoder_ctx *dctx) +{ + const sframe_header *dhp = sframe_decoder_get_header (dctx); + return dhp->sfh_preamble.sfp_flags; +} + /* Get the SFrame's fixed FP offset given the decoder context CTX. */ int8_t sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx) @@ -1015,6 +1037,29 @@ sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *ctx) return dhp->sfh_cfa_fixed_ra_offset; } +/* 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. + + If FUNC_IDX is more than the number of SFrame FDEs in the section, sets + error code in ERRP, but returns the (hypothetical) offset. This is useful + for the linker when arranging input FDEs into the output section to be + emitted. */ + +uint32_t +sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx, + uint32_t func_idx, int *errp) +{ + if (func_idx >= sframe_decoder_get_num_fidx (dctx)) + sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTFOUND); + else if (errp) + *errp = 0; + + return (sframe_decoder_get_hdr_size (dctx) + + func_idx * sizeof (sframe_func_desc_entry) + + offsetof (sframe_func_desc_entry, sfde_func_start_address)); +} + /* Find the function descriptor entry which contains the specified address ADDR. This function is deprecated and will be removed from libsframe.so.2. */ @@ -1032,11 +1077,11 @@ sframe_get_funcdesc_with_addr (sframe_decoder_ctx *ctx __attribute__ ((unused)), static sframe_func_desc_entry * 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_func_desc_entry *fdp; - int low, high, cnt; + int low, high; if (ctx == NULL) return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL); @@ -1047,29 +1092,30 @@ sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr, return sframe_ret_set_errno (errp, SFRAME_ERR_DCTX_INVAL); /* 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) + if ((sframe_decoder_get_flags (ctx) & SFRAME_F_FDE_SORTED) == 0) return sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTSORTED); /* Do the binary search. */ fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc; low = 0; - high = dhp->sfh_num_fdes; - cnt = high; + high = dhp->sfh_num_fdes - 1; while (low <= high) { int mid = low + (high - low) / 2; - if (fdp[mid].sfde_func_start_address == addr) - return fdp + mid; - - if (fdp[mid].sfde_func_start_address < addr) + /* 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)) { - if (mid == (cnt - 1)) /* Check if it's the last one. */ - return fdp + (cnt - 1); - else if (fdp[mid+1].sfde_func_start_address > addr) - return fdp + mid; - low = mid + 1; + *func_idx = mid; + return fdp + mid; } + + if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr) + low = mid + 1; else high = mid - 1; } @@ -1112,29 +1158,26 @@ sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc, { sframe_frame_row_entry cur_fre; sframe_func_desc_entry *fdep; - uint32_t fre_type, fde_type, i; - int32_t start_ip_offset; + uint32_t func_idx; + uint32_t fre_type, i; int32_t func_start_addr; - int32_t end_ip_offset; + uint32_t start_ip_offset, end_ip_offset; const char *fres; size_t size = 0; int err = 0; - bool mask_p; if ((ctx == NULL) || (frep == NULL)) return sframe_set_errno (&err, SFRAME_ERR_INVAL); /* 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) return sframe_set_errno (&err, SFRAME_ERR_DCTX_INVAL); fre_type = sframe_get_fre_type (fdep); - fde_type = sframe_get_fde_type (fdep); - mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK); 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++) { @@ -1145,11 +1188,13 @@ sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc, start_ip_offset = cur_fre.fre_start_addr; end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size); - /* First FRE's start_ip must be more than pc for regular SFrame FDEs. */ - if (i == 0 && !mask_p && (start_ip_offset + func_start_addr) > pc) + /* 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 sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); - 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)) { sframe_frame_row_entry_copy (frep, &cur_fre); return 0; @@ -1345,6 +1390,12 @@ sframe_encode (uint8_t ver, uint8_t flags, uint8_t abi_arch, hp->sfh_preamble.sfp_magic = SFRAME_MAGIC; hp->sfh_preamble.sfp_flags = flags; + /* Implementation in the SFrame encoder APIs, e.g., + sframe_encoder_write_sframe assume flag SFRAME_F_FDE_FUNC_START_PCREL + set. */ + if (!(flags & SFRAME_F_FDE_FUNC_START_PCREL)) + return sframe_ret_set_errno (errp, SFRAME_ERR_ECTX_INVAL); + hp->sfh_abi_arch = abi_arch; hp->sfh_cfa_fixed_fp_offset = fixed_fp_offset; hp->sfh_cfa_fixed_ra_offset = fixed_ra_offset; @@ -1417,6 +1468,15 @@ sframe_encoder_get_version (sframe_encoder_ctx *encoder) return ehp->sfh_preamble.sfp_version; } +/* Get the section flags from the SFrame encoder context ENCODER. */ + +uint8_t +sframe_encoder_get_flags (sframe_encoder_ctx *encoder) +{ + const sframe_header *ehp = sframe_encoder_get_header (encoder); + return ehp->sfh_preamble.sfp_flags; +} + /* Return the number of function descriptor entries in the SFrame encoder ENCODER. */ @@ -1431,6 +1491,29 @@ sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder) return num_fdes; } +/* 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 encoder + context ENCODER. + + If FUNC_IDX is more than the number of SFrame FDEs in the section, sets + error code in ERRP, but returns the (hypothetical) offset. This is useful + for the linker when arranging input FDEs into the output section to be + emitted. */ + +uint32_t +sframe_encoder_get_offsetof_fde_start_addr (sframe_encoder_ctx *encoder, + uint32_t func_idx, int *errp) +{ + if (func_idx >= sframe_encoder_get_num_fidx (encoder)) + sframe_ret_set_errno (errp, SFRAME_ERR_FDE_INVAL); + else if (errp) + *errp = 0; + + return (sframe_encoder_get_hdr_size (encoder) + + func_idx * sizeof (sframe_func_desc_entry) + + offsetof (sframe_func_desc_entry, sfde_func_start_address)); +} + /* Add an FRE to function at FUNC_IDX'th function descriptor entry in the encoder context. */ @@ -1642,15 +1725,28 @@ sframe_encoder_add_funcdesc_v2 (sframe_encoder_ctx *encoder, static int sframe_sort_funcdesc (sframe_encoder_ctx *encoder) { - sframe_header *ehp; + sframe_header *ehp = sframe_encoder_get_header (encoder); - ehp = sframe_encoder_get_header (encoder); /* Sort and write out the FDE table. */ sf_fde_tbl *fd_info = encoder->sfe_funcdesc; if (fd_info) { + /* The new encoding of sfde_func_start_address means the distances are + not from the same anchor, so cannot be sorted directly. At the moment + we adress this by manual value adjustments before and after sorting. + FIXME - qsort_r may be more optimal. */ + + for (unsigned int i = 0; i < fd_info->count; i++) + fd_info->entry[i].sfde_func_start_address + += sframe_encoder_get_offsetof_fde_start_addr (encoder, i, NULL); + qsort (fd_info->entry, fd_info->count, sizeof (sframe_func_desc_entry), fde_func); + + for (unsigned int i = 0; i < fd_info->count; i++) + fd_info->entry[i].sfde_func_start_address + -= sframe_encoder_get_offsetof_fde_start_addr (encoder, i, NULL); + /* Update preamble's flags. */ ehp->sfh_preamble.sfp_flags |= SFRAME_F_FDE_SORTED; } @@ -1751,7 +1847,6 @@ sframe_encoder_write_sframe (sframe_encoder_ctx *encoder) size_t fre_size; size_t esz = 0; sframe_header *ehp; - unsigned char flags; sf_fde_tbl *fd_info; sf_fre_tbl *fr_info; uint32_t i, num_fdes; @@ -1821,8 +1916,7 @@ sframe_encoder_write_sframe (sframe_encoder_ctx *encoder) /* Sanity checks: - the FDE section must have been sorted by now on the start address of each function. */ - flags = ehp->sfh_preamble.sfp_flags; - if (!(flags & SFRAME_F_FDE_SORTED) + if (!(sframe_encoder_get_flags (encoder) & SFRAME_F_FDE_SORTED) || (fd_info == NULL)) return sframe_set_errno (&err, SFRAME_ERR_FDE_INVAL); |