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) |