| /* sframe.c - SFrame decoder/encoder. | 
 |  | 
 |    Copyright (C) 2022-2023 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 "sframe-impl.h" | 
 | #include "swap.h" | 
 |  | 
 | typedef struct sf_funidx_tbl | 
 | { | 
 |   unsigned int count; | 
 |   unsigned int alloced; | 
 |   sframe_func_desc_entry entry[1]; | 
 | } sf_funidx_tbl; | 
 |  | 
 | typedef struct sf_fre_tbl | 
 | { | 
 |   unsigned int count; | 
 |   unsigned int alloced; | 
 |   sframe_frame_row_entry entry[1]; | 
 | } sf_fre_tbl; | 
 |  | 
 | #define _sf_printflike_(string_index,first_to_check) \ | 
 |     __attribute__ ((__format__ (__printf__, (string_index), (first_to_check)))) | 
 |  | 
 | static void debug_printf (const char *, ...); | 
 |  | 
 | static int _sframe_debug;	/* Control for printing out debug info.  */ | 
 | static int number_of_entries = 64; | 
 |  | 
 | static void | 
 | sframe_init_debug (void) | 
 | { | 
 |   static int inited; | 
 |  | 
 |   if (!inited) | 
 |     { | 
 |       _sframe_debug = getenv ("SFRAME_DEBUG") != NULL; | 
 |       inited = 1; | 
 |     } | 
 | } | 
 |  | 
 | _sf_printflike_ (1, 2) | 
 | static void debug_printf (const char *format, ...) | 
 | { | 
 |   if (_sframe_debug) | 
 |     { | 
 |       va_list args; | 
 |  | 
 |       va_start (args, format); | 
 |       vfprintf (stderr, format, args); | 
 |       va_end (args); | 
 |     } | 
 | } | 
 |  | 
 | /* Generate bitmask of given size in bytes.  This is used for | 
 |    some checks on the FRE start address. | 
 |    SFRAME_FRE_TYPE_ADDR1 => 1 byte => [ bitmask = 0xff ] | 
 |    SFRAME_FRE_TYPE_ADDR2 => 2 byte => [ bitmask = 0xffff ] | 
 |    SFRAME_FRE_TYPE_ADDR4 => 4 byte => [ bitmask = 0xffffffff ].  */ | 
 | #define SFRAME_BITMASK_OF_SIZE(size_in_bytes) \ | 
 |   (((uint64_t)1 << (size_in_bytes*8)) - 1) | 
 |  | 
 | /* Store the specified error code into errp if it is non-NULL. | 
 |    Return SFRAME_ERR.  */ | 
 |  | 
 | static int | 
 | sframe_set_errno (int *errp, int error) | 
 | { | 
 |   if (errp != NULL) | 
 |     *errp = error; | 
 |   return SFRAME_ERR; | 
 | } | 
 |  | 
 | /* Store the specified error code into errp if it is non-NULL. | 
 |    Return NULL.  */ | 
 |  | 
 | static void * | 
 | sframe_ret_set_errno (int *errp, int error) | 
 | { | 
 |   if (errp != NULL) | 
 |     *errp = error; | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* Get the SFrame header size.  */ | 
 |  | 
 | static uint32_t | 
 | sframe_get_hdr_size (sframe_header *sfh) | 
 | { | 
 |   return SFRAME_V1_HDR_SIZE (*sfh); | 
 | } | 
 |  | 
 | /* Access functions for frame row entry data.  */ | 
 |  | 
 | static 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 unsigned int | 
 | sframe_get_fre_type (sframe_func_desc_entry *fdep) | 
 | { | 
 |   unsigned int fre_type = 0; | 
 |   if (fdep) | 
 |     fre_type = SFRAME_V1_FUNC_FRE_TYPE (fdep->sfde_func_info); | 
 |   return fre_type; | 
 | } | 
 |  | 
 | static unsigned int | 
 | sframe_get_fde_type (sframe_func_desc_entry *fdep) | 
 | { | 
 |   unsigned int fde_type = 0; | 
 |   if (fdep) | 
 |     fde_type = SFRAME_V1_FUNC_FDE_TYPE (fdep->sfde_func_info); | 
 |   return fde_type; | 
 | } | 
 |  | 
 | /* Check if flipping is needed, based on ENDIAN.  */ | 
 |  | 
 | static int | 
 | need_swapping (int endian) | 
 | { | 
 |   unsigned int ui = 1; | 
 |   char *c = (char *)&ui; | 
 |   int is_little = (int)*c; | 
 |  | 
 |   switch (endian) | 
 |     { | 
 |       case SFRAME_ABI_AARCH64_ENDIAN_LITTLE: | 
 |       case SFRAME_ABI_AMD64_ENDIAN_LITTLE: | 
 | 	return !is_little; | 
 |       case SFRAME_ABI_AARCH64_ENDIAN_BIG: | 
 | 	return is_little; | 
 |       default: | 
 | 	break; | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Flip the endianness of the SFrame header.  */ | 
 |  | 
 | static void | 
 | flip_header (sframe_header *sfheader) | 
 | { | 
 |   swap_thing (sfheader->sfh_preamble.sfp_magic); | 
 |   swap_thing (sfheader->sfh_preamble.sfp_version); | 
 |   swap_thing (sfheader->sfh_preamble.sfp_flags); | 
 |   swap_thing (sfheader->sfh_cfa_fixed_fp_offset); | 
 |   swap_thing (sfheader->sfh_cfa_fixed_ra_offset); | 
 |   swap_thing (sfheader->sfh_num_fdes); | 
 |   swap_thing (sfheader->sfh_num_fres); | 
 |   swap_thing (sfheader->sfh_fre_len); | 
 |   swap_thing (sfheader->sfh_fdeoff); | 
 |   swap_thing (sfheader->sfh_freoff); | 
 | } | 
 |  | 
 | static void | 
 | flip_fde (sframe_func_desc_entry *fdep) | 
 | { | 
 |   swap_thing (fdep->sfde_func_start_address); | 
 |   swap_thing (fdep->sfde_func_size); | 
 |   swap_thing (fdep->sfde_func_start_fre_off); | 
 |   swap_thing (fdep->sfde_func_num_fres); | 
 | } | 
 |  | 
 | /* Check if SFrame header has valid data.  */ | 
 |  | 
 | static bool | 
 | sframe_header_sanity_check_p (sframe_header *hp) | 
 | { | 
 |   unsigned char all_flags = SFRAME_F_FDE_SORTED | SFRAME_F_FRAME_POINTER; | 
 |   /* Check preamble is valid.  */ | 
 |   if ((hp->sfh_preamble.sfp_magic != SFRAME_MAGIC) | 
 |       || (hp->sfh_preamble.sfp_version != SFRAME_VERSION) | 
 |       || ((hp->sfh_preamble.sfp_flags | all_flags) != all_flags)) | 
 |     return 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 *fp, unsigned int fre_type) | 
 | { | 
 |   void *start = (void*)fp; | 
 |   if (fre_type == SFRAME_FRE_TYPE_ADDR2) | 
 |     { | 
 |       unsigned short *start_addr = (unsigned short *)(start); | 
 |       swap_thing (*start_addr); | 
 |     } | 
 |   else if (fre_type == SFRAME_FRE_TYPE_ADDR4) | 
 |     { | 
 |       uint32_t *start_addr = (uint32_t *)(start); | 
 |       swap_thing (*start_addr); | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | flip_fre_stack_offsets (char *fp, uint8_t offset_size, uint8_t offset_cnt) | 
 | { | 
 |   int j; | 
 |   void *offsets = (void *)fp; | 
 |  | 
 |   if (offset_size == SFRAME_FRE_OFFSET_2B) | 
 |     { | 
 |       unsigned short *ust = (unsigned short *)offsets; | 
 |       for (j = offset_cnt; j > 0; ust++, j--) | 
 | 	swap_thing (*ust); | 
 |     } | 
 |   else if (offset_size == SFRAME_FRE_OFFSET_4B) | 
 |     { | 
 |       uint32_t *uit = (uint32_t *)offsets; | 
 |       for (j = offset_cnt; j > 0; uit++, j--) | 
 | 	swap_thing (*uit); | 
 |     } | 
 | } | 
 |  | 
 | /* Get the FRE start address size, given the FRE_TYPE.  */ | 
 |  | 
 | static size_t | 
 | sframe_fre_start_addr_size (unsigned int fre_type) | 
 | { | 
 |   size_t addr_size = 0; | 
 |   switch (fre_type) | 
 |     { | 
 |     case SFRAME_FRE_TYPE_ADDR1: | 
 |       addr_size = 1; | 
 |       break; | 
 |     case SFRAME_FRE_TYPE_ADDR2: | 
 |       addr_size = 2; | 
 |       break; | 
 |     case SFRAME_FRE_TYPE_ADDR4: | 
 |       addr_size = 4; | 
 |       break; | 
 |     default: | 
 |       /* No other value is expected.  */ | 
 |       sframe_assert (0); | 
 |       break; | 
 |     } | 
 |   return addr_size; | 
 | } | 
 |  | 
 | /* Check if the FREP has valid data.  */ | 
 |  | 
 | static 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 > 3) | 
 |     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, unsigned int 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)); | 
 | } | 
 |  | 
 | static int | 
 | flip_fre (char *fp, unsigned int 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; | 
 |   unsigned int 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 + sframe_get_hdr_size (ihp) + ihp->sfh_freoff; | 
 |       fp += fre_offset; | 
 |       for (; j < prev_frep_index + num_fres; j++) | 
 | 	{ | 
 | 	  if (flip_fre (fp, fre_type, &esz)) | 
 | 	    goto bad; | 
 | 	  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; | 
 |  | 
 |   /* 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 (unsigned int fre_type, | 
 | 			     unsigned int fde_type) | 
 | { | 
 |   unsigned char func_info; | 
 |   sframe_assert (fre_type == SFRAME_FRE_TYPE_ADDR1 | 
 | 		   || fre_type == SFRAME_FRE_TYPE_ADDR2 | 
 | 		   || fre_type == SFRAME_FRE_TYPE_ADDR4); | 
 |   sframe_assert (fde_type == SFRAME_FDE_TYPE_PCINC | 
 | 		    || fde_type == SFRAME_FDE_TYPE_PCMASK); | 
 |   func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type); | 
 |   return func_info; | 
 | } | 
 |  | 
 | /* Get the FRE type given the function size.  */ | 
 | /* FIXME API for linker.  Revisit if its better placed somewhere else?  */ | 
 |  | 
 | unsigned int | 
 | sframe_calc_fre_type (size_t func_size) | 
 | { | 
 |   unsigned int 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.  */ | 
 |  | 
 | unsigned int | 
 | sframe_fre_get_base_reg_id (sframe_frame_row_entry *fre, int *errp) | 
 | { | 
 |   if (fre == NULL) | 
 |     return sframe_set_errno (errp, SFRAME_ERR_FRE_INVAL); | 
 |  | 
 |   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 ATTRIBUTE_UNUSED, | 
 | 			   sframe_frame_row_entry *fre, int *errp) | 
 | { | 
 |   return sframe_get_fre_offset (fre, SFRAME_FRE_CFA_OFFSET_IDX, errp); | 
 | } | 
 |  | 
 | /* Get the FP offset from the FRE.  If the offset is invalid, sets errp.  */ | 
 |  | 
 | int32_t | 
 | sframe_fre_get_fp_offset (sframe_decoder_ctx *dctx, | 
 | 			  sframe_frame_row_entry *fre, int *errp) | 
 | { | 
 |   uint32_t fp_offset_idx = 0; | 
 |   sframe_header *dhp = sframe_decoder_get_header (dctx); | 
 |   /* If the FP offset is not being tracked, return an error code so the caller | 
 |      can gather the fixed FP offset from the SFrame header.  */ | 
 |   if (dhp->sfh_cfa_fixed_fp_offset != SFRAME_CFA_FIXED_FP_INVALID) | 
 |     return sframe_set_errno (errp, SFRAME_ERR_FREOFFSET_NOPRESENT); | 
 |  | 
 |   /* 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 = ((dhp->sfh_cfa_fixed_ra_offset != 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.  */ | 
 |  | 
 | int32_t | 
 | sframe_fre_get_ra_offset (sframe_decoder_ctx *dctx, | 
 | 			  sframe_frame_row_entry *fre, int *errp) | 
 | { | 
 |   sframe_header *dhp = sframe_decoder_get_header (dctx); | 
 |   /* If the RA offset was not being tracked, return an error code so the caller | 
 |      can gather the fixed RA offset from the SFrame header.  */ | 
 |   if (dhp->sfh_cfa_fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID) | 
 |     return sframe_set_errno (errp, SFRAME_ERR_FREOFFSET_NOPRESENT); | 
 |  | 
 |   /* 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, | 
 | 				 unsigned int 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; | 
 |       int32_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, | 
 | 		   unsigned int 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 CF_BUF of size CF_SIZE and return the | 
 |    new SFrame decoder context. | 
 |  | 
 |    Sets ERRP for the caller if any error.  Frees up the allocated memory in | 
 |    case of error.  */ | 
 |  | 
 | sframe_decoder_ctx * | 
 | sframe_decode (const char *sf_buf, size_t sf_size, int *errp) | 
 | { | 
 |   const sframe_preamble *sfp; | 
 |   size_t hdrsz; | 
 |   sframe_header *sfheaderp; | 
 |   sframe_decoder_ctx *dctx; | 
 |   char *frame_buf; | 
 |   char *tempbuf = NULL; | 
 |  | 
 |   int fidx_size; | 
 |   uint32_t fre_bytes; | 
 |   int foreign_endian = 0; | 
 |  | 
 |   sframe_init_debug (); | 
 |  | 
 |   if ((sf_buf == NULL) || (!sf_size)) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL); | 
 |   else if (sf_size < sizeof (sframe_header)) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); | 
 |  | 
 |   sfp = (const sframe_preamble *) sf_buf; | 
 |  | 
 |   debug_printf ("sframe_decode: magic=0x%x version=%u flags=%u\n", | 
 | 		sfp->sfp_magic, sfp->sfp_version, sfp->sfp_flags); | 
 |  | 
 |   /* Check for foreign endianness.  */ | 
 |   if (sfp->sfp_magic != SFRAME_MAGIC) | 
 |     { | 
 |       if (sfp->sfp_magic == bswap_16 (SFRAME_MAGIC)) | 
 | 	foreign_endian = 1; | 
 |       else | 
 | 	return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); | 
 |     } | 
 |  | 
 |   /* Initialize a new decoder context.  */ | 
 |   if ((dctx = malloc (sizeof (sframe_decoder_ctx))) == NULL) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
 |   memset (dctx, 0, sizeof (sframe_decoder_ctx)); | 
 |  | 
 |   if (foreign_endian) | 
 |     { | 
 |       /* Allocate a new buffer and initialize it.  */ | 
 |       tempbuf = (char *) malloc (sf_size * sizeof (char)); | 
 |       if (tempbuf == NULL) | 
 | 	return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
 |       memcpy (tempbuf, sf_buf, sf_size); | 
 |  | 
 |       /* Flip the header.  */ | 
 |       sframe_header *ihp = (sframe_header *) tempbuf; | 
 |       flip_header (ihp); | 
 |       /* Flip the rest of the SFrame section data buffer.  */ | 
 |       if (flip_sframe (tempbuf, sf_size, 0)) | 
 | 	{ | 
 | 	  free (tempbuf); | 
 | 	  return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); | 
 | 	} | 
 |       frame_buf = tempbuf; | 
 |       /* 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_NOMEM); | 
 |       goto decode_fail_free; | 
 |     } | 
 |   hdrsz = sframe_get_hdr_size (sfheaderp); | 
 |   frame_buf += hdrsz; | 
 |  | 
 |   /* Handle the SFrame Function Descriptor Entry section.  */ | 
 |   fidx_size | 
 |     = sfheaderp->sfh_num_fdes * sizeof (sframe_func_desc_entry); | 
 |   dctx->sfd_funcdesc = malloc (fidx_size); | 
 |   if (dctx->sfd_funcdesc == NULL) | 
 |     { | 
 |       sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
 |       goto decode_fail_free; | 
 |     } | 
 |   memcpy (dctx->sfd_funcdesc, frame_buf, fidx_size); | 
 |  | 
 |   debug_printf ("%u total fidx size\n", fidx_size); | 
 |  | 
 |   frame_buf += (fidx_size); | 
 |  | 
 |   /* Handle the SFrame Frame Row Entry section.  */ | 
 |   dctx->sfd_fres = malloc (sfheaderp->sfh_fre_len); | 
 |   if (dctx->sfd_fres == NULL) | 
 |     { | 
 |       sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
 |       goto decode_fail_free; | 
 |     } | 
 |   memcpy (dctx->sfd_fres, frame_buf, sfheaderp->sfh_fre_len); | 
 |  | 
 |   fre_bytes = sfheaderp->sfh_fre_len; | 
 |   dctx->sfd_fre_nbytes = fre_bytes; | 
 |  | 
 |   debug_printf ("%u total fre bytes\n", fre_bytes); | 
 |  | 
 |   return dctx; | 
 |  | 
 | decode_fail_free: | 
 |   if (foreign_endian && tempbuf != NULL) | 
 |     free (tempbuf); | 
 |   sframe_decoder_free (&dctx); | 
 |   dctx = NULL; | 
 |   return dctx; | 
 | } | 
 |  | 
 | /* Get the size of the SFrame header from the decoder context CTX.  */ | 
 |  | 
 | unsigned int | 
 | sframe_decoder_get_hdr_size (sframe_decoder_ctx *ctx) | 
 | { | 
 |   sframe_header *dhp; | 
 |   dhp = sframe_decoder_get_header (ctx); | 
 |   return sframe_get_hdr_size (dhp); | 
 | } | 
 |  | 
 | /* Get the SFrame's abi/arch info given the decoder context CTX.  */ | 
 |  | 
 | unsigned char | 
 | sframe_decoder_get_abi_arch (sframe_decoder_ctx *ctx) | 
 | { | 
 |   sframe_header *sframe_header; | 
 |   sframe_header = sframe_decoder_get_header (ctx); | 
 |   return sframe_header->sfh_abi_arch; | 
 | } | 
 |  | 
 | /* Get the SFrame's fixed FP offset given the decoder context CTX.  */ | 
 | int8_t | 
 | sframe_decoder_get_fixed_fp_offset (sframe_decoder_ctx *ctx) | 
 | { | 
 |   sframe_header *dhp; | 
 |   dhp = sframe_decoder_get_header (ctx); | 
 |   return dhp->sfh_cfa_fixed_fp_offset; | 
 | } | 
 |  | 
 | /* Get the SFrame's fixed RA offset given the decoder context CTX.  */ | 
 | int8_t | 
 | sframe_decoder_get_fixed_ra_offset (sframe_decoder_ctx *ctx) | 
 | { | 
 |   sframe_header *dhp; | 
 |   dhp = sframe_decoder_get_header (ctx); | 
 |   return dhp->sfh_cfa_fixed_ra_offset; | 
 | } | 
 |  | 
 | /* Find the function descriptor entry starting which contains the specified | 
 |    address ADDR.  */ | 
 |  | 
 | sframe_func_desc_entry * | 
 | sframe_get_funcdesc_with_addr (sframe_decoder_ctx *ctx, | 
 | 			       int32_t addr, int *errp) | 
 | { | 
 |   sframe_header *dhp; | 
 |   sframe_func_desc_entry *fdp; | 
 |   int low, high, cnt; | 
 |  | 
 |   if (ctx == NULL) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL); | 
 |  | 
 |   dhp = sframe_decoder_get_header (ctx); | 
 |  | 
 |   if (dhp == NULL || dhp->sfh_num_fdes == 0 || ctx->sfd_funcdesc == NULL) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_DCTX_INVAL); | 
 |   /* If the FDE sub-section is not sorted on PCs, skip the lookup because | 
 |      binary search cannot be used.  */ | 
 |   if ((dhp->sfh_preamble.sfp_flags & SFRAME_F_FDE_SORTED) == 0) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTSORTED); | 
 |  | 
 |   /* Do the binary search.  */ | 
 |   fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc; | 
 |   low = 0; | 
 |   high = dhp->sfh_num_fdes; | 
 |   cnt = high; | 
 |   while (low <= high) | 
 |     { | 
 |       int mid = low + (high - low) / 2; | 
 |  | 
 |       if (fdp[mid].sfde_func_start_address == addr) | 
 | 	return fdp + mid; | 
 |  | 
 |       if (fdp[mid].sfde_func_start_address < addr) | 
 | 	{ | 
 | 	  if (mid == (cnt - 1)) 	/* Check if it's the last one.  */ | 
 | 	    return fdp + (cnt - 1); | 
 | 	  else if (fdp[mid+1].sfde_func_start_address > addr) | 
 | 	    return fdp + mid; | 
 | 	  low = mid + 1; | 
 | 	} | 
 |       else | 
 | 	high = mid - 1; | 
 |     } | 
 |  | 
 |   return sframe_ret_set_errno (errp, SFRAME_ERR_FDE_NOTFOUND); | 
 | } | 
 |  | 
 | /* 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; | 
 |   unsigned int fre_type, fde_type; | 
 |   uint32_t end_ip_offset, i; | 
 |   int32_t start_ip, end_ip; | 
 |   int32_t func_start_addr; | 
 |   const char *fres; | 
 |   size_t size = 0; | 
 |   int err = 0; | 
 |   /* For regular FDEs (i.e. fde_type SFRAME_FDE_TYPE_PCINC), | 
 |      where the start address in the FRE is an offset from start pc, | 
 |      use a bitmask with all bits set so that none of the address bits are | 
 |      ignored.  In this case, we need to return the FRE where | 
 |      (PC >= FRE_START_ADDR) */ | 
 |   uint64_t bitmask = 0xffffffff; | 
 |  | 
 |   if ((ctx == NULL) || (frep == NULL)) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
 |  | 
 |   /* Find the FDE which contains the PC, then scan its fre entries.  */ | 
 |   fdep = sframe_get_funcdesc_with_addr (ctx, pc, &err); | 
 |   if (fdep == NULL || ctx->sfd_fres == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_DCTX_INVAL); | 
 |  | 
 |   fre_type = sframe_get_fre_type (fdep); | 
 |   fde_type = sframe_get_fde_type (fdep); | 
 |  | 
 |   /* For FDEs for repetitive pattern of insns, we need to return the FRE | 
 |      such that (PC & FRE_START_ADDR_AS_MASK >= FRE_START_ADDR_AS_MASK). | 
 |      so, update the bitmask to the start address.  */ | 
 |   /* FIXME - the bitmask should be picked per ABI or encoded in the format | 
 |      somehow. For AMD64, the pltN entry stub is 16 bytes. */ | 
 |   if (fde_type == SFRAME_FDE_TYPE_PCMASK) | 
 |     bitmask = 0xff; | 
 |  | 
 |   fres = ctx->sfd_fres + fdep->sfde_func_start_fre_off; | 
 |   func_start_addr = fdep->sfde_func_start_address; | 
 |  | 
 |   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 = func_start_addr + cur_fre.fre_start_addr; | 
 |      end_ip_offset = sframe_fre_get_end_ip_offset (fdep, i, fres + size); | 
 |      end_ip = func_start_addr + end_ip_offset; | 
 |  | 
 |      if ((start_ip & bitmask) > (pc & bitmask)) | 
 |        return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
 |  | 
 |      if (((start_ip & bitmask) <= (pc & bitmask)) | 
 | 	 && (end_ip & bitmask) >= (pc & bitmask)) | 
 |        { | 
 | 	 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.  */ | 
 |  | 
 | unsigned int | 
 | sframe_decoder_get_num_fidx (sframe_decoder_ctx *ctx) | 
 | { | 
 |   unsigned int num_fdes = 0; | 
 |   sframe_header *dhp = NULL; | 
 |   dhp = sframe_decoder_get_header (ctx); | 
 |   if (dhp) | 
 |     num_fdes = dhp->sfh_num_fdes; | 
 |   return num_fdes; | 
 | } | 
 |  | 
 | /* Get the data (NUM_FRES, FUNC_START_ADDRESS) from the function | 
 |    descriptor entry at index I'th in the decoder CTX.  If failed, | 
 |    return error code.  */ | 
 | /* FIXME - consolidate the args and return a | 
 |    sframe_func_desc_index_elem rather?  */ | 
 |  | 
 | int | 
 | sframe_decoder_get_funcdesc (sframe_decoder_ctx *ctx, | 
 | 			     unsigned int i, | 
 | 			     uint32_t *num_fres, | 
 | 			     uint32_t *func_size, | 
 | 			     int32_t *func_start_address, | 
 | 			     unsigned char *func_info) | 
 | { | 
 |   sframe_func_desc_entry *fdp; | 
 |   unsigned int num_fdes; | 
 |   int err = 0; | 
 |  | 
 |   if (ctx == NULL || func_start_address == NULL || num_fres == NULL | 
 |       || func_size == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
 |  | 
 |   num_fdes = sframe_decoder_get_num_fidx (ctx); | 
 |   if (num_fdes == 0 | 
 |       || i >= num_fdes | 
 |       || ctx->sfd_funcdesc == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_DCTX_INVAL); | 
 |  | 
 |   fdp = (sframe_func_desc_entry *) ctx->sfd_funcdesc + i; | 
 |   *num_fres = fdp->sfde_func_num_fres; | 
 |   *func_start_address = fdp->sfde_func_start_address; | 
 |   *func_size = fdp->sfde_func_size; | 
 |   *func_info = fdp->sfde_func_info; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Get the function descriptor entry at index FUNC_IDX in the decoder | 
 |    context CTX.  */ | 
 |  | 
 | static sframe_func_desc_entry * | 
 | sframe_decoder_get_funcdesc_at_index (sframe_decoder_ctx *ctx, | 
 | 				      uint32_t func_idx) | 
 | { | 
 |   /* Invalid argument.  No FDE will be found.  */ | 
 |   if (func_idx >= sframe_decoder_get_num_fidx (ctx)) | 
 |     return NULL; | 
 |  | 
 |   sframe_func_desc_entry *fdep; | 
 |   fdep = (sframe_func_desc_entry *) ctx->sfd_funcdesc; | 
 |   return fdep + func_idx; | 
 | } | 
 |  | 
 | /* Get the FRE_IDX'th FRE of the function at FUNC_IDX'th function | 
 |    descriptor entry in the SFrame decoder CTX.  Returns error code as | 
 |    applicable.  */ | 
 |  | 
 | int | 
 | sframe_decoder_get_fre (sframe_decoder_ctx *ctx, | 
 | 			unsigned int func_idx, | 
 | 			unsigned int fre_idx, | 
 | 			sframe_frame_row_entry *fre) | 
 | { | 
 |   sframe_func_desc_entry *fdep; | 
 |   sframe_frame_row_entry ifre; | 
 |   const char *fres; | 
 |   uint32_t i; | 
 |   unsigned int fre_type; | 
 |   size_t esz = 0; | 
 |   int err = 0; | 
 |  | 
 |   if (ctx == NULL || fre == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
 |  | 
 |   /* Get function descriptor entry at index func_idx.  */ | 
 |   fdep = sframe_decoder_get_funcdesc_at_index (ctx, func_idx); | 
 |  | 
 |   if (fdep == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND); | 
 |  | 
 |   fre_type = sframe_get_fre_type (fdep); | 
 |   /* Now scan the FRE entries.  */ | 
 |   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); | 
 |  | 
 | 	 sframe_frame_row_entry_copy (fre, &ifre); | 
 |  | 
 | 	 if (fdep->sfde_func_size) | 
 | 	   sframe_assert (fre->fre_start_addr < fdep->sfde_func_size); | 
 | 	 else | 
 | 	   /* A SFrame FDE with func size equal to zero is possible.  */ | 
 | 	   sframe_assert (fre->fre_start_addr == fdep->sfde_func_size); | 
 |  | 
 | 	 return 0; | 
 |        } | 
 |      /* Next FRE.  */ | 
 |      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_funidx_tbl *func_tbl = (sf_funidx_tbl *) encoder->sfe_funcdesc; | 
 |       fde = func_tbl->entry + func_idx; | 
 |     } | 
 |   return fde; | 
 | } | 
 |  | 
 | /* Create an encoder context with the given SFrame format version VER, FLAGS | 
 |    and ABI information.  Sets errp if failure.  */ | 
 |  | 
 | sframe_encoder_ctx * | 
 | sframe_encode (unsigned char ver, unsigned char flags, int abi_arch, | 
 | 	       int8_t fixed_fp_offset, int8_t fixed_ra_offset, int *errp) | 
 | { | 
 |   sframe_header *hp; | 
 |   sframe_encoder_ctx *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; | 
 |  | 
 |   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.  */ | 
 |  | 
 | unsigned char | 
 | sframe_encoder_get_abi_arch (sframe_encoder_ctx *encoder) | 
 | { | 
 |   unsigned char abi_arch = 0; | 
 |   sframe_header *ehp; | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   if (ehp) | 
 |     abi_arch = ehp->sfh_abi_arch; | 
 |   return abi_arch; | 
 | } | 
 |  | 
 | /* Return the number of function descriptor entries in the SFrame encoder | 
 |    ENCODER.  */ | 
 |  | 
 | unsigned int | 
 | sframe_encoder_get_num_fidx (sframe_encoder_ctx *encoder) | 
 | { | 
 |   unsigned int num_fdes = 0; | 
 |   sframe_header *ehp = NULL; | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   if (ehp) | 
 |     num_fdes = ehp->sfh_num_fdes; | 
 |   return num_fdes; | 
 | } | 
 |  | 
 | /* Add an FRE to function at FUNC_IDX'th function descriptor entry in | 
 |    the encoder context.  */ | 
 |  | 
 | int | 
 | sframe_encoder_add_fre (sframe_encoder_ctx *encoder, | 
 | 			unsigned int func_idx, | 
 | 			sframe_frame_row_entry *frep) | 
 | { | 
 |   sframe_header *ehp; | 
 |   sframe_func_desc_entry *fdep; | 
 |   sframe_frame_row_entry *ectx_frep; | 
 |   size_t offsets_sz, esz; | 
 |   unsigned int fre_type; | 
 |   size_t fre_tbl_sz; | 
 |   int err = 0; | 
 |  | 
 |   if (encoder == NULL || frep == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
 |   if (!sframe_fre_sanity_check_p (frep)) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
 |  | 
 |   /* Use func_idx to gather the function descriptor entry.  */ | 
 |   fdep = sframe_encoder_get_funcdesc_at_index (encoder, func_idx); | 
 |  | 
 |   if (fdep == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_FDE_NOTFOUND); | 
 |  | 
 |   fre_type = sframe_get_fre_type (fdep); | 
 |   sf_fre_tbl *fre_tbl = (sf_fre_tbl *) encoder->sfe_fres; | 
 |  | 
 |   if (fre_tbl == NULL) | 
 |     { | 
 |       fre_tbl_sz = (sizeof (sf_fre_tbl) | 
 | 		    + (number_of_entries * sizeof (sframe_frame_row_entry))); | 
 |       fre_tbl = malloc (fre_tbl_sz); | 
 |  | 
 |       if (fre_tbl == NULL) | 
 | 	{ | 
 | 	  sframe_set_errno (&err, SFRAME_ERR_NOMEM); | 
 | 	  goto bad;		/* OOM.  */ | 
 | 	} | 
 |       memset (fre_tbl, 0, fre_tbl_sz); | 
 |       fre_tbl->alloced = number_of_entries; | 
 |     } | 
 |   else if (fre_tbl->count == fre_tbl->alloced) | 
 |     { | 
 |       fre_tbl_sz = (sizeof (sf_fre_tbl) | 
 | 		    + ((fre_tbl->alloced + number_of_entries) | 
 | 		       * sizeof (sframe_frame_row_entry))); | 
 |       fre_tbl = realloc (fre_tbl, fre_tbl_sz); | 
 |       if (fre_tbl == NULL) | 
 | 	{ | 
 | 	  sframe_set_errno (&err, SFRAME_ERR_NOMEM); | 
 | 	  goto bad;		/* OOM.  */ | 
 | 	} | 
 |  | 
 |       memset (&fre_tbl->entry[fre_tbl->alloced], 0, | 
 | 	      number_of_entries * sizeof (sframe_frame_row_entry)); | 
 |       fre_tbl->alloced += number_of_entries; | 
 |     } | 
 |  | 
 |   ectx_frep = &fre_tbl->entry[fre_tbl->count]; | 
 |   ectx_frep->fre_start_addr | 
 |     = frep->fre_start_addr; | 
 |   ectx_frep->fre_info = frep->fre_info; | 
 |  | 
 |   if (fdep->sfde_func_size) | 
 |     sframe_assert (frep->fre_start_addr < fdep->sfde_func_size); | 
 |   else | 
 |     /* A SFrame FDE with func size equal to zero is possible.  */ | 
 |     sframe_assert (frep->fre_start_addr == fdep->sfde_func_size); | 
 |  | 
 |   /* frep has already been sanity check'd.  Get offsets size.  */ | 
 |   offsets_sz = sframe_fre_offset_bytes_size (frep->fre_info); | 
 |   memcpy (&ectx_frep->fre_offsets, &frep->fre_offsets, offsets_sz); | 
 |  | 
 |   esz = sframe_fre_entry_size (frep, fre_type); | 
 |   fre_tbl->count++; | 
 |  | 
 |   encoder->sfe_fres = (void *) fre_tbl; | 
 |   encoder->sfe_fre_nbytes += esz; | 
 |  | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   ehp->sfh_num_fres = fre_tbl->count; | 
 |  | 
 |   /* Update the value of the number of FREs for the function.  */ | 
 |   fdep->sfde_func_num_fres++; | 
 |  | 
 |   return 0; | 
 |  | 
 | bad: | 
 |   if (fre_tbl != NULL) | 
 |     free (fre_tbl); | 
 |   encoder->sfe_fres = NULL; | 
 |   encoder->sfe_fre_nbytes = 0; | 
 |   return -1; | 
 | } | 
 |  | 
 | /* Add a new function descriptor entry with START_ADDR, FUNC_SIZE and NUM_FRES | 
 |    to the encoder.  */ | 
 |  | 
 | int | 
 | sframe_encoder_add_funcdesc (sframe_encoder_ctx *encoder, | 
 | 			     int32_t start_addr, | 
 | 			     uint32_t func_size, | 
 | 			     unsigned char func_info, | 
 | 			     uint32_t num_fres __attribute__ ((unused))) | 
 | { | 
 |   sframe_header *ehp; | 
 |   sf_funidx_tbl *fd_info; | 
 |   size_t fd_tbl_sz; | 
 |   int err = 0; | 
 |  | 
 |   /* FIXME book-keep num_fres for error checking.  */ | 
 |   if (encoder == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_INVAL); | 
 |  | 
 |   fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc; | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |  | 
 |   if (fd_info == NULL) | 
 |     { | 
 |       fd_tbl_sz = (sizeof (sf_funidx_tbl) | 
 | 		   + (number_of_entries * sizeof (sframe_func_desc_entry))); | 
 |       fd_info = malloc (fd_tbl_sz); | 
 |       if (fd_info == NULL) | 
 | 	{ | 
 | 	  sframe_set_errno (&err, SFRAME_ERR_NOMEM); | 
 | 	  goto bad;		/* OOM.  */ | 
 | 	} | 
 |       memset (fd_info, 0, fd_tbl_sz); | 
 |       fd_info->alloced = number_of_entries; | 
 |     } | 
 |   else if (fd_info->count == fd_info->alloced) | 
 |     { | 
 |       fd_tbl_sz = (sizeof (sf_funidx_tbl) | 
 | 		   + ((fd_info->alloced + number_of_entries) | 
 | 		      * sizeof (sframe_func_desc_entry))); | 
 |       fd_info = realloc (fd_info, fd_tbl_sz); | 
 |       if (fd_info == NULL) | 
 | 	{ | 
 | 	  sframe_set_errno (&err, SFRAME_ERR_NOMEM); | 
 | 	  goto bad;		/* OOM.  */ | 
 | 	} | 
 |  | 
 |       memset (&fd_info->entry[fd_info->alloced], 0, | 
 | 	      number_of_entries * sizeof (sframe_func_desc_entry)); | 
 |       fd_info->alloced += number_of_entries; | 
 |     } | 
 |  | 
 |   fd_info->entry[fd_info->count].sfde_func_start_address = start_addr; | 
 |   /* Num FREs is updated as FREs are added for the function later via | 
 |      sframe_encoder_add_fre.  */ | 
 |   fd_info->entry[fd_info->count].sfde_func_size = func_size; | 
 |   fd_info->entry[fd_info->count].sfde_func_start_fre_off | 
 |     = encoder->sfe_fre_nbytes; | 
 | #if 0 | 
 |   // Linker optimization test code cleanup later ibhagat TODO FIXME | 
 |   unsigned int fre_type = sframe_calc_fre_type (func_size); | 
 |  | 
 |   fd_info->entry[fd_info->count].sfde_func_info | 
 |     = sframe_fde_func_info (fre_type); | 
 | #endif | 
 |   fd_info->entry[fd_info->count].sfde_func_info = func_info; | 
 |   fd_info->count++; | 
 |   encoder->sfe_funcdesc = (void *) fd_info; | 
 |   ehp->sfh_num_fdes++; | 
 |   return 0; | 
 |  | 
 | bad: | 
 |   if (fd_info != NULL) | 
 |     free (fd_info); | 
 |   encoder->sfe_funcdesc = NULL; | 
 |   ehp->sfh_num_fdes = 0; | 
 |   return -1; | 
 | } | 
 |  | 
 | static int | 
 | sframe_sort_funcdesc (sframe_encoder_ctx *encoder) | 
 | { | 
 |   sframe_header *ehp; | 
 |  | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   /* Sort and write out the FDE table.  */ | 
 |   sf_funidx_tbl *fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc; | 
 |   if (fd_info) | 
 |     { | 
 |       qsort (fd_info->entry, fd_info->count, | 
 | 	     sizeof (sframe_func_desc_entry), fde_func); | 
 |       /* Update preamble's flags.  */ | 
 |       ehp->sfh_preamble.sfp_flags |= SFRAME_F_FDE_SORTED; | 
 |     } | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Write 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, | 
 | 				     unsigned int 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, | 
 | 			  unsigned int 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; | 
 |   unsigned char flags; | 
 |   sf_funidx_tbl *fd_info; | 
 |   sf_fre_tbl *fr_info; | 
 |   uint32_t i, num_fdes; | 
 |   uint32_t j, num_fres; | 
 |   sframe_func_desc_entry *fdep; | 
 |   sframe_frame_row_entry *frep; | 
 |  | 
 |   unsigned int fre_type; | 
 |   int err = 0; | 
 |  | 
 |   contents = encoder->sfe_data; | 
 |   buf_size = encoder->sfe_data_size; | 
 |   num_fdes = sframe_encoder_get_num_fidx (encoder); | 
 |   all_fdes_size = num_fdes * sizeof (sframe_func_desc_entry); | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   hdr_size = sframe_get_hdr_size (ehp); | 
 |  | 
 |   fd_info = (sf_funidx_tbl *) encoder->sfe_funcdesc; | 
 |   fr_info = (sf_fre_tbl *) encoder->sfe_fres; | 
 |  | 
 |   /* Sanity checks: | 
 |      - buffers must be malloc'd by the caller.  */ | 
 |   if ((contents == NULL) || (buf_size < hdr_size)) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_BUF_INVAL); | 
 |   if (fr_info == NULL) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_FRE_INVAL); | 
 |  | 
 |   /* Write out the FRE table first. | 
 |  | 
 |      Recall that read/write of FREs needs information from the corresponding | 
 |      FDE; the latter stores the information about the FRE type record used for | 
 |      the function.  Also note that sorting of FDEs does NOT impact the order | 
 |      in which FREs are stored in the SFrame's FRE sub-section.  This means | 
 |      that writing out FREs after sorting of FDEs will need some additional | 
 |      book-keeping.  At this time, we can afford to avoid it by writing out | 
 |      the FREs first to the output buffer.  */ | 
 |   fre_size = 0; | 
 |   uint32_t global = 0; | 
 |   uint32_t fre_index = 0; | 
 |  | 
 |   contents += hdr_size + all_fdes_size; | 
 |   for (i = 0; i < num_fdes; i++) | 
 |     { | 
 |       fdep = &fd_info->entry[i]; | 
 |       fre_type = sframe_get_fre_type (fdep); | 
 |       num_fres = fdep->sfde_func_num_fres; | 
 |  | 
 |       for (j = 0; j < num_fres; j++) | 
 | 	{ | 
 | 	  fre_index = global + j; | 
 | 	  frep = &fr_info->entry[fre_index]; | 
 |  | 
 | 	  sframe_encoder_write_fre (contents, frep, fre_type, &esz); | 
 | 	  contents += esz; | 
 | 	  fre_size += esz; /* For debugging only.  */ | 
 | 	} | 
 |       global += j; | 
 |     } | 
 |  | 
 |   sframe_assert (fre_size == ehp->sfh_fre_len); | 
 |   sframe_assert (global == ehp->sfh_num_fres); | 
 |   sframe_assert ((size_t)(contents - encoder->sfe_data) == buf_size); | 
 |  | 
 |   /* Sort the FDE table */ | 
 |   sframe_sort_funcdesc (encoder); | 
 |  | 
 |   /* Sanity checks: | 
 |      - the FDE section must have been sorted by now on the start address | 
 |      of each function.  */ | 
 |   flags = ehp->sfh_preamble.sfp_flags; | 
 |   if (!(flags & SFRAME_F_FDE_SORTED) | 
 |       || (fd_info == NULL)) | 
 |     return sframe_set_errno (&err, SFRAME_ERR_FDE_INVAL); | 
 |  | 
 |   contents = encoder->sfe_data; | 
 |   /* Write out the SFrame header.  The SFrame header in the encoder | 
 |      object has already been updated with correct offsets by the caller.  */ | 
 |   memcpy (contents, ehp, hdr_size); | 
 |   contents += hdr_size; | 
 |  | 
 |   /* Write out the FDE table sorted on funtion start address.  */ | 
 |   memcpy (contents, fd_info->entry, all_fdes_size); | 
 |   contents += all_fdes_size; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Serialize the contents of the encoder and return the buffer.  ENCODED_SIZE | 
 |    is updated to the size of the buffer.  */ | 
 |  | 
 | char * | 
 | sframe_encoder_write (sframe_encoder_ctx *encoder, | 
 | 		      size_t *encoded_size, int *errp) | 
 | { | 
 |   sframe_header *ehp; | 
 |   size_t hdrsize, fsz, fresz, bufsize; | 
 |   int foreign_endian; | 
 |  | 
 |   /* Initialize the encoded_size to zero.  This makes it simpler to just | 
 |      return from the function in case of failure.  Free'ing up of | 
 |      encoder->sfe_data is the responsibility of the caller.  */ | 
 |   *encoded_size = 0; | 
 |  | 
 |   if (encoder == NULL || encoded_size == NULL || errp == NULL) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_INVAL); | 
 |  | 
 |   ehp = sframe_encoder_get_header (encoder); | 
 |   hdrsize = sframe_get_hdr_size (ehp); | 
 |   fsz = sframe_encoder_get_num_fidx (encoder) | 
 |     * sizeof (sframe_func_desc_entry); | 
 |   fresz = encoder->sfe_fre_nbytes; | 
 |  | 
 |   /* The total size of buffer is the sum of header, SFrame Function Descriptor | 
 |      Entries section and the FRE section.  */ | 
 |   bufsize = hdrsize + fsz + fresz; | 
 |   encoder->sfe_data = (char *) malloc (bufsize); | 
 |   if (encoder->sfe_data == NULL) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_NOMEM); | 
 |   encoder->sfe_data_size = bufsize; | 
 |  | 
 |   /* Update the information in the SFrame header.  */ | 
 |   /* SFrame FDE section follows immediately after the header.  */ | 
 |   ehp->sfh_fdeoff = 0; | 
 |   /* SFrame FRE section follows immediately after the SFrame FDE section.  */ | 
 |   ehp->sfh_freoff = fsz; | 
 |   ehp->sfh_fre_len = fresz; | 
 |  | 
 |   foreign_endian = need_swapping (ehp->sfh_abi_arch); | 
 |  | 
 |   /* Write out the FDE Index and the FRE table in the sfe_data. */ | 
 |   if (sframe_encoder_write_sframe (encoder)) | 
 |     return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); | 
 |  | 
 |   /* Endian flip the contents if necessary.  */ | 
 |   if (foreign_endian) | 
 |     { | 
 |       if (flip_sframe (encoder->sfe_data, bufsize, 1)) | 
 | 	return sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL); | 
 |       flip_header ((sframe_header*)encoder->sfe_data); | 
 |     } | 
 |  | 
 |   *encoded_size = bufsize; | 
 |   return encoder->sfe_data; | 
 | } |