gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / bfd / format.c
index 5b82996453c6e6f4501aee849c52b9954e6cb4a4..e9212bc6f2498c0277e69d6500ccfcdfb54c964b 100644 (file)
@@ -1,13 +1,12 @@
 /* Generic BFD support for file formats.
-   Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1999, 2000, 2001, 2002, 2003
-   Free Software Foundation, Inc.
+   Copyright (C) 1990-2020 Free Software Foundation, Inc.
    Written by Cygnus Support.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    This program 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 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -17,7 +16,9 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
 
 /*
 SECTION
@@ -38,10 +39,12 @@ SECTION
 
        The BFD contains the result of an executable core dump.
 
+SUBSECTION
+       File format functions
 */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libbfd.h"
 
 /* IMPORT from targets.c.  */
@@ -91,6 +94,113 @@ bfd_check_format (bfd *abfd, bfd_format format)
   return bfd_check_format_matches (abfd, format, NULL);
 }
 
+struct bfd_preserve
+{
+  void *marker;
+  void *tdata;
+  flagword flags;
+  const struct bfd_arch_info *arch_info;
+  struct bfd_section *sections;
+  struct bfd_section *section_last;
+  unsigned int section_count;
+  unsigned int section_id;
+  struct bfd_hash_table section_htab;
+  const struct bfd_build_id *build_id;
+  bfd_cleanup cleanup;
+};
+
+/* When testing an object for compatibility with a particular target
+   back-end, the back-end object_p function needs to set up certain
+   fields in the bfd on successfully recognizing the object.  This
+   typically happens in a piecemeal fashion, with failures possible at
+   many points.  On failure, the bfd is supposed to be restored to its
+   initial state, which is virtually impossible.  However, restoring a
+   subset of the bfd state works in practice.  This function stores
+   the subset.  */
+
+static bfd_boolean
+bfd_preserve_save (bfd *abfd, struct bfd_preserve *preserve,
+                  bfd_cleanup cleanup)
+{
+  preserve->tdata = abfd->tdata.any;
+  preserve->arch_info = abfd->arch_info;
+  preserve->flags = abfd->flags;
+  preserve->sections = abfd->sections;
+  preserve->section_last = abfd->section_last;
+  preserve->section_count = abfd->section_count;
+  preserve->section_id = _bfd_section_id;
+  preserve->section_htab = abfd->section_htab;
+  preserve->marker = bfd_alloc (abfd, 1);
+  preserve->build_id = abfd->build_id;
+  preserve->cleanup = cleanup;
+  if (preserve->marker == NULL)
+    return FALSE;
+
+  return bfd_hash_table_init (&abfd->section_htab, bfd_section_hash_newfunc,
+                             sizeof (struct section_hash_entry));
+}
+
+/* Clear out a subset of BFD state.  */
+
+static void
+bfd_reinit (bfd *abfd, unsigned int section_id, bfd_cleanup cleanup)
+{
+  _bfd_section_id = section_id;
+  if (cleanup)
+    cleanup (abfd);
+  abfd->tdata.any = NULL;
+  abfd->arch_info = &bfd_default_arch_struct;
+  abfd->flags &= BFD_FLAGS_SAVED;
+  bfd_section_list_clear (abfd);
+}
+
+/* Restores bfd state saved by bfd_preserve_save.  */
+
+static bfd_cleanup
+bfd_preserve_restore (bfd *abfd, struct bfd_preserve *preserve)
+{
+  bfd_hash_table_free (&abfd->section_htab);
+
+  abfd->tdata.any = preserve->tdata;
+  abfd->arch_info = preserve->arch_info;
+  abfd->flags = preserve->flags;
+  abfd->section_htab = preserve->section_htab;
+  abfd->sections = preserve->sections;
+  abfd->section_last = preserve->section_last;
+  abfd->section_count = preserve->section_count;
+  _bfd_section_id = preserve->section_id;
+  abfd->build_id = preserve->build_id;
+
+  /* bfd_release frees all memory more recently bfd_alloc'd than
+     its arg, as well as its arg.  */
+  bfd_release (abfd, preserve->marker);
+  preserve->marker = NULL;
+  return preserve->cleanup;
+}
+
+/* Called when the bfd state saved by bfd_preserve_save is no longer
+   needed.  */
+
+static void
+bfd_preserve_finish (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_preserve *preserve)
+{
+  if (preserve->cleanup)
+    {
+      /* Run the cleanup, assuming that all it will need is the
+        tdata at the time the cleanup was returned.  */
+      void *tdata = abfd->tdata.any;
+      abfd->tdata.any = preserve->tdata;
+      preserve->cleanup (abfd);
+      abfd->tdata.any = tdata;
+    }
+  /* It would be nice to be able to free more memory here, eg. old
+     tdata, but that's not possible since these blocks are sitting
+     inside bfd_alloc'd memory.  The section hash is on a separate
+     objalloc.  */
+  bfd_hash_table_free (&preserve->section_htab);
+  preserve->marker = NULL;
+}
+
 /*
 FUNCTION
        bfd_check_format_matches
@@ -115,11 +225,20 @@ bfd_boolean
 bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
 {
   extern const bfd_target binary_vec;
+#if BFD_SUPPORTS_PLUGINS
+  extern const bfd_target plugin_vec;
+#endif
   const bfd_target * const *target;
   const bfd_target **matching_vector = NULL;
-  const bfd_target *save_targ, *right_targ, *ar_right_targ;
-  int match_count;
+  const bfd_target *save_targ, *right_targ, *ar_right_targ, *match_targ;
+  int match_count, best_count, best_match;
   int ar_match_index;
+  unsigned int initial_section_id = _bfd_section_id;
+  struct bfd_preserve preserve, preserve_match;
+  bfd_cleanup cleanup = NULL;
+
+  if (matching != NULL)
+    *matching = NULL;
 
   if (!bfd_read_p (abfd)
       || (unsigned int) abfd->format >= (unsigned int) bfd_type_end)
@@ -131,50 +250,34 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
   if (abfd->format != bfd_unknown)
     return abfd->format == format;
 
-  /* Since the target type was defaulted, check them
-     all in the hope that one will be uniquely recognized.  */
-  save_targ = abfd->xvec;
-  match_count = 0;
-  ar_match_index = _bfd_target_vector_entries;
-
-  if (matching)
+  if (matching != NULL || *bfd_associated_vector != NULL)
     {
-      bfd_size_type amt;
+      size_t amt;
 
-      *matching = NULL;
       amt = sizeof (*matching_vector) * 2 * _bfd_target_vector_entries;
-      matching_vector = bfd_malloc (amt);
+      matching_vector = (const bfd_target **) bfd_malloc (amt);
       if (!matching_vector)
        return FALSE;
     }
 
