| Index: bfd/format.c
|
| diff --git a/bfd/format.c b/bfd/format.c
|
| index 66b9051efe55814c0e34d880d23a6e2bc83b7b61..b8f39ca0f0878fffbc9ff466cb5021b0088e0736 100644
|
| --- a/bfd/format.c
|
| +++ b/bfd/format.c
|
| @@ -95,6 +95,91 @@ 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;
|
| + struct bfd_hash_table section_htab;
|
| +};
|
| +
|
| +/* 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)
|
| +{
|
| + 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_htab = abfd->section_htab;
|
| + preserve->marker = bfd_alloc (abfd, 1);
|
| + 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)
|
| +{
|
| + 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 void
|
| +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_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;
|
| +}
|
| +
|
| +/* 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)
|
| +{
|
| + /* 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
|
| @@ -124,6 +209,7 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| const bfd_target *save_targ, *right_targ, *ar_right_targ, *match_targ;
|
| int match_count, best_count, best_match;
|
| int ar_match_index;
|
| + struct bfd_preserve preserve;
|
|
|
| if (matching != NULL)
|
| *matching = NULL;
|
| @@ -138,12 +224,6 @@ 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 != NULL || *bfd_associated_vector != NULL)
|
| {
|
| bfd_size_type amt;
|
| @@ -154,14 +234,10 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| return FALSE;
|
| }
|
|
|
| - right_targ = 0;
|
| - ar_right_targ = 0;
|
| - match_targ = 0;
|
| - best_match = 256;
|
| - best_count = 0;
|
| -
|
| /* Presume the answer is yes. */
|
| abfd->format = format;
|
| + save_targ = abfd->xvec;
|
| + preserve.marker = NULL;
|
|
|
| /* If the target type was explicitly specified, just check that target. */
|
| if (!abfd->target_defaulted)
|
| @@ -190,10 +266,19 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| 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;
|
|
|
| /* Don't check the default target twice. */
|
| if (*target == &binary_vec
|
| @@ -201,7 +286,13 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| || (*target)->match_priority > best_match)
|
| continue;
|
|
|
| - abfd->xvec = *target; /* Change BFD's target temporarily. */
|
| + /* 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);
|
| +
|
| + /* Change BFD's target temporarily. */
|
| + abfd->xvec = *target;
|
|
|
| if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
|
| goto err_ret;
|
| @@ -214,44 +305,51 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
|
|
| temp = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
|
| if (temp)
|
| - match_targ = temp;
|
| -
|
| - if (temp && (abfd->format != bfd_archive || bfd_has_map (abfd)))
|
| {
|
| - /* 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])
|
| - goto ok_ret;
|
| -
|
| - if (matching_vector)
|
| - matching_vector[match_count] = temp;
|
| - match_count++;
|
| + match_targ = temp;
|
| + if (preserve.marker != NULL)
|
| + bfd_preserve_finish (abfd, &preserve);
|
|
|
| - if (temp->match_priority < best_match)
|
| + if (abfd->format != bfd_archive
|
| + || (bfd_has_map (abfd)
|
| + && bfd_get_error () != bfd_error_wrong_object_format))
|
| {
|
| - best_match = temp->match_priority;
|
| - best_count = 0;
|
| + /* 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])
|
| + goto ok_ret;
|
| +
|
| + if (matching_vector)
|
| + matching_vector[match_count] = temp;
|
| + match_count++;
|
| +
|
| + if (temp->match_priority < best_match)
|
| + {
|
| + best_match = temp->match_priority;
|
| + best_count = 0;
|
| + }
|
| + best_count++;
|
| }
|
| - best_count++;
|
| - }
|
| - else if (temp
|
| - || (err = bfd_get_error ()) == bfd_error_wrong_object_format
|
| - || err == bfd_error_file_ambiguously_recognized)
|
| - {
|
| - /* An archive with no armap or 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_vector)
|
| - matching_vector[ar_match_index] = *target;
|
| - ar_match_index++;
|
| + 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 (!bfd_preserve_save (abfd, &preserve))
|
| + goto err_ret;
|
| }
|
| - else if (err != bfd_error_wrong_format)
|
| + else if (bfd_get_error () != bfd_error_wrong_format)
|
| goto err_ret;
|
| }
|
|
|
| @@ -278,6 +376,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***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;
|
| @@ -287,7 +388,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)
|
| @@ -298,6 +400,29 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| }
|
| }
|
|
|
| + /* 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 (match_count > 1 && best_count != match_count)
|
| + {
|
| + int i;
|
| +
|
| + for (i = 0; i < match_count; i++)
|
| + {
|
| + right_targ = matching_vector[i];
|
| + if (right_targ->match_priority <= best_match)
|
| + break;
|
| + }
|
| + match_count = 1;
|
| + }
|
| +
|
| + /* 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.marker != NULL)
|
| + bfd_preserve_restore (abfd, &preserve);
|
| +
|
| if (match_count == 1)
|
| {
|
| abfd->xvec = right_targ;
|
| @@ -306,9 +431,11 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
| state (except possibly for XVEC). */
|
| if (match_targ != right_targ)
|
| {
|
| + bfd_reinit (abfd);
|
| if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
|
| goto err_ret;
|
| match_targ = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd));
|
| + BFD_ASSERT (match_targ != NULL);
|
| }
|
|
|
| ok_ret:
|
| @@ -322,7 +449,9 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
|
|
|
| if (matching_vector)
|
| free (matching_vector);
|
| - return TRUE; /* File position has moved, BTW. */
|
| +
|
| + /* File position has moved, BTW. */
|
| + return TRUE;
|
| }
|
|
|
| if (match_count == 0)
|
| @@ -334,11 +463,14 @@ 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);
|
| return FALSE;
|
| }
|
|
|
| - abfd->xvec = save_targ; /* Restore original target type. */
|
| - abfd->format = bfd_unknown; /* Restore original format. */
|
| + /* Restore original target type and format. */
|
| + abfd->xvec = save_targ;
|
| + abfd->format = bfd_unknown;
|
| bfd_set_error (bfd_error_file_ambiguously_recognized);
|
|
|
| if (matching)
|
|
|