summaryrefslogtreecommitdiff
path: root/gas/sframe-opt.c
diff options
context:
space:
mode:
authorIndu Bhagat <indu.bhagat@oracle.com>2022-11-15 15:06:46 -0800
committerIndu Bhagat <indu.bhagat@oracle.com>2022-11-15 15:24:06 -0800
commitb52c4ee46657eb5a70095d48cbc2938d024cc3b6 (patch)
tree10fb1afba608156a25ebe9732cfe5e3fe1d85833 /gas/sframe-opt.c
parentb07a29781610756a9b75a931c7c13735b7555d9f (diff)
downloadbinutils-gdb-b52c4ee46657eb5a70095d48cbc2938d024cc3b6.tar.gz
gas: generate .sframe from CFI directives
Currently supported for x86_64 and aarch64 only. [PS: Currently, the compiler has not been adapted to generate ".cfi_sections" with ".sframe" in it. The newly added command line option of --gsframe provides an easy way to try out .sframe support in the toolchain.] gas interprets the CFI directives to generate DWARF-based .eh_frame info. These internal DWARF structures are now consumed by gen-sframe.[ch] sub-system to, in turn, create the SFrame unwind information. These internal DWARF structures are read-only for the purpose of SFrame unwind info generation. SFrame unwind info generation does not impact .eh_frame unwind info generation. Both .eh_frame and .sframe can co-exist in an ELF file, if so desired by the user. Recall that SFrame unwind information only contains the minimal necessary information to generate backtraces and does not provide information to recover all callee-saved registers. The reason being that callee-saved registers other than FP are not needed for stack unwinding, and hence are not included in the .sframe section. Consequently, gen-sframe.[ch] only needs to interpret a subset of DWARF opcodes in gas. More details follow. [Set 1, Interpreted] The following opcodes are interpreted: - DW_CFA_advance_loc - DW_CFA_def_cfa - DW_CFA_def_cfa_register - DW_CFA_def_cfa_offset - DW_CFA_offset - DW_CFA_remember_state - DW_CFA_restore_state - DW_CFA_restore [Set 2, Bypassed] The following opcodes are acknowledged but are not necessary for generating SFrame unwind info: - DW_CFA_undefined - DW_CFA_same_value Anything else apart from the two above-mentioned sets is skipped altogether. This means that any function containing a CFI directive not in Set 1 or Set 2 above, will not have any SFrame unwind information generated for them. Holes in instructions covered by FREs of a single FDE are not representable in the SFrame unwind format. As few examples, following opcodes are not processed for .sframe generation, and are skipped: - .cfi_personality* - .cfi_*lsda - .cfi_escape - .cfi_negate_ra_state - ... Not processing .cfi_escape, .cfi_negate_ra_state will cause SFrame unwind information to be absent for SFrame FDEs that contain these CFI directives, hence affecting the asynchronicity. x86-64 and aarch64 backends need to have a few new definitions and functions for .sframe generation. These provide gas with architecture specific information like the SP/FP/RA register numbers and an SFrame-specific ABI marker. Lastly, the patch also implements an optimization for size, where specific fragments containing SFrame FRE start address and SFrame FDE function are fixed up. This is similar to other similar optimizations in gas, where fragments are sized and fixed up when the associated symbols can be resolved. This optimization is controlled by a #define SFRAME_FRE_TYPE_SELECTION_OPT and should be easy to turn off if needed. The optimization is on by default for both x86_64 and aarch64. ChangeLog: * gas/Makefile.am: Include gen-sframe.c and sframe-opt.c. * gas/Makefile.in: Regenerated. * gas/as.h (enum _relax_state): Add new state rs_sframe. (sframe_estimate_size_before_relax): New function. (sframe_relax_frag): Likewise. (sframe_convert_frag): Likewise. * gas/config/tc-aarch64.c (aarch64_support_sframe_p): New definition. (aarch64_sframe_ra_tracking_p): Likewise. (aarch64_sframe_cfa_ra_offset): Likewise. (aarch64_sframe_get_abi_arch): Likewise. (md_begin): Set values of sp/fp/ra registers. * gas/config/tc-aarch64.h (aarch64_support_sframe_p): New declaration. (support_sframe_p): Likewise. (SFRAME_CFA_SP_REG): Likewise. (SFRAME_CFA_FP_REG): Likewise. (SFRAME_CFA_RA_REG): Likewise. (aarch64_sframe_ra_tracking_p): Likewise. (sframe_ra_tracking_p): Likewise. (aarch64_sframe_cfa_ra_offset): Likewise. (sframe_cfa_ra_offset): Likewise. (aarch64_sframe_get_abi_arch): Likewise. (sframe_get_abi_arch): Likewise. * gas/config/tc-i386.c (x86_support_sframe_p): New definition. (x86_sframe_ra_tracking_p): Likewise. (x86_sframe_cfa_ra_offset): Likewise. (x86_sframe_get_abi_arch): Likewise. * gas/config/tc-i386.h (x86_support_sframe_p): New declaration. (support_sframe_p): Likewise. (SFRAME_CFA_SP_REG): Likewise. (SFRAME_CFA_FP_REG): Likewise. (x86_sframe_ra_tracking_p): Likewise. (sframe_ra_tracking_p): Likewise. (x86_sframe_cfa_ra_offset): Likewise. (sframe_cfa_ra_offset): Likewise. (x86_sframe_get_abi_arch): Likewise. (sframe_get_abi_arch): Likewise. * gas/config/tc-xtensa.c (unrelaxed_frag_max_size): Add case for rs_sframe. * gas/doc/as.texi: Add .sframe to the documentation for .cfi_sections. * gas/dw2gencfi.c (cfi_finish): Create a .sframe section. * gas/dw2gencfi.h (CFI_EMIT_sframe): New definition. * gas/write.c (cvt_frag_to_fill): Handle rs_sframe. (relax_segment): Likewise. * gas/gen-sframe.c: New file. * gas/gen-sframe.h: New file. * gas/sframe-opt.c: New file.
Diffstat (limited to 'gas/sframe-opt.c')
-rw-r--r--gas/sframe-opt.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/gas/sframe-opt.c b/gas/sframe-opt.c
new file mode 100644
index 00000000000..c17fd6b8332
--- /dev/null
+++ b/gas/sframe-opt.c
@@ -0,0 +1,158 @@
+/* sframe-opt.c - optimize FRE and FDE information in SFrame.
+ 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 "sframe.h"
+
+/* The function estimates the size of a rs_sframe variant frag based on
+ the current values of the symbols. It is called before the
+ relaxation loop. We set fr_subtype{0:2} to the expected length. */
+
+int
+sframe_estimate_size_before_relax (fragS *frag)
+{
+ offsetT width;
+ expressionS *exp;
+ symbolS *widthS;
+ int ret;
+
+ /* We are dealing with two different kind of fragments here which need
+ to be fixed up:
+ - first, FRE start address in each FRE, and
+ - second, Function info in each FDE (function info stores the FRE type)
+ The two kind of fragments can be differentiated based on the opcode
+ of the symbol. */
+ exp = symbol_get_value_expression (frag->fr_symbol);
+ gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+ /* Fragment for function info in an SFrame FDE will always write
+ only one byte. */
+ if (exp->X_op == O_subtract)
+ ret = 1;
+ /* Fragment for the start address in an SFrame FRE may write out
+ 1/2/4 bytes depending on the value of the diff. */
+ else
+ {
+ /* Get the width expression from the symbol. */
+ widthS = exp->X_op_symbol;
+ width = resolve_symbol_value (widthS);
+
+ if (width < 0x100)
+ ret = 1;
+ else if (width < 0x10000)
+ ret = 2;
+ else
+ ret = 4;
+ }
+
+ frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
+
+ return ret;
+}
+
+/* This function relaxes a rs_sframe variant frag based on the current
+ values of the symbols. fr_subtype{0:2} is the current length of
+ the frag. This returns the change in frag length. */
+
+int
+sframe_relax_frag (fragS *frag)
+{
+ int oldsize, newsize;
+
+ oldsize = frag->fr_subtype & 7;
+ if (oldsize == 7)
+ oldsize = -1;
+ newsize = sframe_estimate_size_before_relax (frag);
+ return newsize - oldsize;
+}
+
+/* This function converts a rs_sframe variant frag into a normal fill
+ frag. This is called after all relaxation has been done.
+ fr_subtype{0:2} will be the desired length of the frag. */
+
+void
+sframe_convert_frag (fragS *frag)
+{
+ offsetT fsize;
+ offsetT diff;
+ offsetT value;
+ unsigned char func_info = SFRAME_FRE_TYPE_ADDR4;
+ expressionS *exp;
+ symbolS *fsizeS, *diffS;
+
+ /* We are dealing with two different kind of fragments here which need
+ to be fixed up:
+ - first, FRE start address in each FRE, and
+ - second, Function info in each FDE (function info stores the FRE type)
+ The two kind of fragments can be differentiated based on the opcode
+ of the symbol. */
+ exp = symbol_get_value_expression (frag->fr_symbol);
+ gas_assert ((exp->X_op == O_subtract) || (exp->X_op == O_absent));
+ /* Fragment for function info in an SFrame FDE. */
+ if (exp->X_op == O_subtract)
+ {
+ fsizeS = frag->fr_symbol;
+ fsize = resolve_symbol_value (fsizeS);
+ if (fsize < 0x100)
+ func_info = SFRAME_FRE_TYPE_ADDR1;
+ else if (fsize < 0x10000)
+ func_info = SFRAME_FRE_TYPE_ADDR2;
+ else
+ func_info = SFRAME_FRE_TYPE_ADDR4;
+ value = func_info;
+
+ frag->fr_literal[frag->fr_fix] = value;
+ }
+ /* Fragment for the start address in an SFrame FRE. */
+ else
+ {
+ /* Get the fsize expression from the symbol. */
+ fsizeS = exp->X_op_symbol;
+ fsize = resolve_symbol_value (fsizeS);
+ /* Get the diff expression from the symbol. */
+ diffS= exp->X_add_symbol;
+ diff = resolve_symbol_value (diffS);
+ value = diff;
+
+ switch (frag->fr_subtype & 7)
+ {
+ case 1:
+ gas_assert (fsize < 0x100);
+ frag->fr_literal[frag->fr_fix] = diff;
+ break;
+ case 2:
+ gas_assert (fsize < 0x10000);
+ md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
+ break;
+ case 4:
+ md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ frag->fr_fix += frag->fr_subtype & 7;
+ frag->fr_type = rs_fill;
+ frag->fr_subtype = 0;
+ frag->fr_offset = 0;
+ /* FIXME do this now because we have evaluated and fixed up the fragments
+ manually ? */
+ frag->fr_symbol = 0;
+}