-  right_targ = 0;
-  ar_right_targ = 0;
-
   /* Presume the answer is yes.  */
   abfd->format = format;
+  save_targ = abfd->xvec;
+
+  preserve_match.marker = NULL;
+  if (!bfd_preserve_save (abfd, &preserve, NULL))
+    goto err_ret;
 
   /* If the target type was explicitly specified, just check that target.  */
   if (!abfd->target_defaulted)
     {
       if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)        /* rewind! */
-       {
-         if (matching)
-           free (matching_vector);
-         return FALSE;
-       }
-
-      right_targ = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
-
-      if (right_targ)
-       {
-         abfd->xvec = right_targ;      /* Set the target as returned.  */
+       goto err_ret;
 
-         if (matching)
-           free (matching_vector);
+      cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
 
-         return TRUE;                  /* File position has moved, BTW.  */
-       }
+      if (cleanup)
+       goto ok_ret;
 
       /* For a long time the code has dropped through to check all
         targets if the specified target was wrong.  I don't know why,
@@ -189,87 +292,118 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
         this sort, I changed this test to check only for the binary
         target.  */
       if (format == bfd_archive && save_targ == &binary_vec)
-       {
-         abfd->xvec = save_targ;
-         abfd->format = bfd_unknown;
-
-         if (matching)
-           free (matching_vector);
-
-         bfd_set_error (bfd_error_file_not_recognized);
-
-         return FALSE;
-       }
+       goto err_unrecog;
     }
 
