/* sframe.c - SFrame decoder/encoder. Copyright (C) 2022-2026 Free Software Foundation, Inc. This file is part of libsframe. 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 <stdarg.h>#include <string.h>#include <stddef.h>#include "sframe-impl.h"#include "swap.h" /* Representation of SFrame FDE internal to libsframe. */typedef struct sframe_func_desc_entry_int{ int64_t func_start_pc_offset; uint32_t func_size; uint32_t func_start_fre_off; uint32_t func_num_fres; uint8_t func_info; uint8_t func_info2; uint8_t func_rep_size;} sframe_func_desc_entry_int; struct sf_fde_tbl{ unsigned int count; unsigned int alloced; sframe_func_desc_entry_int entry[1];}; struct sf_fre_tbl{ unsigned int count; unsigned int alloced; sframe_frame_row_entry entry[1];}; #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. */ #define SFRAME_FRE_ALLOC_LEN 64static int number_of_entries = 64; static voidsframe_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 intsframe_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;} /* Allocate space for NUM_FDES number of SFrame FDEs of type sframe_func_desc_entry_int. This is version-unaware because this pertains to libsframe's internal in-memory representation of SFrame FDE. */ static intsframe_fde_tbl_alloc (sf_fde_tbl **fde_tbl, unsigned int num_fdes){ size_t fidx_size = num_fdes * sizeof (sframe_func_desc_entry_int); size_t fd_tbl_sz = (sizeof (sf_fde_tbl) + fidx_size); *fde_tbl = malloc (fd_tbl_sz); if (*fde_tbl == NULL) return SFRAME_ERR; (*fde_tbl)->alloced = num_fdes; return 0;} /* Initialize libsframe's internal representation of SFrame FDEs. */ static intsframe_fde_tbl_init (sf_fde_tbl *fde_tbl, const char *fde_buf, const char *fre_buf, size_t *fidx_size, unsigned int num_fdes, uint8_t ver){ if (ver == SFRAME_VERSION_3 && SFRAME_VERSION == SFRAME_VERSION_3) { *fidx_size = num_fdes * sizeof (sframe_func_desc_idx_v3); for (unsigned int i = 0; i < num_fdes; i++) { const sframe_func_desc_idx_v3 *fdep = (sframe_func_desc_idx_v3 *)fde_buf + i; fde_tbl->entry[i].func_start_pc_offset = fdep->sfdi_func_start_offset; fde_tbl->entry[i].func_size = fdep->sfdi_func_size; fde_tbl->entry[i].func_start_fre_off = fdep->sfdi_func_start_fre_off; /* V3 organizes the following data closer to the SFrame FREs for the function. Access them via the sfde_func_start_fre_off. */ const sframe_func_desc_attr_v3 *fattr = (sframe_func_desc_attr_v3 *)(fre_buf + fdep->sfdi_func_start_fre_off); fde_tbl->entry[i].func_num_fres = fattr->sfda_func_num_fres; fde_tbl->entry[i].func_info = fattr->sfda_func_info; fde_tbl->entry[i].func_info2 = fattr->sfda_func_info2; fde_tbl->entry[i].func_rep_size = fattr->sfda_func_rep_size; } fde_tbl->count = num_fdes; } /* If ver is not the latest, read buffer manually and upgrade from sframe_func_desc_entry_v2 to populate the sf_fde_tbl entries. */ else if (ver == SFRAME_VERSION_2 && SFRAME_VERSION == SFRAME_VERSION_3) { *fidx_size = num_fdes * sizeof (sframe_func_desc_entry_v2); for (unsigned int i = 0; i < num_fdes; i++) { const sframe_func_desc_entry_v2 *fdep = (sframe_func_desc_entry_v2 *)fde_buf + i; fde_tbl->entry[i].func_start_pc_offset = fdep->sfde_func_start_address; fde_tbl->entry[i].func_size = fdep->sfde_func_size; fde_tbl->entry[i].func_start_fre_off = fdep->sfde_func_start_fre_off; fde_tbl->entry[i].func_num_fres = fdep->sfde_func_num_fres; fde_tbl->entry[i].func_info = fdep->sfde_func_info; fde_tbl->entry[i].func_info2 = 0; fde_tbl->entry[i].func_rep_size = fdep->sfde_func_rep_size; } fde_tbl->count = num_fdes; } else { /* Not possible ATM. */ *fidx_size = 0; return SFRAME_ERR; } return 0;} /* Get the SFrame header size. */ static uint32_tsframe_get_hdr_size (const sframe_header *sfh){ return SFRAME_V1_HDR_SIZE (*sfh);} /* Access functions for frame row entry data. */ static uint8_tsframe_fre_get_dataword_count (uint8_t fre_info){ return SFRAME_V1_FRE_OFFSET_COUNT (fre_info);} static uint8_tsframe_fre_get_dataword_size (uint8_t fre_info){ return SFRAME_V1_FRE_OFFSET_SIZE (fre_info);} static boolsframe_get_fre_ra_mangled_p (uint8_t fre_info){ return SFRAME_V1_FRE_MANGLED_RA_P (fre_info);} static boolsframe_get_fre_ra_undefined_p (uint8_t fre_info){ return SFRAME_V2_FRE_RA_UNDEFINED_P (fre_info);} /* Access functions for info from function descriptor entry. */ static uint32_tsframe_get_fre_type (sframe_func_desc_entry_int *fdep){ uint32_t fre_type = 0; if (fdep) fre_type = SFRAME_V2_FUNC_FRE_TYPE (fdep->func_info); return fre_type;} static uint32_tsframe_get_fde_pc_type (sframe_func_desc_entry_int *fdep){ uint32_t fde_pc_type = 0; if (fdep) fde_pc_type = SFRAME_V2_FUNC_PC_TYPE (fdep->func_info); return fde_pc_type;} /* Check if flipping is needed, based on ENDIAN. */ static intneed_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: case SFRAME_ABI_S390X_ENDIAN_BIG: return is_little; default: break; } return 0;} /* Flip the endianness of the SFrame header starting at BUF. VER is the version of the SFrame data in the buffer. Returns SFRAME_ERR if any error. If error code is returned, the flipped header should not be used. */ static intflip_header (char *buf, uint8_t ver ATTRIBUTE_UNUSED){ /* SFrame header binary format has remained the same in SFRAME_VERSION_1, SFRAME_VERSION_2. */ sframe_header *sfh = (sframe_header *) buf; swap_thing (sfh->sfh_preamble.sfp_magic); swap_thing (sfh->sfh_preamble.sfp_version); swap_thing (sfh->sfh_preamble.sfp_flags); swap_thing (sfh->sfh_abi_arch); swap_thing (sfh->sfh_cfa_fixed_fp_offset); swap_thing (sfh->sfh_cfa_fixed_ra_offset); swap_thing (sfh->sfh_auxhdr_len); swap_thing (sfh->sfh_num_fdes); swap_thing (sfh->sfh_num_fres); swap_thing (sfh->sfh_fre_len); swap_thing (sfh->sfh_fdeoff); swap_thing (sfh->sfh_freoff); /* Alert for missing functionatlity. Auxiliary header, if present, needs to flipped based on per abi/arch semantics. */ if (sfh->sfh_auxhdr_len) return SFRAME_ERR; return 0;} /* Endian flip the SFrame FDE at BUF (buffer size provided in BUF_SIZE), given the SFrame version VER. Update the FDE_SIZE to the size of the SFrame FDE flipped. Return SFRAME_ERR if any error. If error code is returned, the flipped FDEP should not be used. */ static intflip_fde_desc (char *buf, size_t buf_size, uint8_t ver){ if (ver == SFRAME_VERSION_3) { if (buf_size < sizeof (sframe_func_desc_idx_v3)) return SFRAME_ERR; sframe_func_desc_idx_v3 *fdep = (sframe_func_desc_idx_v3 *) buf; swap_thing (fdep->sfdi_func_start_offset); swap_thing (fdep->sfdi_func_size); swap_thing (fdep->sfdi_func_start_fre_off); } else if (ver == SFRAME_VERSION_2) { if (buf_size < sizeof (sframe_func_desc_entry_v2)) return SFRAME_ERR; sframe_func_desc_entry_v2 *fdep = (sframe_func_desc_entry_v2 *) buf; 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); } else return SFRAME_ERR; return 0;} static intflip_fde_attr_v3 (char *buf, size_t buf_size){ if (buf_size < sizeof (sframe_func_desc_attr_v3)) return SFRAME_ERR; /* sfda_func_num_fres is the first member of sframe_func_desc_attr_v3. */ struct { uint16_t x; } ATTRIBUTE_PACKED *p = (void*)buf; swap_thing (p->x); return 0;}/* Check if SFrame header has valid data. */ static boolsframe_header_sanity_check_p (const sframe_header *hp){ /* Check preamble is valid. */ if (hp->sfh_preamble.sfp_magic != SFRAME_MAGIC || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_1 && hp->sfh_preamble.sfp_version != SFRAME_VERSION_2 && hp->sfh_preamble.sfp_version != SFRAME_VERSION_3)) return false; /* Check flags (version-aware). Do not validate V3 headers against V2 flag definitions, as V3 may introduce new flags. */ uint8_t valid_flags = SFRAME_V2_F_ALL_FLAGS; if (hp->sfh_preamble.sfp_version == SFRAME_VERSION_3) /* Replace with SFRAME_V3_F_ALL_FLAGS. */ valid_flags = SFRAME_V3_F_ALL_FLAGS; if (hp->sfh_preamble.sfp_flags & ~valid_flags) return false; /* Check offsets are valid. */ if (hp->sfh_fdeoff > hp->sfh_freoff) return false; return true;} /* Flip the start address pointed to by FP. */ static voidflip_fre_start_address (void *addr, uint32_t fre_type){ if (fre_type == SFRAME_FRE_TYPE_ADDR2) { struct { uint16_t x; } ATTRIBUTE_PACKED *p = addr; swap_thing (p->x); } else if (fre_type == SFRAME_FRE_TYPE_ADDR4) { struct { uint32_t x; } ATTRIBUTE_PACKED *p = addr; swap_thing (p->x); }} static voidflip_fre_datawords (void *datawords, uint8_t dataword_size, uint8_t dataword_cnt){ int j; if (dataword_size == SFRAME_FRE_DATAWORD_2B) { struct { uint16_t x; } ATTRIBUTE_PACKED *p = datawords; for (j = dataword_cnt; j > 0; p++, j--) swap_thing (p->x); } else if (dataword_size == SFRAME_FRE_DATAWORD_4B) { struct { uint32_t x; } ATTRIBUTE_PACKED *p = datawords; for (j = dataword_cnt; j > 0; p++, j--) swap_thing (p->x); }} /* Get the FRE start address size, given the FRE_TYPE. */ static size_tsframe_fre_start_addr_size (uint32_t 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 boolsframe_fre_sanity_check_p (const sframe_frame_row_entry *frep){ uint8_t dataword_size, dataword_cnt; uint8_t fre_info; if (frep == NULL) return false; fre_info = frep->fre_info; dataword_size = sframe_fre_get_dataword_size (fre_info); if (dataword_size != SFRAME_FRE_DATAWORD_1B && dataword_size != SFRAME_FRE_DATAWORD_2B && dataword_size != SFRAME_FRE_DATAWORD_4B) return false; dataword_cnt = sframe_fre_get_dataword_count (fre_info); if (dataword_cnt > MAX_NUM_DATAWORDS) return false; return true;} /* Get FRE_INFO's data words' size in bytes. */ static size_tsframe_fre_datawords_bytes_size (uint8_t fre_info){ uint8_t dataword_size, dataword_cnt; dataword_size = sframe_fre_get_dataword_size (fre_info); debug_printf ("dataword_size = %u\n", dataword_size); dataword_cnt = sframe_fre_get_dataword_count (fre_info); if (dataword_size == SFRAME_FRE_DATAWORD_2B || dataword_size == SFRAME_FRE_DATAWORD_4B) /* 2 or 4 bytes. */ return (dataword_cnt * (dataword_size * 2)); return dataword_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_tsframe_fre_entry_size (sframe_frame_row_entry *frep, uint32_t fre_type){Lines 1–500 of 2755. Load more
/* sframe.c - SFrame decoder/encoder. Copyright (C) 2022-2026 Free Software Foundation, Inc. This file is part of libsframe. 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 <stdarg.h> #include <string.h> #include <stddef.h> #include "sframe-impl.h" #include "swap.h" /* Representation of SFrame FDE internal to libsframe. */ typedef struct sframe_func_desc_entry_int { int64_t func_start_pc_offset; uint32_t func_size; uint32_t func_start_fre_off; uint32_t func_num_fres; uint8_t func_info; uint8_t func_info2; uint8_t func_rep_size; } sframe_func_desc_entry_int; struct sf_fde_tbl { unsigned int count; unsigned int alloced; sframe_func_desc_entry_int entry[1]; }; struct sf_fre_tbl { unsigned int count; unsigned int alloced; sframe_frame_row_entry entry[1]; }; #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. */ #define SFRAME_FRE_ALLOC_LEN 64 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; } /* Allocate space for NUM_FDES number of SFrame FDEs of type sframe_func_desc_entry_int. This is version-unaware because this pertains to libsframe's internal in-memory representation of SFrame FDE. */ static int sframe_fde_tbl_alloc (sf_fde_tbl **fde_tbl, unsigned int num_fdes) { size_t fidx_size = num_fdes * sizeof (sframe_func_desc_entry_int); size_t fd_tbl_sz = (sizeof (sf_fde_tbl) + fidx_size); *fde_tbl = malloc (fd_tbl_sz); if (*fde_tbl == NULL) return SFRAME_ERR; (*fde_tbl)->alloced = num_fdes; return 0; } /* Initialize libsframe's internal representation of SFrame FDEs. */ static int sframe_fde_tbl_init (sf_fde_tbl *fde_tbl, const char *fde_buf, const char *fre_buf, size_t *fidx_size, unsigned int num_fdes, uint8_t ver) { if (ver == SFRAME_VERSION_3 && SFRAME_VERSION == SFRAME_VERSION_3) { *fidx_size = num_fdes * sizeof (sframe_func_desc_idx_v3); for (unsigned int i = 0; i < num_fdes; i++) { const sframe_func_desc_idx_v3 *fdep = (sframe_func_desc_idx_v3 *)fde_buf + i; fde_tbl->entry[i].func_start_pc_offset = fdep->sfdi_func_start_offset; fde_tbl->entry[i].func_size = fdep->sfdi_func_size; fde_tbl->entry[i].func_start_fre_off = fdep->sfdi_func_start_fre_off; /* V3 organizes the following data closer to the SFrame FREs for the function. Access them via the sfde_func_start_fre_off. */ const sframe_func_desc_attr_v3 *fattr = (sframe_func_desc_attr_v3 *)(fre_buf + fdep->sfdi_func_start_fre_off); fde_tbl->entry[i].func_num_fres = fattr->sfda_func_num_fres; fde_tbl->entry[i].func_info = fattr->sfda_func_info; fde_tbl->entry[i].func_info2 = fattr->sfda_func_info2; fde_tbl->entry[i].func_rep_size = fattr->sfda_func_rep_size; } fde_tbl->count = num_fdes; } /* If ver is not the latest, read buffer manually and upgrade from sframe_func_desc_entry_v2 to populate the sf_fde_tbl entries. */ else if (ver == SFRAME_VERSION_2 && SFRAME_VERSION == SFRAME_VERSION_3) { *fidx_size = num_fdes * sizeof (sframe_func_desc_entry_v2); for (unsigned int i = 0; i < num_fdes; i++) { const sframe_func_desc_entry_v2 *fdep = (sframe_func_desc_entry_v2 *)fde_buf + i; fde_tbl->entry[i].func_start_pc_offset = fdep->sfde_func_start_address; fde_tbl->entry[i].func_size = fdep->sfde_func_size; fde_tbl->entry[i].func_start_fre_off = fdep->sfde_func_start_fre_off; fde_tbl->entry[i].func_num_fres = fdep->sfde_func_num_fres; fde_tbl->entry[i].func_info = fdep->sfde_func_info; fde_tbl->entry[i].func_info2 = 0; fde_tbl->entry[i].func_rep_size = fdep->sfde_func_rep_size; } fde_tbl->count = num_fdes; } else { /* Not possible ATM. */ *fidx_size = 0; return SFRAME_ERR; } return 0; } /* Get the SFrame header size. */ static uint32_t sframe_get_hdr_size (const sframe_header *sfh) { return SFRAME_V1_HDR_SIZE (*sfh); } /* Access functions for frame row entry data. */ static uint8_t sframe_fre_get_dataword_count (uint8_t fre_info) { return SFRAME_V1_FRE_OFFSET_COUNT (fre_info); } static uint8_t sframe_fre_get_dataword_size (uint8_t fre_info) { return SFRAME_V1_FRE_OFFSET_SIZE (fre_info); } static bool sframe_get_fre_ra_mangled_p (uint8_t fre_info) { return SFRAME_V1_FRE_MANGLED_RA_P (fre_info); } static bool sframe_get_fre_ra_undefined_p (uint8_t fre_info) { return SFRAME_V2_FRE_RA_UNDEFINED_P (fre_info); } /* Access functions for info from function descriptor entry. */ static uint32_t sframe_get_fre_type (sframe_func_desc_entry_int *fdep) { uint32_t fre_type = 0; if (fdep) fre_type = SFRAME_V2_FUNC_FRE_TYPE (fdep->func_info); return fre_type; } static uint32_t sframe_get_fde_pc_type (sframe_func_desc_entry_int *fdep) { uint32_t fde_pc_type = 0; if (fdep) fde_pc_type = SFRAME_V2_FUNC_PC_TYPE (fdep->func_info); return fde_pc_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: case SFRAME_ABI_S390X_ENDIAN_BIG: return is_little; default: break; } return 0; } /* Flip the endianness of the SFrame header starting at BUF. VER is the version of the SFrame data in the buffer. Returns SFRAME_ERR if any error. If error code is returned, the flipped header should not be used. */ static int flip_header (char *buf, uint8_t ver ATTRIBUTE_UNUSED) { /* SFrame header binary format has remained the same in SFRAME_VERSION_1, SFRAME_VERSION_2. */ sframe_header *sfh = (sframe_header *) buf; swap_thing (sfh->sfh_preamble.sfp_magic); swap_thing (sfh->sfh_preamble.sfp_version); swap_thing (sfh->sfh_preamble.sfp_flags); swap_thing (sfh->sfh_abi_arch); swap_thing (sfh->sfh_cfa_fixed_fp_offset); swap_thing (sfh->sfh_cfa_fixed_ra_offset); swap_thing (sfh->sfh_auxhdr_len); swap_thing (sfh->sfh_num_fdes); swap_thing (sfh->sfh_num_fres); swap_thing (sfh->sfh_fre_len); swap_thing (sfh->sfh_fdeoff); swap_thing (sfh->sfh_freoff); /* Alert for missing functionatlity. Auxiliary header, if present, needs to flipped based on per abi/arch semantics. */ if (sfh->sfh_auxhdr_len) return SFRAME_ERR; return 0; } /* Endian flip the SFrame FDE at BUF (buffer size provided in BUF_SIZE), given the SFrame version VER. Update the FDE_SIZE to the size of the SFrame FDE flipped. Return SFRAME_ERR if any error. If error code is returned, the flipped FDEP should not be used. */ static int flip_fde_desc (char *buf, size_t buf_size, uint8_t ver) { if (ver == SFRAME_VERSION_3) { if (buf_size < sizeof (sframe_func_desc_idx_v3)) return SFRAME_ERR; sframe_func_desc_idx_v3 *fdep = (sframe_func_desc_idx_v3 *) buf; swap_thing (fdep->sfdi_func_start_offset); swap_thing (fdep->sfdi_func_size); swap_thing (fdep->sfdi_func_start_fre_off); } else if (ver == SFRAME_VERSION_2) { if (buf_size < sizeof (sframe_func_desc_entry_v2)) return SFRAME_ERR; sframe_func_desc_entry_v2 *fdep = (sframe_func_desc_entry_v2 *) buf; 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); } else return SFRAME_ERR; return 0; } static int flip_fde_attr_v3 (char *buf, size_t buf_size) { if (buf_size < sizeof (sframe_func_desc_attr_v3)) return SFRAME_ERR; /* sfda_func_num_fres is the first member of sframe_func_desc_attr_v3. */ struct { uint16_t x; } ATTRIBUTE_PACKED *p = (void*)buf; swap_thing (p->x); return 0; } /* Check if SFrame header has valid data. */ static bool sframe_header_sanity_check_p (const sframe_header *hp) { /* Check preamble is valid. */ if (hp->sfh_preamble.sfp_magic != SFRAME_MAGIC || (hp->sfh_preamble.sfp_version != SFRAME_VERSION_1 && hp->sfh_preamble.sfp_version != SFRAME_VERSION_2 && hp->sfh_preamble.sfp_version != SFRAME_VERSION_3)) return false; /* Check flags (version-aware). Do not validate V3 headers against V2 flag definitions, as V3 may introduce new flags. */ uint8_t valid_flags = SFRAME_V2_F_ALL_FLAGS; if (hp->sfh_preamble.sfp_version == SFRAME_VERSION_3) /* Replace with SFRAME_V3_F_ALL_FLAGS. */ valid_flags = SFRAME_V3_F_ALL_FLAGS; if (hp->sfh_preamble.sfp_flags & ~valid_flags) return false; /* Check offsets are valid. */ if (hp->sfh_fdeoff > hp->sfh_freoff) return false; return true; } /* Flip the start address pointed to by FP. */ static void flip_fre_start_address (void *addr, uint32_t fre_type) { if (fre_type == SFRAME_FRE_TYPE_ADDR2) { struct { uint16_t x; } ATTRIBUTE_PACKED *p = addr; swap_thing (p->x); } else if (fre_type == SFRAME_FRE_TYPE_ADDR4) { struct { uint32_t x; } ATTRIBUTE_PACKED *p = addr; swap_thing (p->x); } } static void flip_fre_datawords (void *datawords, uint8_t dataword_size, uint8_t dataword_cnt) { int j; if (dataword_size == SFRAME_FRE_DATAWORD_2B) { struct { uint16_t x; } ATTRIBUTE_PACKED *p = datawords; for (j = dataword_cnt; j > 0; p++, j--) swap_thing (p->x); } else if (dataword_size == SFRAME_FRE_DATAWORD_4B) { struct { uint32_t x; } ATTRIBUTE_PACKED *p = datawords; for (j = dataword_cnt; j > 0; p++, j--) swap_thing (p->x); } } /* Get the FRE start address size, given the FRE_TYPE. */ static size_t sframe_fre_start_addr_size (uint32_t 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 bool sframe_fre_sanity_check_p (const sframe_frame_row_entry *frep) { uint8_t dataword_size, dataword_cnt; uint8_t fre_info; if (frep == NULL) return false; fre_info = frep->fre_info; dataword_size = sframe_fre_get_dataword_size (fre_info); if (dataword_size != SFRAME_FRE_DATAWORD_1B && dataword_size != SFRAME_FRE_DATAWORD_2B && dataword_size != SFRAME_FRE_DATAWORD_4B) return false; dataword_cnt = sframe_fre_get_dataword_count (fre_info); if (dataword_cnt > MAX_NUM_DATAWORDS) return false; return true; } /* Get FRE_INFO's data words' size in bytes. */ static size_t sframe_fre_datawords_bytes_size (uint8_t fre_info) { uint8_t dataword_size, dataword_cnt; dataword_size = sframe_fre_get_dataword_size (fre_info); debug_printf ("dataword_size = %u\n", dataword_size); dataword_cnt = sframe_fre_get_dataword_count (fre_info); if (dataword_size == SFRAME_FRE_DATAWORD_2B || dataword_size == SFRAME_FRE_DATAWORD_4B) /* 2 or 4 bytes. */ return (dataword_cnt * (dataword_size * 2)); return dataword_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, uint32_t fre_type) {