--- /dev/null
+/* gen-sframe.c - Support for generating SFrame section.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS 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 GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "sframe.h"
+#include "gen-sframe.h"
+#include "dw2gencfi.h"
+
+#ifdef support_sframe_p
+
+/* By default, use 32-bit relocations from .sframe into .text. */
+#ifndef SFRAME_RELOC_SIZE
+# define SFRAME_RELOC_SIZE 4
+#endif
+
+/* Whether frame row entries track RA.
+
+ A target may not need return address tracking for stack unwinding. If it
+ does need the same, SFRAME_CFA_RA_REG must be defined with the return
+ address register number. */
+
+#if defined (sframe_ra_tracking_p) && defined (SFRAME_CFA_RA_REG)
+# ifndef SFRAME_FRE_RA_TRACKING
+# define SFRAME_FRE_RA_TRACKING 1
+# endif
+#endif
+
+/* SFrame FRE type selection optimization is an optimization for size.
+
+ There are three flavors of SFrame FRE representation in the binary format:
+ - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte.
+ - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes.
+ - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes.
+
+ Note that in the SFrame format, all SFrame FREs of a function use one
+ single representation. The SFrame FRE type itself is identified via the
+ information in the SFrame FDE function info.
+
+ Now, to select the minimum required one from the list above, one needs to
+ make a decision based on the size (in bytes) of the function.
+
+ As a result, for this optimization, some fragments (generated with a new
+ type rs_sframe) for the SFrame section are fixed up later.
+
+ This optimization (for size) is enabled by default. */
+
+#ifndef SFRAME_FRE_TYPE_SELECTION_OPT
+# define SFRAME_FRE_TYPE_SELECTION_OPT 1
+#endif
+
+/* Emit a single byte into the current segment. */
+
+static inline void
+out_one (int byte)
+{
+ FRAG_APPEND_1_CHAR (byte);
+}
+
+/* Emit a two-byte word into the current segment. */
+
+static inline void
+out_two (int data)
+{
+ md_number_to_chars (frag_more (2), data, 2);
+}
+
+/* Emit a four byte word into the current segment. */
+
+static inline void
+out_four (int data)
+{
+ md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Get the start address symbol from the DWARF FDE. */
+
+static symbolS*
+get_dw_fde_start_addrS (const struct fde_entry *dw_fde)
+{
+ return dw_fde->start_address;
+}
+
+/* Get the start address symbol from the DWARF FDE. */
+
+static symbolS*
+get_dw_fde_end_addrS (const struct fde_entry *dw_fde)
+{
+ return dw_fde->end_address;
+}
+
+/* SFrame Frame Row Entry (FRE) related functions. */
+
+static void
+sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS)
+{
+ fre->pc_begin = beginS;
+}
+
+static void
+sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS)
+{
+ fre->pc_end = endS;
+}
+
+static void
+sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre,
+ unsigned int cfa_base_reg)
+{
+ fre->cfa_base_reg = cfa_base_reg;
+ fre->merge_candidate = false;
+}
+
+static void
+sframe_fre_set_cfa_offset (struct sframe_row_entry *fre,
+ offsetT cfa_offset)
+{
+ fre->cfa_offset = cfa_offset;
+ fre->merge_candidate = false;
+}
+
+#ifdef SFRAME_FRE_RA_TRACKING
+static void
+sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset)
+{
+ fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK;
+ fre->ra_offset = ra_offset;
+ fre->merge_candidate = false;
+}
+#endif
+
+static void
+sframe_fre_set_bp_track (struct sframe_row_entry *fre, offsetT bp_offset)
+{
+ fre->bp_loc = SFRAME_FRE_ELEM_LOC_STACK;
+ fre->bp_offset = bp_offset;
+ fre->merge_candidate = false;
+}
+
+/* All stack offset values within an FRE are uniformly encoded in the same
+ number of bytes. The size of the stack offset values will, however, vary
+ across FREs. */
+
+#define VALUE_8BIT 0x7f
+#define VALUE_16BIT 0x7fff
+#define VALUE_32BIT 0x7fffffff
+#define VALUE_64BIT 0x7fffffffffffffff
+
+/* Given a signed offset, return the size in bytes needed to represent it. */
+
+static unsigned int
+get_offset_size_in_bytes (offsetT value)
+{
+ unsigned int size = 0;
+
+ if (value <= VALUE_8BIT && value >= (offsetT) -VALUE_8BIT)
+ size = 1;
+ else if (value <= VALUE_16BIT && value >= (offsetT) -VALUE_16BIT)
+ size = 2;
+ else if (value <= VALUE_32BIT && value >= (offsetT) -VALUE_32BIT)
+ size = 4;
+ else if ((sizeof (offsetT) > 4) && (value <= (offsetT) VALUE_64BIT
+ && value >= (offsetT) -VALUE_64BIT))
+ size = 8;
+
+ return size;
+}
+
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B 0 /* SFRAME_FRE_OFFSET_1B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B 1 /* SFRAME_FRE_OFFSET_2B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B 2 /* SFRAME_FRE_OFFSET_4B. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B 3 /* Not supported in SFrame. */
+#define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B
+
+/* Helper struct for mapping offset size to output functions. */
+
+struct sframe_fre_offset_func_map
+{
+ unsigned int offset_size;
+ void (*out_func)(int);
+};
+
+/* Given an OFFSET_SIZE, return the size in bytes needed to represent it. */
+
+static unsigned int
+sframe_fre_offset_func_map_index (unsigned int offset_size)
+{
+ unsigned int index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX;
+
+ switch (offset_size)
+ {
+ case SFRAME_FRE_OFFSET_1B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B;
+ break;
+ case SFRAME_FRE_OFFSET_2B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B;
+ break;
+ case SFRAME_FRE_OFFSET_4B:
+ index = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B;
+ break;
+ default:
+ /* Not supported in SFrame. */
+ break;
+ }
+
+ return index;
+}
+
+/* Mapping from offset size to the output function to emit the value. */
+
+static const
+struct sframe_fre_offset_func_map
+fre_offset_func_map[SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] =
+{
+ { SFRAME_FRE_OFFSET_1B, out_one },
+ { SFRAME_FRE_OFFSET_2B, out_two },
+ { SFRAME_FRE_OFFSET_4B, out_four },
+ { -1, NULL } /* Not Supported in SFrame. */
+};
+
+/* SFrame version specific operations access. */
+
+static struct sframe_version_ops sframe_ver_ops;
+
+/* SFrame (SFRAME_VERSION_1) set FRE info. */
+
+static unsigned char
+sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+ unsigned int offset_size)
+{
+ unsigned char fre_info;
+ fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size);
+ return fre_info;
+}
+
+/* SFrame (SFRAME_VERSION_1) set function info. */
+static unsigned char
+sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+ unsigned char func_info;
+ func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type);
+ return func_info;
+}
+
+/* SFrame version specific operations setup. */
+
+static void
+sframe_set_version (uint32_t sframe_version __attribute__((unused)))
+{
+ sframe_ver_ops.format_version = SFRAME_VERSION_1;
+
+ sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info;
+
+ sframe_ver_ops.set_func_info = sframe_v1_set_func_info;
+}
+
+/* SFrame set FRE info. */
+
+static unsigned char
+sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets,
+ unsigned int offset_size)
+{
+ return sframe_ver_ops.set_fre_info (base_reg, num_offsets,
+ offset_size);
+}
+
+/* SFrame set func info. */
+
+ATTRIBUTE_UNUSED static unsigned char
+sframe_set_func_info (unsigned int fde_type, unsigned int fre_type)
+{
+ return sframe_ver_ops.set_func_info (fde_type, fre_type);
+}
+
+/* Get the number of SFrame FDEs for the current file. */
+
+static unsigned int
+get_num_sframe_fdes (void);
+
+/* Get the number of SFrame frame row entries for the current file. */
+
+static unsigned int
+get_num_sframe_fres (void);
+
+/* Get CFA base register ID as represented in SFrame Frame Row Entry. */
+
+static unsigned int
+get_fre_base_reg_id (struct sframe_row_entry *sframe_fre)
+{
+ unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg;
+ unsigned fre_base_reg = SFRAME_BASE_REG_SP;
+
+ if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG)
+ fre_base_reg = SFRAME_BASE_REG_FP;
+
+ /* Only one bit is reserved in SFRAME_VERSION_1. */
+ gas_assert (fre_base_reg == SFRAME_BASE_REG_SP
+ || fre_base_reg == SFRAME_BASE_REG_FP);
+
+ return fre_base_reg;
+}
+
+/* Get number of offsets necessary for the SFrame Frame Row Entry. */
+
+static unsigned int
+get_fre_num_offsets (struct sframe_row_entry *sframe_fre)
+{
+ /* Atleast 1 must always be present (to recover CFA). */
+ unsigned int fre_num_offsets = 1;
+
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ fre_num_offsets++;
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ fre_num_offsets++;
+#endif
+ return fre_num_offsets;
+}
+
+/* Get the minimum necessary offset size (in bytes) for this
+ SFrame frame row entry. */
+
+static unsigned int
+sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre)
+{
+ unsigned int max_offset_size = 0;
+ unsigned int cfa_offset_size = 0;
+ unsigned int bp_offset_size = 0;
+ unsigned int ra_offset_size = 0;
+
+ unsigned int fre_offset_size = 0;
+
+ /* What size of offsets appear in this frame row entry. */
+ cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset);
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset);
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_ra_tracking_p ()
+ && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset);
+#endif
+
+ /* Get the maximum size needed to represent the offsets. */
+ max_offset_size = cfa_offset_size;
+ if (bp_offset_size > max_offset_size)
+ max_offset_size = bp_offset_size;
+ if (ra_offset_size > max_offset_size)
+ max_offset_size = ra_offset_size;
+
+ gas_assert (max_offset_size);
+
+ switch (max_offset_size)
+ {
+ case 1:
+ fre_offset_size = SFRAME_FRE_OFFSET_1B;
+ break;
+ case 2:
+ fre_offset_size = SFRAME_FRE_OFFSET_2B;
+ break;
+ case 4:
+ fre_offset_size = SFRAME_FRE_OFFSET_4B;
+ break;
+ default:
+ /* Offset of size 8 bytes is not supported in SFrame format
+ version 1. */
+ as_fatal (_("SFrame unsupported offset value\n"));
+ break;
+ }
+
+ return fre_offset_size;
+}
+
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+
+/* Create a composite exression CEXP (for SFrame FRE start address) such that:
+
+ exp = <val> OP_absent <width>, where,
+
+ - <val> and <width> are themselves expressionS.
+ - <val> stores the expression which when evaluated gives the value of the
+ start address offset of the FRE.
+ - <width> stores the expression when when evaluated gives the number of
+ bytes needed to encode the start address offset of the FRE.
+
+ The use of OP_absent as the X_op_symbol helps identify this expression
+ later when fragments are fixed up. */
+
+static void
+create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin,
+ symbolS *fde_start_address,
+ symbolS *fde_end_address)
+{
+ expressionS val;
+ expressionS width;
+
+ /* val expression stores the FDE start address offset from the start PC
+ of function. */
+ val.X_op = O_subtract;
+ val.X_add_symbol = fre_pc_begin;
+ val.X_op_symbol = fde_start_address;
+ val.X_add_number = 0;
+
+ /* width expressions stores the size of the function. This is used later
+ to determine the number of bytes to be used to encode the FRE start
+ address of each FRE of the function. */
+ width.X_op = O_subtract;
+ width.X_add_symbol = fde_end_address;
+ width.X_op_symbol = fde_start_address;
+ width.X_add_number = 0;
+
+ cexp->X_op = O_absent;
+ cexp->X_add_symbol = make_expr_symbol (&val);
+ cexp->X_op_symbol = make_expr_symbol (&width);
+ cexp->X_add_number = 0;
+}
+
+#endif
+
+static void
+output_sframe_row_entry (symbolS *fde_start_addr,
+ symbolS *fde_end_addr,
+ struct sframe_row_entry *sframe_fre)
+{
+ unsigned char fre_info;
+ unsigned int fre_num_offsets;
+ unsigned int fre_offset_size;
+ unsigned int fre_base_reg;
+ expressionS exp;
+ unsigned int fre_addr_size;
+
+ unsigned int index = 0;
+ unsigned int fre_write_offsets = 0;
+
+ fre_addr_size = 4; /* 4 bytes by default. FIXME tie it to fre_type? */
+
+ /* SFrame FRE Start Address. */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+ create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr,
+ fde_end_addr);
+ frag_grow (fre_addr_size);
+ frag_var (rs_sframe, fre_addr_size, 0, (relax_substateT) 0,
+ make_expr_symbol (&exp), 0, (char *) frag_now);
+#else
+ gas_assert (fde_end_addr);
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = sframe_fre->pc_begin; /* to. */
+ exp.X_op_symbol = fde_start_addr; /* from. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, fre_addr_size);
+#endif
+
+ /* Create the fre_info using the CFA base register, number of offsets and max
+ size of offset in this frame row entry. */
+ fre_base_reg = get_fre_base_reg_id (sframe_fre);
+ fre_num_offsets = get_fre_num_offsets (sframe_fre);
+ fre_offset_size = sframe_get_fre_offset_size (sframe_fre);
+ fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets,
+ fre_offset_size);
+ out_one (fre_info);
+
+ index = sframe_fre_offset_func_map_index (fre_offset_size);
+ gas_assert (index < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX);
+
+ /* Write out the offsets in order - cfa, bp, ra. */
+ fre_offset_func_map[index].out_func (sframe_fre->cfa_offset);
+ fre_write_offsets++;
+
+#ifdef SFRAME_FRE_RA_TRACKING
+ if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[index].out_func (sframe_fre->ra_offset);
+ fre_write_offsets++;
+ }
+#endif
+ if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK)
+ {
+ fre_offset_func_map[index].out_func (sframe_fre->bp_offset);
+ fre_write_offsets++;
+ }
+
+ /* Check if the expected number offsets have been written out
+ in this FRE. */
+ gas_assert (fre_write_offsets == fre_num_offsets);
+}
+
+static void
+output_sframe_funcdesc (symbolS *start_of_fre_section,
+ symbolS *fre_symbol,
+ struct sframe_func_entry *sframe_fde)
+{
+ expressionS exp;
+ unsigned int addr_size;
+ symbolS *dw_fde_start_addrS, *dw_fde_end_addrS;
+
+ addr_size = SFRAME_RELOC_SIZE;
+ dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde);
+ dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde);
+
+ /* Start address of the function. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = dw_fde_start_addrS; /* to location. */
+ exp.X_op_symbol = symbol_temp_new_now (); /* from location. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Size of the function in bytes. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = dw_fde_end_addrS;
+ exp.X_op_symbol = dw_fde_start_addrS;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset to the first frame row entry. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = fre_symbol; /* Minuend. */
+ exp.X_op_symbol = start_of_fre_section; /* Subtrahend. */
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Number of FREs. */
+ out_four (sframe_fde->num_fres);
+
+ /* SFrame FDE function info. */
+#if SFRAME_FRE_TYPE_SELECTION_OPT
+ expressionS width;
+ width.X_op = O_subtract;
+ width.X_add_symbol = dw_fde_end_addrS;
+ width.X_op_symbol = dw_fde_start_addrS;
+ width.X_add_number = 0;
+ frag_grow (1); /* Size of func info is unsigned char. */
+ frag_var (rs_sframe, 1, 0, (relax_substateT) 0,
+ make_expr_symbol (&width), 0, (char *) frag_now);
+#else
+ unsigned char func_info;
+ func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC,
+ SFRAME_FRE_TYPE_ADDR4);
+ out_one (func_info);
+#endif
+}
+
+static void
+output_sframe_internal (void)
+{
+ expressionS exp;
+ unsigned int i = 0;
+
+ symbolS *end_of_frame_hdr;
+ symbolS *end_of_frame_section;
+ symbolS *start_of_func_desc_section;
+ symbolS *start_of_fre_section;
+ struct sframe_func_entry *sframe_fde;
+ struct sframe_row_entry *sframe_fre;
+ unsigned char abi_arch = 0;
+ int fixed_bp_offset = SFRAME_CFA_FIXED_FP_INVALID;
+ int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID;
+ unsigned int addr_size;
+
+ addr_size = SFRAME_RELOC_SIZE;
+
+ /* The function desciptor entries as dumped by the assembler are not
+ sorted on PCs. */
+ unsigned char sframe_flags = 0;
+ sframe_flags |= !SFRAME_F_FDE_SORTED;
+
+ unsigned int num_fdes = get_num_sframe_fdes ();
+ unsigned int num_fres = get_num_sframe_fres ();
+ symbolS **fre_symbols = XNEWVEC (symbolS *, num_fres);
+ for (i = 0; i < num_fres; i++)
+ fre_symbols[i] = symbol_temp_make ();
+
+ end_of_frame_hdr = symbol_temp_make ();
+ start_of_fre_section = symbol_temp_make ();
+ start_of_func_desc_section = symbol_temp_make ();
+ end_of_frame_section = symbol_temp_make ();
+
+ /* Output the preamble of SFrame section. */
+ out_two (SFRAME_MAGIC);
+ out_one (SFRAME_VERSION);
+ out_one (sframe_flags);
+ /* abi/arch. */
+#ifdef sframe_get_abi_arch
+ abi_arch = sframe_get_abi_arch ();
+#endif
+ gas_assert (abi_arch);
+ out_one (abi_arch);
+
+ /* Offset for the BP register from CFA. Neither of the AMD64 or AAPCS64
+ ABIs have a fixed offset for the BP register from the CFA. This may be
+ useful in future (but not without additional support in the toolchain)
+ for specialized handling/encoding for cases where, for example,
+ -fno-omit-frame-pointer is used. */
+ out_one (fixed_bp_offset);
+
+ /* Offset for the return address from CFA is fixed for some ABIs
+ (e.g., AMD64), output a zero otherwise. */
+#ifdef sframe_ra_tracking_p
+ if (!sframe_ra_tracking_p ())
+ fixed_ra_offset = sframe_cfa_ra_offset ();
+#endif
+ out_one (fixed_ra_offset);
+
+ /* None of the AMD64, or AARCH64 ABIs need the auxilliary header.
+ When the need does arise to use this field, the appropriate backend
+ must provide this information. */
+ out_one (0); /* Auxilliary SFrame header length. */
+
+ out_four (num_fdes); /* Number of FDEs. */
+ out_four (num_fres); /* Number of FREs. */
+
+ /* FRE sub-section len. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = end_of_frame_section;
+ exp.X_op_symbol = start_of_fre_section;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset of Function Index sub-section. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = end_of_frame_hdr;
+ exp.X_op_symbol = start_of_func_desc_section;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ /* Offset of FRE sub-section. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = start_of_fre_section;
+ exp.X_op_symbol = end_of_frame_hdr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, addr_size);
+
+ symbol_set_value_now (end_of_frame_hdr);
+ symbol_set_value_now (start_of_func_desc_section);
+
+ /* Output the SFrame function descriptor entries. */
+ i = 0;
+ for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+ {
+ output_sframe_funcdesc (start_of_fre_section,
+ fre_symbols[i], sframe_fde);
+ i += sframe_fde->num_fres;
+ }
+
+ symbol_set_value_now (start_of_fre_section);
+
+ /* Output the SFrame FREs. */
+ i = 0;
+ sframe_fde = all_sframe_fdes;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next)
+ {
+ for (sframe_fre = sframe_fde->sframe_fres;
+ sframe_fre;
+ sframe_fre = sframe_fre->next)
+ {
+ symbol_set_value_now (fre_symbols[i]);
+ output_sframe_row_entry (get_dw_fde_start_addrS (sframe_fde->dw_fde),
+ get_dw_fde_end_addrS (sframe_fde->dw_fde),
+ sframe_fre);
+ i++;
+ }
+ }
+
+ symbol_set_value_now (end_of_frame_section);
+
+ gas_assert (i == num_fres);
+
+ free (fre_symbols);
+ fre_symbols = NULL;
+}
+
+/* List of SFrame FDE entries. */
+
+struct sframe_func_entry *all_sframe_fdes;
+
+/* Tail of the list to add to. */
+
+static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes;
+
+static unsigned int
+get_num_sframe_fdes (void)
+{
+ struct sframe_func_entry *sframe_fde;
+ unsigned int total_fdes = 0;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+ total_fdes++;
+
+ return total_fdes;
+}
+
+/* Get the total number of SFrame row entries across the FDEs. */
+
+static unsigned int
+get_num_sframe_fres (void)
+{
+ struct sframe_func_entry *sframe_fde;
+ unsigned int total_fres = 0;
+
+ for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next)
+ total_fres += sframe_fde->num_fres;
+
+ return total_fres;
+}
+
+/* Allocate an SFrame FDE. */
+
+static struct sframe_func_entry*
+sframe_fde_alloc (void)
+{
+ struct sframe_func_entry *sframe_fde = XCNEW (struct sframe_func_entry);
+ return sframe_fde;
+}
+
+/* Link the SFrame FDE in. */
+
+static int
+sframe_fde_link (struct sframe_func_entry *sframe_fde)
+{
+ *last_sframe_fde = sframe_fde;
+ last_sframe_fde = &sframe_fde->next;
+
+ return 0;
+}
+
+/* Free up the SFrame FDE. */
+
+static void
+sframe_fde_free (struct sframe_func_entry *sframe_fde)
+{
+ XDELETE (sframe_fde);
+ sframe_fde = NULL;
+}
+
+/* SFrame translation context functions. */
+
+/* Allocate a new SFrame translation context. */
+
+static struct sframe_xlate_ctx*
+sframe_xlate_ctx_alloc (void)
+{
+ struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx);
+ return xlate_ctx;
+}
+
+/* Initialize the given SFrame translation context. */
+
+static void
+sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx)
+{
+ xlate_ctx->dw_fde = NULL;
+ xlate_ctx->first_fre = NULL;
+ xlate_ctx->last_fre = NULL;
+ xlate_ctx->cur_fre = NULL;
+ xlate_ctx->remember_fre = NULL;
+ xlate_ctx->num_xlate_fres = 0;
+}
+
+/* Cleanup the given SFrame translation context. */
+
+static void
+sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx)
+{
+ struct sframe_row_entry *fre, *fre_next;
+
+ if (xlate_ctx->num_xlate_fres)
+ {
+ fre = xlate_ctx->first_fre;
+ while (fre)
+ {
+ fre_next = fre->next;
+ XDELETE (fre);
+ fre = fre_next;
+ }
+ }
+
+ sframe_xlate_ctx_init (xlate_ctx);
+}
+
+/* Transfer the state from the SFrame translation context to the SFrame FDE. */
+
+static void
+sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx,
+ struct sframe_func_entry *sframe_fde)
+{
+ sframe_fde->dw_fde = xlate_ctx->dw_fde;
+ sframe_fde->sframe_fres = xlate_ctx->first_fre;
+ sframe_fde->num_fres = xlate_ctx->num_xlate_fres;
+}
+
+static struct sframe_row_entry*
+sframe_row_entry_new (void)
+{
+ struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry);
+ /* Reset cfa_base_reg to -1. A value of 0 will imply some valid register
+ for the supported arches. */
+ fre->cfa_base_reg = -1;
+ fre->merge_candidate = true;
+
+ return fre;
+}
+
+/* Add the given FRE in the list of frame row entries in the given FDE
+ translation context. */
+
+static void
+sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx,
+ struct sframe_row_entry *fre)
+{
+ gas_assert (xlate_ctx && fre);
+
+ /* Add the frame row entry. */
+ if (!xlate_ctx->first_fre)
+ xlate_ctx->first_fre = fre;
+ else if (xlate_ctx->last_fre)
+ xlate_ctx->last_fre->next = fre;
+
+ xlate_ctx->last_fre = fre;
+
+ /* Keep track of the total number of SFrame frame row entries. */
+ xlate_ctx->num_xlate_fres++;
+}
+
+/* A SFrame Frame Row Entry is self-sufficient in terms of unwind information
+ for a given PC. It contains information assimilated from multiple CFI
+ instructions, and hence, a new SFrame FRE is initialized with the data from
+ the previous known FRE, if any.
+
+ Understandably, not all information (especially the instruction begin
+ and end boundaries) needs to be relayed. Hence, the caller of this API
+ must set the pc_begin and pc_end as applicable. */
+
+static void
+sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
+ struct sframe_row_entry *prev_fre)
+{
+ gas_assert (prev_fre);
+ cur_fre->cfa_base_reg = prev_fre->cfa_base_reg;
+ cur_fre->cfa_offset = prev_fre->cfa_offset;
+ cur_fre->bp_loc = prev_fre->bp_loc;
+ cur_fre->bp_offset = prev_fre->bp_offset;
+ cur_fre->ra_loc = prev_fre->ra_loc;
+ cur_fre->ra_offset = prev_fre->ra_offset;
+}
+
+/* Translate DW_CFA_advance_loc into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+ /* Get the scratchpad FRE currently being updated as the cfi_insn's
+ get interpreted. This FRE eventually gets linked in into the
+ list of FREs for the specific function. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ if (cur_fre)
+ {
+ if (!cur_fre->merge_candidate)
+ {
+ sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+ sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre);
+ last_fre = xlate_ctx->last_fre;
+
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+
+ if (last_fre)
+ sframe_row_entry_initialize (cur_fre, last_fre);
+ }
+ else
+ {
+ sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2);
+ gas_assert (last_fre->merge_candidate == false);
+ }
+ }
+ else
+ {
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+ }
+
+ gas_assert (cur_fre);
+ sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2);
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+
+{
+ /* Get the scratchpad FRE. This FRE will eventually get linked in. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ if (!cur_fre)
+ {
+ xlate_ctx->cur_fre = sframe_row_entry_new ();
+ cur_fre = xlate_ctx->cur_fre;
+ sframe_fre_set_begin_addr (cur_fre,
+ get_dw_fde_start_addrS (xlate_ctx->dw_fde));
+ }
+ /* Define the current CFA rule to use the provided register and
+ offset. */
+ sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+ sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa_register into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+ /* Get the scratchpad FRE. This FRE will eventually get linked in. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+ gas_assert (cur_fre);
+ /* Define the current CFA rule to use the provided register (but to
+ keep the old offset). */
+ sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg);
+ sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset);
+ cur_fre->merge_candidate = false;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_def_cfa_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_def_cfa_offset (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ /* The scratchpad FRE currently being updated with each cfi_insn
+ being interpreted. This FRE eventually gets linked in into the
+ list of FREs for the specific function. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ gas_assert (cur_fre);
+ /* Define the current CFA rule to use the provided offset (but to keep
+ the old register). However, if the old register is not FP/SP,
+ skip creating SFrame unwind info for the function. */
+ if ((cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG)
+ || (cur_fre->cfa_base_reg == SFRAME_CFA_SP_REG))
+ {
+ sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i);
+ cur_fre->merge_candidate = false;
+ }
+ else
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ /* The scratchpad FRE currently being updated with each cfi_insn
+ being interpreted. This FRE eventually gets linked in into the
+ list of FREs for the specific function. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ gas_assert (cur_fre);
+ /* Change the rule for the register indicated by the register number to
+ be the specified offset. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ {
+ gas_assert (!cur_fre->base_reg);
+ sframe_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+ }
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ {
+ sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset);
+ cur_fre->merge_candidate = false;
+ }
+#endif
+ /* This is used to track changes to non-rsp registers, skip all others
+ except FP / RA for now. */
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_val_offset into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_val_offset (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED,
+ struct cfi_insn_data *cfi_insn)
+{
+ /* Previous value of register is CFA + offset. However, if the specified
+ register is not interesting (FP or RA reg), the current DW_CFA_val_offset
+ instruction can be safely skipped without sacrificing the asynchonicity of
+ unwind information. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */
+#endif
+
+ /* Safe to skip. */
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_remember_state into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+ struct sframe_row_entry *last_fre = xlate_ctx->last_fre;
+
+ /* If there is no FRE state to remember, nothing to do here. Return
+ early with non-zero error code, this will cause no SFrame unwind info
+ for the function involved. */
+ if (!last_fre)
+ return SFRAME_XLATE_ERR_INVAL;
+
+ if (!xlate_ctx->remember_fre)
+ xlate_ctx->remember_fre = sframe_row_entry_new ();
+ sframe_row_entry_initialize (xlate_ctx->remember_fre, last_fre);
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_restore_state into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx)
+{
+ /* The scratchpad FRE currently being updated with each cfi_insn
+ being interpreted. This FRE eventually gets linked in into the
+ list of FREs for the specific function. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ gas_assert (xlate_ctx->remember_fre);
+ gas_assert (cur_fre && cur_fre->merge_candidate);
+
+ /* Get the CFA state from the DW_CFA_remember_state insn. */
+ sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre);
+ /* The PC boundaries of the current SFrame FRE are updated
+ via other machinery. */
+ cur_fre->merge_candidate = false;
+ return SFRAME_XLATE_OK;
+}
+
+/* Translate DW_CFA_restore into SFrame context.
+ Return SFRAME_XLATE_OK if success. */
+
+static int
+sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ struct sframe_row_entry *cie_fre = xlate_ctx->first_fre;
+ /* The scratchpad FRE currently being updated with each cfi_insn
+ being interpreted. This FRE eventually gets linked in into the
+ list of FREs for the specific function. */
+ struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+ /* Change the rule for the indicated register to the rule assigned to
+ it by the initial_instructions in the CIE. */
+ gas_assert (cie_fre);
+ /* SFrame FREs track only CFA and FP / RA for backtracing purposes;
+ skip the other .cfi_restore directives. */
+ if (cfi_insn->u.r == SFRAME_CFA_FP_REG)
+ {
+ gas_assert (cur_fre);
+ cur_fre->bp_loc = cie_fre->bp_loc;
+ cur_fre->bp_offset = cie_fre->bp_offset;
+ cur_fre->merge_candidate = false;
+ }
+#ifdef SFRAME_FRE_RA_TRACKING
+ else if (sframe_ra_tracking_p ()
+ && cfi_insn->u.r == SFRAME_CFA_RA_REG)
+ {
+ gas_assert (cur_fre);
+ cur_fre->ra_loc = cie_fre->ra_loc;
+ cur_fre->ra_offset = cie_fre->ra_offset;
+ cur_fre->merge_candidate = false;
+ }
+#endif
+ return SFRAME_XLATE_OK;
+}
+
+/* Process CFI_INSN and update the translation context with the FRE
+ information.
+
+ Returns an error code (sframe_xlate_err) if CFI_INSN is not successfully
+ processed. */
+
+static int
+sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
+ struct cfi_insn_data *cfi_insn)
+{
+ int err = 0;
+
+ /* Atleast one cfi_insn per FDE is expected. */
+ gas_assert (cfi_insn);
+ int op = cfi_insn->insn;
+
+ switch (op)
+ {
+ case DW_CFA_advance_loc:
+ err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa:
+ err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa_register:
+ err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_def_cfa_offset:
+ err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_offset:
+ err = sframe_xlate_do_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_val_offset:
+ err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_remember_state:
+ err = sframe_xlate_do_remember_state (xlate_ctx);
+ break;
+ case DW_CFA_restore_state:
+ err = sframe_xlate_do_restore_state (xlate_ctx);
+ break;
+ case DW_CFA_restore:
+ err = sframe_xlate_do_restore (xlate_ctx, cfi_insn);
+ break;
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ break;
+ default:
+ {
+ /* Other CFI opcodes are not processed at this time.
+ These do not impact the coverage of the basic stack unwinding
+ information as conveyed in the SFrame format.
+ - DW_CFA_register,
+ - ...
+
+ Following skipped operations do, however, impact the asynchronicity:
+ - CFI_escape,
+ - DW_CFA_GNU_window_save,
+ - DW_CFA_AARCH64_negate_ra_state (multiplexed with
+ DW_CFA_GNU_window_save) */
+
+ err = SFRAME_XLATE_ERR_NOTREPRESENTED;
+ // printf (_("SFrame Unsupported or unknown Dwarf CFI number: %#x\n"), op);
+ }
+ }
+
+ return err;
+}
+
+
+static int
+sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx,
+ const struct fde_entry *dw_fde)
+{
+ struct cfi_insn_data *cfi_insn;
+ int err = SFRAME_XLATE_OK;
+
+ xlate_ctx->dw_fde = dw_fde;
+
+ /* If the return column is not RIP, SFrame format cannot represent it. */
+ if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN)
+ return SFRAME_XLATE_ERR_NOTREPRESENTED;
+
+ /* Iterate over the CFIs and create SFrame FREs. */
+ for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next)
+ {
+ /* Translate each CFI, and buffer the state in translation context. */
+ err = sframe_do_cfi_insn (xlate_ctx, cfi_insn);
+ if (err != SFRAME_XLATE_OK)
+ {
+ /* Skip generating SFrame unwind info for the function if any
+ offending CFI is encountered by sframe_do_cfi_insn (). */
+ return err; /* Return the error code. */
+ }
+ }
+
+ /* No errors encountered. */
+
+ /* Link in the scratchpad FRE that the last few CFI insns helped create. */
+ if (xlate_ctx->cur_fre)
+ {
+ sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre);
+ xlate_ctx->cur_fre = NULL;
+ }
+ /* Designate the end of the last SFrame FRE. */
+ if (xlate_ctx->last_fre)
+ {
+ xlate_ctx->last_fre->pc_end
+ = get_dw_fde_end_addrS (xlate_ctx->dw_fde);
+ }
+
+ return SFRAME_XLATE_OK;
+}
+
+/* Create SFrame unwind info for all functions.
+
+ This function consumes the already generated FDEs (by dw2gencfi) and
+ generates unwind data in SFrame format. */
+
+static void
+create_sframe_all (void)
+{
+ struct fde_entry *dw_fde = NULL;
+ struct sframe_func_entry *sframe_fde = NULL;
+
+ struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc ();
+
+ for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next)
+ {
+ sframe_fde = sframe_fde_alloc ();
+ /* Initialize the translation context with information anew. */
+ sframe_xlate_ctx_init (xlate_ctx);
+
+ /* Process and link SFrame FDEs if no error. Also skip adding an SFrame
+ FDE if it does not contain any SFrame FREs. There is little use of an
+ SFrame FDE if there is no unwind information about the function. */
+ int err = sframe_do_fde (xlate_ctx, dw_fde);
+ if (err || xlate_ctx->num_xlate_fres == 0)
+ {
+ sframe_xlate_ctx_cleanup (xlate_ctx);
+ sframe_fde_free (sframe_fde);
+ }
+ else
+ {
+ /* All done. Transfer the state from the SFrame translation
+ context to the SFrame FDE. */
+ sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde);
+ sframe_fde_link (sframe_fde);
+ }
+ }
+}
+
+void
+output_sframe (segT sframe_seg)
+{
+ (void) sframe_seg;
+
+ /* Setup the version specific access functions. */
+ sframe_set_version (SFRAME_VERSION_1);
+
+ /* Process all fdes and create SFrame unwind information. */
+ create_sframe_all ();
+
+ output_sframe_internal ();
+}
+
+#else /* support_sframe_p */
+
+void
+output_sframe (segT sframe_seg __attribute__((unused)))
+{
+}
+
+#endif /* support_sframe_p */