[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH][gnulib] Add the Sframe package
From: |
Weimin Pan |
Subject: |
[PATCH][gnulib] Add the Sframe package |
Date: |
Tue, 15 Nov 2022 12:09:51 -0800 |
This patch adds two new modules to gnulib, related to the Simple Frame
format (SFrame) recently introduced in binutils [1].
The SFrame format (Simple Frame format) represents the minimal necessary
information for stack backtrace and it is stored in the .sframe section.
The format's detailed definition is in lib/sframe.h. The main design
goals after the format are 1) to be simple and easy to decode and
implement and 2) to be compact. The main use-case for this format are
"online" debugging tools like stack tracers (as opposed to full-fledged
"offline" debugging tools like source debuggers.)
The first module, sframe-header, provides a header file sframe.h with
the data structures comprising the format.
The second module, `sframe', provides a flexible and convenient
implementation of SFrame encoding and decoding. This code is based on
the libsframe submitted to binutils, which is in turn used by
BFD/linker, and the plan is to switch the later to use this gnulib
module instead in order to minimize external dependencies.
[1] https://sourceware.org/pipermail/binutils/2022-October/123641.html
---
ChangeLog | 3 +
MODULES.html.sh | 2 +
lib/sframe-api.h | 229 ++++
lib/sframe-internal.h | 53 +
lib/sframe.c | 1853 +++++++++++++++++++++++++++++++
lib/sframe.h | 287 +++++
modules/sframe | 30 +
modules/sframe-header | 23 +
modules/sframe-tests | 16 +
tests/DATA-BE | Bin 0 -> 64 bytes
tests/DATA1 | Bin 0 -> 60 bytes
tests/DATA2 | Bin 0 -> 92 bytes
tests/sframe-api.h | 229 ++++
tests/test-sframe-be-flipping.c | 115 ++
tests/test-sframe-encode1.c | 187 ++++
tests/test-sframe-frecnt1.c | 99 ++
tests/test-sframe-frecnt2.c | 103 ++
17 files changed, 3229 insertions(+)
create mode 100644 lib/sframe-api.h
create mode 100644 lib/sframe-internal.h
create mode 100644 lib/sframe.c
create mode 100644 lib/sframe.h
create mode 100644 modules/sframe
create mode 100644 modules/sframe-header
create mode 100644 modules/sframe-tests
create mode 100644 tests/DATA-BE
create mode 100644 tests/DATA1
create mode 100644 tests/DATA2
create mode 100644 tests/sframe-api.h
create mode 100644 tests/test-sframe-be-flipping.c
create mode 100644 tests/test-sframe-encode1.c
create mode 100644 tests/test-sframe-frecnt1.c
create mode 100644 tests/test-sframe-frecnt2.c
diff --git a/ChangeLog b/ChangeLog
index 76a56c78f..0f16f24b2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+2022-11-09 Weimin Pan <weimin.pan@oracle.com>
+ Add the SFrame package.
+
2022-10-16 Bruno Haible <bruno@clisp.org>
getdelim: Work around buggy implementation on macOS 10.13.
diff --git a/MODULES.html.sh b/MODULES.html.sh
index d48912b13..38a203d19 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -1935,6 +1935,8 @@ func_all_modules ()
func_module linkedhash-list
func_module avltreehash-list
func_module rbtreehash-list
+ func_module sframe
+ func_module sframe-header
func_module sublist
func_module xsublist
func_module oset
diff --git a/lib/sframe-api.h b/lib/sframe-api.h
new file mode 100644
index 000000000..254e9149e
--- /dev/null
+++ b/lib/sframe-api.h
@@ -0,0 +1,229 @@
+/* Public API to SFrame.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _SFRAME_API_H
+#define _SFRAME_API_H
+
+#include <sframe.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct sframe_decoder_ctx sframe_decoder_ctx;
+typedef struct sframe_encoder_ctx sframe_encoder_ctx;
+
+#define MAX_OFFSET_BYTES (SFRAME_FRE_OFFSET_4B * 2 * 3)
+
+/* User interfacing SFrame Row Entry.
+ An abstraction provided by SFrame so the consumer is decoupled from
+ the binary format representation of the same. */
+
+typedef struct sframe_frame_row_entry
+{
+ uint32_t fre_start_addr;
+ unsigned char fre_info;
+ unsigned char fre_offsets[MAX_OFFSET_BYTES];
+} sframe_frame_row_entry;
+
+#define SFRAME_ERR ((int) -1)
+
+/* This macro holds information about all the available SFrame
+ errors. It is used to form both an enum holding all the error
+ constants, and also the error strings themselves. To use, define
+ _SFRAME_FIRST and _SFRAME_ITEM to expand as you like, then
+ mention the macro name. See the enum after this for an example. */
+#define _SFRAME_ERRORS \
+ _SFRAME_FIRST (SFRAME_ERR_VERSION_INVAL, "SFrame version not supported.") \
+ _SFRAME_ITEM (SFRAME_ERR_NOMEM, "Out of Memory.") \
+ _SFRAME_ITEM (SFRAME_ERR_INVAL, "Corrupt SFrame.") \
+ _SFRAME_ITEM (SFRAME_ERR_BUF_INVAL, "Buffer does not contain SFrame data.") \
+ _SFRAME_ITEM (SFRAME_ERR_DCTX_INVAL, "Corrupt SFrame decoder.") \
+ _SFRAME_ITEM (SFRAME_ERR_ECTX_INVAL, "Corrupt SFrame encoder.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_INVAL, "Corrput FDE.") \
+ _SFRAME_ITEM (SFRAME_ERR_FRE_INVAL, "Corrupt FRE.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_NOTFOUND,"FDE not found.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_NOTSORTED, "FDEs not sorted.") \
+ _SFRAME_ITEM (SFRAME_ERR_FRE_NOTFOUND,"FRE not found.") \
+ _SFRAME_ITEM (SFRAME_ERR_FREOFFSET_NOPRESENT,"FRE offset not present.")
+
+#define SFRAME_ERR_BASE 2000 /* Base value for SFrame errnos. */
+
+enum
+ {
+#define _SFRAME_FIRST(NAME, STR) NAME = SFRAME_ERR_BASE
+#define _SFRAME_ITEM(NAME, STR) , NAME
+_SFRAME_ERRORS
+#undef _SFRAME_ITEM
+#undef _SFRAME_FIRST
+ };
+
+/* Count of SFrame errors. */
+#define SFRAME_ERR_NERR (SFRAME_ERR_FREOFFSET_NOPRESENT - SFRAME_ERR_BASE + 1)
+
+/* Get the error message string. */
+
+extern const char *
+sframe_errmsg (int error);
+
+/* Get FDE function info given a FRE_TYPE. */
+
+extern unsigned char
+sframe_fde_func_info (unsigned int fre_type, unsigned int fde_type);
+
+/* Gather the FRE type given the function size. */
+
+extern unsigned int
+sframe_calc_fre_type (unsigned int func_size);
+
+/* The SFrame Decoder. */
+
+/* Decode the specified SFrame buffer CF_BUF of size CF_SIZE and return the
+ new SFrame decoder context. Sets ERRP for the caller if any error. */
+extern sframe_decoder_ctx *
+sframe_decode (const char *cf_buf, size_t cf_size, int *errp);
+
+/* Free the decoder context. */
+extern void
+sframe_decoder_free (sframe_decoder_ctx **dctx);
+
+/* Get the size of the SFrame header from the decoder context DCTX. */
+extern unsigned int
+sframe_decoder_get_hdr_size (sframe_decoder_ctx *dctx);
+
+/* Get the SFrame's abi/arch info. */
+extern unsigned char
+sframe_decoder_get_abi_arch (sframe_decoder_ctx *dctx);
+
+/* Return the number of function descriptor entries in the SFrame decoder
+ DCTX. */
+unsigned int
+sframe_decoder_get_num_fidx (sframe_decoder_ctx *dctx);
+
+/* Get the fixed FP offset from the decoder context DCTX. */
+extern int8_t
+sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *dctx);
+
+/* Get the fixed RA offset from the decoder context DCTX. */
+extern int8_t
+sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *dctx);
+
+/* Find the function descriptor entry which contains the specified address. */
+extern sframe_func_desc_entry *
+sframe_get_funcdesc_with_addr (sframe_decoder_ctx *dctx,
+ int32_t addr, int *errp);
+
+/* Find the SFrame Frame Row Entry which contains the PC. Returns
+ SFRAME_ERR if failure. */
+
+extern int
+sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
+ sframe_frame_row_entry *frep);
+
+/* Get the FRE_IDX'th FRE of the function at FUNC_IDX'th function
+ index entry in the SFrame decoder CTX. Returns error code as
+ applicable. */
+extern int
+sframe_decoder_get_fre (sframe_decoder_ctx *ctx,
+ unsigned int func_idx,
+ unsigned int fre_idx,
+ sframe_frame_row_entry *fre);
+
+/* Get the data (NUM_FRES, FUNC_START_ADDRESS) from the function
+ descriptor entry at index I'th in the decoder CTX. If failed,
+ return error code. */
+extern int
+sframe_decoder_get_funcdesc (sframe_decoder_ctx *ctx,
+ unsigned int i,
+ uint32_t *num_fres,
+ uint32_t *func_size,
+ int32_t *func_start_address,
+ unsigned char *func_info);
+
+/* SFrame textual dump. */
+extern void
+dump_sframe (sframe_decoder_ctx *decoder, uint64_t addr);
+
+/* Get the base reg id from the FRE info. Sets errp if fails. */
+extern unsigned int
+sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_cfa_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_fp_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the RA offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_ra_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* The SFrame Encoder. */
+
+/* Create an encoder context with the given SFrame format version VER, FLAGS
+ and ABI information. Sets errp if failure. */
+extern sframe_encoder_ctx *
+sframe_encode (unsigned char ver, unsigned char flags, int abi,
+ int8_t fixed_fp_offset, int8_t fixed_ra_offset, int *errp);
+
+/* Free the encoder context. */
+extern void
+sframe_encoder_free (sframe_encoder_ctx **encoder);
+
+/* Get the size of the SFrame header from the encoder ctx ENCODER. */
+extern unsigned int
+sframe_encoder_get_hdr_size (sframe_encoder_ctx *encoder);
+
+/* Get the abi/arch info from the SFrame encoder context CTX. */
+extern unsigned char
+sframe_encoder_get_abi_arch (sframe_encoder_ctx *encoder);
+
+/* Return the number of function descriptor entries in the SFrame encoder
+ ENCODER. */
+extern unsigned int
+sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder);
+
+/* Add an FRE to function at FUNC_IDX'th function descriptor index entry in
+ the encoder context. */
+extern int
+sframe_encoder_add_fre (sframe_encoder_ctx *encoder,
+ unsigned int func_idx,
+ sframe_frame_row_entry *frep);
+
+/* Add a new function descriptor entry with START_ADDR, FUNC_SIZE and NUM_FRES
+ to the encoder. */
+extern int
+sframe_encoder_add_funcdesc (sframe_encoder_ctx *encoder,
+ int32_t start_addr,
+ uint32_t func_size,
+ unsigned char func_info,
+ uint32_t num_fres);
+
+/* Serialize the contents of the encoder and return the buffer. ENCODED_SIZE
+ is updated to the size of the buffer. Sets ERRP if failure. */
+extern char *
+sframe_encoder_write (sframe_encoder_ctx *encoder,
+ size_t *encoded_size, int *errp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFRAME_API_H */
diff --git a/lib/sframe-internal.h b/lib/sframe-internal.h
new file mode 100644
index 000000000..af4939ebc
--- /dev/null
+++ b/lib/sframe-internal.h
@@ -0,0 +1,53 @@
+/* Implementation header.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _SFRAME_IMPL_H
+#define _SFRAME_IMPL_H
+
+#include "sframe-api.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <assert.h>
+#define sframe_assert(expr) (assert (expr))
+
+struct sframe_decoder_ctx
+{
+ sframe_header sfd_header; /* SFrame header. */
+ uint32_t *sfd_funcdesc; /* SFrame function desc entries table. */
+ void *sfd_fres; /* SFrame FRE table. */
+ int sfd_fre_nbytes; /* Number of bytes needed for SFrame
FREs. */
+};
+
+struct sframe_encoder_ctx
+{
+ sframe_header sfe_header; /* SFrame header. */
+ uint32_t *sfe_funcdesc; /* SFrame function desc entries table.
*/
+ sframe_frame_row_entry *sfe_fres; /* SFrame FRE table. */
+ uint32_t sfe_fre_nbytes; /* Number of bytes needed for SFrame
FREs. */
+ char *sfe_data; /* SFrame data buffer. */
+ size_t sfe_data_size; /* Size of the SFrame data
buffer. */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFRAME_IMPL_H */
diff --git a/lib/sframe.c b/lib/sframe.c
new file mode 100644
index 000000000..74990dc7d
--- /dev/null
+++ b/lib/sframe.c
@@ -0,0 +1,1853 @@
+/* sframe.c - SFrame decoder/encoder.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "sframe-internal.h"
+#include <byteswap.h>
+
+/* Textual dump of .sframe. */
+
+#define SFRAME_HEADER_FLAGS_STR_MAX_LEN 50
+
+static void
+dump_sframe_header (sframe_decoder_ctx *sfd_ctx)
+{
+ const char *verstr = NULL;
+ const sframe_header *header = &(sfd_ctx->sfd_header);
+
+ /* Prepare SFrame section version string. */
+ const char *version_names[]
+ = { "NULL",
+ "SFRAME_VERSION_1" };
+ unsigned char ver = header->sfh_preamble.sfp_version;
+ if (ver <= SFRAME_VERSION)
+ verstr = version_names[ver];
+
+ /* Prepare SFrame section flags string. */
+ unsigned char flags = header->sfh_preamble.sfp_flags;
+ char *flags_str
+ = (char*) calloc (sizeof (char), SFRAME_HEADER_FLAGS_STR_MAX_LEN);
+ if (flags)
+ {
+ const char *flag_names[]
+ = { "SFRAME_F_FDE_SORTED",
+ "SFRAME_F_FRAME_POINTER" };
+ unsigned char flags = header->sfh_preamble.sfp_flags;
+ if (flags & SFRAME_F_FDE_SORTED)
+ strcpy (flags_str, flag_names[0]);
+ if (flags & SFRAME_F_FRAME_POINTER)
+ {
+ if (strlen (flags_str) > 0)
+ strcpy (flags_str, ",");
+ strcpy (flags_str, flag_names[1]);
+ }
+ }
+ else
+ strcpy (flags_str, "NONE");
+
+ const char* subsec_name = "Header";
+ printf ("\n");
+ printf (" %s :\n", subsec_name);
+ printf ("\n");
+ printf (" Version: %s\n", verstr);
+ printf (" Flags: %s\n", flags_str);
+ printf (" Num FDEs: %d\n", header->sfh_num_fdes);
+ printf (" Num FREs: %d\n", header->sfh_num_fres);
+
+ free (flags_str);
+}
+
+static void
+dump_sframe_func_with_fres (sframe_decoder_ctx *sfd_ctx,
+ unsigned int funcidx,
+ uint64_t sec_addr)
+{
+ uint32_t j = 0;
+ uint32_t num_fres = 0;
+ uint32_t func_size = 0;
+ int32_t func_start_address = 0;
+ unsigned char func_info = 0;
+
+ uint64_t func_start_pc_vma = 0;
+ uint64_t fre_start_pc_vma = 0;
+ const char *base_reg_str[] = {"fp", "sp"};
+ int32_t cfa_offset = 0;
+ int32_t fp_offset = 0;
+ int32_t ra_offset = 0;
+ unsigned int base_reg_id = 0;
+ int err[3] = {0, 0, 0};
+
+ sframe_frame_row_entry fre;
+
+ /* Get the SFrame function descriptor. */
+ sframe_decoder_get_funcdesc (sfd_ctx, funcidx, &num_fres,
+ &func_size, &func_start_address, &func_info);
+ /* Calculate the virtual memory address for function start pc. */
+ func_start_pc_vma = func_start_address + sec_addr;
+
+ /* Mark FDEs with [m] where the FRE start address is interpreted as a
+ mask. */
+ int fde_type_addrmask_p = (SFRAME_V1_FUNC_FDE_TYPE (func_info)
+ == SFRAME_FDE_TYPE_PCMASK);
+ const char *fde_type_marker
+ = (fde_type_addrmask_p ? "[m]" : " ");
+
+ printf ("\n func idx [%d]: pc = 0x%lx, size = %d bytes",
+ funcidx,
+ func_start_pc_vma,
+ func_size);
+
+ char temp[100];
+ memset (temp, 0, 100);
+
+ printf ("\n %-7s%-8s %-10s%-10s%-10s", "STARTPC", fde_type_marker, "CFA",
"FP", "RA");
+ for (j = 0; j < num_fres; j++)
+ {
+ sframe_decoder_get_fre (sfd_ctx, funcidx, j, &fre);
+
+ fre_start_pc_vma = (fde_type_addrmask_p
+ ? fre.fre_start_addr
+ : func_start_pc_vma + fre.fre_start_addr);
+
+ /* FIXME - fixup the err caching in array.
+ assert no error for base reg id. */
+ base_reg_id = sframe_fre_get_base_reg_id (&fre, &err[0]);
+ cfa_offset = sframe_fre_get_cfa_offset (&fre, &err[0]);
+ fp_offset = sframe_fre_get_fp_offset (&fre, &err[1]);
+ ra_offset = sframe_fre_get_ra_offset (&fre, &err[2]);
+
+ /* Dump CFA info. */
+ printf ("\n");
+ printf (" %016lx", fre_start_pc_vma);
+ sprintf (temp, "%s+%d", base_reg_str[base_reg_id], cfa_offset);
+ printf (" %-10s", temp);
+
+ /* Dump SP/FP info. */
+ memset (temp, 0, 100);
+ if (err[1] == 0)
+ sprintf (temp, "c%+d", fp_offset);
+ else
+ strcpy (temp, "u");
+ printf ("%-10s", temp);
+
+ /* Dump RA info. */
+ memset (temp, 0, 100);
+ if (err[2] == 0)
+ sprintf (temp, "c%+d", ra_offset);
+ else
+ strcpy (temp, "u");
+ printf ("%-10s", temp);
+ }
+}
+
+static void
+dump_sframe_functions (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
+{
+ uint32_t i;
+ uint32_t num_fdes;
+
+ const char* subsec_name = "Function Index";
+ printf ("\n %s :\n", subsec_name);
+
+ num_fdes = sframe_decoder_get_num_fidx (sfd_ctx);
+ for (i = 0; i < num_fdes; i++)
+ {
+ dump_sframe_func_with_fres (sfd_ctx, i, sec_addr);
+ printf ("\n");
+ }
+}
+
+void
+dump_sframe (sframe_decoder_ctx *sfd_ctx, uint64_t sec_addr)
+{
+ dump_sframe_header (sfd_ctx);
+ dump_sframe_functions (sfd_ctx, sec_addr);
+}
+
+/* We want to treat the first item of the SFrame error macro
+ like subsequent items. */
+#define _SFRAME_FIRST(NAME, VALUE) _SFRAME_ITEM(NAME, VALUE)
+
+/* The error message strings, each in a unique structure member precisely big
+ enough for that error, plus a str member to access them all as a string
+ table. */
+
+static const char *const _sframe_errlist[] = {
+#define _SFRAME_ITEM(n, s) s,
+_SFRAME_ERRORS
+#undef _SFRAME_ITEM
+};
+
+const char *
+sframe_errmsg (int error)
+{
+ const char *str;
+
+ if (error >= SFRAME_ERR_BASE && (error - SFRAME_ERR_BASE) < SFRAME_ERR_NERR)
+ str = _sframe_errlist[error - SFRAME_ERR_BASE];
+ else
+ str = (const char *) strerror (error);
+
+ return (str ? str : "Unknown error");
+}
+
+/* Swap the endianness of something. */
+
+#define swap_thing(x) \
+ do \
+ { \
+ _Static_assert (sizeof (x) == 1 || (sizeof (x) % 2 == 0 \
+ && sizeof (x) <= 8), \
+ "Invalid size, update endianness code"); \
+ switch (sizeof (x)) { \
+ case 2: x = bswap_16 (x); break; \
+ case 4: x = bswap_32 (x); break; \
+ case 8: x = bswap_64 (x); break; \
+ case 1: /* Nothing needs doing */
\
+ break; \
+ }
\
+ } \
+ while (0);
+
+typedef struct sf_funidx_tbl
+{
+ unsigned int count;
+ unsigned int alloced;
+ sframe_func_desc_entry entry[1];
+} sf_funidx_tbl;
+
+typedef struct sf_fre_tbl
+{
+ unsigned int count;
+ unsigned int alloced;
+ sframe_frame_row_entry entry[1];
+} sf_fre_tbl;
+
+#define _sf_printflike_(string_index,first_to_check) \
+ __attribute__ ((__format__ (__printf__, (string_index), (first_to_check))))
+
+static void debug_printf (const char *, ...);
+
+static int _sframe_debug; /* Control for printing out debug info. */
+static int number_of_entries = 64;
+
+static void
+sframe_init_debug (void)
+{
+ static int inited;
+
+ if (!inited)
+ {
+ _sframe_debug = getenv ("SFRAME_DEBUG") != NULL;
+ inited = 1;
+ }
+}
+
+_sf_printflike_ (1, 2)
+static void debug_printf (const char *format, ...)
+{
+ if (_sframe_debug)
+ {
+ va_list args;
+
+ va_start (args, format);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ }
+}
+
+/* Generate bitmask of given size in bytes. This is used for
+ some checks on the FRE start address.
+ SFRAME_FRE_TYPE_ADDR1 => 1 byte => [ bitmask = 0xff ]
+ SFRAME_FRE_TYPE_ADDR2 => 2 byte => [ bitmask = 0xffff ]
+ SFRAME_FRE_TYPE_ADDR4 => 4 byte => [ bitmask = 0xffffffff ]. */
+#define SFRAME_BITMASK_OF_SIZE(size_in_bytes) \
+ (((uint64_t)1 << (size_in_bytes*8)) - 1)
+
+/* Store the specified error code into errp if it is non-NULL.
+ Return SFRAME_ERR. */
+
+static int
+sframe_set_errno (int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+ return SFRAME_ERR;
+}
+
+/* Store the specified error code into errp if it is non-NULL.
+ Return NULL. */
+
+static void *
+sframe_ret_set_errno (int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+ return NULL;
+}
+
+/* Get the SFrame header size. */
+
+static uint32_t
+sframe_get_hdr_size (sframe_header *sfh)
+{
+ return SFRAME_V1_HDR_SIZE (*sfh);
+}
+
+/* Access functions for frame row entry data. */
+
+static unsigned int
+sframe_fre_get_offset_count (unsigned char fre_info)
+{
+ return SFRAME_V1_FRE_OFFSET_COUNT (fre_info);
+}
+
+static unsigned int
+sframe_fre_get_offset_size (unsigned char fre_info)
+{
+ return SFRAME_V1_FRE_OFFSET_SIZE (fre_info);
+}
+
+/* Access functions for info from function descriptor entry. */
+
+static unsigned int
+sframe_get_fre_type (sframe_func_desc_entry *fdep)
+{
+ unsigned int fre_type = 0;
+ if (fdep)
+ fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info);
+ return fre_type;
+}
+
+static unsigned int
+sframe_get_fde_type (sframe_func_desc_entry *fdep)
+{
+ unsigned int fde_type = 0;
+ if (fdep)
+ fde_type = SFRAME_V1_FUNC_FDE_TYPE (fdep->sfde_func_info);
+ return fde_type;
+}
+
+/* Check if flipping is needed, based on ENDIAN. */
+
+static int
+need_swapping (int endian)
+{
+ unsigned int ui = 1;
+ char *c = (char *)&ui;
+ int is_little = (int)*c;
+
+ switch (endian)
+ {
+ case SFRAME_ABI_AARCH64_ENDIAN_LITTLE:
+ case SFRAME_ABI_AMD64_ENDIAN_LITTLE:
+ return !is_little;
+ case SFRAME_ABI_AARCH64_ENDIAN_BIG:
+ return is_little;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Flip the endianness of the SFrame header. */
+
+static void
+flip_header (sframe_header *sfheader)
+{
+ swap_thing (sfheader->sfh_preamble.sfp_magic);
+ swap_thing (sfheader->sfh_preamble.sfp_version);
+ swap_thing (sfheader->sfh_preamble.sfp_flags);
+ swap_thing (sfheader->sfh_cfa_fixed_fp_offset);
+ swap_thing (sfheader->sfh_cfa_fixed_ra_offset);
+ swap_thing (sfheader->sfh_num_fdes);
+ swap_thing (sfheader->sfh_num_fres);
+ swap_thing (sfheader->sfh_fre_len);
+ swap_thing (sfheader->sfh_fdeoff);
+ swap_thing (sfheader->sfh_freoff);
+}
+
+static void
+flip_fde (sframe_func_desc_entry *fdep)
+{
+ swap_thing (fdep->sfde_func_start_address);
+ swap_thing (fdep->sfde_func_size);
+ swap_thing (fdep->sfde_func_start_fre_off);
+ swap_thing (fdep->sfde_func_num_fres);
+}
+
+/* Check if SFrame header has valid data. */
+
+static int
+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)
+ || ((hp->sfh_preamble.sfp_flags | all_flags)
+ != all_flags))
+ return 0;
+
+ /* Check offsets are valid. */
+ if (hp->sfh_fdeoff > hp->sfh_freoff)
+ return 0;
+
+ return 1;
+}
+
+/* Flip the start address pointed to by FP. */
+
+static void
+flip_fre_start_address (char *fp, unsigned int fre_type)
+{
+ void *start = (void*)fp;
+ if (fre_type == SFRAME_FRE_TYPE_ADDR2)
+ {
+ unsigned short *start_addr = (unsigned short *)(start);
+ swap_thing (*start_addr);
+ }
+ else if (fre_type == SFRAME_FRE_TYPE_ADDR4)
+ {
+ uint32_t *start_addr = (uint32_t *)(start);
+ swap_thing (*start_addr);
+ }
+}
+
+static void
+flip_fre_stack_offsets (char *fp, unsigned char offset_size,
+ unsigned char offset_cnt)
+{
+ int j;
+ void *offsets = (void *)fp;
+
+ if (offset_size == SFRAME_FRE_OFFSET_2B)
+ {
+ unsigned short *ust = (unsigned short *)offsets;
+ for (j = offset_cnt; j > 0; ust++, j--)
+ swap_thing (*ust);
+ }
+ else if (offset_size == SFRAME_FRE_OFFSET_4B)
+ {
+ uint32_t *uit = (uint32_t *)offsets;
+ for (j = offset_cnt; j > 0; uit++, j--)
+ swap_thing (*uit);
+ }
+}
+
+/* Get the FRE start address size, given the FRE_TYPE. */
+
+static size_t
+sframe_fre_start_addr_size (unsigned int fre_type)
+{
+ size_t addr_size = 0;
+ switch (fre_type)
+ {
+ case SFRAME_FRE_TYPE_ADDR1:
+ addr_size = 1;
+ break;
+ case SFRAME_FRE_TYPE_ADDR2:
+ addr_size = 2;
+ break;
+ case SFRAME_FRE_TYPE_ADDR4:
+ addr_size = 4;
+ break;
+ default:
+ /* No other value is expected. */
+ sframe_assert (0);
+ break;
+ }
+ return addr_size;
+}
+
+/* Check if the FREP has valid data. */
+
+static int
+sframe_fre_sanity_check_p (sframe_frame_row_entry *frep)
+{
+ unsigned int offset_size, offset_cnt;
+ unsigned int fre_info;
+
+ if (frep == NULL)
+ return 0;
+
+ fre_info = frep->fre_info;
+ offset_size = sframe_fre_get_offset_size (fre_info);
+
+ if (offset_size != SFRAME_FRE_OFFSET_1B
+ && offset_size != SFRAME_FRE_OFFSET_2B
+ && offset_size != SFRAME_FRE_OFFSET_4B)
+ return 0;
+
+ offset_cnt = sframe_fre_get_offset_count (fre_info);
+ if (offset_cnt > 3)
+ return 0;
+
+ return 1;
+}
+
+/* Get FRE_INFO's offset size in bytes. */
+
+static size_t
+sframe_fre_offset_bytes_size (unsigned char fre_info)
+{
+ unsigned int offset_size, offset_cnt;
+
+ offset_size = sframe_fre_get_offset_size (fre_info);
+
+ debug_printf ("offset_size = %u\n", offset_size);
+
+ offset_cnt = sframe_fre_get_offset_count (fre_info);
+
+ if (offset_size == SFRAME_FRE_OFFSET_2B
+ || offset_size == SFRAME_FRE_OFFSET_4B) /* 2 or 4 bytes. */
+ return (offset_cnt * (offset_size * 2));
+
+ return (offset_cnt);
+}
+
+/* Get total size in bytes to represent FREP in the binary format. This
+ includes the starting address, FRE info, and all the offsets. */
+
+static size_t
+sframe_fre_entry_size (sframe_frame_row_entry *frep, unsigned int fre_type)
+{
+ if (frep == NULL)
+ return 0;
+
+ unsigned char fre_info = frep->fre_info;
+ size_t addr_size = sframe_fre_start_addr_size (fre_type);
+
+ return (addr_size + sizeof (frep->fre_info)
+ + sframe_fre_offset_bytes_size (fre_info));
+}
+
+static int
+flip_fre (char *fp, unsigned int fre_type, size_t *fre_size)
+{
+ unsigned char fre_info;
+ unsigned int offset_size, offset_cnt;
+ size_t addr_size, fre_info_size = 0;
+ int err = 0;
+
+ if (fre_size == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ flip_fre_start_address (fp, fre_type);
+
+ /* Advance the buffer pointer to where the FRE info is. */
+ addr_size = sframe_fre_start_addr_size (fre_type);
+ fp += addr_size;
+
+ /* FRE info is unsigned char. No need to flip. */
+ fre_info = *(unsigned char*)fp;
+ offset_size = sframe_fre_get_offset_size (fre_info);
+ offset_cnt = sframe_fre_get_offset_count (fre_info);
+
+ /* Advance the buffer pointer to where the stack offsets are. */
+ fre_info_size = sizeof (unsigned char);
+ fp += fre_info_size;
+ flip_fre_stack_offsets (fp, offset_size, offset_cnt);
+
+ *fre_size
+ = addr_size + fre_info_size + sframe_fre_offset_bytes_size (fre_info);
+
+ return 0;
+}
+
+/* Endian flip the contents of FRAME_BUF of size BUF_SIZE.
+ The SFrame header in the FRAME_BUF must be endian flipped prior to
+ calling flip_sframe.
+
+ Endian flipping at decode time vs encode time have different needs. At
+ encode time, the frame_buf is in host endianness, and hence, values should
+ be read up before the buffer is changed to foreign endianness. This change
+ of behaviour is specified via TO_FOREIGN arg.
+
+ If an error code is returned, the buffer should not be used. */
+
+static int
+flip_sframe (char *frame_buf, size_t buf_size, uint32_t to_foreign)
+{
+ unsigned int i, j, prev_frep_index;
+ sframe_header *ihp;
+ char *fdes;
+ char *fp = NULL;
+ sframe_func_desc_entry *fdep;
+ unsigned int num_fdes, num_fres;
+ unsigned int fre_type;
+ uint32_t fre_offset;
+ size_t esz;
+ int err = 0;
+
+ /* Header must be in host endianness at this time. */
+ ihp = (sframe_header *)frame_buf;
+
+ if (!sframe_header_sanity_check_p (ihp))
+ return sframe_set_errno (&err, SFRAME_ERR_BUF_INVAL);
+
+ /* The contents of the SFrame header are safe to read. Get the number of
+ FDEs and the first FDE in the buffer. */
+ num_fdes = ihp->sfh_num_fdes;
+ fdes = frame_buf + sframe_get_hdr_size (ihp) + ihp->sfh_fdeoff;
+ fdep = (sframe_func_desc_entry *)fdes;
+
+ j = 0;
+ prev_frep_index = 0;
+ for (i = 0; i < num_fdes; fdep++, i++)
+ {
+ if (to_foreign)
+ {
+ num_fres = fdep->sfde_func_num_fres;
+ fre_type = sframe_get_fre_type (fdep);
+ fre_offset = fdep->sfde_func_start_fre_off;
+ }
+
+ flip_fde (fdep);
+
+ if (!to_foreign)
+ {
+ num_fres = fdep->sfde_func_num_fres;
+ fre_type = sframe_get_fre_type (fdep);
+ fre_offset = fdep->sfde_func_start_fre_off;
+ }
+
+ fp = frame_buf + sframe_get_hdr_size (ihp) + ihp->sfh_freoff;
+ fp += fre_offset;
+ for (; j < prev_frep_index + num_fres; j++)
+ {
+ if (flip_fre (fp, fre_type, &esz))
+ goto bad;
+
+ if (esz == 0)
+ goto bad;
+ fp += esz;
+ }
+ prev_frep_index = j;
+ }
+ /* All FREs must have been endian flipped by now. */
+ if (j != ihp->sfh_num_fres)
+ goto bad;
+ /* Contents, if any, must have been processed by now.
+ Recall that .sframe section with just a SFrame header may be generated by
+ GAS if no SFrame FDEs were found for the input file. */
+ if (ihp->sfh_num_fres && ((frame_buf + buf_size) != (void*)fp))
+ goto bad;
+
+ /* Success. */
+ return 0;
+bad:
+ return SFRAME_ERR;
+}
+
+/* The SFrame Decoder. */
+
+/* Compare function for qsort'ing the FDE table. */
+
+static int
+fde_func (const void *p1, const void *p2)
+{
+ const sframe_func_desc_entry *aa = p1;
+ const sframe_func_desc_entry *bb = p2;
+
+ if (aa->sfde_func_start_address < bb->sfde_func_start_address)
+ return -1;
+ else if (aa->sfde_func_start_address > bb->sfde_func_start_address)
+ return 1;
+ return 0;
+}
+
+/* Get IDX'th offset from FRE. Set errp as applicable. */
+
+static int32_t
+sframe_get_fre_offset (sframe_frame_row_entry *fre, int idx, int *errp)
+{
+ int offset_cnt, offset_size;
+
+ if (fre == NULL || !sframe_fre_sanity_check_p (fre))
+ return sframe_set_errno (errp, SFRAME_ERR_FRE_INVAL);
+
+ offset_cnt = sframe_fre_get_offset_count (fre->fre_info);
+ offset_size = sframe_fre_get_offset_size (fre->fre_info);
+
+ if (offset_cnt < idx + 1)
+ return sframe_set_errno (errp, SFRAME_ERR_FREOFFSET_NOPRESENT);
+
+ if (errp)
+ *errp = 0; /* Offset Valid. */
+
+ if (offset_size == SFRAME_FRE_OFFSET_1B)
+ {
+ int8_t *sp = (int8_t *)fre->fre_offsets;
+ return sp[idx];
+ }
+ else if (offset_size == SFRAME_FRE_OFFSET_2B)
+ {
+ int16_t *sp = (int16_t *)fre->fre_offsets;
+ return sp[idx];
+ }
+ else
+ {
+ int32_t *ip = (int32_t *)fre->fre_offsets;
+ return ip[idx];
+ }
+}
+
+/* Free the decoder context. */
+
+void
+sframe_decoder_free (sframe_decoder_ctx **decoder)
+{
+ if (decoder != NULL)
+ {
+ sframe_decoder_ctx *dctx = *decoder;
+ if (dctx == NULL)
+ return;
+
+ if (dctx->sfd_funcdesc != NULL)
+ {
+ free (dctx->sfd_funcdesc);
+ dctx->sfd_funcdesc = NULL;
+ }
+ if (dctx->sfd_fres != NULL)
+ {
+ free (dctx->sfd_fres);
+ dctx->sfd_fres = NULL;
+ }
+
+ free (*decoder);
+ *decoder = NULL;
+ }
+}
+
+/* Create a FDE function info byte given an FRE_TYPE and an FDE_TYPE. */
+/* FIXME API for linker. Revisit if its better placed somewhere else? */
+
+unsigned char
+sframe_fde_func_info (unsigned int fre_type,
+ unsigned int fde_type)
+{
+ unsigned char func_info;
+ sframe_assert (fre_type == SFRAME_FRE_TYPE_ADDR1
+ || fre_type == SFRAME_FRE_TYPE_ADDR2
+ || fre_type == SFRAME_FRE_TYPE_ADDR4);
+ sframe_assert (fde_type == SFRAME_FDE_TYPE_PCINC
+ || fde_type == SFRAME_FDE_TYPE_PCMASK);
+ func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
+ return func_info;
+}
+
+/* Get the FRE type given the function size. */
+/* FIXME API for linker. Revisit if its better placed somewhere else? */
+
+unsigned int
+sframe_calc_fre_type (unsigned int func_size)
+{
+ unsigned int fre_type = 0;
+ if (func_size <= 0xff)
+ fre_type = SFRAME_FRE_TYPE_ADDR1;
+ else if (func_size <= 0xffff)
+ fre_type = SFRAME_FRE_TYPE_ADDR2;
+ else if (func_size <= 0xffffffff)
+ fre_type = SFRAME_FRE_TYPE_ADDR4;
+ return fre_type;
+}
+
+/* Get the base reg id from the FRE info. Set errp if failure. */
+
+unsigned int
+sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp)
+{
+ if (fre == NULL)
+ return sframe_set_errno (errp, SFRAME_ERR_FRE_INVAL);
+
+ unsigned int fre_info = fre->fre_info;
+ return SFRAME_V1_FRE_CFA_BASE_REG_ID (fre_info);
+}
+
+/* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
+
+int32_t
+sframe_fre_get_cfa_offset (sframe_frame_row_entry *fre, int *errp)
+{
+ return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp);
+}
+
+/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */
+
+int32_t
+sframe_fre_get_fp_offset (sframe_frame_row_entry *fre, int *errp)
+{
+ return sframe_get_fre_offset (fre, SFRAME_FRE_FP_OFFSET_IDX, errp);
+}
+
+/* Get the RA offset from the FRE. If the offset is invalid, sets errp. */
+
+int32_t
+sframe_fre_get_ra_offset (sframe_frame_row_entry *fre, int *errp)
+{
+ return sframe_get_fre_offset (fre, SFRAME_FRE_RA_OFFSET_IDX, errp);
+}
+
+static int
+sframe_frame_row_entry_copy (sframe_frame_row_entry *dst,
sframe_frame_row_entry *src)
+{
+ int err = 0;
+
+ if (dst == NULL || src == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ memcpy (dst, src, sizeof (sframe_frame_row_entry));
+ return 0;
+}
+
+static int
+sframe_decode_fre_start_address (const char *fre_buf,
+ uint32_t *fre_start_addr,
+ unsigned int fre_type)
+{
+ uint32_t saddr = 0;
+ int err = 0;
+
+ if (fre_type == SFRAME_FRE_TYPE_ADDR1)
+ {
+ uint8_t *uc = (uint8_t *)fre_buf;
+ saddr = (uint32_t)*uc;
+ }
+ else if (fre_type == SFRAME_FRE_TYPE_ADDR2)
+ {
+ uint16_t *ust = (uint16_t *)fre_buf;
+ saddr = (uint32_t)*ust;
+ }
+ else if (fre_type == SFRAME_FRE_TYPE_ADDR4)
+ {
+ uint32_t *uit = (uint32_t *)fre_buf;
+ saddr = (uint32_t)*uit;
+ }
+ else
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ *fre_start_addr = saddr;
+ return 0;
+}
+
+/* Decode a frame row entry FRE which starts at location FRE_BUF. The function
+ updates ESZ to the size of the FRE as stored in the binary format.
+
+ This function works closely with the SFrame binary format.
+
+ Returns SFRAME_ERR if failure. */
+
+static int
+sframe_decode_fre (const char *fre_buf, sframe_frame_row_entry *fre,
+ unsigned int fre_type,
+ size_t *esz)
+{
+ int err = 0;
+ void *stack_offsets = NULL;
+ size_t stack_offsets_sz;
+ size_t addr_size;
+ size_t fre_size;
+
+ if (fre_buf == NULL || fre == NULL || esz == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ /* Copy over the FRE start address. */
+ sframe_decode_fre_start_address (fre_buf, &fre->fre_start_addr, fre_type);
+
+ addr_size = sframe_fre_start_addr_size (fre_type);
+ fre->fre_info = *(unsigned char *)(fre_buf + addr_size);
+ /* Sanity check as the API works closely with the binary format. */
+ sframe_assert (sizeof (fre->fre_info) == sizeof (unsigned char));
+
+ /* Cleanup the space for fre_offsets first, then copy over the valid
+ bytes. */
+ memset (fre->fre_offsets, 0, MAX_OFFSET_BYTES);
+ /* Get offsets size. */
+ stack_offsets_sz = sframe_fre_offset_bytes_size (fre->fre_info);
+ stack_offsets = (unsigned char *)fre_buf + addr_size + sizeof
(fre->fre_info);
+ memcpy (fre->fre_offsets, stack_offsets, stack_offsets_sz);
+
+ /* The FRE has been decoded. Use it to perform one last sanity check. */
+ fre_size = sframe_fre_entry_size (fre, fre_type);
+ sframe_assert (fre_size == (addr_size + sizeof (fre->fre_info)
+ + stack_offsets_sz));
+ *esz = fre_size;
+
+ return 0;
+}
+
+/* Decode the specified SFrame buffer CF_BUF of size CF_SIZE and return the
+ new SFrame decoder context.
+
+ Sets ERRP for the caller if any error. Frees up the allocated memory in
+ case of error. */
+
+sframe_decoder_ctx *
+sframe_decode (const char *sf_buf, size_t sf_size, int *errp)
+{
+ const sframe_preamble *sfp;
+ size_t hdrsz;
+ sframe_header *sfheaderp;
+ sframe_decoder_ctx *dctx;
+ char *frame_buf;
+ char *tempbuf = NULL;
+
+ int fidx_size;
+ uint32_t fre_bytes;
+ int foreign_endian = 0;
+
+ sframe_init_debug ();
+
+ if ((sf_buf == NULL) || (!sf_size))
+ return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL);
+ else if (sf_size < sizeof (sframe_header))
+ return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+
+ sfp = (const sframe_preamble *) sf_buf;
+
+ debug_printf ("sframe_decode: magic=0x%x version=%u flags=%u\n",
+ sfp->sfp_magic, sfp->sfp_version, sfp->sfp_flags);
+
+ /* Check for foreign endianness. */
+ if (sfp->sfp_magic != SFRAME_MAGIC)
+ {
+ if (sfp->sfp_magic == bswap_16 (SFRAME_MAGIC))
+ foreign_endian = 1;
+ else
+ return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+ }
+
+ /* Initialize a new decoder context. */
+ if ((dctx = malloc (sizeof (sframe_decoder_ctx))) == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ memset (dctx, 0, sizeof (sframe_decoder_ctx));
+
+ if (foreign_endian)
+ {
+ /* Allocate a new buffer and initialize it. */
+ tempbuf = (char *) malloc (sf_size * sizeof (char));
+ if (tempbuf == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ memcpy (tempbuf, sf_buf, sf_size);
+
+ /* Flip the header. */
+ sframe_header *ihp = (sframe_header *) tempbuf;
+ flip_header (ihp);
+ /* Flip the rest of the SFrame section data buffer. */
+ if (flip_sframe (tempbuf, sf_size, 0))
+ {
+ free (tempbuf);
+ return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+ }
+ frame_buf = tempbuf;
+ }
+ else
+ frame_buf = (char *)sf_buf;
+
+ /* Handle the SFrame header. */
+ dctx->sfd_header = *(sframe_header *) frame_buf;
+ /* Validate the contents of SFrame header. */
+ sfheaderp = &dctx->sfd_header;
+ if (!sframe_header_sanity_check_p (sfheaderp))
+ {
+ sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ goto decode_fail_free;
+ }
+ hdrsz = sframe_get_hdr_size (sfheaderp);
+ frame_buf += hdrsz;
+
+ /* Handle the SFrame Function Descriptor Entry section. */
+ fidx_size
+ = sfheaderp->sfh_num_fdes * sizeof (sframe_func_desc_entry);
+ dctx->sfd_funcdesc = malloc (fidx_size);
+ if (dctx->sfd_funcdesc == NULL)
+ {
+ sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ goto decode_fail_free;
+ }
+ memcpy (dctx->sfd_funcdesc, frame_buf, fidx_size);
+
+ debug_printf ("%u total fidx size\n", fidx_size);
+
+ frame_buf += (fidx_size);
+
+ /* Handle the SFrame Frame Row Entry section. */
+ dctx->sfd_fres = malloc (sfheaderp->sfh_fre_len);
+ if (dctx->sfd_fres == NULL)
+ {
+ sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ goto decode_fail_free;
+ }
+ memcpy (dctx->sfd_fres, frame_buf, sfheaderp->sfh_fre_len);
+
+ fre_bytes = sfheaderp->sfh_fre_len;
+ dctx->sfd_fre_nbytes = fre_bytes;
+
+ debug_printf ("%u total fre bytes\n", fre_bytes);
+
+ return dctx;
+
+decode_fail_free:
+ if (foreign_endian && tempbuf != NULL)
+ free (tempbuf);
+ sframe_decoder_free (&dctx);
+ dctx = NULL;
+ return dctx;
+}
+
+/* Get DECODER's SFrame header. */
+
+static sframe_header *
+sframe_decoder_get_header (sframe_decoder_ctx *decoder)
+{
+ sframe_header *hp = NULL;
+ if (decoder != NULL)
+ hp = &decoder->sfd_header;
+ return hp;
+}
+
+/* Get the size of the SFrame header from the decoder context CTX. */
+
+unsigned int
+sframe_decoder_get_hdr_size (sframe_decoder_ctx *ctx)
+{
+ sframe_header *dhp;
+ dhp = sframe_decoder_get_header (ctx);
+ return sframe_get_hdr_size (dhp);
+}
+
+/* Get the SFrame's abi/arch info given the decoder context CTX. */
+
+unsigned char
+sframe_decoder_get_abi_arch (sframe_decoder_ctx *ctx)
+{
+ sframe_header *sframe_header;
+ sframe_header = sframe_decoder_get_header (ctx);
+ return sframe_header->sfh_abi_arch;
+}
+
+/* Get the SFrame's fixed FP offset given the decoder context CTX. */
+int8_t
+sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx)
+{
+ sframe_header *dhp;
+ dhp = sframe_decoder_get_header (ctx);
+ return dhp->sfh_cfa_fixed_fp_offset;
+}
+
+/* Get the SFrame's fixed RA offset given the decoder context CTX. */
+int8_t
+sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *ctx)
+{
+ sframe_header *dhp;
+ dhp = sframe_decoder_get_header (ctx);
+ return dhp->sfh_cfa_fixed_ra_offset;
+}
+
+/* Find the function descriptor entry starting which contains the specified
+ address ADDR. */
+
+sframe_func_desc_entry *
+sframe_get_funcdesc_with_addr (sframe_decoder_ctx *ctx,
+ int32_t addr, int *errp)
+{
+ sframe_header *dhp;
+ sframe_func_desc_entry *fdp;
+ int low, high, cnt;
+
+ if (ctx == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL);
+
+ dhp = sframe_decoder_get_header (ctx);
+
+ if (dhp == NULL || dhp->sfh_num_fdes == 0 || ctx->sfd_funcdesc == NULL)
+ 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)
+ 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;
+ 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)
+ {
+ 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;
+ }
+ else
+ high = mid - 1;
+ }
+
+ return sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTFOUND);
+}
+
+/* Find the SFrame Row Entry which contains the PC. Returns
+ SFRAME_ERR if failure. */
+
+int
+sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
+ sframe_frame_row_entry *frep)
+{
+ sframe_func_desc_entry *fdep;
+ uint32_t start_address, i;
+ sframe_frame_row_entry cur_fre, next_fre;
+ unsigned char *sp;
+ unsigned int fre_type, fde_type;
+ size_t esz;
+ int err = 0;
+ size_t size = 0;
+ /* For regular FDEs (i.e. fde_type SFRAME_FDE_TYPE_PCINC),
+ where the start address in the FRE is an offset from start pc,
+ use a bitmask with all bits set so that none of the address bits are
+ ignored. In this case, we need to return the FRE where
+ (PC >= FRE_START_ADDR) */
+ uint64_t bitmask = 0xffffffff;
+
+ 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 (ctx, pc, &err);
+ 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);
+
+ /* For FDEs for repetitive pattern of insns, we need to return the FRE
+ such that (PC & FRE_START_ADDR_AS_MASK >= FRE_START_ADDR_AS_MASK).
+ so, update the bitmask to the start address. */
+ /* FIXME - the bitmask should be picked per ABI or encoded in the format
+ somehow. For AMD64, the pltN entry stub is 16 bytes. */
+ if (fde_type == SFRAME_FDE_TYPE_PCMASK)
+ bitmask = 0xff;
+
+ sp = (unsigned char *) ctx->sfd_fres + fdep->sfde_func_start_fre_off;
+ for (i = 0; i < fdep->sfde_func_num_fres; i++)
+ {
+ err = sframe_decode_fre ((const char *)sp, &next_fre,
+ fre_type, &esz);
+ start_address = next_fre.fre_start_addr;
+
+ if (((fdep->sfde_func_start_address
+ + (int32_t) start_address) & bitmask) <= (pc & bitmask))
+ {
+ sframe_frame_row_entry_copy (&cur_fre, &next_fre);
+
+ /* Get the next FRE in sequence. */
+ if (i < fdep->sfde_func_num_fres - 1)
+ {
+ sp += esz;
+ err = sframe_decode_fre ((const char*)sp, &next_fre,
+ fre_type, &esz);
+
+ /* Sanity check the next FRE. */
+ if (!sframe_fre_sanity_check_p (&next_fre))
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+ size = next_fre.fre_start_addr;
+ }
+ else size = fdep->sfde_func_size;
+
+ /* If the cur FRE is the one that contains the PC, return it. */
+ if (((fdep->sfde_func_start_address
+ + (int32_t)size) & bitmask) > (pc & bitmask))
+ {
+ sframe_frame_row_entry_copy (frep, &cur_fre);
+ return 0;
+ }
+ }
+ else
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+ }
+ return sframe_set_errno (&err, SFRAME_ERR_FDE_INVAL);
+}
+
+/* Return the number of function descriptor entries in the SFrame decoder
+ DCTX. */
+
+unsigned int
+sframe_decoder_get_num_fidx (sframe_decoder_ctx *ctx)
+{
+ unsigned int num_fdes = 0;
+ sframe_header *dhp = NULL;
+ dhp = sframe_decoder_get_header (ctx);
+ if (dhp)
+ num_fdes = dhp->sfh_num_fdes;
+ return num_fdes;
+}
+
+/* Get the data (NUM_FRES, FUNC_START_ADDRESS) from the function
+ descriptor entry at index I'th in the decoder CTX. If failed,
+ return error code. */
+/* FIXME - consolidate the args and return a
+ sframe_func_desc_index_elem rather? */
+
+int
+sframe_decoder_get_funcdesc (sframe_decoder_ctx *ctx,
+ unsigned int i,
+ uint32_t *num_fres,
+ uint32_t *func_size,
+ int32_t *func_start_address,
+ unsigned char *func_info)
+{
+ sframe_func_desc_entry *fdp;
+ unsigned int num_fdes;
+ int err = 0;
+
+ if (ctx == NULL || func_start_address == NULL || num_fres == NULL
+ || func_size == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ num_fdes = sframe_decoder_get_num_fidx (ctx);
+ if (num_fdes == 0
+ || i >= num_fdes
+ || ctx->sfd_funcdesc == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_DCTX_INVAL);
+
+ fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc + i;
+ *num_fres = fdp->sfde_func_num_fres;
+ *func_start_address = fdp->sfde_func_start_address;
+ *func_size = fdp->sfde_func_size;
+ *func_info = fdp->sfde_func_info;
+
+ return 0;
+}
+
+/* Get the function descriptor entry at index FUNC_IDX in the decoder
+ context CTX. */
+
+static sframe_func_desc_entry *
+sframe_decoder_get_funcdesc_at_index (sframe_decoder_ctx *ctx,
+ uint32_t func_idx)
+{
+ /* Invalid argument. No FDE will be found. */
+ if (func_idx >= sframe_decoder_get_num_fidx (ctx))
+ return NULL;
+
+ sframe_func_desc_entry *fdep;
+ fdep = (sframe_func_desc_entry *) ctx->sfd_funcdesc;
+ return fdep + func_idx;
+}
+
+/* Get the FRE_IDX'th FRE of the function at FUNC_IDX'th function
+ descriptor entry in the SFrame decoder CTX. Returns error code as
+ applicable. */
+
+int
+sframe_decoder_get_fre (sframe_decoder_ctx *ctx,
+ unsigned int func_idx,
+ unsigned int fre_idx,
+ sframe_frame_row_entry *fre)
+{
+ sframe_func_desc_entry *fdep;
+ sframe_frame_row_entry ifre;
+ unsigned char *sp;
+ uint32_t i;
+ unsigned int fre_type;
+ size_t esz = 0;
+ int err = 0;
+
+ if (ctx == NULL || fre == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ /* Get function descriptor entry at index func_idx. */
+ fdep = sframe_decoder_get_funcdesc_at_index (ctx, func_idx);
+
+ if (fdep == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
+
+ fre_type = sframe_get_fre_type (fdep);
+ /* Now scan the FRE entries. */
+ sp = (unsigned char *) ctx->sfd_fres + fdep->sfde_func_start_fre_off;
+ for (i = 0; i < fdep->sfde_func_num_fres; i++)
+ {
+ /* Decode the FRE at the current position. Return it if valid. */
+ err = sframe_decode_fre ((const char *)sp, &ifre, fre_type, &esz);
+ if (i == fre_idx)
+ {
+ if (!sframe_fre_sanity_check_p (&ifre))
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+ sframe_frame_row_entry_copy (fre, &ifre);
+
+ if (fdep->sfde_func_size)
+ sframe_assert (fre->fre_start_addr < fdep->sfde_func_size);
+ else
+ /* A SFrame FDE with func size equal to zero is possible. */
+ sframe_assert (fre->fre_start_addr == fdep->sfde_func_size);
+
+ return 0;
+ }
+ /* Next FRE. */
+ sp += esz;
+ }
+
+ return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
+}
+
+
+/* SFrame Encoder. */
+
+/* Get a reference to the ENCODER's SFrame header. */
+
+static sframe_header *
+sframe_encoder_get_header (sframe_encoder_ctx *encoder)
+{
+ sframe_header *hp = NULL;
+ if (encoder)
+ hp = &encoder->sfe_header;
+ return hp;
+}
+
+static sframe_func_desc_entry *
+sframe_encoder_get_funcdesc_at_index (sframe_encoder_ctx *encoder,
+ uint32_t func_idx)
+{
+ sframe_func_desc_entry *fde = NULL;
+ if (func_idx < sframe_encoder_get_num_fidx (encoder))
+ {
+ sf_funidx_tbl *func_tbl = (sf_funidx_tbl *) encoder->sfe_funcdesc;
+ fde = func_tbl->entry + func_idx;
+ }
+ return fde;
+}
+
+/* Create an encoder context with the given SFrame format version VER, FLAGS
+ and ABI information. Sets errp if failure. */
+
+sframe_encoder_ctx *
+sframe_encode (unsigned char ver, unsigned char flags, int abi_arch,
+ int8_t fixed_fp_offset, int8_t fixed_ra_offset, int *errp)
+{
+ sframe_header *hp;
+ sframe_encoder_ctx *fp;
+
+ if (ver != SFRAME_VERSION)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_VERSION_INVAL);
+
+ if ((fp = malloc (sizeof (sframe_encoder_ctx))) == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+
+ memset (fp, 0, sizeof (sframe_encoder_ctx));
+
+ /* Get the SFrame header and update it. */
+ hp = sframe_encoder_get_header (fp);
+ hp->sfh_preamble.sfp_version = ver;
+ hp->sfh_preamble.sfp_magic = SFRAME_MAGIC;
+ hp->sfh_preamble.sfp_flags = flags;
+
+ hp->sfh_abi_arch = abi_arch;
+ hp->sfh_cfa_fixed_fp_offset = fixed_fp_offset;
+ hp->sfh_cfa_fixed_ra_offset = fixed_ra_offset;
+
+ return fp;
+}
+
+/* Free the encoder context. */
+
+void
+sframe_encoder_free (sframe_encoder_ctx **encoder)
+{
+ if (encoder != NULL)
+ {
+ sframe_encoder_ctx *ectx = *encoder;
+ if (ectx == NULL)
+ return;
+
+ if (ectx->sfe_funcdesc != NULL)
+ {
+ free (ectx->sfe_funcdesc);
+ ectx->sfe_funcdesc = NULL;
+ }
+ if (ectx->sfe_fres != NULL)
+ {
+ free (ectx->sfe_fres);
+ ectx->sfe_fres = NULL;
+ }
+ if (ectx->sfe_data != NULL)
+ {
+ free (ectx->sfe_data);
+ ectx->sfe_data = NULL;
+ }
+
+ free (*encoder);
+ *encoder = NULL;
+ }
+}
+
+/* Get the size of the SFrame header from the encoder ctx ENCODER. */
+
+unsigned int
+sframe_encoder_get_hdr_size (sframe_encoder_ctx *encoder)
+{
+ sframe_header *ehp;
+ ehp = sframe_encoder_get_header (encoder);
+ return sframe_get_hdr_size (ehp);
+}
+
+/* Get the abi/arch info from the SFrame encoder context ENCODER. */
+
+unsigned char
+sframe_encoder_get_abi_arch (sframe_encoder_ctx *encoder)
+{
+ unsigned char abi_arch = 0;
+ sframe_header *ehp;
+ ehp = sframe_encoder_get_header (encoder);
+ if (ehp)
+ abi_arch = ehp->sfh_abi_arch;
+ return abi_arch;
+}
+
+/* Return the number of function descriptor entries in the SFrame encoder
+ ENCODER. */
+
+unsigned int
+sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder)
+{
+ unsigned int num_fdes = 0;
+ sframe_header *ehp = NULL;
+ ehp = sframe_encoder_get_header (encoder);
+ if (ehp)
+ num_fdes = ehp->sfh_num_fdes;
+ return num_fdes;
+}
+
+/* Add an FRE to function at FUNC_IDX'th function descriptor entry in
+ the encoder context. */
+
+int
+sframe_encoder_add_fre (sframe_encoder_ctx *encoder,
+ unsigned int func_idx,
+ sframe_frame_row_entry *frep)
+{
+ sframe_header *ehp;
+ sframe_func_desc_entry *fdep;
+ sframe_frame_row_entry *ectx_frep;
+ size_t offsets_sz, esz;
+ unsigned int fre_type;
+ size_t fre_tbl_sz;
+ int err = 0;
+
+ if (encoder == NULL || frep == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+ if (!sframe_fre_sanity_check_p (frep))
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+ /* Use func_idx to gather the function descriptor entry. */
+ fdep = sframe_encoder_get_funcdesc_at_index (encoder, func_idx);
+
+ if (fdep == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND);
+
+ fre_type = sframe_get_fre_type (fdep);
+ sf_fre_tbl *fre_tbl = (sf_fre_tbl *) encoder->sfe_fres;
+
+ if (fre_tbl == NULL)
+ {
+ fre_tbl_sz = (sizeof (sf_fre_tbl)
+ + (number_of_entries * sizeof (sframe_frame_row_entry)));
+ fre_tbl = malloc (fre_tbl_sz);
+
+ if (fre_tbl == NULL)
+ {
+ sframe_set_errno (&err, SFRAME_ERR_NOMEM);
+ goto bad; /* OOM. */
+ }
+ memset (fre_tbl, 0, fre_tbl_sz);
+ fre_tbl->alloced = number_of_entries;
+ }
+ else if (fre_tbl->count == fre_tbl->alloced)
+ {
+ fre_tbl_sz = (sizeof (sf_fre_tbl)
+ + ((fre_tbl->alloced + number_of_entries)
+ * sizeof (sframe_frame_row_entry)));
+ fre_tbl = realloc (fre_tbl, fre_tbl_sz);
+ if (fre_tbl == NULL)
+ {
+ sframe_set_errno (&err, SFRAME_ERR_NOMEM);
+ goto bad; /* OOM. */
+ }
+
+ memset (&fre_tbl->entry[fre_tbl->alloced], 0,
+ number_of_entries * sizeof (sframe_frame_row_entry));
+ fre_tbl->alloced += number_of_entries;
+ }
+
+ ectx_frep = &fre_tbl->entry[fre_tbl->count];
+ ectx_frep->fre_start_addr
+ = frep->fre_start_addr;
+ ectx_frep->fre_info = frep->fre_info;
+
+ if (fdep->sfde_func_size)
+ sframe_assert (frep->fre_start_addr < fdep->sfde_func_size);
+ else
+ /* A SFrame FDE with func size equal to zero is possible. */
+ sframe_assert (frep->fre_start_addr == fdep->sfde_func_size);
+
+ /* frep has already been sanity check'd. Get offsets size. */
+ offsets_sz = sframe_fre_offset_bytes_size (frep->fre_info);
+ memcpy (&ectx_frep->fre_offsets, &frep->fre_offsets, offsets_sz);
+
+ esz = sframe_fre_entry_size (frep, fre_type);
+ fre_tbl->count++;
+
+ encoder->sfe_fres = (void *) fre_tbl;
+ encoder->sfe_fre_nbytes += esz;
+
+ ehp = sframe_encoder_get_header (encoder);
+ ehp->sfh_num_fres = fre_tbl->count;
+
+ /* Update the value of the number of FREs for the function. */
+ fdep->sfde_func_num_fres++;
+
+ return 0;
+
+bad:
+ if (fre_tbl != NULL)
+ free (fre_tbl);
+ encoder->sfe_fres = NULL;
+ encoder->sfe_fre_nbytes = 0;
+ return -1;
+}
+
+/* Add a new function descriptor entry with START_ADDR, FUNC_SIZE and NUM_FRES
+ to the encoder. */
+
+int
+sframe_encoder_add_funcdesc (sframe_encoder_ctx *encoder,
+ int32_t start_addr,
+ uint32_t func_size,
+ unsigned char func_info,
+ uint32_t num_fres __attribute__ ((unused)))
+{
+ sframe_header *ehp;
+ sf_funidx_tbl *fd_info;
+ size_t fd_tbl_sz;
+ int err = 0;
+
+ /* FIXME book-keep num_fres for error checking. */
+ if (encoder == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_INVAL);
+
+ fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc;
+ ehp = sframe_encoder_get_header (encoder);
+
+ if (fd_info == NULL)
+ {
+ fd_tbl_sz = (sizeof (sf_funidx_tbl)
+ + (number_of_entries * sizeof (sframe_func_desc_entry)));
+ fd_info = malloc (fd_tbl_sz);
+ if (fd_info == NULL)
+ {
+ sframe_set_errno (&err, SFRAME_ERR_NOMEM);
+ goto bad; /* OOM. */
+ }
+ memset (fd_info, 0, fd_tbl_sz);
+ fd_info->alloced = number_of_entries;
+ }
+ else if (fd_info->count == fd_info->alloced)
+ {
+ fd_tbl_sz = (sizeof (sf_funidx_tbl)
+ + ((fd_info->alloced + number_of_entries)
+ * sizeof (sframe_func_desc_entry)));
+ fd_info = realloc (fd_info, fd_tbl_sz);
+ if (fd_info == NULL)
+ {
+ sframe_set_errno (&err, SFRAME_ERR_NOMEM);
+ goto bad; /* OOM. */
+ }
+
+ memset (&fd_info->entry[fd_info->alloced], 0,
+ number_of_entries * sizeof (sframe_func_desc_entry));
+ fd_info->alloced += number_of_entries;
+ }
+
+ fd_info->entry[fd_info->count].sfde_func_start_address = start_addr;
+ /* Num FREs is updated as FREs are added for the function later via
+ sframe_encoder_add_fre. */
+ fd_info->entry[fd_info->count].sfde_func_size = func_size;
+ fd_info->entry[fd_info->count].sfde_func_start_fre_off
+ = encoder->sfe_fre_nbytes;
+#if 0
+ // Linker optimization test code cleanup later ibhagat TODO FIXME
+ unsigned int fre_type = sframe_calc_fre_type (func_size);
+
+ fd_info->entry[fd_info->count].sfde_func_info
+ = sframe_fde_func_info (fre_type);
+#endif
+ fd_info->entry[fd_info->count].sfde_func_info = func_info;
+ fd_info->count++;
+ encoder->sfe_funcdesc = (void *) fd_info;
+ ehp->sfh_num_fdes++;
+ return 0;
+
+bad:
+ if (fd_info != NULL)
+ free (fd_info);
+ encoder->sfe_funcdesc = NULL;
+ ehp->sfh_num_fdes = 0;
+ return -1;
+}
+
+static int
+sframe_sort_funcdesc (sframe_encoder_ctx *encoder)
+{
+ sframe_header *ehp;
+
+ ehp = sframe_encoder_get_header (encoder);
+ /* Sort and write out the FDE table. */
+ sf_funidx_tbl *fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc;
+ if (fd_info)
+ {
+ qsort (fd_info->entry, fd_info->count,
+ sizeof (sframe_func_desc_entry), fde_func);
+ /* Update preamble's flags. */
+ ehp->sfh_preamble.sfp_flags |= SFRAME_F_FDE_SORTED;
+ }
+ return 0;
+}
+
+/* Write a frame row entry pointed to by FREP into the buffer CONTENTS. The
+ size in bytes written out are updated in ESZ.
+
+ This function works closely with the SFrame binary format.
+
+ Returns SFRAME_ERR if failure. */
+
+static int
+sframe_encoder_write_fre (char *contents, sframe_frame_row_entry *frep,
+ unsigned int fre_type, size_t *esz)
+{
+ size_t fre_size;
+ size_t fre_start_addr_sz;
+ size_t fre_stack_offsets_sz;
+ int err = 0;
+
+ if (!sframe_fre_sanity_check_p (frep))
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+ fre_start_addr_sz = sframe_fre_start_addr_size (fre_type);
+ fre_stack_offsets_sz = sframe_fre_offset_bytes_size (frep->fre_info);
+
+ /* The FRE start address must be encodable in the available number of
+ bytes. */
+ uint64_t bitmask = SFRAME_BITMASK_OF_SIZE (fre_start_addr_sz);
+ sframe_assert ((uint64_t)frep->fre_start_addr <= bitmask);
+
+ memcpy (contents,
+ &frep->fre_start_addr,
+ fre_start_addr_sz);
+ contents += fre_start_addr_sz;
+
+ memcpy (contents,
+ &frep->fre_info,
+ sizeof (frep->fre_info));
+ contents += sizeof (frep->fre_info);
+
+ memcpy (contents,
+ frep->fre_offsets,
+ fre_stack_offsets_sz);
+ contents+= fre_stack_offsets_sz;
+
+ fre_size = sframe_fre_entry_size (frep, fre_type);
+ /* Sanity checking. */
+ sframe_assert ((fre_start_addr_sz
+ + sizeof (frep->fre_info)
+ + fre_stack_offsets_sz) == fre_size);
+
+ *esz = fre_size;
+
+ return 0;
+}
+
+/* Serialize the core contents of the SFrame section and write out to the
+ output buffer held in the ENCODER. Return SFRAME_ERR if failure. */
+
+static int
+sframe_encoder_write_sframe (sframe_encoder_ctx *encoder)
+{
+ char *contents;
+ size_t buf_size;
+ size_t hdr_size;
+ size_t all_fdes_size;
+ size_t fre_size;
+ size_t esz = 0;
+ sframe_header *ehp;
+ unsigned char flags;
+ sf_funidx_tbl *fd_info;
+ sf_fre_tbl *fr_info;
+ uint32_t i, num_fdes;
+ uint32_t j, num_fres;
+ sframe_func_desc_entry *fdep;
+ sframe_frame_row_entry *frep;
+
+ unsigned int fre_type;
+ int err = 0;
+
+ contents = encoder->sfe_data;
+ buf_size = encoder->sfe_data_size;
+ num_fdes = sframe_encoder_get_num_fidx (encoder);
+ all_fdes_size = num_fdes * sizeof (sframe_func_desc_entry);
+ ehp = sframe_encoder_get_header (encoder);
+ hdr_size = sframe_get_hdr_size (ehp);
+
+ fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc;
+ fr_info = (sf_fre_tbl *) encoder->sfe_fres;
+
+ /* Sanity checks:
+ - buffers must be malloc'd by the caller. */
+ if ((contents == NULL) || (buf_size < hdr_size))
+ return sframe_set_errno (&err, SFRAME_ERR_BUF_INVAL);
+ if (fr_info == NULL)
+ return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL);
+
+ /* Write out the FRE table first.
+
+ Recall that read/write of FREs needs information from the corresponding
+ FDE; the latter stores the information about the FRE type record used for
+ the function. Also note that sorting of FDEs does NOT impact the order
+ in which FREs are stored in the SFrame's FRE sub-section. This means
+ that writing out FREs after sorting of FDEs will need some additional
+ book-keeping. At this time, we can afford to avoid it by writing out
+ the FREs first to the output buffer. */
+ fre_size = 0;
+ uint32_t global = 0;
+ uint32_t fre_index = 0;
+
+ contents += hdr_size + all_fdes_size;
+ for (i = 0; i < num_fdes; i++)
+ {
+ fdep = &fd_info->entry[i];
+ fre_type = sframe_get_fre_type (fdep);
+ num_fres = fdep->sfde_func_num_fres;
+
+ for (j = 0; j < num_fres; j++)
+ {
+ fre_index = global + j;
+ frep = &fr_info->entry[fre_index];
+
+ sframe_encoder_write_fre (contents, frep, fre_type, &esz);
+ contents += esz;
+ fre_size += esz; /* For debugging only. */
+ }
+ global += j;
+ }
+
+ sframe_assert (fre_size == ehp->sfh_fre_len);
+ sframe_assert (global == ehp->sfh_num_fres);
+ sframe_assert ((size_t)(contents - encoder->sfe_data) == buf_size);
+
+ /* Sort the FDE table */
+ sframe_sort_funcdesc (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)
+ || (fd_info == NULL))
+ return sframe_set_errno (&err, SFRAME_ERR_FDE_INVAL);
+
+ contents = encoder->sfe_data;
+ /* Write out the SFrame header. The SFrame header in the encoder
+ object has already been updated with correct offsets by the caller. */
+ memcpy (contents, ehp, hdr_size);
+ contents += hdr_size;
+
+ /* Write out the FDE table sorted on funtion start address. */
+ memcpy (contents, fd_info->entry, all_fdes_size);
+ contents += all_fdes_size;
+
+ return 0;
+}
+
+/* Serialize the contents of the encoder and return the buffer. ENCODED_SIZE
+ is updated to the size of the buffer. */
+
+char *
+sframe_encoder_write (sframe_encoder_ctx *encoder,
+ size_t *encoded_size, int *errp)
+{
+ sframe_header *ehp;
+ size_t hdrsize, fsz, fresz, bufsize;
+ int foreign_endian;
+
+ /* Initialize the encoded_size to zero. This makes it simpler to just
+ return from the function in case of failure. Free'ing up of
+ encoder->sfe_data is the responsibility of the caller. */
+ *encoded_size = 0;
+
+ if (encoder == NULL || encoded_size == NULL || errp == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL);
+
+ ehp = sframe_encoder_get_header (encoder);
+ hdrsize = sframe_get_hdr_size (ehp);
+ fsz = sframe_encoder_get_num_fidx (encoder)
+ * sizeof (sframe_func_desc_entry);
+ fresz = encoder->sfe_fre_nbytes;
+
+ /* The total size of buffer is the sum of header, SFrame Function Descriptor
+ Entries section and the FRE section. */
+ bufsize = hdrsize + fsz + fresz;
+ encoder->sfe_data = (char *) malloc (bufsize);
+ if (encoder->sfe_data == NULL)
+ return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM);
+ encoder->sfe_data_size = bufsize;
+
+ /* Update the information in the SFrame header. */
+ /* SFrame FDE section follows immediately after the header. */
+ ehp->sfh_fdeoff = 0;
+ /* SFrame FRE section follows immediately after the SFrame FDE section. */
+ ehp->sfh_freoff = fsz;
+ ehp->sfh_fre_len = fresz;
+
+ foreign_endian = need_swapping (ehp->sfh_abi_arch);
+
+ /* Write out the FDE Index and the FRE table in the sfe_data. */
+ if (sframe_encoder_write_sframe (encoder))
+ return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+
+ /* Endian flip the contents if necessary. */
+ if (foreign_endian)
+ {
+ if (flip_sframe (encoder->sfe_data, bufsize, 1))
+ return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+ flip_header ((sframe_header*)encoder->sfe_data);
+ }
+
+ *encoded_size = bufsize;
+ return encoder->sfe_data;
+}
diff --git a/lib/sframe.h b/lib/sframe.h
new file mode 100644
index 000000000..dd9b49eac
--- /dev/null
+++ b/lib/sframe.h
@@ -0,0 +1,287 @@
+/* SFrame format description.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ SFrame is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _SFRAME_H
+#define _SFRAME_H
+
+#include <sys/types.h>
+#include <limits.h>
+#include <stdint.h>
+
+#include "ansidecl.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* SFrame format.
+
+ SFrame format is a simple format to represent the information needed
+ for vanilla virtual stack unwinding. SFrame format keeps track of the
+ minimal necessary information needed for stack unwinding:
+ - Canonical Frame Address (CFA)
+ - Frame Pointer (FP)
+ - Return Address (RA)
+
+ The SFrame section itself has the following structure:
+
+ +--------+------------+---------+
+ | file | function | frame |
+ | header | descriptor | row |
+ | | entries | entries |
+ +--------+------------+---------+
+
+ The file header stores a magic number and version information, flags, and
+ the byte offset of each of the sections relative to the end of the header
+ itself. The file header also specifies the total number of Function
+ Descriptor Entries, Frame Row Entries and length of the FRE sub-section.
+
+ Following the header is a list of Function Descriptor Entries (FDEs).
+ This list may be sorted if the flags in the file header indicate it to be
+ so. The sort order, if applicable, is the order of functions in the
+ .text.* sections in the resulting binary artifact. Each Function
+ Descriptor Entry specifies the start PC of a function, the size in bytes
+ of the function and an offset to its first Frame Row Entry (FRE). Each FDE
+ additionally also specifies the type of FRE it uses to encode the unwind
+ information.
+
+ Next, the Frame Row Entry section is a list of variable size records,
+ each of which represent SFrame unwind information for a set of PCs. A
+ singular Frame Row Entry is a self-sufficient record with information on
+ how to virtually unwind the stack for the applicable set of PCs.
+
+ */
+
+
+/* SFrame format versions. */
+#define SFRAME_VERSION_1 1
+/* SFrame magic number. */
+#define SFRAME_MAGIC 0xdee2
+/* Current version of SFrame format. */
+#define SFRAME_VERSION SFRAME_VERSION_1
+
+/* Various flags for SFrame. */
+
+/* Function Descriptor Entries are sorted on PC. */
+#define SFRAME_F_FDE_SORTED 0x1
+/* Frame-pointer based unwinding. */
+#define SFRAME_F_FRAME_POINTER 0x2
+
+#define SFRAME_CFA_FIXED_FP_INVALID 0
+#define SFRAME_CFA_FIXED_RA_INVALID 0
+
+/* Supported ABIs/Arch. */
+#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. */
+
+/* SFrame FRE types. */
+#define SFRAME_FRE_TYPE_ADDR1 0
+#define SFRAME_FRE_TYPE_ADDR2 1
+#define SFRAME_FRE_TYPE_ADDR4 2
+
+/* SFrame Function Descriptor Entry types.
+
+ The SFrame format has two possible representations for functions. The
+ choice of which type to use is made according to the instruction patterns
+ in the relevant program stub.
+
+ An SFrame FDE of type SFRAME_FDE_TYPE_PCINC 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.
+
+ An SFrame FDE of type SFRAME_FDE_TYPE_PCMASK 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. */
+
+/* Unwinders perform a (PC >= FRE_START_ADDR) to look up a matching FRE. */
+#define SFRAME_FDE_TYPE_PCINC 0
+/* Unwinders perform a (PC & FRE_START_ADDR_AS_MASK >= FRE_START_ADDR_AS_MASK)
+ to look up a matching FRE. */
+#define SFRAME_FDE_TYPE_PCMASK 1
+
+typedef struct sframe_preamble
+{
+ uint16_t sfp_magic; /* Magic number (SFRAME_MAGIC). */
+ uint8_t sfp_version; /* Data format version number (SFRAME_VERSION). */
+ uint8_t sfp_flags; /* Flags. */
+} ATTRIBUTE_PACKED sframe_preamble;
+
+typedef struct sframe_header
+{
+ sframe_preamble sfh_preamble;
+ /* Information about the arch (endianness) and ABI. */
+ uint8_t sfh_abi_arch;
+ /* Offset for the Frame Pointer (FP) from CFA may be fixed for some
+ ABIs (e.g, in AMD64 when -fno-omit-frame-pointer is used). When fixed,
+ this field specifies the fixed stack frame offset and the individual
+ FREs do not need to track it. When not fixed, it is set to
+ SFRAME_CFA_FIXED_FP_INVALID, and the individual FREs may provide
+ the applicable stack frame offset, if any. */
+ int8_t sfh_cfa_fixed_fp_offset;
+ /* Offset for the Return Address from CFA is fixed for some ABIs
+ (e.g., AMD64 has it as CFA-8). When fixed, the header specifies the
+ fixed stack frame offset and the individual FREs do not track it. When
+ not fixed, it is set to SFRAME_CFA_FIXED_RA_INVALID, and individual
+ FREs provide the applicable stack frame offset, if any. */
+ int8_t sfh_cfa_fixed_ra_offset;
+ /* Number of bytes making up the auxilliary header, if any.
+ Some ABI/arch, in the future, may use this space for extending the
+ information in SFrame header. Auxilliary header is contained in
+ bytes sequentially following the sframe_header. */
+ uint8_t sfh_auxhdr_len;
+ /* Number of SFrame FDEs in this SFrame section. */
+ uint32_t sfh_num_fdes;
+ /* Number of SFrame Frame Row Entries. */
+ uint32_t sfh_num_fres;
+ /* Number of bytes in the SFrame Frame Row Entry section. */
+ uint32_t sfh_fre_len;
+ /* Offset of SFrame Function Descriptor Entry section. */
+ uint32_t sfh_fdeoff;
+ /* Offset of SFrame Frame Row Entry section. */
+ uint32_t sfh_freoff;
+} ATTRIBUTE_PACKED sframe_header;
+
+#define SFRAME_V1_HDR_SIZE(sframe_hdr) \
+ ((sizeof (sframe_header) + (sframe_hdr).sfh_auxhdr_len))
+
+typedef struct sframe_func_desc_entry
+{
+ /* Function start address. Encoded as a signed offset, relative to the
+ beginning of the current FDE. */
+ int32_t sfde_func_start_address;
+ /* Size of the function in bytes. */
+ uint32_t sfde_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 sfde_func_start_fre_off;
+ /* Number of frame row entries for the function. */
+ uint32_t sfde_func_num_fres;
+ /* Additional information for deciphering the unwind information for the
+ function.
+ - 4-bits: Identify the FRE type used for the function.
+ - 1-bit: Identify the FDE type of the function - mask or inc.
+ - 3-bits: Unused.
+ --------------------------------------------
+ | Unused | FDE type | FRE type |
+ --------------------------------------------
+ 8 5 4 0 */
+ uint8_t sfde_func_info;
+} ATTRIBUTE_PACKED sframe_func_desc_entry;
+
+/* Macros to compose and decompose function info in FDE. */
+
+#define SFRAME_V1_FUNC_INFO(fde_type, fre_enc_type) \
+ (((fde_type) & 0x1) << 4 | (fre_enc_type))
+
+#define SFRAME_V1_FUNC_FRE_TYPE(data) ((data) & 0xf)
+#define SFRAME_V1_FUNC_FDE_TYPE(data) ((data >> 4) & 0x1)
+
+/* 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. */
+#define SFRAME_FRE_OFFSET_1B 0
+#define SFRAME_FRE_OFFSET_2B 1
+#define SFRAME_FRE_OFFSET_4B 2
+
+/* An SFrame Frame Row Entry can be SP or FP based. */
+#define SFRAME_BASE_REG_FP 0
+#define SFRAME_BASE_REG_SP 1
+
+/* The index at which a specific offset is presented in the variable length
+ bytes of an FRE. */
+#define SFRAME_FRE_CFA_OFFSET_IDX 0
+#define SFRAME_FRE_FP_OFFSET_IDX 1
+#define SFRAME_FRE_RA_OFFSET_IDX 2
+
+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
+ - 1 bit: Unused.
+ -----------------------------------------------------------------------
+ | Unused | Size of offsets | Number of offsets | base_reg |
+ -----------------------------------------------------------------------
+ 8 7 5 1 0
+
+ */
+ uint8_t fre_info;
+} sframe_fre_info;
+
+/* Macros to compose and decompose FRE info. */
+
+#define SFRAME_V1_FRE_INFO(base_reg_id, offset_num, offset_size) \
+ ((offset_size << 5) | (offset_num << 1) | (base_reg_id))
+
+#define SFRAME_V1_FRE_CFA_BASE_REG_ID(data) ((data) & 0x1)
+#define SFRAME_V1_FRE_OFFSET_COUNT(data) (((data) >> 1) & 0xf)
+#define SFRAME_V1_FRE_OFFSET_SIZE(data) (((data) >> 5) & 0x3)
+
+/* SFrame Frame Row Entry definitions.
+
+ Used for both AMD64 and AARCH64.
+
+ An SFrame Frame Row Entry is a self-sufficient record containing SFrame
+ unwind info for a range of addresses, starting at the specified offset in
+ the function. Each SFrame Frame Row Entry is followed by S*N bytes, where:
+ 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 offsets are interpreted in order as follows:
+ offset1 (interpreted as CFA = BASE_REG + offset1)
+ offset2 (interpreted as FP = CFA + offset2)
+ offset3 (interpreted as RA = CFA + offset3)
+*/
+
+typedef struct sframe_frame_row_entry_addr1
+{
+ /* Start address of the frame row entry. Encoded as an 1-byte unsigned
+ offset, relative to the start address of the function. */
+ uint8_t sfre_start_address;
+ sframe_fre_info sfre_info;
+} ATTRIBUTE_PACKED sframe_frame_row_entry_addr1;
+
+typedef struct sframe_frame_row_entry_addr2
+{
+ /* Start address of the frame row entry. Encoded as an 2-byte unsigned
+ offset, relative to the start address of the function. */
+ uint16_t sfre_start_address;
+ sframe_fre_info sfre_info;
+} ATTRIBUTE_PACKED sframe_frame_row_entry_addr2;
+
+typedef struct sframe_frame_row_entry_addr4
+{
+ /* Start address of the frame row entry. Encoded as a 4-byte unsigned
+ offset, relative to the start address of the function. */
+ uint32_t sfre_start_address;
+ sframe_fre_info sfre_info;
+} ATTRIBUTE_PACKED sframe_frame_row_entry_addr4;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFRAME_H */
diff --git a/modules/sframe b/modules/sframe
new file mode 100644
index 000000000..f5eb78200
--- /dev/null
+++ b/modules/sframe
@@ -0,0 +1,30 @@
+Description:
+Sframe's encoder and decoder.
+
+Files:
+lib/sframe-api.h
+lib/sframe-internal.h
+lib/sframe.h
+lib/sframe.c
+
+Depends-on:
+stdio
+stdlib
+stdarg
+string
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += sframe-api.h sframe-internal.h sframe.h sframe.c
+
+Include:
+"sframe-api.h"
+"sframe-internal.h"
+"sframe.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/sframe-header b/modules/sframe-header
new file mode 100644
index 000000000..e098991a5
--- /dev/null
+++ b/modules/sframe-header
@@ -0,0 +1,23 @@
+Description:
+Define the SFrame format.
+
+Files:
+lib/sframe.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+lib_SOURCES += sframe.h
+
+Include:
+<sys/types.h>
+<limits.h>
+<stdint.h>
+
+License:
+LGPLv2+
+
+Maintainer:
+all
diff --git a/modules/sframe-tests b/modules/sframe-tests
new file mode 100644
index 000000000..80a2a39c5
--- /dev/null
+++ b/modules/sframe-tests
@@ -0,0 +1,16 @@
+Files:
+tests/test-sframe-frecnt1.c
+tests/test-sframe-frecnt2.c
+tests/test-sframe-encode1.c
+tests/test-sframe-be-flipping.c
+tests/DATA1
+tests/DATA2
+tests/DATA-BE
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-sframe-frecnt1 test-sframe-frecnt2 test-sframe-encode1
test-sframe-be-flipping
+check_PROGRAMS += test-sframe-frecnt1 test-sframe-frecnt2 test-sframe-encode1
test-sframe-be-flipping
diff --git a/tests/DATA-BE b/tests/DATA-BE
new file mode 100644
index
0000000000000000000000000000000000000000..3e19ff48e9c67f30645a9d8bdca0af834dd345f4
GIT binary patch
literal 64
tcmccjh>?Mj0SrJCD-a7qxD0|&+677j<(L^**cBeU&|zjU0MQQ23;?AA2E70P
literal 0
HcmV?d00001
diff --git a/tests/DATA1 b/tests/DATA1
new file mode 100644
index
0000000000000000000000000000000000000000..22ed40e5751caf8edd06d0a28f2cb9ea6824febc
GIT binary patch
literal 60
zcmaEKkCBm?fq{V$h*^M`ABaIf5Qx)0{QoZv=0W5b7??R2Sp`0@un2sRV&(V%0O#fi
AjsO4v
literal 0
HcmV?d00001
diff --git a/tests/DATA2 b/tests/DATA2
new file mode 100644
index
0000000000000000000000000000000000000000..68fc2d240cd34878747f552d1b12bbc0e59a5217
GIT binary patch
literal 92
zcmaEKkCBm?;Rgc~0|NsG5X%8E2q*!u-G~4GrNKM~1{M$<2^0_j()>US7GPlJU}P2e
Sz``Q%L5h{*0}_`X$OQnwj}Q_7
literal 0
HcmV?d00001
diff --git a/tests/sframe-api.h b/tests/sframe-api.h
new file mode 100644
index 000000000..254e9149e
--- /dev/null
+++ b/tests/sframe-api.h
@@ -0,0 +1,229 @@
+/* Public API to SFrame.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _SFRAME_API_H
+#define _SFRAME_API_H
+
+#include <sframe.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct sframe_decoder_ctx sframe_decoder_ctx;
+typedef struct sframe_encoder_ctx sframe_encoder_ctx;
+
+#define MAX_OFFSET_BYTES (SFRAME_FRE_OFFSET_4B * 2 * 3)
+
+/* User interfacing SFrame Row Entry.
+ An abstraction provided by SFrame so the consumer is decoupled from
+ the binary format representation of the same. */
+
+typedef struct sframe_frame_row_entry
+{
+ uint32_t fre_start_addr;
+ unsigned char fre_info;
+ unsigned char fre_offsets[MAX_OFFSET_BYTES];
+} sframe_frame_row_entry;
+
+#define SFRAME_ERR ((int) -1)
+
+/* This macro holds information about all the available SFrame
+ errors. It is used to form both an enum holding all the error
+ constants, and also the error strings themselves. To use, define
+ _SFRAME_FIRST and _SFRAME_ITEM to expand as you like, then
+ mention the macro name. See the enum after this for an example. */
+#define _SFRAME_ERRORS \
+ _SFRAME_FIRST (SFRAME_ERR_VERSION_INVAL, "SFrame version not supported.") \
+ _SFRAME_ITEM (SFRAME_ERR_NOMEM, "Out of Memory.") \
+ _SFRAME_ITEM (SFRAME_ERR_INVAL, "Corrupt SFrame.") \
+ _SFRAME_ITEM (SFRAME_ERR_BUF_INVAL, "Buffer does not contain SFrame data.") \
+ _SFRAME_ITEM (SFRAME_ERR_DCTX_INVAL, "Corrupt SFrame decoder.") \
+ _SFRAME_ITEM (SFRAME_ERR_ECTX_INVAL, "Corrupt SFrame encoder.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_INVAL, "Corrput FDE.") \
+ _SFRAME_ITEM (SFRAME_ERR_FRE_INVAL, "Corrupt FRE.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_NOTFOUND,"FDE not found.") \
+ _SFRAME_ITEM (SFRAME_ERR_FDE_NOTSORTED, "FDEs not sorted.") \
+ _SFRAME_ITEM (SFRAME_ERR_FRE_NOTFOUND,"FRE not found.") \
+ _SFRAME_ITEM (SFRAME_ERR_FREOFFSET_NOPRESENT,"FRE offset not present.")
+
+#define SFRAME_ERR_BASE 2000 /* Base value for SFrame errnos. */
+
+enum
+ {
+#define _SFRAME_FIRST(NAME, STR) NAME = SFRAME_ERR_BASE
+#define _SFRAME_ITEM(NAME, STR) , NAME
+_SFRAME_ERRORS
+#undef _SFRAME_ITEM
+#undef _SFRAME_FIRST
+ };
+
+/* Count of SFrame errors. */
+#define SFRAME_ERR_NERR (SFRAME_ERR_FREOFFSET_NOPRESENT - SFRAME_ERR_BASE + 1)
+
+/* Get the error message string. */
+
+extern const char *
+sframe_errmsg (int error);
+
+/* Get FDE function info given a FRE_TYPE. */
+
+extern unsigned char
+sframe_fde_func_info (unsigned int fre_type, unsigned int fde_type);
+
+/* Gather the FRE type given the function size. */
+
+extern unsigned int
+sframe_calc_fre_type (unsigned int func_size);
+
+/* The SFrame Decoder. */
+
+/* Decode the specified SFrame buffer CF_BUF of size CF_SIZE and return the
+ new SFrame decoder context. Sets ERRP for the caller if any error. */
+extern sframe_decoder_ctx *
+sframe_decode (const char *cf_buf, size_t cf_size, int *errp);
+
+/* Free the decoder context. */
+extern void
+sframe_decoder_free (sframe_decoder_ctx **dctx);
+
+/* Get the size of the SFrame header from the decoder context DCTX. */
+extern unsigned int
+sframe_decoder_get_hdr_size (sframe_decoder_ctx *dctx);
+
+/* Get the SFrame's abi/arch info. */
+extern unsigned char
+sframe_decoder_get_abi_arch (sframe_decoder_ctx *dctx);
+
+/* Return the number of function descriptor entries in the SFrame decoder
+ DCTX. */
+unsigned int
+sframe_decoder_get_num_fidx (sframe_decoder_ctx *dctx);
+
+/* Get the fixed FP offset from the decoder context DCTX. */
+extern int8_t
+sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *dctx);
+
+/* Get the fixed RA offset from the decoder context DCTX. */
+extern int8_t
+sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *dctx);
+
+/* Find the function descriptor entry which contains the specified address. */
+extern sframe_func_desc_entry *
+sframe_get_funcdesc_with_addr (sframe_decoder_ctx *dctx,
+ int32_t addr, int *errp);
+
+/* Find the SFrame Frame Row Entry which contains the PC. Returns
+ SFRAME_ERR if failure. */
+
+extern int
+sframe_find_fre (sframe_decoder_ctx *ctx, int32_t pc,
+ sframe_frame_row_entry *frep);
+
+/* Get the FRE_IDX'th FRE of the function at FUNC_IDX'th function
+ index entry in the SFrame decoder CTX. Returns error code as
+ applicable. */
+extern int
+sframe_decoder_get_fre (sframe_decoder_ctx *ctx,
+ unsigned int func_idx,
+ unsigned int fre_idx,
+ sframe_frame_row_entry *fre);
+
+/* Get the data (NUM_FRES, FUNC_START_ADDRESS) from the function
+ descriptor entry at index I'th in the decoder CTX. If failed,
+ return error code. */
+extern int
+sframe_decoder_get_funcdesc (sframe_decoder_ctx *ctx,
+ unsigned int i,
+ uint32_t *num_fres,
+ uint32_t *func_size,
+ int32_t *func_start_address,
+ unsigned char *func_info);
+
+/* SFrame textual dump. */
+extern void
+dump_sframe (sframe_decoder_ctx *decoder, uint64_t addr);
+
+/* Get the base reg id from the FRE info. Sets errp if fails. */
+extern unsigned int
+sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the CFA offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_cfa_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the FP offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_fp_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* Get the RA offset from the FRE. If the offset is invalid, sets errp. */
+extern int32_t
+sframe_fre_get_ra_offset (sframe_frame_row_entry *fre, int *errp);
+
+/* The SFrame Encoder. */
+
+/* Create an encoder context with the given SFrame format version VER, FLAGS
+ and ABI information. Sets errp if failure. */
+extern sframe_encoder_ctx *
+sframe_encode (unsigned char ver, unsigned char flags, int abi,
+ int8_t fixed_fp_offset, int8_t fixed_ra_offset, int *errp);
+
+/* Free the encoder context. */
+extern void
+sframe_encoder_free (sframe_encoder_ctx **encoder);
+
+/* Get the size of the SFrame header from the encoder ctx ENCODER. */
+extern unsigned int
+sframe_encoder_get_hdr_size (sframe_encoder_ctx *encoder);
+
+/* Get the abi/arch info from the SFrame encoder context CTX. */
+extern unsigned char
+sframe_encoder_get_abi_arch (sframe_encoder_ctx *encoder);
+
+/* Return the number of function descriptor entries in the SFrame encoder
+ ENCODER. */
+extern unsigned int
+sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder);
+
+/* Add an FRE to function at FUNC_IDX'th function descriptor index entry in
+ the encoder context. */
+extern int
+sframe_encoder_add_fre (sframe_encoder_ctx *encoder,
+ unsigned int func_idx,
+ sframe_frame_row_entry *frep);
+
+/* Add a new function descriptor entry with START_ADDR, FUNC_SIZE and NUM_FRES
+ to the encoder. */
+extern int
+sframe_encoder_add_funcdesc (sframe_encoder_ctx *encoder,
+ int32_t start_addr,
+ uint32_t func_size,
+ unsigned char func_info,
+ uint32_t num_fres);
+
+/* Serialize the contents of the encoder and return the buffer. ENCODED_SIZE
+ is updated to the size of the buffer. Sets ERRP if failure. */
+extern char *
+sframe_encoder_write (sframe_encoder_ctx *encoder,
+ size_t *encoded_size, int *errp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SFRAME_API_H */
diff --git a/tests/test-sframe-be-flipping.c b/tests/test-sframe-be-flipping.c
new file mode 100644
index 000000000..89c3fa8a6
--- /dev/null
+++ b/tests/test-sframe-be-flipping.c
@@ -0,0 +1,115 @@
+/* test-sframe-be-flipping.c -- Test for handling different endianness in
Sframe.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "sframe-api.h"
+
+/* DejaGnu should not use gnulib's vsnprintf replacement here. */
+#undef vsnprintf
+#include <dejagnu.h>
+
+/* SFrame info from the following source (1 fde 5 fres):
+ static int cnt;
+ extern void foo (void);
+
+ int bar()
+ {
+ cnt++;
+ if (cnt == 3)
+ foo();
+ return (cnt);
+ }
+ gcc -mbig-endian -Wa,--gsframe -c -O3 t.c
+ objcopy --dump-section .sframe=DATA-BE t.o
+ */
+#define DATA "DATA-BE"
+
+int
+main ()
+{
+ sframe_decoder_ctx *dctx = NULL;
+ uint32_t nfres, fsize;
+ int32_t fstart;
+ unsigned char finfo;
+ int err = 0;
+ FILE *fp;
+ struct stat st;
+ char *sf_buf;
+ size_t sf_size;
+
+#define TEST(name, cond) \
+ do \
+ { \
+ if (cond) \
+ pass (name); \
+ else \
+ fail (name); \
+ } \
+ while (0)
+
+ /* Test setup. */
+ fp = fopen (DATA, "r");
+ if (fp == NULL)
+ goto setup_fail;
+ if (fstat (fileno (fp), &st) < 0)
+ {
+ perror ("fstat");
+ fclose (fp);
+ goto setup_fail;
+ }
+ sf_buf = malloc (st.st_size);
+ if (sf_buf == NULL)
+ {
+ perror ("malloc");
+ goto setup_fail;
+ }
+ sf_size = fread (sf_buf, 1, st.st_size, fp);
+ fclose (fp);
+ if (sf_size == 0)
+ {
+ fprintf (stderr, "Decode: Read buffer failed\n");
+ goto setup_fail;
+ }
+
+ /* Execute tests. */
+
+ /* Call to sframe_decode will endian flip the input buffer (big-endian) if
+ the host running the test is a little-endian system. This endian-flipped
+ copy of the buffer is kept internally in dctx. */
+ dctx = sframe_decode (sf_buf, sf_size, &err);
+ TEST ("be-flipping: Decoder setup", dctx != NULL);
+
+ unsigned int fde_cnt = sframe_decoder_get_num_fidx (dctx);
+ TEST ("be-flipping: Decoder FDE count", fde_cnt == 1);
+
+ err = sframe_decoder_get_funcdesc (dctx, 0, &nfres, &fsize, &fstart, &finfo);
+ TEST ("be-flipping: Decoder get FDE", err == 0);
+ TEST ("be-flipping: Decoder FRE count", nfres == 5);
+
+ sframe_decoder_free (&dctx);
+ return 0;
+
+setup_fail:
+ sframe_decoder_free (&dctx);
+ fail ("be-flipping: Test setup");
+ return 1;
+}
diff --git a/tests/test-sframe-encode1.c b/tests/test-sframe-encode1.c
new file mode 100644
index 000000000..29a6942d9
--- /dev/null
+++ b/tests/test-sframe-encode1.c
@@ -0,0 +1,187 @@
+/* test-sframe-encode1.c -- Test for encoder in SFrame.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "sframe-api.h"
+
+/* DejaGnu should not use gnulib's vsnprintf replacement here. */
+#undef vsnprintf
+#include <dejagnu.h>
+
+static int
+add_fde1 (sframe_encoder_ctx *encode, int idx)
+{
+ int i, err;
+ /* A contiguous block containing 4 FREs. */
+ sframe_frame_row_entry fres[]
+ = { {0x0, 0x3, {0x8, 0, 0}},
+ {0x1, 0x5, {0x10, 0xf0, 0}},
+ {0x4, 0x4, {0x10, 0xf0, 0}},
+ {0x1a, 0x5, {0x8, 0xf0, 0}}
+ };
+
+ unsigned char finfo = sframe_fde_func_info (SFRAME_FRE_TYPE_ADDR1,
+ SFRAME_FDE_TYPE_PCINC);
+ err = sframe_encoder_add_funcdesc (encode, 0xfffff03e, 0x1b, finfo, 4);
+ if (err == -1)
+ return err;
+
+ for (i = 0; i < 4; i++)
+ if (sframe_encoder_add_fre (encode, idx,fres+i) == SFRAME_ERR)
+ return -1;
+
+ return 0;
+}
+
+static int
+add_fde2 (sframe_encoder_ctx *encode, int idx)
+{
+ int i, err;
+ /* A contiguous block containing 4 FREs. */
+ sframe_frame_row_entry fres[]
+ = { {0x0, 0x3, {0x8, 0, 0}},
+ {0x1, 0x5, {0x10, 0xf0, 0}},
+ {0x4, 0x4, {0x10, 0xf0, 0}},
+ {0xf, 0x5, {0x8, 0xf0, 0}}
+ };
+
+ unsigned char finfo = sframe_fde_func_info (SFRAME_FRE_TYPE_ADDR1,
+ SFRAME_FDE_TYPE_PCINC);
+ err = sframe_encoder_add_funcdesc (encode, 0xfffff059, 0x10, finfo, 4);
+ if (err == -1)
+ return err;
+
+ for (i = 0; i < 4; i++)
+ if (sframe_encoder_add_fre (encode, idx, fres+i) == SFRAME_ERR)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * SFrame info from the following source (2 fdes, 4 fres in each fde):
+ * static int cnt;
+ * int foo() { return ++cnt; }
+ * int main() { return foo(); }
+ */
+#define DATA "DATA2"
+
+static int
+data_match (char *sframe_buf, size_t sz)
+{
+ FILE *fp;
+ struct stat st;
+ char *sf_buf;
+ size_t sf_size;
+ int diffs;
+
+ fp = fopen (DATA, "r");
+ if (fp == NULL)
+ return 0;
+ if (fstat (fileno (fp), &st) < 0)
+ {
+ perror ("fstat");
+ fclose (fp);
+ return 0;
+ }
+ sf_buf = malloc (st.st_size);
+ if (sf_buf == NULL)
+ {
+ perror ("malloc");
+ return 0;
+ }
+ sf_size = fread (sf_buf, 1, st.st_size, fp);
+ fclose (fp);
+ if (sf_size == 0 || sf_buf == NULL)
+ {
+ fprintf (stderr, "Encode: Read section failed\n");
+ return 0;
+ }
+ if (sf_size != sz)
+ return 0;
+
+ diffs = memcmp (sf_buf, sframe_buf, sz);
+
+ free (sf_buf);
+ return diffs == 0;
+}
+
+int main ()
+{
+ sframe_encoder_ctx *encode;
+ sframe_frame_row_entry frep;
+ char *sframe_buf;
+ size_t sf_size;
+ int err = 0;
+
+ encode = sframe_encode (SFRAME_VERSION, 0,
+ SFRAME_ABI_AMD64_ENDIAN_LITTLE, 0, -8, &err);
+
+ if (sframe_encoder_get_num_fidx (encode) != 0)
+ {
+ fprintf (stderr, "Encode: incorrect FDEs count\n");
+ goto fail;
+ }
+
+ /* Error test. */
+ if (sframe_encoder_add_fre (encode, 1, &frep) != SFRAME_ERR)
+ {
+ fprintf (stderr, "Encode: Adding FRE befoer FDE does\n");
+ goto fail;
+ }
+
+ if (add_fde1 (encode, 0) == -1)
+ {
+ fprintf (stderr, "Encode: Adding FDE1\n");
+ goto fail;
+ }
+ if (add_fde2 (encode, 1) == -1)
+ {
+ fprintf (stderr, "Encode: Adding FDE2\n");
+ goto fail;
+ }
+
+ if (sframe_encoder_get_num_fidx (encode) != 2)
+ {
+ fprintf (stderr, "Encode: Wrong FDE count\n");
+ goto fail;
+ }
+
+ sframe_buf = sframe_encoder_write (encode, &sf_size, &err);
+ if (err)
+ {
+ fprintf (stderr, "Encode: Write failed\n");
+ goto fail;
+ }
+ if (data_match (sframe_buf, sf_size))
+ {
+ sframe_encoder_free (&encode);
+ pass ("encode test");
+ return 0;
+ }
+
+fail:
+ sframe_encoder_free (&encode);
+ fail ("encode test");
+ return 1;
+}
diff --git a/tests/test-sframe-frecnt1.c b/tests/test-sframe-frecnt1.c
new file mode 100644
index 000000000..ecee0842a
--- /dev/null
+++ b/tests/test-sframe-frecnt1.c
@@ -0,0 +1,99 @@
+/* test-sframe-frecnt1.c -- Test for decoder in SFrame.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "sframe-api.h"
+
+/* DejaGnu should not use gnulib's vsnprintf replacement here. */
+#undef vsnprintf
+#include <dejagnu.h>
+
+/*
+ * SFrame info from the following source (1 fde 4 fres):
+ * static int cnt;
+ * int main() { cnt++; return (cnt); }
+ */
+#define DATA "DATA1"
+
+int
+main ()
+{
+ sframe_decoder_ctx *dctx = NULL;
+ uint32_t nfres, fsize;
+ int32_t fstart;
+ unsigned char finfo;
+ int err = 0;
+ FILE *fp;
+ struct stat st;
+ char *sf_buf;
+ size_t sf_size;
+
+#define TEST(name, cond) \
+ do \
+ { \
+ if (cond) \
+ pass (name); \
+ else \
+ fail (name); \
+ } \
+ while (0)
+
+ /* Test Setup. */
+ fp = fopen (DATA, "r");
+ if (fp == NULL)
+ goto setup_fail;
+ if (fstat (fileno (fp), &st) < 0)
+ {
+ perror ("fstat");
+ fclose (fp);
+ goto setup_fail;
+ }
+ sf_buf = malloc (st.st_size);
+ if (sf_buf == NULL)
+ {
+ perror ("malloc");
+ goto setup_fail;
+ }
+
+ /* Execute tests. */
+ sf_size = fread (sf_buf, 1, st.st_size, fp);
+ fclose (fp);
+ TEST ("frecnt-1: Read section", sf_size != 0);
+
+ dctx = sframe_decode (sf_buf, sf_size, &err);
+ TEST ("frecnt-1: Decoder setup", dctx != NULL);
+
+ unsigned int fde_cnt = sframe_decoder_get_num_fidx (dctx);
+ TEST ("frecnt-1: Decoder FDE count", fde_cnt == 1);
+
+ err = sframe_decoder_get_funcdesc (dctx, 0, &nfres, &fsize, &fstart, &finfo);
+ TEST ("frecnt-1: Decoder get FDE", err == 0);
+ TEST ("frecnt-1: Decoder FRE count", nfres == 4);
+
+ sframe_decoder_free (&dctx);
+ return 0;
+
+setup_fail:
+ sframe_decoder_free (&dctx);
+ fail ("frecnt-1: Test setup");
+ return 1;
+}
diff --git a/tests/test-sframe-frecnt2.c b/tests/test-sframe-frecnt2.c
new file mode 100644
index 000000000..6479a70e4
--- /dev/null
+++ b/tests/test-sframe-frecnt2.c
@@ -0,0 +1,103 @@
+/* test-sframe-frecnt2.c -- Test for decoder in SFrame.
+
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "sframe-api.h"
+
+/* DejaGnu should not use gnulib's vsnprintf replacement here. */
+#undef vsnprintf
+#include <dejagnu.h>
+
+/*
+ * SFrame info from the following source (2 fde 8 fres):
+ * static int cnt;
+ * int foo() { return ++cnt; }
+ * int main() { return foo(); }
+ */
+#define DATA "DATA2"
+
+int
+main ()
+{
+ sframe_decoder_ctx *dctx = NULL;
+ uint32_t nfres, fsize;
+ int32_t fstart;
+ unsigned char finfo;
+ int i, err = 0;
+ FILE *fp;
+ struct stat st;
+ char *sf_buf;
+ size_t sf_size;
+
+#define TEST(name, cond) \
+ do \
+ { \
+ if (cond) \
+ pass (name); \
+ else \
+ fail (name); \
+ } \
+ while (0)
+
+ fp = fopen (DATA, "r");
+ if (fp == NULL)
+ goto setup_fail;
+ if (fstat (fileno (fp), &st) < 0)
+ {
+ perror ("fstat");
+ fclose (fp);
+ goto setup_fail;
+ }
+ sf_buf = malloc (st.st_size);
+ if (sf_buf == NULL)
+ {
+ perror ("malloc");
+ goto setup_fail;
+ }
+
+ /* Execute tests. */
+ sf_size = fread (sf_buf, 1, st.st_size, fp);
+ fclose (fp);
+ TEST ("frecnt-2: Read section", sf_size != 0);
+
+ dctx = sframe_decode (sf_buf, sf_size, &err);
+ TEST ("frecnt-2: Decode setup", dctx != NULL);
+
+ unsigned int fde_cnt = sframe_decoder_get_num_fidx (dctx);
+ TEST ("frecnt-2: Decode FDE count", fde_cnt == 2);
+
+ for (i = 0; i < fde_cnt; ++i)
+ {
+ err = sframe_decoder_get_funcdesc (dctx, i, &nfres, &fsize, &fstart,
+ &finfo);
+ TEST ("frecnt-2: Decode get FDE", err == 0);
+ TEST ("frecnt-2: Decode get FRE", nfres == 4);
+ }
+
+ sframe_decoder_free (&dctx);
+ return 0;
+
+setup_fail:
+ sframe_decoder_free (&dctx);
+ fail ("frecnt-2: Test setup");
+ return 1;
+}
--
2.18.2
- [PATCH][gnulib] Add the Sframe package,
Weimin Pan <=
- Re: [PATCH][gnulib] Add the Sframe package, Bruno Haible, 2022/11/15
- Re: [PATCH][gnulib] Add the Sframe package, Jose E. Marchesi, 2022/11/16
- Re: [PATCH][gnulib] Add the Sframe package, Weimin Pan, 2022/11/17
- Re: [PATCH][gnulib] Add the Sframe package, Bruno Haible, 2022/11/17
- Re: [PATCH][gnulib] Add the Sframe package, Jeffrey Walton, 2022/11/17
- Re: [PATCH][gnulib] Add the Sframe package, Jose E. Marchesi, 2022/11/18
- Re: [PATCH][gnulib] Add the Sframe package, Bruno Haible, 2022/11/18
- Re: [PATCH][gnulib] Add the Sframe package, Weimin Pan, 2022/11/18
- Re: [PATCH][gnulib] Add the Sframe package, Bruno Haible, 2022/11/19
- Re: [PATCH][gnulib] Add the Sframe package, Weimin Pan, 2022/11/23