@@ -21,6 +21,18 @@
#include <assert.h>
#include <sframe-read.h>
+/* Representation of SFrame FDE internal to libsframe. */
+typedef struct sframe_func_desc_entry_int
+{
+ int64_t func_start_address;
+ uint32_t func_size;
+ uint32_t func_start_fre_off;
+ uint32_t func_num_fres;
+ uint8_t func_info;
+ uint8_t func_info2;
+ uint8_t func_rep_size;
+} sframe_func_desc_entry_int;
+
/* Get the SFrame header size. */
static inline uint32_t
@@ -52,33 +64,51 @@ sframe_get_fre_ra_mangled_p (uint8_t fre_info)
/* Access functions for info from function descriptor entry. */
static uint32_t
-sframe_get_fre_type (sframe_func_desc_entry *fdep)
+sframe_get_fre_type (sframe_func_desc_entry_int *fdep, uint8_t version)
{
- uint32_t fre_type = 0;
- if (fdep != NULL)
- fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info);
- return fre_type;
+ if (fdep == NULL)
+ return 0;
+ if (version == SFRAME_VERSION_3)
+ return SFRAME_V3_FDE_FRE_TYPE (fdep->func_info);
+ return SFRAME_V1_FUNC_FRE_TYPE (fdep->func_info);
+}
+
+static uint32_t
+sframe_get_fde_pc_type (sframe_func_desc_entry_int *fdep, uint8_t version)
+{
+ if (fdep == NULL)
+ return 0;
+ if (version == SFRAME_VERSION_3)
+ return SFRAME_V3_FDE_PC_TYPE (fdep->func_info);
+ return SFRAME_V1_FUNC_FDE_TYPE (fdep->func_info);
}
static uint32_t
-sframe_get_fde_type (sframe_func_desc_entry *fdep)
+sframe_get_fde_type (sframe_func_desc_entry_int *fdep, uint8_t version)
{
- uint32_t fde_type = 0;
- if (fdep != NULL)
- fde_type = SFRAME_V1_FUNC_FDE_TYPE (fdep->sfde_func_info);
- return fde_type;
+ if (fdep == NULL)
+ return 0;
+ if (version == SFRAME_VERSION_3)
+ return SFRAME_V3_FDE_TYPE (fdep->func_info2);
+ return SFRAME_FDE_TYPE_DEFAULT;
}
-/* Check if SFrame header has valid data. Only consider SFrame type
- 2. */
+/* Check if SFrame header has valid data (v2 or v3). */
static bool
sframe_header_sanity_check_p (sframe_header *hp)
{
+ uint8_t valid_flags;
+
/* 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))
+ || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_2
+ && hp->sfh_preamble.sfp_version != SFRAME_VERSION_3))
+ return false;
+
+ valid_flags = (hp->sfh_preamble.sfp_version == SFRAME_VERSION_3
+ ? SFRAME_V3_F_ALL_FLAGS : SFRAME_V2_F_ALL_FLAGS);
+ if (hp->sfh_preamble.sfp_flags & ~valid_flags)
return false;
/* Check offsets are valid. */
@@ -181,6 +211,78 @@ sframe_decoder_get_header (sframe_decoder_ctx *dctx)
return hp;
}
+static inline uint8_t
+sframe_decoder_get_version (sframe_decoder_ctx *dctx)
+{
+ sframe_header *hp = sframe_decoder_get_header (dctx);
+ return hp != NULL ? hp->sfh_preamble.sfp_version : 0;
+}
+
+/* Read function descriptor data at FUNC_IDX in DCTX and fill FDEP. */
+
+static bool
+sframe_decoder_get_funcdesc_at_index (sframe_decoder_ctx *dctx,
+ uint32_t func_idx,
+ sframe_func_desc_entry_int *fdep)
+{
+ sframe_header *dhp;
+ uint8_t version;
+
+ if (fdep == NULL)
+ return false;
+
+ dhp = sframe_decoder_get_header (dctx);
+ if (dhp == NULL
+ || func_idx >= dhp->sfh_num_fdes
+ || dctx->sfd_funcdesc == NULL)
+ return false;
+
+ version = sframe_decoder_get_version (dctx);
+ if (version == SFRAME_VERSION_3)
+ {
+ const sframe_func_desc_idx_v3 *fdip;
+ const sframe_func_desc_attr_v3 *fdap;
+ uint32_t fre_off, fre_bytes;
+
+ if (dctx->sfd_fres == NULL || dctx->sfd_fre_nbytes < 0)
+ return false;
+
+ fdip = ((const sframe_func_desc_idx_v3 *) dctx->sfd_funcdesc) + func_idx;
+ fre_off = fdip->sfdi_func_start_fre_off;
+ fre_bytes = (uint32_t) dctx->sfd_fre_nbytes;
+ if (fre_off > fre_bytes
+ || fre_bytes - fre_off < sizeof (sframe_func_desc_attr_v3))
+ return false;
+
+ fdap = (const sframe_func_desc_attr_v3 *) (dctx->sfd_fres + fre_off);
+ fdep->func_start_address = fdip->sfdi_func_start_offset;
+ fdep->func_size = fdip->sfdi_func_size;
+ fdep->func_start_fre_off = fre_off;
+ fdep->func_num_fres = fdap->sfda_func_num_fres;
+ fdep->func_info = fdap->sfda_func_info;
+ fdep->func_info2 = fdap->sfda_func_info2;
+ fdep->func_rep_size = fdap->sfda_func_rep_size;
+ return true;
+ }
+
+ if (version == SFRAME_VERSION_2)
+ {
+ const sframe_func_desc_entry_v2 *fdp;
+
+ fdp = ((const sframe_func_desc_entry_v2 *) dctx->sfd_funcdesc) + func_idx;
+ fdep->func_start_address = fdp->sfde_func_start_address;
+ fdep->func_size = fdp->sfde_func_size;
+ fdep->func_start_fre_off = fdp->sfde_func_start_fre_off;
+ fdep->func_num_fres = fdp->sfde_func_num_fres;
+ fdep->func_info = fdp->sfde_func_info;
+ fdep->func_info2 = 0;
+ fdep->func_rep_size = fdp->sfde_func_rep_size;
+ return true;
+ }
+
+ return false;
+}
+
/* 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. */
@@ -209,9 +311,16 @@ sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx,
else if (errp != NULL)
*errp = _URC_NO_REASON;
+ if (sframe_decoder_get_version (dctx) == SFRAME_VERSION_3)
+ return (sframe_get_hdr_size (dhp)
+ + dhp->sfh_fdeoff
+ + func_idx * sizeof (sframe_func_desc_idx_v3)
+ + offsetof (sframe_func_desc_idx_v3, sfdi_func_start_offset));
+
return (sframe_get_hdr_size (dhp)
- + func_idx * sizeof (sframe_func_desc_entry)
- + offsetof (sframe_func_desc_entry, sfde_func_start_address));
+ + dhp->sfh_fdeoff
+ + func_idx * sizeof (sframe_func_desc_entry_v2)
+ + offsetof (sframe_func_desc_entry_v2, sfde_func_start_address));
}
@@ -222,13 +331,22 @@ sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx,
If FUNC_IDX is not a valid index in the given decoder object, returns 0. */
-static int32_t
+static int64_t
sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx,
- uint32_t func_idx)
+ uint32_t func_idx,
+ sframe_func_desc_entry_int *fdep)
{
- int32_t func_start_addr;
+ int64_t func_start_addr;
_Unwind_Reason_Code err = 0;
- int32_t offsetof_fde_in_sec = 0;
+ uint32_t offsetof_fde_in_sec = 0;
+ sframe_func_desc_entry_int fdesc;
+
+ if (fdep == NULL)
+ {
+ if (!sframe_decoder_get_funcdesc_at_index (dctx, func_idx, &fdesc))
+ return 0;
+ fdep = &fdesc;
+ }
/* Check if we have SFRAME_F_FDE_FUNC_START_PCREL. */
sframe_header *sh = &dctx->sfd_header;
@@ -241,7 +359,7 @@ sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx,
return 0;
}
- func_start_addr = dctx->sfd_funcdesc[func_idx].sfde_func_start_address;
+ func_start_addr = fdep->func_start_address;
return func_start_addr + offsetof_fde_in_sec;
}
@@ -251,25 +369,28 @@ sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx,
FUNC_IDX). */
static bool
-sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx,
+sframe_fre_check_range_p (sframe_decoder_ctx *dctx,
+ sframe_func_desc_entry_int *fdep,
+ uint32_t func_idx,
uint32_t start_ip_offset, uint32_t end_ip_offset,
- int32_t pc)
+ int64_t pc)
{
- sframe_func_desc_entry *fdep;
- int32_t func_start_addr;
+ int64_t func_start_addr;
uint8_t rep_block_size;
- uint32_t fde_type;
- uint32_t pc_offset;
+ uint32_t fde_pc_type;
+ uint64_t pc_offset;
bool mask_p;
+ uint8_t version;
- 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;
+ version = sframe_decoder_get_version (dctx);
+ func_start_addr = sframe_decoder_get_secrel_func_start_addr (dctx, func_idx,
+ fdep);
+ fde_pc_type = sframe_get_fde_pc_type (fdep, version);
+ mask_p = (fde_pc_type == SFRAME_FDE_TYPE_PCMASK);
+ rep_block_size = fdep->func_rep_size;
if (func_start_addr > pc)
return false;
@@ -279,7 +400,11 @@ sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx,
/* 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)
+ {
+ if (rep_block_size == 0)
+ return false;
pc_offset = pc_offset % rep_block_size;
+ }
return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset);
}
@@ -360,68 +485,72 @@ sframe_decode_fre_start_address (const char *fre_buf,
/* 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)
+static bool
+sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int64_t addr,
+ sframe_func_desc_entry_int *fdep,
+ uint32_t *func_idx)
{
sframe_header *dhp;
- sframe_func_desc_entry *fdp;
int low, high;
- if (ctx == NULL)
- return NULL;
+ if (ctx == NULL || fdep == NULL || func_idx == NULL)
+ return false;
dhp = sframe_decoder_get_header (ctx);
if (dhp == NULL || dhp->sfh_num_fdes == 0 || ctx->sfd_funcdesc == NULL)
- return NULL;
+ return false;
/* 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;
+ return false;
- /* 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;
+ sframe_func_desc_entry_int mid_fde;
+ int64_t start_addr;
+
+ if (!sframe_decoder_get_funcdesc_at_index (ctx, mid, &mid_fde))
+ return false;
- /* 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))
+ start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, mid,
+ &mid_fde);
+ /* Given start_addr <= addr, addr - start_addr must be positive. */
+ if (start_addr <= addr
+ && ((uint64_t) (addr - start_addr) < mid_fde.func_size))
{
*func_idx = mid;
- return fdp + mid;
+ *fdep = mid_fde;
+ return true;
}
- if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr)
+ if (start_addr < addr)
low = mid + 1;
else
high = mid - 1;
}
- return NULL;
+ return false;
}
/* 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)
+sframe_fre_get_end_ip_offset (sframe_func_desc_entry_int *fdep,
+ uint8_t version,
+ unsigned int i, const char *fres)
{
uint32_t end_ip_offset = 0;
uint32_t fre_type;
- fre_type = sframe_get_fre_type (fdep);
+ fre_type = sframe_get_fre_type (fdep, version);
/* Get the start address of the next FRE in sequence. */
- if (i < fdep->sfde_func_num_fres - 1)
+ if (i < fdep->func_num_fres - 1)
{
sframe_decode_fre_start_address (fres, &end_ip_offset, fre_type);
end_ip_offset -= 1;
@@ -429,7 +558,7 @@ sframe_fre_get_end_ip_offset (sframe_func_desc_entry *fdep, unsigned int i,
else
/* The end IP offset for the FRE needs to be deduced from the function
size. */
- end_ip_offset = fdep->sfde_func_size - 1;
+ end_ip_offset = fdep->func_size - 1;
return end_ip_offset;
}
@@ -528,11 +657,13 @@ _Unwind_Reason_Code
__sframe_decode (sframe_decoder_ctx *dctx, const char *sf_buf)
{
const sframe_preamble *sfp;
+ char *frame_buf;
+ const char *payload;
size_t hdrsz;
+ size_t fde_desc_size;
+ size_t fde_table_size;
sframe_header *sfheaderp;
- char *frame_buf;
-
- int fidx_size;
+ uint8_t version;
uint32_t fre_bytes;
if (sf_buf == NULL)
@@ -555,15 +686,23 @@ __sframe_decode (sframe_decoder_ctx *dctx, const char *sf_buf)
return _URC_END_OF_STACK;
hdrsz = sframe_get_hdr_size (sfheaderp);
- frame_buf += hdrsz;
+ payload = frame_buf + hdrsz;
+ version = sfheaderp->sfh_preamble.sfp_version;
+
+ fde_desc_size = (version == SFRAME_VERSION_3
+ ? sizeof (sframe_func_desc_idx_v3)
+ : sizeof (sframe_func_desc_entry_v2));
+ fde_table_size = sfheaderp->sfh_num_fdes * fde_desc_size;
+ if (sfheaderp->sfh_freoff < sfheaderp->sfh_fdeoff
+ || (size_t) (sfheaderp->sfh_freoff - sfheaderp->sfh_fdeoff)
+ < fde_table_size)
+ return _URC_END_OF_STACK;
/* 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_funcdesc
+ = (sframe_func_desc_entry_v2 *) (payload + sfheaderp->sfh_fdeoff);
- dctx->sfd_fres = frame_buf;
+ dctx->sfd_fres = (char *) (payload + sfheaderp->sfh_freoff);
fre_bytes = sfheaderp->sfh_fre_len;
dctx->sfd_fre_nbytes = fre_bytes;
@@ -574,55 +713,84 @@ __sframe_decode (sframe_decoder_ctx *dctx, const char *sf_buf)
_URC_END_OF_STACK if failure. */
_Unwind_Reason_Code
-__sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
+__sframe_find_fre (sframe_decoder_ctx *ctx, int64_t pc,
sframe_frame_row_entry *frep)
{
- sframe_func_desc_entry *fdep;
+ sframe_func_desc_entry_int fdep;
uint32_t func_idx;
+ uint8_t version;
uint32_t fre_type, i;
uint32_t start_ip_offset;
- int32_t func_start_addr;
+ int64_t func_start_addr;
uint32_t end_ip_offset;
+ const char *fres_end;
const char *fres;
size_t size = 0;
- int err = 0;
if ((ctx == NULL) || (frep == NULL))
return _URC_END_OF_STACK;
+ version = sframe_decoder_get_version (ctx);
+
/* 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)
+ if (!sframe_get_funcdesc_with_addr_internal (ctx, pc, &fdep, &func_idx)
+ || ctx->sfd_fres == NULL)
return _URC_END_OF_STACK;
- fre_type = sframe_get_fre_type (fdep);
+ /* Glibc's SFrame unwinder currently supports only DEFAULT FDEs. */
+ if (sframe_get_fde_type (&fdep, version) != SFRAME_FDE_TYPE_DEFAULT)
+ return _URC_END_OF_STACK;
- fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off;
- func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx);
+ fre_type = sframe_get_fre_type (&fdep, version);
+ fres = ctx->sfd_fres + fdep.func_start_fre_off;
+ if (version == SFRAME_VERSION_3)
+ fres += sizeof (sframe_func_desc_attr_v3);
- for (i = 0; i < fdep->sfde_func_num_fres; i++)
+ if (ctx->sfd_fre_nbytes < 0)
+ return _URC_END_OF_STACK;
+ fres_end = ctx->sfd_fres + (size_t) ctx->sfd_fre_nbytes;
+ if (fres > fres_end)
+ return _URC_END_OF_STACK;
+
+ func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx,
+ &fdep);
+
+ for (i = 0; i < fdep.func_num_fres; i++)
{
size_t addr_size;
+ addr_size = sframe_fre_start_addr_size (fre_type);
+ if (addr_size == 0)
+ return _URC_END_OF_STACK;
+
+ if ((size_t) (fres_end - fres) < addr_size)
+ return _URC_END_OF_STACK;
+
/* 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)
+ if ((size_t) (fres_end - fres) < addr_size + sizeof (frep->fre_info))
return _URC_END_OF_STACK;
frep->fre_info = *(uint8_t *)(fres + addr_size);
size = sframe_fre_entry_size (frep, addr_size);
+ if ((size_t) (fres_end - fres) < size)
+ return _URC_END_OF_STACK;
+ if (i < fdep.func_num_fres - 1
+ && (size_t) (fres_end - (fres + size)) < addr_size)
+ return _URC_END_OF_STACK;
start_ip_offset = frep->fre_start_addr;
- end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size);
+ end_ip_offset = sframe_fre_get_end_ip_offset (&fdep, version, 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))
+ if (func_start_addr > pc
+ || start_ip_offset > (uint64_t) (pc - func_start_addr))
return _URC_END_OF_STACK;
- if (sframe_fre_check_range_p (ctx, func_idx, start_ip_offset,
+ if (sframe_fre_check_range_p (ctx, &fdep, func_idx, start_ip_offset,
end_ip_offset, pc))
{
/* Decode last FRE bits: offsets size. */
@@ -31,7 +31,7 @@ typedef struct sframe_decoder_ctx
sframe_header sfd_header;
- sframe_func_desc_entry *sfd_funcdesc;
+ sframe_func_desc_entry_v2 *sfd_funcdesc;
/* SFrame FRE table. */
char *sfd_fres;
/* Number of bytes needed for SFrame FREs. */
@@ -67,7 +67,7 @@ __sframe_decode (sframe_decoder_ctx *dctx, const char *cf_buf);
_URC_END_OF_STACK if failure. */
extern _Unwind_Reason_Code
-__sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
+__sframe_find_fre (sframe_decoder_ctx *ctx, int64_t pc,
sframe_frame_row_entry *frep);
/* Get the base reg id from the FRE info. */
@@ -117,6 +117,10 @@ __stacktrace_sframe (void **ra_lst, int count, frame *frame)
return 0;
}
+ /* A FRE with undefined RA indicates the outermost frame. */
+ if (SFRAME_V2_FRE_RA_UNDEFINED_P (frep->fre_info))
+ return i;
+
/* Get the CFA offset from the FRE. If offset is unavailable,
sets err. */
cfa_offset = __sframe_fre_get_cfa_offset (dctx, frep, &err);
@@ -72,10 +72,11 @@ extern "C"
/* SFrame format versions. */
#define SFRAME_VERSION_1 1
#define SFRAME_VERSION_2 2
+#define SFRAME_VERSION_3 3
/* SFrame magic number. */
#define SFRAME_MAGIC 0xdee2
/* Current version of SFrame format. */
-#define SFRAME_VERSION SFRAME_VERSION_2
+#define SFRAME_VERSION SFRAME_VERSION_3
/* Various flags for SFrame. */
@@ -95,6 +96,10 @@ extern "C"
(SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER \
| SFRAME_F_FDE_FUNC_START_PCREL)
+/* Set of all defined flags in SFrame V3. */
+#define SFRAME_V3_F_ALL_FLAGS \
+ (SFRAME_F_FDE_SORTED | SFRAME_F_FDE_FUNC_START_PCREL)
+
#define SFRAME_CFA_FIXED_FP_INVALID 0
#define SFRAME_CFA_FIXED_RA_INVALID 0
@@ -102,6 +107,7 @@ extern "C"
#define SFRAME_ABI_AARCH64_ENDIAN_BIG 1 /* AARCH64 big endian. */
#define SFRAME_ABI_AARCH64_ENDIAN_LITTLE 2 /* AARCH64 little endian. */
#define SFRAME_ABI_AMD64_ENDIAN_LITTLE 3 /* AMD64 little endian. */
+#define SFRAME_ABI_S390X_ENDIAN_BIG 4 /* s390x big endian. */
/* SFrame FRE types. */
#define SFRAME_FRE_TYPE_ADDR1 0
@@ -131,6 +137,19 @@ extern "C"
matching FRE. */
#define SFRAME_FDE_TYPE_PCMASK 1
+/* SFrame FDE types. */
+
+/* Default FDE type. */
+#define SFRAME_FDE_TYPE_DEFAULT 0
+/* Flexible Frame FDE type.
+ The recovery rule for CFA, RA and FP allow more flexibility. Examples of
+ patterns supported include:
+ - CFA may be non-SP/FP based.
+ - CFA, FP may encode dereferencing of register after offset adjustment
+ - RA may be in a non-default register.
+ Currently used for SFRAME_ABI_AMD64_ENDIAN_LITTLE. */
+#define SFRAME_FDE_TYPE_FLEX 1
+
typedef struct sframe_preamble
{
uint16_t sfp_magic; /* Magic number (SFRAME_MAGIC). */
@@ -180,7 +199,7 @@ typedef struct sframe_header
#define SFRAME_AARCH64_PAUTH_KEY_A 0 /* Key A. */
#define SFRAME_AARCH64_PAUTH_KEY_B 1 /* Key B. */
-typedef struct sframe_func_desc_entry
+typedef struct sframe_func_desc_entry_v2
{
/* Function start address. Encoded as a signed offset, relative to the
beginning of the current FDE. */
@@ -199,7 +218,7 @@ typedef struct sframe_func_desc_entry
- 2-bits: Unused.
------------------------------------------------------------------------
| Unused | PAC auth A/B key (aarch64) | FDE type | FRE type |
- | | Unused (amd64) | | |
+ | | Unused (amd64, s390x) | | |
------------------------------------------------------------------------
8 6 5 4 0 */
uint8_t sfde_func_info;
@@ -207,7 +226,7 @@ typedef struct sframe_func_desc_entry
SFRAME_FDE_TYPE_PCMASK. */
uint8_t sfde_func_rep_size;
uint16_t sfde_func_padding2;
-} __attribute__ ((packed)) sframe_func_desc_entry;
+} __attribute__ ((packed)) sframe_func_desc_entry_v2;
/* Macros to compose and decompose function info in FDE. */
@@ -224,6 +243,87 @@ typedef struct sframe_func_desc_entry
#define SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY(pauth_key, fde_info) \
((((pauth_key) & 0x1) << 5) | ((fde_info) & 0xdf))
+/* SFrame V2 has similar SFrame FDE representation as SFrame V1. */
+#define SFRAME_V2_FUNC_PC_TYPE(data) (SFRAME_V1_FUNC_FDE_TYPE (data))
+#define SFRAME_V2_FUNC_FRE_TYPE(data) (SFRAME_V1_FUNC_FRE_TYPE (data))
+
+/* SFrame Function Descriptor Entry PC types.
+
+ The SFrame format has two possible representations for functions' PC Type.
+ The choice of which PC type to use is made according to the instruction
+ patterns in the relevant program stub.
+
+ The PC type SFRAME_V3_FDE_PCTYPE_INC is an indication that the PCs in the
+ FREs should be treated as increments in bytes. This is used for a bulk of
+ the executable code of a program, which contains instructions with no
+ specific pattern.
+
+ The PC type SFRAME_V3_FDE_PCTYPE_MASK is an indication that the PCs in the
+ FREs should be treated as masks. This type is useful for the cases when a
+ small pattern of instructions in a program stub is repeatedly to cover a
+ specific functionality. Typical usescases are pltN entries, trampolines
+ etc.
+
+ NB: In SFrame version 2 or lower, the names SFRAME_FDE_TYPE_PCINC and
+ SFRAME_FDE_TYPE_PCMASK were used. */
+
+/* Unwinders perform a (PC >= FRE_START_ADDR) to look up a matching FRE. */
+#define SFRAME_V3_FDE_PCTYPE_INC SFRAME_FDE_TYPE_PCINC
+/* Unwinders perform a (PC % REP_BLOCK_SIZE >= FRE_START_ADDR) to look up a
+ matching FRE. */
+#define SFRAME_V3_FDE_PCTYPE_MASK SFRAME_FDE_TYPE_PCMASK
+
+typedef struct sframe_func_desc_idx_v3
+{
+ /* Offset to the function start address. Encoded as a signed offset,
+ relative to the beginning of the current FDE. */
+ int64_t sfdi_func_start_offset;
+ /* Size of the function in bytes. */
+ uint32_t sfdi_func_size;
+ /* Offset of the first SFrame Frame Row Entry of the function, relative to the
+ beginning of the SFrame Frame Row Entry sub-section. */
+ uint32_t sfdi_func_start_fre_off;
+} __attribute__ ((packed)) sframe_func_desc_idx_v3;
+
+typedef struct sframe_func_desc_attr_v3
+{
+ /* Number of frame row entries for the function. */
+ uint16_t sfda_func_num_fres;
+ /* Additional information for stack tracing from the function:
+ - 4-bits: Identify the FRE type used for the function.
+ - 1-bit: Identify the PC type of the function - mask or inc.
+ - 1-bit: PAC authorization A/B key (aarch64).
+ - 1-bits: Unused.
+ - 1-bit: Signal frame.
+ -------------------------------------------------------------------------------
+ | Signal | Unused | PAC auth A/B key (aarch64) | FDE | FRE Type |
+ | frame | | Unused (amd64) | PC Type | |
+ -------------------------------------------------------------------------------
+ 8 7 6 5 4 0 */
+ uint8_t sfda_func_info;
+ /* Additional information for stack tracing from the function:
+ - 5-bits: FDE type.
+ - 3-bits: Unused.
+ ------------------------------------------------------------
+ | Unused | FDE Type |
+ | | |
+ ------------------------------------------------------------
+ 8 7 6 5 0 */
+ uint8_t sfda_func_info2;
+ /* Size of the block of repeating insns. Used for SFrame FDEs of type
+ SFRAME_V3_FDE_PCTYPE_MASK. */
+ uint8_t sfda_func_rep_size;
+} __attribute__ ((packed)) sframe_func_desc_attr_v3;
+
+/* Mask for the ABI/arch specific FDE type (lower 5 bits). */
+#define SFRAME_V3_FDE_TYPE_MASK 0x1f
+
+/* Get the FDE type from the info2 byte. */
+#define SFRAME_V3_FDE_TYPE(info2) \
+ ((info2) & SFRAME_V3_FDE_TYPE_MASK)
+#define SFRAME_V3_FDE_FRE_TYPE(info) (SFRAME_V2_FUNC_FRE_TYPE (info))
+#define SFRAME_V3_FDE_PC_TYPE(info) (SFRAME_V2_FUNC_PC_TYPE (info))
+
/* Size of stack frame offsets in an SFrame Frame Row Entry. A single
SFrame FRE has all offsets of the same size. Offset size may vary
across frame row entries. */
@@ -231,6 +331,14 @@ typedef struct sframe_func_desc_entry
#define SFRAME_FRE_OFFSET_2B 1
#define SFRAME_FRE_OFFSET_4B 2
+/* In SFrame V3, with the addition of flexible FDE, usage of term "offsets"
+ (for the varlen data trailing the SFrame FRE) is inappropriate. Use the
+ terminology of "data word" instead. A single SFrame FRE has all data words
+ of the same size. Size of data words may vary across frame row entries. */
+#define SFRAME_FRE_DATAWORD_1B SFRAME_FRE_OFFSET_1B
+#define SFRAME_FRE_DATAWORD_2B SFRAME_FRE_OFFSET_2B
+#define SFRAME_FRE_DATAWORD_4B SFRAME_FRE_OFFSET_4B
+
/* An SFrame Frame Row Entry can be SP or FP based. */
#define SFRAME_BASE_REG_FP 0
#define SFRAME_BASE_REG_SP 1
@@ -249,15 +357,16 @@ typedef struct sframe_fre_info
{
/* Information about
- 1 bit: base reg for CFA
- - 4 bits: Number of offsets (N). A value of upto 3 is allowed to track
- all three of CFA, FP and RA (fixed implicit order).
- - 2 bits: information about size of the offsets (S) in bytes.
- Valid values are SFRAME_FRE_OFFSET_1B, SFRAME_FRE_OFFSET_2B,
- SFRAME_FRE_OFFSET_4B
+ - 4 bits: Number of data words (N). Typically for default FDE type, a
+ value of upto 3 suffices to track all three of CFA, FP and RA (fixed
+ implicit order).
+ - 2 bits: information about size of the data words (S) in bytes.
+ Valid values are SFRAME_FRE_DATAWORD_1B, SFRAME_FRE_DATAWORD_2B,
+ SFRAME_FRE_DATAWORD_4B.
- 1 bit: Mangled RA state bit (aarch64 only).
----------------------------------------------------------------------------------
- | Mangled-RA (aarch64) | Size of offsets | Number of offsets | base_reg |
- | Unused (amd64) | | | |
+ | Mangled-RA (aarch64) | Size of Data Words | Number of Data Words | base_reg |
+ | Unused (amd64, s390x)| | | |
----------------------------------------------------------------------------------
8 7 5 1 0
@@ -280,10 +389,12 @@ typedef struct sframe_fre_info
#define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf)
#define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3)
#define SFRAME_V1_FRE_MANGLED_RA_P(data) (((data) >> 7) & 0x1)
+/* A zero offset count indicates that RA is undefined for this FRE. */
+#define SFRAME_V2_FRE_RA_UNDEFINED_P(data) (SFRAME_V1_FRE_OFFSET_COUNT (data) == 0)
/* SFrame Frame Row Entry definitions.
- Used for both AMD64 and AARCH64.
+ Used for Default FDEs in AMD64, AARCH64, and s390x.
An SFrame Frame Row Entry is a self-sufficient record which contains
information on how to generate the stack trace for the specified range of
@@ -291,7 +402,7 @@ typedef struct sframe_fre_info
S is the size of the stack frame offset for the FRE, and
N is the number of stack frame offsets in the FRE
- The interpretation of FRE stack offsets is ABI-specific:
+ The interpretation of FRE stack offsets for default FDEs is ABI-specific:
AMD64:
offset1 (interpreted as CFA = BASE_REG + offset1)
@@ -307,6 +418,25 @@ typedef struct sframe_fre_info
fi
Note that in AAPCS64, a frame record, if created, will save both FP and
LR on stack.
+
+ s390x:
+ offset1 (interpreted as CFA = BASE_REG + offset1)
+ if RA is being tracked
+ offset2 (interpreted as RA = CFA + offset2; an offset value of
+ SFRAME_FRE_RA_OFFSET_INVALID indicates a dummy padding RA offset
+ to represent FP without RA saved on stack)
+ if FP is being tracked
+ offset3 (intrepreted as FP = CFA + offset3)
+ fi
+ else
+ if FP is being tracked
+ offset2 (intrepreted as FP = CFA + offset2)
+ fi
+ fi
+ Note that in s390x, if a FP/RA is to be restored from a register, flex FDEs
+ are used in SFrame V3. In SFrame V2, default FDEs were used: the
+ least-significant bit of the offset was set to indicate that the encoded
+ value is a DWARF register number shifted to the left by 1.
*/
/* Used when SFRAME_FRE_TYPE_ADDR1 is specified as FRE type. */