/* ehopt.c--optimize gcc exception frame information.
- Copyright 1998, 2000, 2001 Free Software Foundation, Inc.
+ Copyright (C) 1998-2019 Free Software Foundation, Inc.
Written by Ian Lance Taylor <ian@cygnus.com>.
-This file is part of GAS, the GNU Assembler.
+ 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 2, or (at your option)
-any later version.
+ 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.
+ 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, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+ 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"
ELF, since the exception frame information is always in a format
derived from DWARF. */
-#include "elf/dwarf2.h"
+#include "dwarf2.h"
/* Try to optimize gcc 2.8 exception frame information.
int z_augmentation;
};
-static int get_cie_info PARAMS ((struct cie_info *));
+static int get_cie_info (struct cie_info *);
/* Extract information from the CIE. */
static int
-get_cie_info (info)
- struct cie_info *info;
+get_cie_info (struct cie_info *info)
{
fragS *f;
fixS *fix;
- int offset;
+ unsigned int offset;
char CIE_id;
char augmentation[10];
int iaug;
/* We should find the CIE at the start of the section. */
-#if defined (BFD_ASSEMBLER) || defined (MANY_SEGMENTS)
f = seg_info (now_seg)->frchainP->frch_root;
-#else
- f = frchain_now->frch_root;
-#endif
-#ifdef BFD_ASSEMBLER
fix = seg_info (now_seg)->frchainP->fix_root;
-#else
- fix = *seg_fix_rootP;
-#endif
/* Look through the frags of the section to find the code alignment. */
/* First make sure that the CIE Identifier Tag is 0/-1. */
- if (strcmp (segment_name (now_seg), ".debug_frame") == 0)
+ if (strncmp (segment_name (now_seg), ".debug_frame", 12) == 0)
CIE_id = (char)0xff;
else
CIE_id = 0;
return 1;
}
+enum frame_state
+{
+ state_idle,
+ state_saw_size,
+ state_saw_cie_offset,
+ state_saw_pc_begin,
+ state_seeing_aug_size,
+ state_skipping_aug,
+ state_wait_loc4,
+ state_saw_loc4,
+ state_error,
+};
+
/* This function is called from emit_expr. It looks for cases which
we can optimize.
change *EXP and *PNBYTES. */
int
-check_eh_frame (exp, pnbytes)
- expressionS *exp;
- unsigned int *pnbytes;
+check_eh_frame (expressionS *exp, unsigned int *pnbytes)
{
struct frame_data
{
- enum frame_state
- {
- state_idle,
- state_saw_size,
- state_saw_cie_offset,
- state_saw_pc_begin,
- state_seeing_aug_size,
- state_skipping_aug,
- state_wait_loc4,
- state_saw_loc4,
- state_error,
- } state;
+ enum frame_state state;
int cie_info_ok;
struct cie_info cie_info;
if (flag_traditional_format)
return 0;
+#ifdef md_allow_eh_opt
+ if (! md_allow_eh_opt)
+ return 0;
+#endif
+
/* Select the proper section data. */
- if (strcmp (segment_name (now_seg), ".eh_frame") == 0)
+ if (strncmp (segment_name (now_seg), ".eh_frame", 9) == 0
+ && segment_name (now_seg)[9] != '_')
d = &eh_frame_data;
- else if (strcmp (segment_name (now_seg), ".debug_frame") == 0)
+ else if (strncmp (segment_name (now_seg), ".debug_frame", 12) == 0)
d = &debug_frame_data;
else
return 0;
case state_saw_size:
case state_saw_cie_offset:
/* Assume whatever form it appears in, it appears atomically. */
- d->state += 1;
+ d->state = (enum frame_state) (d->state + 1);
break;
case state_saw_pc_begin:
}
else
d->state = state_error;
+ if (d->state == state_skipping_aug && d->aug_size == 0)
+ d->state = state_wait_loc4;
break;
case state_skipping_aug:
d->state = state_error;
else
{
- int left = (d->aug_size -= *pnbytes);
+ int left = (d->aug_size -= *pnbytes);
if (left == 0)
d->state = state_wait_loc4;
else if (left < 0)
subtracted were in the same frag and the expression was
reduced to a constant. We can do the optimization entirely
in this function. */
- if (d->cie_info.code_alignment > 0
- && exp->X_add_number % d->cie_info.code_alignment == 0
- && exp->X_add_number / d->cie_info.code_alignment < 0x40)
+ if (exp->X_add_number < 0x40)
{
d->loc4_frag->fr_literal[d->loc4_fix]
- = DW_CFA_advance_loc
- | (exp->X_add_number / d->cie_info.code_alignment);
+ = DW_CFA_advance_loc | exp->X_add_number;
/* No more bytes needed. */
return 1;
}
*pnbytes = 2;
}
}
- else if (exp->X_op == O_subtract)
+ else if (exp->X_op == O_subtract && d->cie_info.code_alignment == 1)
{
/* This is a case we can optimize. The expression was not
reduced, so we can not finish the optimization until the end
of the assembly. We set up a variant frag which we handle
later. */
- int fr_subtype;
-
- if (d->cie_info.code_alignment > 0)
- fr_subtype = d->cie_info.code_alignment << 3;
- else
- fr_subtype = 0;
-
- frag_var (rs_cfa, 4, 0, fr_subtype, make_expr_symbol (exp),
+ frag_var (rs_cfa, 4, 0, 1 << 3, make_expr_symbol (exp),
d->loc4_fix, (char *) d->loc4_frag);
return 1;
}
+ else if ((exp->X_op == O_divide
+ || exp->X_op == O_right_shift)
+ && d->cie_info.code_alignment > 1)
+ {
+ if (symbol_symbolS (exp->X_add_symbol)
+ && symbol_constant_p (exp->X_op_symbol)
+ && S_GET_SEGMENT (exp->X_op_symbol) == absolute_section
+ && ((exp->X_op == O_divide
+ ? *symbol_X_add_number (exp->X_op_symbol)
+ : (offsetT) 1 << *symbol_X_add_number (exp->X_op_symbol))
+ == (offsetT) d->cie_info.code_alignment))
+ {
+ expressionS *symval;
+
+ symval = symbol_get_value_expression (exp->X_add_symbol);
+ if (symval->X_op == O_subtract)
+ {
+ /* This is a case we can optimize as well. The
+ expression was not reduced, so we can not finish
+ the optimization until the end of the assembly.
+ We set up a variant frag which we handle later. */
+ frag_var (rs_cfa, 4, 0, d->cie_info.code_alignment << 3,
+ make_expr_symbol (symval),
+ d->loc4_fix, (char *) d->loc4_frag);
+ return 1;
+ }
+ }
+ }
break;
case state_error:
relaxation loop. We set fr_subtype{0:2} to the expected length. */
int
-eh_frame_estimate_size_before_relax (frag)
- fragS *frag;
+eh_frame_estimate_size_before_relax (fragS *frag)
{
offsetT diff;
int ca = frag->fr_subtype >> 3;
diff = resolve_symbol_value (frag->fr_symbol);
- if (ca > 0 && diff % ca == 0 && diff / ca < 0x40)
+ gas_assert (ca > 0);
+ diff /= ca;
+ if (diff == 0)
+ ret = -1;
+ else if (diff < 0x40)
ret = 0;
else if (diff < 0x100)
ret = 1;
else
ret = 4;
- frag->fr_subtype = (frag->fr_subtype & ~7) | ret;
+ frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
return ret;
}
the frag. This returns the change in frag length. */
int
-eh_frame_relax_frag (frag)
- fragS *frag;
+eh_frame_relax_frag (fragS *frag)
{
int oldsize, newsize;
oldsize = frag->fr_subtype & 7;
+ if (oldsize == 7)
+ oldsize = -1;
newsize = eh_frame_estimate_size_before_relax (frag);
return newsize - oldsize;
}
fr_subtype{0:2} will be the desired length of the frag. */
void
-eh_frame_convert_frag (frag)
- fragS *frag;
+eh_frame_convert_frag (fragS *frag)
{
offsetT diff;
fragS *loc4_frag;
- int loc4_fix;
+ int loc4_fix, ca;
loc4_frag = (fragS *) frag->fr_opcode;
loc4_fix = (int) frag->fr_offset;
diff = resolve_symbol_value (frag->fr_symbol);
+ ca = frag->fr_subtype >> 3;
+ gas_assert (ca > 0);
+ diff /= ca;
switch (frag->fr_subtype & 7)
{
case 0:
- {
- int ca = frag->fr_subtype >> 3;
- assert (ca > 0 && diff % ca == 0 && diff / ca < 0x40);
- loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | (diff / ca);
- }
+ gas_assert (diff < 0x40);
+ loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | diff;
break;
case 1:
- assert (diff < 0x100);
+ gas_assert (diff < 0x100);
loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
frag->fr_literal[frag->fr_fix] = diff;
break;
case 2:
- assert (diff < 0x10000);
+ gas_assert (diff < 0x10000);
loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
break;
- default:
+ case 4:
md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
break;
+
+ case 7:
+ gas_assert (diff == 0);
+ frag->fr_fix -= 8;
+ break;
+
+ default:
+ abort ();
}
frag->fr_fix += frag->fr_subtype & 7;