|  | /* sframe.c - SFrame decoder/encoder. | 
|  |  | 
|  | Copyright (C) 2022-2025 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" | 
|  |  | 
|  | struct sf_fde_tbl | 
|  | { | 
|  | unsigned int count; | 
|  | unsigned int alloced; | 
|  | sframe_func_desc_entry 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.  */ | 
|  | 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 uint8_t | 
|  | sframe_fre_get_offset_count (uint8_t fre_info) | 
|  | { | 
|  | return SFRAME_V1_FRE_OFFSET_COUNT (fre_info); | 
|  | } | 
|  |  | 
|  | static uint8_t | 
|  | sframe_fre_get_offset_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); | 
|  | } | 
|  |  | 
|  | /* Access functions for info from function descriptor entry.  */ | 
|  |  | 
|  | static uint32_t | 
|  | sframe_get_fre_type (sframe_func_desc_entry *fdep) | 
|  | { | 
|  | uint32_t fre_type = 0; | 
|  | if (fdep) | 
|  | fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info); | 
|  | return fre_type; | 
|  | } | 
|  |  | 
|  | static uint32_t | 
|  | sframe_get_fde_type (sframe_func_desc_entry *fdep) | 
|  | { | 
|  | uint32_t 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: | 
|  | case SFRAME_ABI_S390X_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 bool | 
|  | sframe_header_sanity_check_p (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_flags & ~SFRAME_V2_F_ALL_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 (char *addr, uint32_t fre_type) | 
|  | { | 
|  | if (fre_type == SFRAME_FRE_TYPE_ADDR2) | 
|  | { | 
|  | uint16_t *start_addr = (uint16_t *)addr; | 
|  | swap_thing (*start_addr); | 
|  | } | 
|  | else if (fre_type == SFRAME_FRE_TYPE_ADDR4) | 
|  | { | 
|  | uint32_t *start_addr = (uint32_t *)addr; | 
|  | swap_thing (*start_addr); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | flip_fre_stack_offsets (char *offsets, uint8_t offset_size, uint8_t offset_cnt) | 
|  | { | 
|  | int j; | 
|  |  | 
|  | if (offset_size == SFRAME_FRE_OFFSET_2B) | 
|  | { | 
|  | uint16_t *ust = (uint16_t *)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 (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 (sframe_frame_row_entry *frep) | 
|  | { | 
|  | uint8_t offset_size, offset_cnt; | 
|  | uint8_t fre_info; | 
|  |  | 
|  | if (frep == NULL) | 
|  | return false; | 
|  |  | 
|  | 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 false; | 
|  |  | 
|  | offset_cnt = sframe_fre_get_offset_count (fre_info); | 
|  | if (offset_cnt > MAX_NUM_STACK_OFFSETS) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Get FRE_INFO's offset size in bytes.  */ | 
|  |  | 
|  | static size_t | 
|  | sframe_fre_offset_bytes_size (uint8_t fre_info) | 
|  | { | 
|  | uint8_t 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, uint32_t fre_type) | 
|  | { | 
|  | if (frep == NULL) | 
|  | return 0; | 
|  |  | 
|  | uint8_t 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)); | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | sframe_func_desc_entry *fdep; | 
|  | uint32_t num_fdes; | 
|  | int err; | 
|  |  | 
|  | num_fdes = sframe_decoder_get_num_fidx (ctx); | 
|  | if (num_fdes == 0 | 
|  | || func_idx >= num_fdes | 
|  | || ctx->sfd_funcdesc == NULL) | 
|  | return sframe_ret_set_errno (&err, SFRAME_ERR_DCTX_INVAL); | 
|  |  | 
|  | fdep = &ctx->sfd_funcdesc[func_idx]; | 
|  | return fdep; | 
|  | } | 
|  |  | 
|  | /* Get the offset of the start PC of the SFrame FDE at FUNC_IDX from the start | 
|  | of the SFrame section.  This section-relative offset is used within | 
|  | libsframe for sorting the SFrame FDEs, and also information lookup routines | 
|  | like sframe_find_fre. | 
|  |  | 
|  | If FUNC_IDX is not a valid index in the given decoder object, returns 0.  */ | 
|  |  | 
|  | static int32_t | 
|  | sframe_decoder_get_secrel_func_start_addr (sframe_decoder_ctx *dctx, | 
|  | uint32_t func_idx) | 
|  | { | 
|  | int err = 0; | 
|  | int32_t offsetof_fde_in_sec | 
|  | = sframe_decoder_get_offsetof_fde_start_addr (dctx, func_idx, &err); | 
|  | /* If func_idx is not a valid index, return 0.  */ | 
|  | if (err) | 
|  | return 0; | 
|  |  | 
|  | int32_t func_start_addr = dctx->sfd_funcdesc[func_idx].sfde_func_start_address; | 
|  |  | 
|  | return func_start_addr + offsetof_fde_in_sec; | 
|  | } | 
|  |  | 
|  | /* Check whether for the given FDEP, the SFrame Frame Row Entry identified via | 
|  | the START_IP_OFFSET and the END_IP_OFFSET, provides the stack trace | 
|  | information for the PC.  */ | 
|  |  | 
|  | static bool | 
|  | sframe_fre_check_range_p (sframe_decoder_ctx *dctx, uint32_t func_idx, | 
|  | uint32_t start_ip_offset, uint32_t end_ip_offset, | 
|  | int32_t pc) | 
|  | { | 
|  | sframe_func_desc_entry *fdep; | 
|  | int32_t func_start_addr; | 
|  | uint8_t rep_block_size; | 
|  | uint32_t fde_type; | 
|  | uint32_t pc_offset; | 
|  | bool mask_p; | 
|  |  | 
|  | fdep = &dctx->sfd_funcdesc[func_idx]; | 
|  | func_start_addr = sframe_decoder_get_secrel_func_start_addr (dctx, func_idx); | 
|  | fde_type = sframe_get_fde_type (fdep); | 
|  | mask_p = (fde_type == SFRAME_FDE_TYPE_PCMASK); | 
|  | rep_block_size = fdep->sfde_func_rep_size; | 
|  |  | 
|  | if (func_start_addr > pc) | 
|  | return false; | 
|  |  | 
|  | /* Given func_start_addr <= pc, pc - func_start_addr must be positive.  */ | 
|  | pc_offset = pc - func_start_addr; | 
|  | /* For SFrame FDEs encoding information for repetitive pattern of insns, | 
|  | masking with the rep_block_size is necessary to find the matching FRE.  */ | 
|  | if (mask_p) | 
|  | pc_offset = pc_offset % rep_block_size; | 
|  |  | 
|  | return (start_ip_offset <= pc_offset) && (end_ip_offset >= pc_offset); | 
|  | } | 
|  |  | 
|  | static int | 
|  | flip_fre (char *fp, uint32_t fre_type, size_t *fre_size) | 
|  | { | 
|  | uint8_t fre_info; | 
|  | uint8_t 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 uint8_t.  No need to flip.  */ | 
|  | fre_info = *(uint8_t*)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 (uint8_t); | 
|  | 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 = 0; | 
|  | unsigned int num_fres = 0; | 
|  | uint32_t fre_type = 0; | 
|  | uint32_t fre_offset = 0; | 
|  | size_t esz = 0; | 
|  | size_t hdrsz = 0; | 
|  | int err = 0; | 
|  | /* For error checking.  */ | 
|  | size_t bytes_flipped = 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.  */ | 
|  | hdrsz = sframe_get_hdr_size (ihp); | 
|  | num_fdes = ihp->sfh_num_fdes; | 
|  | fdes = frame_buf + hdrsz + ihp->sfh_fdeoff; | 
|  | fdep = (sframe_func_desc_entry *)fdes; | 
|  |  | 
|  | j = 0; | 
|  | prev_frep_index = 0; | 
|  | for (i = 0; i < num_fdes; fdep++, i++) | 
|  | { | 
|  | if ((char*)fdep >= (frame_buf + buf_size)) | 
|  | goto bad; | 
|  |  | 
|  | 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); | 
|  | bytes_flipped += sizeof (sframe_func_desc_entry); | 
|  |  | 
|  | 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 + hdrsz + ihp->sfh_freoff; | 
|  | fp += fre_offset; | 
|  | for (; j < prev_frep_index + num_fres; j++) | 
|  | { | 
|  | if (flip_fre (fp, fre_type, &esz)) | 
|  | goto bad; | 
|  | bytes_flipped += esz; | 
|  |  | 
|  | if (esz == 0 || esz > buf_size) | 
|  | goto bad; | 
|  | fp += esz; | 
|  | } | 
|  | prev_frep_index = j; | 
|  | } | 
|  | /* All FDEs and FREs must have been endian flipped by now.  */ | 
|  | if ((j != ihp->sfh_num_fres) || (bytes_flipped > (buf_size - hdrsz))) | 
|  | goto bad; | 
|  | /* Optional trailing section padding.  */ | 
|  | for (fp = frame_buf + hdrsz + bytes_flipped; fp < frame_buf + buf_size; fp++) | 
|  | if (*fp != '\0') | 
|  | goto bad; | 
|  |  | 
|  | /* Success.  */ | 
|  | return 0; | 
|  | bad: | 
|  | return SFRAME_ERR; | 
|  | } | 
|  |  | 
|  | /* The SFrame Decoder.  */ | 
|  |  | 
|  | /* Get SFrame header from the given decoder context DCTX.  */ | 
|  |  | 
|  | static sframe_header * | 
|  | sframe_decoder_get_header (sframe_decoder_ctx *dctx) | 
|  | { | 
|  | sframe_header *hp = NULL; | 
|  | if (dctx != NULL) | 
|  | hp = &dctx->sfd_header; | 
|  | return hp; | 
|  | } | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | uint8_t 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 **dctxp) | 
|  | { | 
|  | if (dctxp != NULL) | 
|  | { | 
|  | sframe_decoder_ctx *dctx = *dctxp; | 
|  | 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; | 
|  | } | 
|  | if (dctx->sfd_buf != NULL) | 
|  | { | 
|  | free (dctx->sfd_buf); | 
|  | dctx->sfd_buf = NULL; | 
|  | } | 
|  |  | 
|  | free (*dctxp); | 
|  | *dctxp = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Create an 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_create_func_info (uint32_t fre_type, | 
|  | uint32_t 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?  */ | 
|  |  | 
|  | uint32_t | 
|  | sframe_calc_fre_type (size_t func_size) | 
|  | { | 
|  | uint32_t fre_type = 0; | 
|  | if (func_size < SFRAME_FRE_TYPE_ADDR1_LIMIT) | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR1; | 
|  | else if (func_size < SFRAME_FRE_TYPE_ADDR2_LIMIT) | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR2; | 
|  | /* Adjust the check a bit so that it remains warning-free but meaningful | 
|  | on 32-bit systems.  */ | 
|  | else if (func_size <= (size_t) (SFRAME_FRE_TYPE_ADDR4_LIMIT - 1)) | 
|  | fre_type = SFRAME_FRE_TYPE_ADDR4; | 
|  | return fre_type; | 
|  | } | 
|  |  | 
|  | /* Get the base reg id from the FRE info.  Set errp if failure.  */ | 
|  |  | 
|  | uint8_t | 
|  | 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); | 
|  |  | 
|  | uint8_t 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_decoder_ctx *dctx, | 
|  | sframe_frame_row_entry *fre, int *errp) | 
|  | { | 
|  | int32_t offset = sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp); | 
|  |  | 
|  | /* For s390x undo adjustment of CFA offset (to enable 8-bit offsets).  */ | 
|  | if (sframe_decoder_get_abi_arch (dctx) == SFRAME_ABI_S390X_ENDIAN_BIG) | 
|  | offset = SFRAME_V2_S390X_CFA_OFFSET_DECODE (offset); | 
|  |  | 
|  | return offset; | 
|  | } | 
|  |  | 
|  | /* Get the FP offset from the FRE.  If the offset is invalid, sets errp. | 
|  |  | 
|  | For s390x the offset may be an encoded register number, indicated by | 
|  | LSB set to one, which is only valid in the topmost frame.  */ | 
|  |  | 
|  | int32_t | 
|  | sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, | 
|  | sframe_frame_row_entry *fre, int *errp) | 
|  | { | 
|  | uint32_t fp_offset_idx = 0; | 
|  | int8_t fp_offset = sframe_decoder_get_fixed_fp_offset (dctx); | 
|  | /* If the FP offset is not being tracked, return the fixed FP offset | 
|  | from the SFrame header.  */ | 
|  | if (fp_offset != SFRAME_CFA_FIXED_FP_INVALID) | 
|  | { | 
|  | if (errp) | 
|  | *errp = 0; | 
|  | return fp_offset; | 
|  | } | 
|  |  | 
|  | /* In some ABIs, the stack offset to recover RA (using the CFA) from is | 
|  | fixed (like AMD64).  In such cases, the stack offset to recover FP will | 
|  | appear at the second index.  */ | 
|  | fp_offset_idx = ((sframe_decoder_get_fixed_ra_offset (dctx) | 
|  | != SFRAME_CFA_FIXED_RA_INVALID) | 
|  | ? SFRAME_FRE_RA_OFFSET_IDX | 
|  | : SFRAME_FRE_FP_OFFSET_IDX); | 
|  | return sframe_get_fre_offset (fre, fp_offset_idx, errp); | 
|  | } | 
|  |  | 
|  | /* Get the RA offset from the FRE.  If the offset is invalid, sets errp. | 
|  |  | 
|  | For s390x an RA offset value of SFRAME_FRE_RA_OFFSET_INVALID indicates | 
|  | that the RA is not saved, which is only valid in the topmost frame. | 
|  | For s390x the offset may be an encoded register number, indicated by | 
|  | LSB set to one, which is only valid in the topmost frame.  */ | 
|  |  | 
|  | int32_t | 
|  | sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx, | 
|  | sframe_frame_row_entry *fre, int *errp) | 
|  | { | 
|  | int8_t ra_offset = sframe_decoder_get_fixed_ra_offset (dctx); | 
|  | /* If the RA offset was not being tracked, return the fixed RA offset | 
|  | from the SFrame header.  */ | 
|  | if (ra_offset != SFRAME_CFA_FIXED_RA_INVALID) | 
|  | { | 
|  | if (errp) | 
|  | *errp = 0; | 
|  | return ra_offset; | 
|  | } | 
|  |  | 
|  | /* Otherwise, get the RA offset from the FRE.  */ | 
|  | return sframe_get_fre_offset (fre, SFRAME_FRE_RA_OFFSET_IDX, errp); | 
|  | } | 
|  |  | 
|  | /* Get whether the RA is mangled.  */ | 
|  |  | 
|  | bool | 
|  | sframe_fre_get_ra_mangled_p (sframe_decoder_ctx *dctx ATTRIBUTE_UNUSED, | 
|  | sframe_frame_row_entry *fre, int *errp) | 
|  | { | 
|  | if (fre == NULL || !sframe_fre_sanity_check_p (fre)) | 
|  | return sframe_set_errno (errp, SFRAME_ERR_FRE_INVAL); | 
|  |  | 
|  | return sframe_get_fre_ra_mangled_p (fre->fre_info); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Decode the SFrame FRE start address offset value from FRE_BUF in on-disk | 
|  | binary format, given the FRE_TYPE.  Updates the FRE_START_ADDR. | 
|  |  | 
|  | Returns 0 on success, SFRAME_ERR otherwise.  */ | 
|  |  | 
|  | static int | 
|  | sframe_decode_fre_start_address (const char *fre_buf, | 
|  | uint32_t *fre_start_addr, | 
|  | uint32_t fre_type) | 
|  | { | 
|  | uint32_t saddr = 0; | 
|  | int err = 0; | 
|  | size_t addr_size = 0; | 
|  |  | 
|  | addr_size = sframe_fre_start_addr_size (fre_type); | 
|  |  | 
|  | 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; | 
|  | /* SFrame is an unaligned on-disk format.  Using memcpy helps avoid the | 
|  | use of undesirable unaligned loads.  See PR libsframe/29856.  */ | 
|  | uint16_t tmp = 0; | 
|  | memcpy (&tmp, ust, addr_size); | 
|  | saddr = (uint32_t)tmp; | 
|  | } | 
|  | else if (fre_type == SFRAME_FRE_TYPE_ADDR4) | 
|  | { | 
|  | uint32_t *uit = (uint32_t *)fre_buf; | 
|  | uint32_t tmp = 0; | 
|  | memcpy (&tmp, uit, addr_size); | 
|  | saddr = (uint32_t)tmp; | 
|  | } | 
|  | 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, | 
|  | uint32_t fre_type, size_t *esz) | 
|  | { | 
|  | int err = 0; | 
|  | const char *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 = *(uint8_t *)(fre_buf + addr_size); | 
|  | /* Sanity check as the API works closely with the binary format.  */ | 
|  | sframe_assert (sizeof (fre->fre_info) == sizeof (uint8_t)); | 
|  |  | 
|  | /* 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 = 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 SF_BUF of size SF_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; | 
|  | /* This buffer is malloc'd when endian flipping the contents of the input | 
|  | buffer are needed.  Keep a reference to it so it can be free'd up | 
|  | later in sframe_decoder_free ().  */ | 
|  | dctx->sfd_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_BUF_INVAL); | 
|  | 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 = (char *) 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 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 DCTX.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_decoder_get_abi_arch (sframe_decoder_ctx *dctx) | 
|  | { | 
|  | sframe_header *sframe_header; | 
|  | sframe_header = sframe_decoder_get_header (dctx); | 
|  | return sframe_header->sfh_abi_arch; | 
|  | } | 
|  |  | 
|  | /* Get the format version from the SFrame decoder context DCTX.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_decoder_get_version (sframe_decoder_ctx *dctx) | 
|  | { | 
|  | sframe_header *dhp; | 
|  | dhp = sframe_decoder_get_header (dctx); | 
|  | return dhp->sfh_preamble.sfp_version; | 
|  | } | 
|  |  | 
|  | /* Get the section flags from the SFrame decoder context DCTX.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_decoder_get_flags (sframe_decoder_ctx *dctx) | 
|  | { | 
|  | const sframe_header *dhp = sframe_decoder_get_header (dctx); | 
|  | return dhp->sfh_preamble.sfp_flags; | 
|  | } | 
|  |  | 
|  | /* Get the SFrame's fixed FP offset given the decoder context CTX.  */ | 
|  | int8_t | 
|  | sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Get the offset of the sfde_func_start_address field (from the start of the | 
|  | on-disk layout of the SFrame section) of the FDE at FUNC_IDX in the decoder | 
|  | context DCTX. | 
|  |  | 
|  | If FUNC_IDX is more than the number of SFrame FDEs in the section, sets | 
|  | error code in ERRP, but returns the (hypothetical) offset.  This is useful | 
|  | for the linker when arranging input FDEs into the output section to be | 
|  | emitted.  */ | 
|  |  | 
|  | uint32_t | 
|  | sframe_decoder_get_offsetof_fde_start_addr (sframe_decoder_ctx *dctx, | 
|  | uint32_t func_idx, int *errp) | 
|  | { | 
|  | if (func_idx >= sframe_decoder_get_num_fidx (dctx)) | 
|  | sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTFOUND); | 
|  | else if (errp) | 
|  | *errp = 0; | 
|  |  | 
|  | return (sframe_decoder_get_hdr_size (dctx) | 
|  | + func_idx * sizeof (sframe_func_desc_entry) | 
|  | + offsetof (sframe_func_desc_entry, sfde_func_start_address)); | 
|  | } | 
|  |  | 
|  | /* Find the function descriptor entry starting which contains the specified | 
|  | address ADDR.  */ | 
|  |  | 
|  | static sframe_func_desc_entry * | 
|  | sframe_get_funcdesc_with_addr_internal (sframe_decoder_ctx *ctx, int32_t addr, | 
|  | int *errp, uint32_t *func_idx) | 
|  | { | 
|  | sframe_header *dhp; | 
|  | sframe_func_desc_entry *fdp; | 
|  | int low, high; | 
|  |  | 
|  | 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 ((sframe_decoder_get_flags (ctx) & 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 - 1; | 
|  | while (low <= high) | 
|  | { | 
|  | int mid = low + (high - low) / 2; | 
|  |  | 
|  | /* Given sfde_func_start_address <= addr, | 
|  | addr - sfde_func_start_address must be positive.  */ | 
|  | if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) <= addr | 
|  | && ((uint32_t)(addr - sframe_decoder_get_secrel_func_start_addr (ctx, | 
|  | mid)) | 
|  | < fdp[mid].sfde_func_size)) | 
|  | { | 
|  | *func_idx = mid; | 
|  | return fdp + mid; | 
|  | } | 
|  |  | 
|  | if (sframe_decoder_get_secrel_func_start_addr (ctx, mid) < addr) | 
|  | low = mid + 1; | 
|  | else | 
|  | high = mid - 1; | 
|  | } | 
|  |  | 
|  | return sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTFOUND); | 
|  | } | 
|  |  | 
|  | /* Get the end IP offset for the FRE at index i in the FDEP.  The buffer FRES | 
|  | is the starting location for the FRE.  */ | 
|  |  | 
|  | static uint32_t | 
|  | sframe_fre_get_end_ip_offset (sframe_func_desc_entry *fdep, unsigned int i, | 
|  | const char *fres) | 
|  | { | 
|  | uint32_t end_ip_offset; | 
|  | uint32_t fre_type; | 
|  |  | 
|  | fre_type = sframe_get_fre_type (fdep); | 
|  |  | 
|  | /* Get the start address of the next FRE in sequence.  */ | 
|  | if (i < fdep->sfde_func_num_fres - 1) | 
|  | { | 
|  | sframe_decode_fre_start_address (fres, &end_ip_offset, fre_type); | 
|  | end_ip_offset -= 1; | 
|  | } | 
|  | else | 
|  | /* The end IP offset for the FRE needs to be deduced from the function | 
|  | size.  */ | 
|  | end_ip_offset = fdep->sfde_func_size - 1; | 
|  |  | 
|  | return end_ip_offset; | 
|  | } | 
|  |  | 
|  | /* 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_frame_row_entry cur_fre; | 
|  | sframe_func_desc_entry *fdep; | 
|  | uint32_t func_idx; | 
|  | uint32_t fre_type, i; | 
|  | int32_t func_start_addr; | 
|  | uint32_t start_ip_offset, end_ip_offset; | 
|  | const char *fres; | 
|  | size_t size = 0; | 
|  | int err = 0; | 
|  |  | 
|  | 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_internal (ctx, pc, &err, &func_idx); | 
|  | if (fdep == NULL || ctx->sfd_fres == NULL) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_DCTX_INVAL); | 
|  |  | 
|  | fre_type = sframe_get_fre_type (fdep); | 
|  |  | 
|  | fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off; | 
|  | func_start_addr = sframe_decoder_get_secrel_func_start_addr (ctx, func_idx); | 
|  |  | 
|  | for (i = 0; i < fdep->sfde_func_num_fres; i++) | 
|  | { | 
|  | err = sframe_decode_fre (fres, &cur_fre, fre_type, &size); | 
|  | if (err) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
|  |  | 
|  | start_ip_offset = cur_fre.fre_start_addr; | 
|  | end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size); | 
|  |  | 
|  | /* Stop search if FRE's start_ip is greater than pc.  Given | 
|  | func_start_addr <= pc, pc - func_start_addr must be positive.  */ | 
|  | if (start_ip_offset > (uint32_t)(pc - func_start_addr)) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
|  |  | 
|  | if (sframe_fre_check_range_p (ctx, func_idx, start_ip_offset, | 
|  | end_ip_offset, pc)) | 
|  | { | 
|  | sframe_frame_row_entry_copy (frep, &cur_fre); | 
|  | return 0; | 
|  | } | 
|  | fres += size; | 
|  | } | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FDE_INVAL); | 
|  | } | 
|  |  | 
|  | /* Return the number of function descriptor entries in the SFrame decoder | 
|  | DCTX.  */ | 
|  |  | 
|  | uint32_t | 
|  | sframe_decoder_get_num_fidx (sframe_decoder_ctx *ctx) | 
|  | { | 
|  | uint32_t 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; | 
|  | int err = 0; | 
|  |  | 
|  | if (ctx == NULL || func_start_address == NULL || num_fres == NULL | 
|  | || func_size == NULL) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
|  |  | 
|  | fdp = sframe_decoder_get_funcdesc_at_index (ctx, i); | 
|  |  | 
|  | if (fdp == NULL) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND); | 
|  |  | 
|  | *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; | 
|  | } | 
|  |  | 
|  | int | 
|  | sframe_decoder_get_funcdesc_v2 (sframe_decoder_ctx *dctx, | 
|  | unsigned int i, | 
|  | uint32_t *num_fres, | 
|  | uint32_t *func_size, | 
|  | int32_t *func_start_address, | 
|  | unsigned char *func_info, | 
|  | uint8_t *rep_block_size) | 
|  | { | 
|  | sframe_func_desc_entry *fdp; | 
|  | int err = 0; | 
|  |  | 
|  | if (dctx == NULL || func_start_address == NULL | 
|  | || num_fres == NULL || func_size == NULL | 
|  | || sframe_decoder_get_version (dctx) == SFRAME_VERSION_1) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
|  |  | 
|  | fdp = sframe_decoder_get_funcdesc_at_index (dctx, i); | 
|  |  | 
|  | if (fdp == NULL) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND); | 
|  |  | 
|  | *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; | 
|  | *rep_block_size = fdp->sfde_func_rep_size; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | /* 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; | 
|  | const char *fres; | 
|  | uint32_t i; | 
|  | uint32_t 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.  */ | 
|  | fres = 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 (fres, &ifre, fre_type, &esz); | 
|  | if (i == fre_idx) | 
|  | { | 
|  | if (!sframe_fre_sanity_check_p (&ifre)) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
|  |  | 
|  | /* Although a stricter sanity check on fre_start_addr like: | 
|  | if (fdep->sfde_func_size) | 
|  | sframe_assert (frep->fre_start_addr < fdep->sfde_func_size); | 
|  | is more suitable, some code has been seen to not abide by it.  See | 
|  | PR libsframe/33131.  */ | 
|  | sframe_assert (ifre.fre_start_addr <= fdep->sfde_func_size); | 
|  |  | 
|  | sframe_frame_row_entry_copy (fre, &ifre); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | /* Next FRE.  */ | 
|  | fres += 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_fde_tbl *func_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.  Uses the ABI specific FIXED_FP_OFFSET and | 
|  | FIXED_RA_OFFSET values as provided.  Sets errp if failure.  */ | 
|  |  | 
|  | sframe_encoder_ctx * | 
|  | sframe_encode (uint8_t ver, uint8_t flags, uint8_t abi_arch, | 
|  | int8_t fixed_fp_offset, int8_t fixed_ra_offset, int *errp) | 
|  | { | 
|  | sframe_header *hp; | 
|  | sframe_encoder_ctx *encoder; | 
|  |  | 
|  | if (ver != SFRAME_VERSION) | 
|  | return sframe_ret_set_errno (errp, SFRAME_ERR_VERSION_INVAL); | 
|  |  | 
|  | if ((encoder = malloc (sizeof (sframe_encoder_ctx))) == NULL) | 
|  | return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
|  |  | 
|  | memset (encoder, 0, sizeof (sframe_encoder_ctx)); | 
|  |  | 
|  | /* Get the SFrame header and update it.  */ | 
|  | hp = sframe_encoder_get_header (encoder); | 
|  | hp->sfh_preamble.sfp_version = ver; | 
|  | hp->sfh_preamble.sfp_magic = SFRAME_MAGIC; | 
|  | hp->sfh_preamble.sfp_flags = flags; | 
|  |  | 
|  | /* Implementation in the SFrame encoder APIs, e.g., | 
|  | sframe_encoder_write_sframe assume flag SFRAME_F_FDE_FUNC_START_PCREL | 
|  | set.  */ | 
|  | if (!(flags & SFRAME_F_FDE_FUNC_START_PCREL)) | 
|  | return sframe_ret_set_errno (errp, SFRAME_ERR_ECTX_INVAL); | 
|  |  | 
|  | hp->sfh_abi_arch = abi_arch; | 
|  | hp->sfh_cfa_fixed_fp_offset = fixed_fp_offset; | 
|  | hp->sfh_cfa_fixed_ra_offset = fixed_ra_offset; | 
|  |  | 
|  | return encoder; | 
|  | } | 
|  |  | 
|  | /* 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.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_encoder_get_abi_arch (sframe_encoder_ctx *encoder) | 
|  | { | 
|  | uint8_t abi_arch = 0; | 
|  | sframe_header *ehp; | 
|  | ehp = sframe_encoder_get_header (encoder); | 
|  | if (ehp) | 
|  | abi_arch = ehp->sfh_abi_arch; | 
|  | return abi_arch; | 
|  | } | 
|  |  | 
|  | /* Get the format version from the SFrame encoder context ENCODER.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_encoder_get_version (sframe_encoder_ctx *encoder) | 
|  | { | 
|  | sframe_header *ehp; | 
|  | ehp = sframe_encoder_get_header (encoder); | 
|  | return ehp->sfh_preamble.sfp_version; | 
|  | } | 
|  |  | 
|  | /* Get the section flags from the SFrame encoder context ENCODER.  */ | 
|  |  | 
|  | uint8_t | 
|  | sframe_encoder_get_flags (sframe_encoder_ctx *encoder) | 
|  | { | 
|  | const sframe_header *ehp = sframe_encoder_get_header (encoder); | 
|  | return ehp->sfh_preamble.sfp_flags; | 
|  | } | 
|  |  | 
|  | /* Return the number of function descriptor entries in the SFrame encoder | 
|  | ENCODER.  */ | 
|  |  | 
|  | uint32_t | 
|  | sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder) | 
|  | { | 
|  | uint32_t num_fdes = 0; | 
|  | sframe_header *ehp = NULL; | 
|  | ehp = sframe_encoder_get_header (encoder); | 
|  | if (ehp) | 
|  | num_fdes = ehp->sfh_num_fdes; | 
|  | return num_fdes; | 
|  | } | 
|  |  | 
|  | /* Get the offset of the sfde_func_start_address field (from the start of the | 
|  | on-disk layout of the SFrame section) of the FDE at FUNC_IDX in the encoder | 
|  | context ENCODER. | 
|  |  | 
|  | If FUNC_IDX is more than the number of SFrame FDEs in the section, sets | 
|  | error code in ERRP, but returns the (hypothetical) offset.  This is useful | 
|  | for the linker when arranging input FDEs into the output section to be | 
|  | emitted.  */ | 
|  |  | 
|  | uint32_t | 
|  | sframe_encoder_get_offsetof_fde_start_addr (sframe_encoder_ctx *encoder, | 
|  | uint32_t func_idx, int *errp) | 
|  | { | 
|  | if (func_idx >= sframe_encoder_get_num_fidx (encoder)) | 
|  | sframe_ret_set_errno (errp, SFRAME_ERR_FDE_INVAL); | 
|  | else if (errp) | 
|  | *errp = 0; | 
|  |  | 
|  | return (sframe_encoder_get_hdr_size (encoder) | 
|  | + func_idx * sizeof (sframe_func_desc_entry) | 
|  | + offsetof (sframe_func_desc_entry, sfde_func_start_address)); | 
|  | } | 
|  |  | 
|  | /* Add an FRE to function at FUNC_IDX'th function descriptor entry in | 
|  | the encoder context.  */ | 
|  |  | 
|  | 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; | 
|  | uint32_t 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 = 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; | 
|  |  | 
|  | /* Although a stricter sanity check on fre_start_addr like: | 
|  | if (fdep->sfde_func_size) | 
|  | sframe_assert (frep->fre_start_addr < fdep->sfde_func_size); | 
|  | is more suitable, some code has been seen to not abide by it.  See PR | 
|  | libsframe/33131.  */ | 
|  | 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 = 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_fde_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 = encoder->sfe_funcdesc; | 
|  | ehp = sframe_encoder_get_header (encoder); | 
|  |  | 
|  | if (fd_info == NULL) | 
|  | { | 
|  | fd_tbl_sz = (sizeof (sf_fde_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_fde_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 | 
|  | uint32_t 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 = 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; | 
|  | } | 
|  |  | 
|  | /* Add a new function descriptor entry with START_ADDR, FUNC_SIZE, FUNC_INFO | 
|  | and REP_BLOCK_SIZE to the encoder. | 
|  |  | 
|  | This API is valid only for SFrame format version 2.  */ | 
|  |  | 
|  | int | 
|  | sframe_encoder_add_funcdesc_v2 (sframe_encoder_ctx *encoder, | 
|  | int32_t start_addr, | 
|  | uint32_t func_size, | 
|  | unsigned char func_info, | 
|  | uint8_t rep_block_size, | 
|  | uint32_t num_fres ATTRIBUTE_UNUSED) | 
|  | { | 
|  | sf_fde_tbl *fd_info; | 
|  | int err; | 
|  |  | 
|  | if (encoder == NULL | 
|  | || sframe_encoder_get_version (encoder) == SFRAME_VERSION_1) | 
|  | return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
|  |  | 
|  | err = sframe_encoder_add_funcdesc (encoder, start_addr, func_size, func_info, | 
|  | num_fres); | 
|  | if (err) | 
|  | return SFRAME_ERR; | 
|  |  | 
|  | fd_info = encoder->sfe_funcdesc; | 
|  | fd_info->entry[fd_info->count-1].sfde_func_rep_size = rep_block_size; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | sframe_sort_funcdesc (sframe_encoder_ctx *encoder) | 
|  | { | 
|  | sframe_header *ehp = sframe_encoder_get_header (encoder); | 
|  |  | 
|  | /* Sort and write out the FDE table.  */ | 
|  | sf_fde_tbl *fd_info = encoder->sfe_funcdesc; | 
|  | if (fd_info) | 
|  | { | 
|  | /* The new encoding of sfde_func_start_address means the distances are | 
|  | not from the same anchor, so cannot be sorted directly.  At the moment | 
|  | we adress this by manual value adjustments before and after sorting. | 
|  | FIXME - qsort_r may be more optimal.  */ | 
|  |  | 
|  | for (unsigned int i = 0; i < fd_info->count; i++) | 
|  | fd_info->entry[i].sfde_func_start_address | 
|  | += sframe_encoder_get_offsetof_fde_start_addr (encoder, i, NULL); | 
|  |  | 
|  | qsort (fd_info->entry, fd_info->count, | 
|  | sizeof (sframe_func_desc_entry), fde_func); | 
|  |  | 
|  | for (unsigned int i = 0; i < fd_info->count; i++) | 
|  | fd_info->entry[i].sfde_func_start_address | 
|  | -= sframe_encoder_get_offsetof_fde_start_addr (encoder, i, NULL); | 
|  |  | 
|  | /* Update preamble's flags.  */ | 
|  | ehp->sfh_preamble.sfp_flags |= SFRAME_F_FDE_SORTED; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Write the SFrame FRE start address from the in-memory FRE_START_ADDR | 
|  | to the buffer CONTENTS (on-disk format), given the FRE_TYPE and | 
|  | FRE_START_ADDR_SZ.  */ | 
|  |  | 
|  | static int | 
|  | sframe_encoder_write_fre_start_addr (char *contents, | 
|  | uint32_t fre_start_addr, | 
|  | uint32_t fre_type, | 
|  | size_t fre_start_addr_sz) | 
|  | { | 
|  | int err = 0; | 
|  |  | 
|  | if (fre_type == SFRAME_FRE_TYPE_ADDR1) | 
|  | { | 
|  | uint8_t uc = fre_start_addr; | 
|  | memcpy (contents, &uc, fre_start_addr_sz); | 
|  | } | 
|  | else if (fre_type == SFRAME_FRE_TYPE_ADDR2) | 
|  | { | 
|  | uint16_t ust = fre_start_addr; | 
|  | memcpy (contents, &ust, fre_start_addr_sz); | 
|  | } | 
|  | else if (fre_type == SFRAME_FRE_TYPE_ADDR4) | 
|  | { | 
|  | uint32_t uit = fre_start_addr; | 
|  | memcpy (contents, &uit, fre_start_addr_sz); | 
|  | } | 
|  | else | 
|  | return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
|  |  | 
|  | 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, | 
|  | uint32_t fre_type, size_t *esz) | 
|  | { | 
|  | size_t fre_sz; | 
|  | 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); | 
|  |  | 
|  | sframe_encoder_write_fre_start_addr (contents, frep->fre_start_addr, | 
|  | fre_type, 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_sz = sframe_fre_entry_size (frep, fre_type); | 
|  | /* Sanity checking.  */ | 
|  | sframe_assert ((fre_start_addr_sz | 
|  | + sizeof (frep->fre_info) | 
|  | + fre_stack_offsets_sz) == fre_sz); | 
|  |  | 
|  | *esz = fre_sz; | 
|  |  | 
|  | 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; | 
|  | sf_fde_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; | 
|  |  | 
|  | uint32_t 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 = encoder->sfe_funcdesc; | 
|  | fr_info = 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.  */ | 
|  | if (!(sframe_encoder_get_flags (encoder) & 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; | 
|  | } |