| /* Generic BFD support for file formats. |
| Copyright (C) 1990-2024 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 3 of the License, or |
| (at your option) any later version. |
| |
| This program 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 this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| |
| /* |
| SECTION |
| File formats |
| |
| A format is a BFD concept of high level file contents type. The |
| formats supported by BFD are: |
| |
| o <<bfd_object>> |
| |
| The BFD may contain data, symbols, relocations and debug info. |
| |
| o <<bfd_archive>> |
| |
| The BFD contains other BFDs and an optional index. |
| |
| o <<bfd_core>> |
| |
| The BFD contains the result of an executable core dump. |
| |
| SUBSECTION |
| File format functions |
| */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| |
| /* IMPORT from targets.c. */ |
| extern const size_t _bfd_target_vector_entries; |
| |
| /* |
| FUNCTION |
| bfd_check_format |
| |
| SYNOPSIS |
| bool bfd_check_format (bfd *abfd, bfd_format format); |
| |
| DESCRIPTION |
| Verify if the file attached to the BFD @var{abfd} is compatible |
| with the format @var{format} (i.e., one of <<bfd_object>>, |
| <<bfd_archive>> or <<bfd_core>>). |
| |
| If the BFD has been set to a specific target before the |
| call, only the named target and format combination is |
| checked. If the target has not been set, or has been set to |
| <<default>>, then all the known target backends is |
| interrogated to determine a match. If the default target |
| matches, it is used. If not, exactly one target must recognize |
| the file, or an error results. |
| |
| The function returns <<TRUE>> on success, otherwise <<FALSE>> |
| with one of the following error codes: |
| |
| o <<bfd_error_invalid_operation>> - |
| if <<format>> is not one of <<bfd_object>>, <<bfd_archive>> or |
| <<bfd_core>>. |
| |
| o <<bfd_error_system_call>> - |
| if an error occured during a read - even some file mismatches |
| can cause bfd_error_system_calls. |
| |
| o <<file_not_recognised>> - |
| none of the backends recognised the file format. |
| |
| o <<bfd_error_file_ambiguously_recognized>> - |
| more than one backend recognised the file format. |
| |
| When calling bfd_check_format (or bfd_check_format_matches), |
| any underlying file descriptor will be kept open for the |
| duration of the call. This is done to avoid races when |
| another thread calls bfd_cache_close_all. In this scenario, |
| the thread calling bfd_check_format must call bfd_cache_close |
| itself. |
| */ |
| |
| bool |
| 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_iovec *iovec; |
| void *iostream; |
| const struct bfd_arch_info *arch_info; |
| const struct bfd_build_id *build_id; |
| bfd_cleanup cleanup; |
| struct bfd_section *sections; |
| struct bfd_section *section_last; |
| unsigned int section_count; |
| unsigned int section_id; |
| unsigned int symcount; |
| bool read_only; |
| bfd_vma start_address; |
| 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 bool |
| 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->iovec = abfd->iovec; |
| preserve->iostream = abfd->iostream; |
| preserve->sections = abfd->sections; |
| preserve->section_last = abfd->section_last; |
| preserve->section_count = abfd->section_count; |
| preserve->section_id = _bfd_section_id; |
| preserve->symcount = abfd->symcount; |
| preserve->read_only = abfd->read_only; |
| preserve->start_address = abfd->start_address; |
| 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)); |
| } |
| |
| /* A back-end object_p function may flip a bfd from file backed to |
| in-memory, eg. pe_ILF_object_p. In that case to restore the |
| original IO state we need to reopen the file. Conversely, if we |
| are restoring a previously matched pe ILF format and have been |
| checking further target matches using file IO then we need to close |
| the file and detach the bfd from the cache lru list. */ |
| |
| static void |
| io_reinit (bfd *abfd, struct bfd_preserve *preserve) |
| { |
| if (abfd->iovec != preserve->iovec) |
| { |
| /* Handle file backed to in-memory transition. bfd_cache_close |
| won't do anything unless abfd->iovec is the cache_iovec. |
| Don't be tempted to call iovec->bclose here. We don't want |
| to call memory_bclose, which would free the bim. The bim |
| must be kept if bfd_check_format_matches is going to decide |
| later that the PE format needing it is in fact the correct |
| target match. */ |
| bfd_cache_close (abfd); |
| abfd->iovec = preserve->iovec; |
| abfd->iostream = preserve->iostream; |
| |
| /* Handle in-memory to file backed transition. */ |
| if ((abfd->flags & BFD_CLOSED_BY_CACHE) != 0 |
| && (abfd->flags & BFD_IN_MEMORY) != 0 |
| && (preserve->flags & BFD_CLOSED_BY_CACHE) == 0 |
| && (preserve->flags & BFD_IN_MEMORY) == 0) |
| bfd_open_file (abfd); |
| } |
| abfd->flags = preserve->flags; |
| } |
| |
| /* Clear out a subset of BFD state. */ |
| |
| static void |
| bfd_reinit (bfd *abfd, unsigned int section_id, |
| struct bfd_preserve *preserve, bfd_cleanup cleanup) |
| { |
| _bfd_section_id = section_id; |
| if (cleanup) |
| cleanup (abfd); |
| abfd->tdata.any = NULL; |
| abfd->arch_info = &bfd_default_arch_struct; |
| io_reinit (abfd, preserve); |
| abfd->symcount = 0; |
| abfd->read_only = 0; |
| abfd->start_address = 0; |
| abfd->build_id = NULL; |
| 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; |
| io_reinit (abfd, preserve); |
| 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->symcount = preserve->symcount; |
| abfd->read_only = preserve->read_only; |
| abfd->start_address = preserve->start_address; |
| 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; |
| } |
| |
| static void |
| print_warnmsg (struct per_xvec_message **list) |
| { |
| for (struct per_xvec_message *warn = *list; warn; warn = warn->next) |
| _bfd_error_handler ("%s", warn->message); |
| } |
| |
| static void |
| clear_warnmsg (struct per_xvec_message **list) |
| { |
| struct per_xvec_message *warn = *list; |
| while (warn) |
| { |
| struct per_xvec_message *next = warn->next; |
| free (warn); |
| warn = next; |
| } |
| *list = NULL; |
| } |
| |
| /* Free all the storage in LIST. Note that the first element of LIST |
| is special and is assumed to be stack-allocated. TARG is used for |
| re-issuing warning messages. If TARG is PER_XVEC_NO_TARGET, then |
| it acts like a sort of wildcard -- messages are reissued if all |
| targets with messages have identical messages. One copy of the |
| messages are then reissued. If TARG is anything else, then only |
| messages associated with TARG are emitted. */ |
| |
| static void |
| print_and_clear_messages (struct per_xvec_messages *list, |
| const bfd_target *targ) |
| { |
| struct per_xvec_messages *iter; |
| |
| if (targ == PER_XVEC_NO_TARGET) |
| { |
| iter = list->next; |
| while (iter != NULL) |
| { |
| struct per_xvec_message *msg1 = list->messages; |
| struct per_xvec_message *msg2 = iter->messages; |
| do |
| { |
| if (strcmp (msg1->message, msg2->message)) |
| break; |
| msg1 = msg1->next; |
| msg2 = msg2->next; |
| } while (msg1 && msg2); |
| if (msg1 || msg2) |
| break; |
| iter = iter->next; |
| } |
| if (iter == NULL) |
| targ = list->targ; |
| } |
| |
| iter = list; |
| while (iter != NULL) |
| { |
| struct per_xvec_messages *next = iter->next; |
| |
| if (iter->targ == targ) |
| print_warnmsg (&iter->messages); |
| clear_warnmsg (&iter->messages); |
| if (iter != list) |
| free (iter); |
| iter = next; |
| } |
| } |
| |
| /* This a copy of lto_section defined in GCC (lto-streamer.h). */ |
| |
| struct lto_section |
| { |
| int16_t major_version; |
| int16_t minor_version; |
| unsigned char slim_object; |
| |
| /* Flags is a private field that is not defined publicly. */ |
| uint16_t flags; |
| }; |
| |
| /* Set lto_type in ABFD. */ |
| |
| static void |
| bfd_set_lto_type (bfd *abfd ATTRIBUTE_UNUSED) |
| { |
| #if BFD_SUPPORTS_PLUGINS |
| if (abfd->format == bfd_object |
| && abfd->lto_type == lto_non_object |
| && (abfd->flags & (DYNAMIC | EXEC_P)) == 0) |
| { |
| asection *sec; |
| enum bfd_lto_object_type type = lto_non_ir_object; |
| struct lto_section lsection; |
| /* GCC uses .gnu.lto_.lto.<some_hash> as a LTO bytecode information |
| section. */ |
| for (sec = abfd->sections; sec != NULL; sec = sec->next) |
| if (startswith (sec->name, ".gnu.lto_.lto.") |
| && bfd_get_section_contents (abfd, sec, &lsection, 0, |
| sizeof (struct lto_section))) |
| { |
| if (lsection.slim_object) |
| type = lto_slim_ir_object; |
| else |
| type = lto_fat_ir_object; |
| break; |
| } |
| |
| abfd->lto_type = type; |
| } |
| #endif |
| } |
| |
| /* |
| FUNCTION |
| bfd_check_format_matches |
| |
| SYNOPSIS |
| bool bfd_check_format_matches |
| (bfd *abfd, bfd_format format, char ***matching); |
| |
| DESCRIPTION |
| Like <<bfd_check_format>>, except when it returns FALSE with |
| <<bfd_errno>> set to <<bfd_error_file_ambiguously_recognized>>. In that |
| case, if @var{matching} is not NULL, it will be filled in with |
| a NULL-terminated list of the names of the formats that matched, |
| allocated with <<malloc>>. |
| Then the user may choose a format and try again. |
| |
| When done with the list that @var{matching} points to, the caller |
| should free it. |
| */ |
| |
| bool |
| 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, *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; |
| struct per_xvec_messages messages = { abfd, PER_XVEC_NO_TARGET, NULL, NULL }; |
| struct per_xvec_messages *orig_messages; |
| bool old_in_format_matches; |
| |
| if (matching != NULL) |
| *matching = NULL; |
| |
| if (!bfd_read_p (abfd) |
| || (unsigned int) abfd->format >= (unsigned int) bfd_type_end) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return false; |
| } |
| |
| if (abfd->format != bfd_unknown) |
| { |
| bfd_set_lto_type (abfd); |
| return abfd->format == format; |
| } |
| |
| if (matching != NULL || *bfd_associated_vector != NULL) |
| { |
| size_t amt; |
| |
| amt = sizeof (*matching_vector) * 2 * _bfd_target_vector_entries; |
| matching_vector = (const bfd_target **) bfd_malloc (amt); |
| if (!matching_vector) |
| return false; |
| } |
| |
| /* Avoid clashes with bfd_cache_close_all running in another |
| thread. */ |
| if (!bfd_cache_set_uncloseable (abfd, true, &old_in_format_matches)) |
| return false; |
| |
| /* Presume the answer is yes. */ |
| abfd->format = format; |
| save_targ = abfd->xvec; |
| |
| /* Don't report errors on recursive calls checking the first element |
| of an archive. */ |
| orig_messages = _bfd_set_error_handler_caching (&messages); |
| |
| 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, 0, SEEK_SET) != 0) /* rewind! */ |
| goto err_ret; |
| |
| cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd)); |
| |
| 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, |
| and I'm reluctant to change it. However, in the case of an |
| archive, it can cause problems. If the specified target does |
| not permit archives (e.g., the binary target), then we should |
| not allow some other target to recognize it as an archive, but |
| should instead allow the specified target to recognize it as an |
| object. When I first made this change, it broke the PE target, |
| because the specified pei-i386 target did not recognize the |
| actual pe-i386 archive. Since there may be other problems of |
| this sort, I changed this test to check only for the binary |
| target. */ |
| if (format == bfd_archive && save_targ == &binary_vec) |
| 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++) |
| { |
| 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; |
| |
| /* 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, &preserve, 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); |
| |
| /* Change BFD's target temporarily. */ |
| abfd->xvec = *target; |
| |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
| goto err_ret; |
| |
| cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd)); |
| if (cleanup) |
| { |
| 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)) |
| { |
| /* 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 (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. */ |
| right_targ = ar_right_targ; |
| |
| if (right_targ == bfd_default_vector[0]) |
| { |
| match_count = 1; |
| } |
| else |
| { |
| match_count = ar_match_index - _bfd_target_vector_entries; |
| |
| if (matching_vector && match_count > 1) |
| memcpy (matching_vector, |
| matching_vector + _bfd_target_vector_entries, |
| sizeof (*matching_vector) * match_count); |
| } |
| } |
| |
| /* 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; |
| |
| while ((right_targ = *assoc++) != NULL) |
| { |
| int i = match_count; |
| |
| while (--i >= 0) |
| if (matching_vector[i] == right_targ |
| && right_targ->match_priority <= best_match) |
| break; |
| |
| if (i >= 0) |
| { |
| match_count = 1; |
| break; |
| } |
| } |
| } |
| |
| /* 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) |
| { |
| 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_match.marker != NULL) |
| cleanup = 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). 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, &preserve, cleanup); |
| bfd_release (abfd, preserve.marker); |
| if (bfd_seek (abfd, 0, SEEK_SET) != 0) |
| goto err_ret; |
| cleanup = BFD_SEND_FMT (abfd, _bfd_check_format, (abfd)); |
| BFD_ASSERT (cleanup != NULL); |
| } |
| |
| 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); |
| _bfd_restore_error_handler_caching (orig_messages); |
| |
| print_and_clear_messages (&messages, abfd->xvec); |
| |
| bfd_set_lto_type (abfd); |
| |
| /* File position has moved, BTW. */ |
| return bfd_cache_set_uncloseable (abfd, old_in_format_matches, NULL); |
| } |
| |
| if (match_count == 0) |
| { |
| 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); |
| goto out; |
| } |
| |
| /* Restore original target type and format. */ |
| abfd->xvec = save_targ; |
| abfd->format = bfd_unknown; |
| bfd_set_error (bfd_error_file_ambiguously_recognized); |
| |
| 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) |
| { |
| const char *name = matching_vector[match_count]->name; |
| *(const char **) &matching_vector[match_count] = name; |
| } |
| } |
| else |
| free (matching_vector); |
| if (cleanup) |
| cleanup (abfd); |
| out: |
| if (preserve_match.marker != NULL) |
| bfd_preserve_finish (abfd, &preserve_match); |
| bfd_preserve_restore (abfd, &preserve); |
| _bfd_restore_error_handler_caching (orig_messages); |
| print_and_clear_messages (&messages, PER_XVEC_NO_TARGET); |
| bfd_cache_set_uncloseable (abfd, old_in_format_matches, NULL); |
| return false; |
| } |
| |
| /* |
| FUNCTION |
| bfd_set_format |
| |
| SYNOPSIS |
| bool bfd_set_format (bfd *abfd, bfd_format format); |
| |
| DESCRIPTION |
| This function sets the file format of the BFD @var{abfd} to the |
| format @var{format}. If the target set in the BFD does not |
| support the format requested, the format is invalid, or the BFD |
| is not open for writing, then an error occurs. |
| */ |
| |
| bool |
| bfd_set_format (bfd *abfd, bfd_format format) |
| { |
| if (bfd_read_p (abfd) |
| || (unsigned int) abfd->format >= (unsigned int) bfd_type_end) |
| { |
| bfd_set_error (bfd_error_invalid_operation); |
| return false; |
| } |
| |
| if (abfd->format != bfd_unknown) |
| return abfd->format == format; |
| |
| /* Presume the answer is yes. */ |
| abfd->format = format; |
| |
| if (!BFD_SEND_FMT (abfd, _bfd_set_format, (abfd))) |
| { |
| abfd->format = bfd_unknown; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| FUNCTION |
| bfd_format_string |
| |
| SYNOPSIS |
| const char *bfd_format_string (bfd_format format); |
| |
| DESCRIPTION |
| Return a pointer to a const string |
| <<invalid>>, <<object>>, <<archive>>, <<core>>, or <<unknown>>, |
| depending upon the value of @var{format}. |
| */ |
| |
| const char * |
| bfd_format_string (bfd_format format) |
| { |
| if (((int) format < (int) bfd_unknown) |
| || ((int) format >= (int) bfd_type_end)) |
| return "invalid"; |
| |
| switch (format) |
| { |
| case bfd_object: |
| return "object"; /* Linker/assembler/compiler output. */ |
| case bfd_archive: |
| return "archive"; /* Object archive file. */ |
| case bfd_core: |
| return "core"; /* Core dump. */ |
| default: |
| return "unknown"; |
| } |
| } |