]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/ehopt.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / ehopt.c
index a0a5f62f4d7abe18b070764bc911a75e05a1ccd5..268554d89a786659d2605c56598bba14fd7bfc2f 100644 (file)
@@ -1,23 +1,23 @@
 /* ehopt.c--optimize gcc exception frame information.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998-2023 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"
@@ -26,18 +26,22 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
    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.
 
    Exception frame information is emitted for every function in the
-   .eh_frame section.  Simple information for a function with no
-   exceptions looks like this:
+   .eh_frame or .debug_frame sections.  Simple information for a function
+   with no exceptions looks like this:
 
 __FRAME_BEGIN__:
        .4byte  .LLCIE1 / Length of Common Information Entry
 .LSCIE1:
+#if .eh_frame
        .4byte  0x0     / CIE Identifier Tag
+#elif .debug_frame
+       .4byte  0xffffffff / CIE Identifier Tag
+#endif
        .byte   0x1     / CIE Version
        .byte   0x0     / CIE Augmentation (none)
        .byte   0x1     / ULEB128 0x1 (CIE Code Alignment Factor)
@@ -84,45 +88,40 @@ __FRAME_BEGIN__:
    not know this value, it always uses four bytes.  We will know the
    value at the end of assembly, so we can do better.  */
 
-static int eh_frame_code_alignment PARAMS ((void));
+struct cie_info
+{
+  unsigned code_alignment;
+  int z_augmentation;
+};
+
+static int get_cie_info (struct cie_info *);
 
-/* Get the code alignment factor from the CIE.  */
+/* Extract information from the CIE.  */
 
 static int
-eh_frame_code_alignment ()
+get_cie_info (struct cie_info *info)
 {
-  static int code_alignment;
-  segT current_seg;
-  subsegT current_subseg;
   fragS *f;
   fixS *fix;
-  int offset;
+  unsigned int offset;
+  char CIE_id;
   char augmentation[10];
   int iaug;
+  int code_alignment = 0;
 
-  if (code_alignment != 0)
-    return code_alignment;
+  /* We should find the CIE at the start of the section.  */
 
-  /* We should find the CIE at the start of the .eh_frame section.  */
-
-  current_seg = now_seg;
-  current_subseg = now_subseg;
-  subseg_new (".eh_frame", 0);
-#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
-  subseg_set (current_seg, current_subseg);
 
   /* Look through the frags of the section to find the code alignment.  */
 
-  /* First make sure that the CIE Identifier Tag is 0.  */
+  /* First make sure that the CIE Identifier Tag is 0/-1.  */
+
+  if (startswith (segment_name (now_seg), ".debug_frame"))
+    CIE_id = (char)0xff;
+  else
+    CIE_id = 0;
 
   offset = 4;
   while (f != NULL && offset >= f->fr_fix)
@@ -132,14 +131,11 @@ eh_frame_code_alignment ()
     }
   if (f == NULL
       || f->fr_fix - offset < 4
-      || f->fr_literal[offset] != 0
-      || f->fr_literal[offset + 1] != 0
-      || f->fr_literal[offset + 2] != 0
-      || f->fr_literal[offset + 3] != 0)
-    {
-      code_alignment = -1;
-      return -1;
-    }
+      || f->fr_literal[offset] != CIE_id
+      || f->fr_literal[offset + 1] != CIE_id
+      || f->fr_literal[offset + 2] != CIE_id
+      || f->fr_literal[offset + 3] != CIE_id)
+    return 0;
 
   /* Next make sure the CIE version number is 1.  */
 
@@ -152,10 +148,7 @@ eh_frame_code_alignment ()
   if (f == NULL
       || f->fr_fix - offset < 1
       || f->fr_literal[offset] != 1)
-    {
-      code_alignment = -1;
-      return -1;
-    }
+    return 0;
 
   /* Skip the augmentation (a null terminated string).  */
 
@@ -169,10 +162,8 @@ eh_frame_code_alignment ()
          f = f->fr_next;
        }
       if (f == NULL)
-       {
-         code_alignment = -1;
-         return -1;
-       }
+       return 0;
+
       while (offset < f->fr_fix && f->fr_literal[offset] != '\0')
        {
          if ((size_t) iaug < (sizeof augmentation) - 1)
@@ -192,10 +183,7 @@ eh_frame_code_alignment ()
       f = f->fr_next;
     }
   if (f == NULL)
