PR32260: Improve error handling on string merging
Alan Modra
amodra@gmail.com
Wed Oct 16 22:15:12 GMT 2024
On Wed, Oct 16, 2024 at 03:41:22PM +0200, Michael Matz wrote:
> if the input sections are near the max supported size (4G)
> we might fail to enlarge the hash table. The error handling
> for this case didn't quite work. When this happens we can
> gracefully fall back to just not deduplicate this section
> (and continue with further mergable sections). We were mixing
> that with the case of not being able to even allocate a small
> structure (in which case we can as well error out completely),
> this disentables both cases.
Thanks for doing this. Please apply.
> bfd/
>
> PR ld/32260
> * merge.c (sec_merge_maybe_resize): Check overflow in ultimate
> target type.
> (record_section): Return three-state, use new state when unable
> to enlarge hash table.
> (_bfd_merge_sections): Remove current section from merging
> consideration when hashtable can't be enlarged.
> ---
> Tested without regressions on Alans list of targets. Okay for master?
>
> bfd/merge.c | 38 +++++++++++++++++++++++++-------------
> 1 file changed, 25 insertions(+), 13 deletions(-)
>
> diff --git a/bfd/merge.c b/bfd/merge.c
> index 5c8e69bedd9..c811bc57eae 100644
> --- a/bfd/merge.c
> +++ b/bfd/merge.c
> @@ -165,7 +165,7 @@ struct sec_merge_sec_info
>
> /* Given a merge hash table TABLE and a number of entries to be
> ADDED, possibly resize the table for this to fit without further
> - resizing. */
> + resizing. Returns false if that can't be done for whatever reason. */
>
> static bool
> sec_merge_maybe_resize (struct sec_merge_hash *table, unsigned added)
> @@ -174,17 +174,18 @@ sec_merge_maybe_resize (struct sec_merge_hash *table, unsigned added)
> if (NEEDS_RESIZE (bfdtab->count + added, table->nbuckets))
> {
> unsigned i;
> - unsigned long newnb = table->nbuckets * 2;
> + unsigned long newnb = table->nbuckets;
> struct sec_merge_hash_entry **newv;
> uint64_t *newl;
> unsigned long alloc;
>
> - while (NEEDS_RESIZE (bfdtab->count + added, newnb))
> + do
> {
> newnb *= 2;
> - if (!newnb)
> + if (!(unsigned int)newnb)
> return false;
> }
> + while (NEEDS_RESIZE (bfdtab->count + added, newnb));
>
> alloc = newnb * sizeof (newl[0]);
> if (alloc / sizeof (newl[0]) != newnb)
> @@ -698,9 +699,12 @@ _bfd_add_merge_section (bfd *abfd, void **psinfo, asection *sec,
> }
>
> /* Record one whole input section (described by SECINFO) into the hash table
> - SINFO. */
> + SINFO. Returns 0 on hard errors (no sense in continuing link),
> + 1 when section is completely recorded, and 2 when the section wasn't
> + recorded but we can continue (e.g. by simply not deduplicating this
> + section). */
>
> -static bool
> +static int
> record_section (struct sec_merge_info *sinfo,
> struct sec_merge_sec_info *secinfo)
> {
> @@ -737,8 +741,8 @@ record_section (struct sec_merge_info *sinfo,
> merged into this area will make use of that as well. */
> if (!sec_merge_maybe_resize (sinfo->htab, 1 + sec->size / 2))
> {
> - bfd_set_error (bfd_error_no_memory);
> - goto error_return;
> + free (contents);
> + return 2;
> }
>
> /* Walk through the contents, calculate hashes and length of all
> @@ -788,14 +792,14 @@ record_section (struct sec_merge_info *sinfo,
> /*printf ("ZZZ %s:%s %u entries\n", sec->owner->filename, sec->name,
> (unsigned)secinfo->noffsetmap);*/
>
> - return true;
> + return 1;
>
> error_return:
> free (contents);
> contents = NULL;
> for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next)
> *secinfo->psecinfo = NULL;
> - return false;
> + return 0;
> }
>
> /* qsort comparison function. Won't ever return zero as all entries
> @@ -991,9 +995,16 @@ _bfd_merge_sections (bfd *abfd,
> }
> else
> {
> - if (!record_section (sinfo, secinfo))
> + int e = record_section (sinfo, secinfo);
> + if (e == 0)
> return false;
> - if (align)
> + if (e == 2)
> + {
> + *secinfo->psecinfo = NULL;
> + if (remove_hook)
> + (*remove_hook) (abfd, secinfo->sec);
> + }
> + else if (align)
> {
> unsigned int opb = bfd_octets_per_byte (abfd, secinfo->sec);
>
> @@ -1043,7 +1054,8 @@ _bfd_merge_sections (bfd *abfd,
> /* Finally remove all input sections which have not made it into
> the hash table at all. */
> for (secinfo = sinfo->chain; secinfo; secinfo = secinfo->next)
> - if (secinfo->first_str == NULL)
> + if (secinfo->first_str == NULL
> + && secinfo->sec->sec_info_type == SEC_INFO_TYPE_MERGE)
> secinfo->sec->flags |= SEC_EXCLUDE | SEC_KEEP;
> }
>
> --
> 2.42.0
--
Alan Modra
More information about the Binutils
mailing list