[PATCH] bfd: fix missing warnings from bfd_check_format_matches

Alan Modra amodra@gmail.com
Thu Apr 10 06:57:43 GMT 2025


On Sun, Apr 06, 2025 at 11:12:16PM +0100, Andrew Burgess wrote:
> In PR gdb/31846 the user reported an issue where GDB is unable to find
> the build-id within an ELF, despite the build-id being
> present (confirmed using readelf).
> 
> The user was able to try several different builds of GDB, and in one
> build they observed the warning:
> 
>   warning: BFD: FILENAME: unable to decompress section .debug_info
> 
> But in may other builds of GDB this warning was missing.
> 
> There are, I think, a couple of issues that the user is running into,
> but this patch is about why the above warning is often missing from
> GDB's output.
> 
> I wasn't able to reproduce a corrupted .debug_info section such that
> the above warning would be triggered, but it is pretty easy to patch
> the _bfd_elf_make_section_from_shdr function (in bfd/elf.c) such that
> the call to bfd_init_section_decompress_status is reported as a
> failure, thus triggering the warning.  There is a patch that achieves
> this in the bug report.
> 
> I did this, and can confirm that on my build of GDB, I don't see the
> above warning, even though I can confirm that the _bfd_error_handler
> call (in _bfd_elf_make_section_from_shdr) is being reached.
> 
> The problem is back in format.c, in bfd_check_format_matches.  This
> function intercepts all the warnings and places them into a
> per_xvec_messages structure.  These warnings are then printed with a
> call to print_and_clear_messages.
> 
> If bfd_check_format_matches finds a single matching format, then
> print_and_clear_messages, will print all warnings associated with that
> single format.
> 
> But if no format matches, print_and_clear_messages will print all the
> warnings, so long as all targets have emitted the same set of
> warnings, and unfortunately, that is not the case for me.
> 
> The warnings are collected by iterating over bfd_target_vector and
> trying each target.  My target happens to be x86_64_elf64_vec, and, as
> expected this target appears in bfd_target_vector.
> 
> However, bfd_target_vector also includes DEFAULT_VECTOR near the top.
> And in my build, DEFAULT_VECTOR is x86_64_elf64_vec.  Thus, for me,
> the x86_64_elf64_vec entry appears twice in bfd_target_vector, this
> means that x86_64_elf64_vec ends up being tried twice, and, as each
> try generates one warning, the x86_64_elf64_vec entry in the
> per_xvec_messages structure, has two warnings, while the other
> per_xvec_messages entries only have one copy of the warning.
> 
> Because of this difference, print_and_clear_messages decides not to
> print any of the warnings, which is not very helpful.
> 
> I considered a few different approaches to fix this issue:
> 
> We could de-duplicate warnings in the per_xvec_messages structure as
> new entries are added.  So for any particular xvec, each time a new
> warning arrives, if the new warning is identical to an existing
> warning, then don't record it.  This might be an interesting change in
> the future, but for now I rejected this solution as it felt like a
> bodge, the duplicate warnings aren't really from a single attempt at
> an xvec, but are from two distinct attempts at the same xvec. And so:
> 
> I wondered if we could remove the duplicate entries from
> bfd_target_vector.  Or if we could avoid processing the same xvec
> twice maybe?  For the single DEFAULT_VECTOR this wouldn't be too hard
> to do, but bfd_target_vector also includes SELECT_VECS, which I think
> could contain more duplicates.  Changing bfd_check_format_matches to

Yes, SELECT_VECS itself won't contain duplicates due to configure.ac
making selvecs unique, but I think someone could configure with say,
"srec" in --enable-targets which would result in a duplicate.

> avoid attempting any duplicate vectors would now require more
> complexity than a single flag, and I felt there was an easier
> solution, which was:

I'm happy with your solution, and the patch.  To avoid checking
duplicate vectors we'd need to go back to an array effectively
extending _bfd_target_vector with some read/write fields, and use that
for the per-xvec messages plus a flag to say whether that particular
target had already been checked.

> 
> I propose that within bfd_check_format_matches, within the loop that
> tries each entry from bfd_target_vector, as we switch to each vector
> in turn, we should delete any existing warnings within the
> per_xvec_messages structure for the target vector we are about to try.
> 
> This means that, if we repeat a target, only the last set of warnings
> will survive.
> 
> With this change in place, print_and_clear_messages now sees the same
> set of warnings for each target, and so prints out the warning
> message.
> 
> Additionally, while I was investigating this issue I managed to call
> print_and_clear_messages twice.  This caused a crash because the first
> call to print_and_clear_messages frees all the associated memory, but
> leaves the per_xvec_messages::next field pointing to the now
> deallocated object.  I'd like to propose that we set the next field to
> NULL in print_and_clear_messages.  This clearly isn't needed so long
> as print_and_clear_messages is only called once, but (personally) I
> like to set pointers back to NULL if the object they are pointing to
> is free and the parent object is going to live for some additional
> time.  I can drop this extra change if people don't like it.
> 
> This change doesn't really "fix" PR gdb/31846, but it does mean that
> the warning about being unable to decompress .debug_info should now be
> printed consistently, which is a good thing.
> 
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31846
> ---
>  bfd/format.c | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
> 
> diff --git a/bfd/format.c b/bfd/format.c
> index 7769ad095f6..3db0792d412 100644
> --- a/bfd/format.c
> +++ b/bfd/format.c
> @@ -331,6 +331,25 @@ print_and_clear_messages (struct per_xvec_messages *list,
>  	free (iter);
>        iter = next;
>      }
> +
> +  /* Don't retain a pointer to free'd memory.  */
> +  list->next = NULL;
> +}
> +
> +/* Discard all messages associated with TARG in LIST.  Unlike
> +   print_and_clear_messages, PER_XVEC_NO_TARGET is not valid for TARG.  */
> +
> +static void
> +clear_messages (struct per_xvec_messages *list,
> +		const bfd_target *targ)
> +{
> +  struct per_xvec_messages *iter;
> +
> +  for (iter = list; iter != NULL; iter = iter->next)
> +    {
> +      if (iter->targ == targ)
> +	clear_warnmsg (&iter->messages);
> +    }
>  }
>  
>  /* This a copy of lto_section defined in GCC (lto-streamer.h).  */
> @@ -545,6 +564,12 @@ bfd_check_format_matches (bfd *abfd, bfd_format format, char ***matching)
>        /* Change BFD's target temporarily.  */
>        abfd->xvec = *target;
>  
> +      /* It is possible that targets appear multiple times in
> +	 bfd_target_vector.  If this is the case, then we want to avoid
> +	 accumulating duplicate messages for a target in MESSAGES, so
> +	 discard any previous messages associated with this target.  */
> +      clear_messages (&messages, abfd->xvec);
> +
>        if (bfd_seek (abfd, 0, SEEK_SET) != 0)
>  	goto err_ret;
>  
> 
> base-commit: c2f55040d34784a5c40d25f6a58615da1b4a52be
> -- 
> 2.47.1

-- 
Alan Modra


More information about the Binutils mailing list