aboutsummaryrefslogtreecommitdiff
path: root/libsframe/sframe.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsframe/sframe.c')
-rw-r--r--libsframe/sframe.c153
1 files changed, 131 insertions, 22 deletions
diff --git a/libsframe/sframe.c b/libsframe/sframe.c
index d38a61d..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,25 +363,47 @@ 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,
+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;
- if (!fdep)
- return false;
-
- 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;
@@ -913,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);
@@ -988,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)
@@ -1006,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. */
@@ -1023,7 +1077,7 @@ 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;
@@ -1038,7 +1092,7 @@ 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. */
@@ -1051,12 +1105,16 @@ sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr,
/* Given sfde_func_start_address <= addr,
addr - sfde_func_start_address must be positive. */
- if (fdp[mid].sfde_func_start_address <= addr
- && ((uint32_t)(addr - fdp[mid].sfde_func_start_address)
+ 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))
- 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;
else
high = mid - 1;
@@ -1100,6 +1158,7 @@ sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
{
sframe_frame_row_entry cur_fre;
sframe_func_desc_entry *fdep;
+ uint32_t func_idx;
uint32_t fre_type, i;
int32_t func_start_addr;
uint32_t start_ip_offset, end_ip_offset;
@@ -1111,14 +1170,14 @@ sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
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);
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++)
{
@@ -1134,7 +1193,8 @@ sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
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;
@@ -1330,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;
@@ -1402,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. */
@@ -1416,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. */
@@ -1627,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;
}
@@ -1736,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;
@@ -1806,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);