Release bfd_alloc memory in bfd_check_format_matches
authorAlan Modra <amodra@gmail.com>
Thu, 2 Jan 2020 07:09:32 +0000 (17:39 +1030)
committerAlan Modra <amodra@gmail.com>
Sat, 4 Jan 2020 08:50:33 +0000 (19:20 +1030)
It's a little tricky.  We can release any memory back when we have a
match failure, but after a match success which we might want to
preserve for later use the high water mark must change to that of the
matched bfd.

* format.c (bfd_check_format_matches): Add preserve_match.
Save initial bfd state in "preserve", matched bfd state in
"preserve_match".  Save just the first match.  Release
bfd_alloc memory.  Restore and finish preserved state as
appropriate on all function exit paths.

bfd/ChangeLog
bfd/format.c

index 8291a980f4362f067b2e5030dc2033ef3862ac19..148de794bc44e4803d15fb8451b87d5c692d2cbf 100644 (file)
@@ -1,3 +1,11 @@
+2020-01-04  Alan Modra  <amodra@gmail.com>
+
+       * format.c (bfd_check_format_matches): Add preserve_match.
+       Save initial bfd state in "preserve", matched bfd state in
+       "preserve_match".  Save just the first match.  Release
+       bfd_alloc memory.  Restore and finish preserved state as
+       appropriate on all function exit paths.
+
 2020-01-04  Alan Modra  <amodra@gmail.com>
 
        * mmo.c (mmo_mkobject): Allocate tdata with bfd_zalloc.
index 5c30431d7a09cc3b851ccf63853cea51e86f94c1..e53955eb45878dda7a1303bd421084490ba4ca2e 100644 (file)
@@ -219,7 +219,7 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
   int match_count, best_count, best_match;
   int ar_match_index;
   unsigned int initial_section_id = _bfd_section_id;
-  struct bfd_preserve preserve;
+  struct bfd_preserve preserve, preserve_match;
 
   if (matching != NULL)
     *matching = NULL;
@@ -247,7 +247,10 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
   /* Presume the answer is yes.  */
   abfd->format = format;
   save_targ = abfd->xvec;
-  preserve.marker = NULL;
+
+  preserve_match.marker = NULL;
+  if (!bfd_preserve_save (abfd, &preserve))
+    goto err_ret;
 
   /* If the target type was explicitly specified, just check that target.  */
   if (!abfd->target_defaulted)
@@ -289,6 +292,7 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
   for (target = bfd_target_vector; *target != NULL; target++)
     {
       const bfd_target *temp;
+      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
@@ -306,6 +310,14 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
         have sections attached, which will confuse the next
         _bfd_check_format call.  */
       bfd_reinit (abfd, initial_section_id);
+      /* 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);
 
       /* Change BFD's target temporarily.  */
       abfd->xvec = *target;
@@ -332,10 +344,6 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
            match_priority = (*target)->match_priority;
 #endif
 
-         match_targ = temp;
-         if (preserve.marker != NULL)
-           bfd_preserve_finish (abfd, &preserve);
-
          if (abfd->format != bfd_archive
              || (bfd_has_map (abfd)
                  && bfd_get_error () != bfd_error_wrong_object_format))
@@ -374,8 +382,12 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
              ar_match_index++;
            }
 
-         if (!bfd_preserve_save (abfd, &preserve))
-           goto err_ret;
+         if (preserve_match.marker == NULL)
+           {
+             match_targ = temp;
+             if (!bfd_preserve_save (abfd, &preserve_match))
+               goto err_ret;
+           }
        }
       else if (bfd_get_error () != bfd_error_wrong_format)
        goto err_ret;
@@ -448,18 +460,23 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
      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.marker != NULL)
-    bfd_preserve_restore (abfd, &preserve);
+  if (preserve_match.marker != NULL)
+    bfd_preserve_restore (abfd, &preserve_match);
 
   if (match_count == 1)
     {
       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).  */
+        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);
+         bfd_release (abfd, preserve.marker);
          if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
            goto err_ret;
          match_targ = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
@@ -477,6 +494,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
 
       if (matching_vector)
        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;
@@ -491,8 +511,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
       abfd->format = bfd_unknown;
       if (matching_vector)
        free (matching_vector);
-      if (preserve.marker != NULL)
-       bfd_preserve_restore (abfd, &preserve);
+      if (preserve_match.marker != NULL)
+       bfd_preserve_finish (abfd, &preserve_match);
+      bfd_preserve_restore (abfd, &preserve);
       return FALSE;
     }
 
@@ -515,6 +536,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
     }
   else if (matching_vector)
     free (matching_vector);
+  if (preserve_match.marker != NULL)
+    bfd_preserve_finish (abfd, &preserve_match);
+  bfd_preserve_restore (abfd, &preserve);
   return FALSE;
 }