+  /* Since the target type was defaulted, check them all in the hope
+     that one will be uniquely recognized.  */
+  right_targ = NULL;
+  ar_right_targ = NULL;
+  match_targ = NULL;
+  best_match = 256;
+  best_count = 0;
+  match_count = 0;
+  ar_match_index = _bfd_target_vector_entries;
+
   for (target = bfd_target_vector; *target != NULL; target++)
     {
-      const bfd_target *temp;
-      bfd_error_type err;
-
-      if (*target == &binary_vec)
+      void **high_water;
+
+      /* The binary target matches anything, so don't return it when
+        searching.  Don't match the plugin target if we have another
+        alternative since we want to properly set the input format
+        before allowing a plugin to claim the file.  Also, don't
+        check the default target twice.  */
+      if (*target == &binary_vec
+#if BFD_SUPPORTS_PLUGINS
+         || (match_count != 0 && *target == &plugin_vec)
+#endif
+         || (!abfd->target_defaulted && *target == save_targ))
        continue;
 
-      abfd->xvec = *target;    /* Change BFD's target temporarily.  */
-
-      if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
-       {
-         if (matching)
-           free (matching_vector);
-         return FALSE;
-       }
+      /* If we already tried a match, the bfd is modified and may
+        have sections attached, which will confuse the next
+        _bfd_check_format call.  */
+      bfd_reinit (abfd, initial_section_id, cleanup);
+      /* Free bfd_alloc memory too.  If we have matched and preserved
+        a target then the high water mark is that much higher.  */
+      if (preserve_match.marker)
+       high_water = &preserve_match.marker;
+      else
+       high_water = &preserve.marker;
+      bfd_release (abfd, *high_water);
+      *high_water = bfd_alloc (abfd, 1);
 
-      /* If _bfd_check_format neglects to set bfd_error, assume
-        bfd_error_wrong_format.  We didn't used to even pay any
-        attention to bfd_error, so I suspect that some
-        _bfd_check_format might have this problem.  */
-      bfd_set_error (bfd_error_wrong_format);
+      /* Change BFD's target temporarily.  */
+      abfd->xvec = *target;
 
-      temp = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
+      if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
+       goto err_ret;
 
-      if (temp)
+      cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
+      if (cleanup)
        {
-         /* This format checks out as ok!  */
-         right_targ = temp;
-
-         /* If this is the default target, accept it, even if other
-            targets might match.  People who want those other targets
-            have to set the GNUTARGET variable.  */
-         if (temp == bfd_default_vector[0])
+         int match_priority = abfd->xvec->match_priority;
+#if BFD_SUPPORTS_PLUGINS
+         /* If this object can be handled by a plugin, give that the
+            lowest priority; objects both handled by a plugin and
+            with an underlying object format will be claimed
+            separately by the plugin.  */
+         if (*target == &plugin_vec)
+           match_priority = (*target)->match_priority;
+#endif
+
+         if (abfd->format != bfd_archive
+             || (bfd_has_map (abfd)
+                 && bfd_get_error () != bfd_error_wrong_object_format))
            {
-             match_count = 1;
-             break;
+             /* If this is the default target, accept it, even if
+                other targets might match.  People who want those
+                other targets have to set the GNUTARGET variable.  */
+             if (abfd->xvec == bfd_default_vector[0])
+               goto ok_ret;
+
+             if (matching_vector)
+               matching_vector[match_count] = abfd->xvec;
+             match_count++;
+
+             if (match_priority < best_match)
+               {
+                 best_match = match_priority;
+                 best_count = 0;
+               }
+             if (match_priority <= best_match)
+               {
+                 /* This format checks out as ok!  */
+                 right_targ = abfd->xvec;
+                 best_count++;
+               }
+           }
+         else
+           {
+             /* An archive with no armap or objects of the wrong
+                type.  We want this target to match if we get no
+                better matches.  */
+             if (ar_right_targ != bfd_default_vector[0])
+               ar_right_targ = *target;
+             if (matching_vector)
+               matching_vector[ar_match_index] = *target;
+             ar_match_index++;
            }
 
-         if (matching)
-           matching_vector[match_count] = temp;
-
-         match_count++;
-       }
-      else if ((err = bfd_get_error ()) == bfd_error_wrong_object_format
-              || err == bfd_error_file_ambiguously_recognized)
-       {
-         /* An archive with objects of the wrong type, or an
-            ambiguous match.  We want this target to match if we get
-            no better matches.  */
-         if (ar_right_targ != bfd_default_vector[0])
-           ar_right_targ = *target;
-         if (matching)
-           matching_vector[ar_match_index] = *target;
-         ar_match_index++;
-       }
-      else if (err != bfd_error_wrong_format)
-       {
-         abfd->xvec = save_targ;
-         abfd->format = bfd_unknown;
-
-         if (matching)
-           free (matching_vector);
-
-         return FALSE;
+         if (preserve_match.marker == NULL)
+           {
+             match_targ = abfd->xvec;
+             if (!bfd_preserve_save (abfd, &preserve_match, cleanup))
+               goto err_ret;
+             cleanup = NULL;
+           }
        }
     }
 
+  if (best_count == 1)
+    match_count = 1;
+
   if (match_count == 0)
     {
       /* Try partial matches.  */
@@ -283,16 +417,17 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
        {
          match_count = ar_match_index - _bfd_target_vector_entries;
 
-         if (matching && match_count > 1)
+         if (matching_vector && match_count > 1)
            memcpy (matching_vector,
                    matching_vector + _bfd_target_vector_entries,
                    sizeof (*matching_vector) * match_count);
        }
     }
 
-  if (match_count > 1
-      && bfd_associated_vector != NULL
-      && matching)
+  /* We have more than one equally good match.  If any of the best
+     matches is a target in config.bfd targ_defvec or targ_selvecs,
+     choose it.  */
+  if (match_count > 1)
     {
       const bfd_target * const *assoc = bfd_associated_vector;
 
@@ -301,7 +436,8 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
          int i = match_count;
 
          while (--i >= 0)
-           if (matching_vector[i] == right_targ)
+           if (matching_vector[i] == right_targ
+               && right_targ->match_priority <= best_match)
              break;
 
          if (i >= 0)
@@ -312,44 +448,107 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
        }
     }
 