-    {
-      code_alignment = -1;
-      return -1;
-    }
+    return 0;
 
   augmentation[iaug] = '\0';
   if (augmentation[0] == '\0')
@@ -219,30 +207,37 @@ eh_frame_code_alignment ()
          f = f->fr_next;
        }
       if (f == NULL)
-       {
-         code_alignment = -1;
-         return -1;
-       }
-    }
-  else
-    {
-      code_alignment = -1;
-      return -1;
+       return 0;
     }
+  else if (augmentation[0] != 'z')
+    return 0;
 
   /* We're now at the code alignment factor, which is a ULEB128.  If
      it isn't a single byte, forget it.  */
 
   code_alignment = f->fr_literal[offset] & 0xff;
-  if ((code_alignment & 0x80) != 0 || code_alignment == 0)
-    {
-      code_alignment = -1;
-      return -1;
-    }
+  if ((code_alignment & 0x80) != 0)
+    code_alignment = 0;
+
+  info->code_alignment = code_alignment;
+  info->z_augmentation = (augmentation[0] == 'z');
 
-  return code_alignment;
+  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.
 
@@ -257,144 +252,239 @@ eh_frame_code_alignment ()
    change *EXP and *PNBYTES.  */
 
 int
-check_eh_frame (exp, pnbytes)
-     expressionS *exp;
-     unsigned int *pnbytes;
+check_eh_frame (expressionS *exp, unsigned int *pnbytes)
 {
-  static int saw_size;
-  static symbolS *size_end_sym;
-  static int saw_advance_loc4;
-  static fragS *loc4_frag;
-  static int loc4_fix;
-
-  if (saw_size
-      && S_IS_DEFINED (size_end_sym))
+  struct frame_data
+  {
+    enum frame_state state;
+
+    int cie_info_ok;
+    struct cie_info cie_info;
+
+    symbolS *size_end_sym;
+    fragS *loc4_frag;
+    int loc4_fix;
+
+    int aug_size;
+    int aug_shift;
+  };
+
+  static struct frame_data eh_frame_data;
+  static struct frame_data debug_frame_data;
+  struct frame_data *d;
+
+  /* Don't optimize.  */
+  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 (startswith (segment_name (now_seg), ".eh_frame")
+      && segment_name (now_seg)[9] != '_')
+    d = &eh_frame_data;
+  else if (startswith (segment_name (now_seg), ".debug_frame"))
+    d = &debug_frame_data;
+  else
+    return 0;
+
+  if (d->state >= state_saw_size && S_IS_DEFINED (d->size_end_sym))
     {
       /* We have come to the end of the CIE or FDE.  See below where
          we set saw_size.  We must check this first because we may now
          be looking at the next size.  */
-      saw_size = 0;
-      saw_advance_loc4 = 0;
+      d->state = state_idle;
     }
 
-  if (flag_traditional_format)
+  switch (d->state)
     {
-      /* Don't optimize.  */
-    }
-  else if (strcmp (segment_name (now_seg), ".eh_frame") != 0)
-    {
-      saw_size = 0;
-      saw_advance_loc4 = 0;
-    }
-  else if (! saw_size
-          && *pnbytes == 4)
-    {
-      /* This might be the size of the CIE or FDE.  We want to know
-         the size so that we don't accidentally optimize across an FDE
-         boundary.  We recognize the size in one of two forms: a
-         symbol which will later be defined as a difference, or a
-         subtraction of two symbols.  Either way, we can tell when we
-         are at the end of the FDE because the symbol becomes defined
-         (in the case of a subtraction, the end symbol, from which the
-         start symbol is being subtracted).  Other ways of describing
-         the size will not be optimized.  */
-      if ((exp->X_op == O_symbol || exp->X_op == O_subtract)
-         && ! S_IS_DEFINED (exp->X_add_symbol))
+    case state_idle:
+      if (*pnbytes == 4)
        {
-         saw_size = 1;
-         size_end_sym = exp->X_add_symbol;
+         /* This might be the size of the CIE or FDE.  We want to know
+            the size so that we don't accidentally optimize across an FDE
+            boundary.  We recognize the size in one of two forms: a
+            symbol which will later be defined as a difference, or a
+            subtraction of two symbols.  Either way, we can tell when we
+            are at the end of the FDE because the symbol becomes defined
+            (in the case of a subtraction, the end symbol, from which the
+            start symbol is being subtracted).  Other ways of describing
+            the size will not be optimized.  */
+         if ((exp->X_op == O_symbol || exp->X_op == O_subtract)
+             && ! S_IS_DEFINED (exp->X_add_symbol))
+           {
+             d->state = state_saw_size;
+             d->size_end_sym = exp->X_add_symbol;
+           }
        }
-    }
-  else if (saw_size
-          && *pnbytes == 1
-          && exp->X_op == O_constant
-          && exp->X_add_number == DW_CFA_advance_loc4)
-    {
-      /* This might be a DW_CFA_advance_loc4.  Record the frag and the
-         position within the frag, so that we can change it later.  */
-      saw_advance_loc4 = 1;
-      frag_grow (1);
-      loc4_frag = frag_now;
-      loc4_fix = frag_now_fix ();
-    }
-  else if (saw_advance_loc4
-          && *pnbytes == 4
-          && exp->X_op == O_constant)
-    {
-      int ca;
-
-      /* This is a case which we can optimize.  The two symbols being
-         subtracted were in the same frag and the expression was
-         reduced to a constant.  We can do the optimization entirely
-         in this function.  */
-
-      saw_advance_loc4 = 0;
-
-      ca = eh_frame_code_alignment ();
-      if (ca < 0)
+      break;
+
+    case state_saw_size:
+    case state_saw_cie_offset:
+      /* Assume whatever form it appears in, it appears atomically.  */
+      d->state = (enum frame_state) (d->state + 1);
+      break;
+
+    case state_saw_pc_begin:
+      /* Decide whether we should see an augmentation.  */
+      if (! d->cie_info_ok
+         && ! (d->cie_info_ok = get_cie_info (&d->cie_info)))
+       d->state = state_error;
+      else if (d->cie_info.z_augmentation)
        {
-         /* Don't optimize.  */
+         d->state = state_seeing_aug_size;
+         d->aug_size = 0;
+         d->aug_shift = 0;
        }
-      else if (exp->X_add_number % ca == 0
-              && exp->X_add_number / ca < 0x40)
+      else
+       d->state = state_wait_loc4;
+      break;
+
+    case state_seeing_aug_size:
+      /* Bytes == -1 means this comes from an leb128 directive.  */
+      if ((int)*pnbytes == -1 && exp->X_op == O_constant)
        {
-         loc4_frag->fr_literal[loc4_fix]
-           = DW_CFA_advance_loc | (exp->X_add_number / ca);
-         /* No more bytes needed.  */
-         return 1;
+         d->aug_size = exp->X_add_number;
+         d->state = state_skipping_aug;
        }
-      else if (exp->X_add_number < 0x100)
+      else if (*pnbytes == 1 && exp->X_op == O_constant)
        {
-         loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
-         *pnbytes = 1;
+         unsigned char byte = exp->X_add_number;
+         d->aug_size |= (byte & 0x7f) << d->aug_shift;
+         d->aug_shift += 7;
+         if ((byte & 0x80) == 0)
+           d->state = state_skipping_aug;
        }
-      else if (exp->X_add_number < 0x10000)
+      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:
+      if ((int)*pnbytes < 0)
+       d->state = state_error;
+      else
        {
-         loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
-         *pnbytes = 2;
+         int left = (d->aug_size -= *pnbytes);
+         if (left == 0)
+           d->state = state_wait_loc4;
+         else if (left < 0)
+           d->state = state_error;
        }
-    }
-  else if (saw_advance_loc4
-          && *pnbytes == 4
-          && exp->X_op == O_subtract)
-    {
+      break;
 
-      /* 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.  */
-
-      saw_advance_loc4 = 0;
+    case state_wait_loc4:
+      if (*pnbytes == 1
+         && exp->X_op == O_constant
+         && exp->X_add_number == DW_CFA_advance_loc4)
+       {
+         /* This might be a DW_CFA_advance_loc4.  Record the frag and the
+            position within the frag, so that we can change it later.  */
+         frag_grow (1);
+         d->state = state_saw_loc4;
+         d->loc4_frag = frag_now;
+         d->loc4_fix = frag_now_fix ();
+       }
+      break;
 
-      frag_var (rs_cfa, 4, 0, 0, make_expr_symbol (exp),
-               loc4_fix, (char *) loc4_frag);
+    case state_saw_loc4:
+      d->state = state_wait_loc4;
+      if (*pnbytes != 4)
+       break;
+      if (exp->X_op == O_constant)
+       {
+         /* This is a case which we can optimize.  The two symbols being
+            subtracted were in the same frag and the expression was
+            reduced to a constant.  We can do the optimization entirely
+            in this function.  */
+         if (exp->X_add_number < 0x40)
+           {
+             d->loc4_frag->fr_literal[d->loc4_fix]
+               = DW_CFA_advance_loc | exp->X_add_number;
+             /* No more bytes needed.  */
+             return 1;
+           }
+         else if (exp->X_add_number < 0x100)
+           {
+             d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc1;
+             *pnbytes = 1;
+           }
+         else if (exp->X_add_number < 0x10000)
+           {
+             d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc2;
+             *pnbytes = 2;
+           }
+       }
+      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.  */
+         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;
 
-      return 1;
+    case state_error:
+      /* Just skipping everything.  */
+      break;
     }
-  else
-    saw_advance_loc4 = 0;
 
   return 0;
 }
 
 /* The function estimates the size of a rs_cfa variant frag based on
    the current values of the symbols.  It is called before the
-   relaxation loop.  We set fr_subtype to the expected length.  */
+   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)
 {
-  int ca;
   offsetT diff;
+  int ca = frag->fr_subtype >> 3;
   int ret;
 
-  ca = eh_frame_code_alignment ();
-  diff = resolve_symbol_value (frag->fr_symbol, 0);
+  diff = resolve_symbol_value (frag->fr_symbol);
 
-  if (ca < 0)
-    ret = 4;
-  else if (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;
@@ -403,67 +493,80 @@ eh_frame_estimate_size_before_relax (frag)
   else
     ret = 4;
 
-  frag->fr_subtype = ret;
+  frag->fr_subtype = (frag->fr_subtype & ~7) | (ret & 7);
 
   return ret;
 }
 
 /* This function relaxes a rs_cfa variant frag based on the current
-   values of the symbols.  fr_subtype is the current length of the
-   frag.  This returns the change in frag length.  */
+   values of the symbols.  fr_subtype{0:2} is the current length of
+   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;
+  oldsize = frag->fr_subtype & 7;
+  if (oldsize == 7)
+    oldsize = -1;
   newsize = eh_frame_estimate_size_before_relax (frag);
   return newsize - oldsize;
 }
 
 /* This function converts a rs_cfa variant frag into a normal fill
    frag.  This is called after all relaxation has been done.
-   fr_subtype will be the desired length of the frag.  */
+   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, 1);
+  diff = resolve_symbol_value (frag->fr_symbol);
 
-  if (frag->fr_subtype == 0)
+  ca = frag->fr_subtype >> 3;
+  gas_assert (ca > 0);
+  diff /= ca;
+  switch (frag->fr_subtype & 7)
     {
-      int ca;
+    case 0:
+      gas_assert (diff < 0x40);
+      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | diff;
+      break;
 
-      ca = eh_frame_code_alignment ();
-      assert (ca > 0 && diff % ca == 0 && diff / ca < 0x40);
-      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | (diff / ca);
-    }
-  else if (frag->fr_subtype == 1)
-    {
-      assert (diff < 0x100);
+    case 1:
+      gas_assert (diff < 0x100);
       loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
       frag->fr_literal[frag->fr_fix] = diff;
-    }
-  else if (frag->fr_subtype == 2)
-    {
-      assert (diff < 0x10000);
+      break;
+
+    case 2:
+      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;
+
+    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 ();
     }
-  else
-    md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
 
-  frag->fr_fix += frag->fr_subtype;
+  frag->fr_fix += frag->fr_subtype & 7;
   frag->fr_type = rs_fill;
+  frag->fr_subtype = 0;
   frag->fr_offset = 0;
 }