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 <[email protected]> + Add the SFrame package. + 2022-10-16 Bruno Haible <[email protected]> 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