-  if (match_count == 1)
+  /* We still have more than one equally good match, and at least some
+     of the targets support match priority.  Choose the first of the
+     best matches.  */
+  if (matching_vector && match_count > 1 && best_count != match_count)
     {
-      abfd->xvec = right_targ;         /* Change BFD's target permanently.  */
+      int i;
 
-      if (matching)
-       free (matching_vector);
-
-      return TRUE;                     /* File position has moved, BTW.  */
+      for (i = 0; i < match_count; i++)
+       {
+         right_targ = matching_vector[i];
+         if (right_targ->match_priority <= best_match)
+           break;
+       }
+      match_count = 1;
     }
 
-  abfd->xvec = save_targ;              /* Restore original target type.  */
-  abfd->format = bfd_unknown;          /* Restore original format.  */
+  /* There is way too much undoing of half-known state here.  We
+     really shouldn't iterate on live bfd's.  Note that saving the
+     whole bfd and restoring it would be even worse; the first thing
+     you notice is that the cached bfd file position gets out of sync.  */
+  if (preserve_match.marker != NULL)
+    cleanup = bfd_preserve_restore (abfd, &preserve_match);
 
-  if (match_count == 0)
+  if (match_count == 1)
     {
-      bfd_set_error (bfd_error_file_not_recognized);
+      abfd->xvec = right_targ;
+      /* If we come out of the loop knowing that the last target that
+        matched is the one we want, then ABFD should still be in a usable
+        state (except possibly for XVEC).  This is not just an
+        optimisation.  In the case of plugins a match against the
+        plugin target can result in the bfd being changed such that
+        it no longer matches the plugin target, nor will it match
+        RIGHT_TARG again.  */
+      if (match_targ != right_targ)
+       {
+         bfd_reinit (abfd, initial_section_id, cleanup);
+         bfd_release (abfd, preserve.marker);
+         if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
+           goto err_ret;
+         cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
+         BFD_ASSERT (cleanup != NULL);
+       }
 
-      if (matching)
-       free (matching_vector);
+    ok_ret:
+      /* If the file was opened for update, then `output_has_begun'
+        some time ago when the file was created.  Do not recompute
+        sections sizes or alignments in _bfd_set_section_contents.
+        We can not set this flag until after checking the format,
+        because it will interfere with creation of BFD sections.  */
+      if (abfd->direction == both_direction)
+       abfd->output_has_begun = TRUE;
+
+      free (matching_vector);
+      if (preserve_match.marker != NULL)
+       bfd_preserve_finish (abfd, &preserve_match);
+      bfd_preserve_finish (abfd, &preserve);
+
+      /* File position has moved, BTW.  */
+      return TRUE;
     }
-  else
+
+  if (match_count == 0)
     {
-      bfd_set_error (bfd_error_file_ambiguously_recognized);
+    err_unrecog:
+      bfd_set_error (bfd_error_file_not_recognized);
+    err_ret:
+      if (cleanup)
+       cleanup (abfd);
+      abfd->xvec = save_targ;
+      abfd->format = bfd_unknown;
+      free (matching_vector);
+      if (preserve_match.marker != NULL)
+       bfd_preserve_finish (abfd, &preserve_match);
+      bfd_preserve_restore (abfd, &preserve);
+      return FALSE;
+    }
+
+  /* Restore original target type and format.  */
+  abfd->xvec = save_targ;
+  abfd->format = bfd_unknown;
+  bfd_set_error (bfd_error_file_ambiguously_recognized);
 
-      if (matching)
+  if (matching)
+    {
+      *matching = (char **) matching_vector;
+      matching_vector[match_count] = NULL;
+      /* Return target names.  This is a little nasty.  Maybe we
+        should do another bfd_malloc?  */
+      while (--match_count >= 0)
        {
-         *matching = (char **) matching_vector;
-         matching_vector[match_count] = NULL;
-         /* Return target names.  This is a little nasty.  Maybe we
-            should do another bfd_malloc?  */
-         while (--match_count >= 0)
-           {
-             const char *name = matching_vector[match_count]->name;
-             *(const char **) &matching_vector[match_count] = name;
-           }
+         const char *name = matching_vector[match_count]->name;
+         *(const char **) &matching_vector[match_count] = name;
        }
     }
-
+  else
+    free (matching_vector);
+  if (cleanup)
+    cleanup (abfd);
+  if (preserve_match.marker != NULL)
+    bfd_preserve_finish (abfd, &preserve_match);
+  bfd_preserve_restore (abfd, &preserve);
   return FALSE;
 }
 
This page took 0.030021 seconds and 4 git commands to generate.