diff options
Diffstat (limited to 'gas/gen-sframe.c')
-rw-r--r-- | gas/gen-sframe.c | 1294 |
1 files changed, 1294 insertions, 0 deletions
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c new file mode 100644 index 0000000..13f5bda --- /dev/null +++ b/gas/gen-sframe.c @@ -0,0 +1,1294 @@ +/* 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 */ |