--- binutils.orig/bfd/elf64-ppc.c 2022-06-13 12:05:13.325289568 +0100 +++ binutils-2.38/bfd/elf64-ppc.c 2022-06-13 12:05:36.080143584 +0100 @@ -3270,10 +3270,14 @@ struct ppc_link_hash_table /* The size of reliplt used by got entry relocs. */ bfd_size_type got_reli_size; - /* DT_RELR array of r_offset. */ + /* DT_RELR array of section/r_offset. */ size_t relr_alloc; size_t relr_count; - bfd_vma *relr_addr; + struct + { + asection *sec; + bfd_vma off; + } *relr; /* Statistics. */ unsigned long stub_count[ppc_stub_save_res]; @@ -13419,16 +13423,11 @@ maybe_strip_output (struct bfd_link_info } } -static int -compare_relr_address (const void *arg1, const void *arg2) -{ - bfd_vma a = *(bfd_vma *) arg1; - bfd_vma b = *(bfd_vma *) arg2; - return a < b ? -1 : a > b ? 1 : 0; -} +/* Stash R_PPC64_RELATIVE reloc at input section SEC, r_offset OFF to + the array of such relocs. */ static bool -append_relr_off (struct ppc_link_hash_table *htab, bfd_vma off) +append_relr_off (struct ppc_link_hash_table *htab, asection *sec, bfd_vma off) { if (htab->relr_count >= htab->relr_alloc) { @@ -13436,16 +13435,51 @@ append_relr_off (struct ppc_link_hash_ta htab->relr_alloc = 4096; else htab->relr_alloc *= 2; - htab->relr_addr - = bfd_realloc (htab->relr_addr, - htab->relr_alloc * sizeof (htab->relr_addr[0])); - if (htab->relr_addr == NULL) + htab->relr = bfd_realloc (htab->relr, + htab->relr_alloc * sizeof (*htab->relr)); + if (htab->relr == NULL) return false; } - htab->relr_addr[htab->relr_count++] = off; + htab->relr[htab->relr_count].sec = sec; + htab->relr[htab->relr_count].off = off; + htab->relr_count++; return true; } +/* qsort comparator for bfd_vma args. */ + +static int +compare_relr_address (const void *arg1, const void *arg2) +{ + bfd_vma a = *(bfd_vma *) arg1; + bfd_vma b = *(bfd_vma *) arg2; + return a < b ? -1 : a > b ? 1 : 0; +} + +/* Produce a malloc'd sorted array of reloc addresses from the info + stored by append_relr_off. */ + +static bfd_vma * +sort_relr (struct ppc_link_hash_table *htab) +{ + bfd_vma *addr = bfd_malloc (htab->relr_count * sizeof (*addr)); + if (addr == NULL) + return NULL; + + for (size_t i = 0; i < htab->relr_count; i++) + addr[i] = (htab->relr[i].sec->output_section->vma + + htab->relr[i].sec->output_offset + + htab->relr[i].off); + + if (htab->relr_count > 1) + qsort (addr, htab->relr_count, sizeof (*addr), compare_relr_address); + + return addr; +} + +/* Look over GOT and PLT entries saved on elf_local_got_ents for all + input files, stashing info about needed relative relocs. */ + static bool got_and_plt_relr_for_local_syms (struct bfd_link_info *info) { @@ -13493,10 +13527,7 @@ got_and_plt_relr_for_local_syms (struct && isym->st_shndx != SHN_ABS) { asection *got = ppc64_elf_tdata (gent->owner)->got; - bfd_vma r_offset = (got->output_section->vma - + got->output_offset - + gent->got.offset); - if (!append_relr_off (htab, r_offset)) + if (!append_relr_off (htab, got, gent->got.offset)) { htab->stub_error = true; return false; @@ -13511,10 +13542,7 @@ got_and_plt_relr_for_local_syms (struct if (pent->plt.offset != (bfd_vma) -1 && ELF_ST_TYPE (isym->st_info) != STT_GNU_IFUNC) { - bfd_vma r_offset = (pent->plt.offset - + htab->pltlocal->output_offset - + htab->pltlocal->output_section->vma); - if (!append_relr_off (htab, r_offset)) + if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset)) { if (symtab_hdr->contents != (unsigned char *) local_syms) free (local_syms); @@ -13534,6 +13562,9 @@ got_and_plt_relr_for_local_syms (struct return true; } +/* Stash info about needed GOT and PLT entry relative relocs for + global symbol H. */ + static bool got_and_plt_relr (struct elf_link_hash_entry *h, void *inf) { @@ -13565,10 +13596,7 @@ got_and_plt_relr (struct elf_link_hash_e && gent->got.offset != (bfd_vma) -1) { asection *got = ppc64_elf_tdata (gent->owner)->got; - bfd_vma r_offset = (got->output_section->vma - + got->output_offset - + gent->got.offset); - if (!append_relr_off (htab, r_offset)) + if (!append_relr_off (htab, got, gent->got.offset)) { htab->stub_error = true; return false; @@ -13580,10 +13608,7 @@ got_and_plt_relr (struct elf_link_hash_e for (pent = h->plt.plist; pent != NULL; pent = pent->next) if (pent->plt.offset != (bfd_vma) -1) { - bfd_vma r_offset = (htab->pltlocal->output_section->vma - + htab->pltlocal->output_offset - + pent->plt.offset); - if (!append_relr_off (htab, r_offset)) + if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset)) { htab->stub_error = true; return false; @@ -13861,9 +13886,7 @@ ppc64_elf_size_stubs (struct bfd_link_in irela->r_offset); if (r_offset >= (bfd_vma) -2) continue; - r_offset += (section->output_section->vma - + section->output_offset); - if (!append_relr_off (htab, r_offset)) + if (!append_relr_off (htab, section, r_offset)) goto error_ret_free_internal; continue; } @@ -14214,9 +14237,7 @@ ppc64_elf_size_stubs (struct bfd_link_in bfd_vma r_offset; for (r_offset = 0; r_offset < htab->brlt->size; r_offset += 8) - if (!append_relr_off (htab, (r_offset - + htab->brlt->output_section->vma - + htab->brlt->output_offset))) + if (!append_relr_off (htab, htab->brlt, r_offset)) return false; if (!got_and_plt_relr_for_local_syms (info)) @@ -14225,28 +14246,28 @@ ppc64_elf_size_stubs (struct bfd_link_in if (htab->stub_error) return false; - if (htab->relr_count > 1) - qsort (htab->relr_addr, htab->relr_count, sizeof (*htab->relr_addr), - compare_relr_address); + bfd_vma *relr_addr = sort_relr (htab); + if (htab->relr_count != 0 && relr_addr == NULL) + return false; size_t i = 0; while (i < htab->relr_count) { - bfd_vma base = htab->relr_addr[i]; + bfd_vma base = relr_addr[i]; htab->elf.srelrdyn->size += 8; i++; /* Handle possible duplicate address. This can happen as sections increase in size when adding stubs. */ while (i < htab->relr_count - && htab->relr_addr[i] == base) + && relr_addr[i] == base) i++; base += 8; while (1) { size_t start_i = i; while (i < htab->relr_count - && htab->relr_addr[i] - base < 63 * 8 - && (htab->relr_addr[i] - base) % 8 == 0) + && relr_addr[i] - base < 63 * 8 + && (relr_addr[i] - base) % 8 == 0) i++; if (i == start_i) break; @@ -14254,6 +14275,7 @@ ppc64_elf_size_stubs (struct bfd_link_in base += 63 * 8; } } + free (relr_addr); } for (group = htab->group; group != NULL; group = group->next) @@ -15193,17 +15215,21 @@ ppc64_elf_build_stubs (struct bfd_link_i if (htab->elf.srelrdyn->contents == NULL) return false; + bfd_vma *relr_addr = sort_relr (htab); + if (htab->relr_count != 0 && relr_addr == NULL) + return false; + size_t i = 0; bfd_byte *loc = htab->elf.srelrdyn->contents; while (i < htab->relr_count) { - bfd_vma base = htab->relr_addr[i]; + bfd_vma base = relr_addr[i]; BFD_ASSERT (base % 2 == 0); bfd_put_64 (htab->elf.dynobj, base, loc); loc += 8; i++; while (i < htab->relr_count - && htab->relr_addr[i] == base) + && relr_addr[i] == base) { htab->stub_error = true; i++; @@ -15213,10 +15239,10 @@ ppc64_elf_build_stubs (struct bfd_link_i { bfd_vma bits = 0; while (i < htab->relr_count - && htab->relr_addr[i] - base < 63 * 8 - && (htab->relr_addr[i] - base) % 8 == 0) + && relr_addr[i] - base < 63 * 8 + && (relr_addr[i] - base) % 8 == 0) { - bits |= (bfd_vma) 1 << ((htab->relr_addr[i] - base) / 8); + bits |= (bfd_vma) 1 << ((relr_addr[i] - base) / 8); i++; } if (bits == 0) @@ -15226,6 +15252,7 @@ ppc64_elf_build_stubs (struct bfd_link_i base += 63 * 8; } } + free (relr_addr); /* Pad any excess with 1's, a do-nothing encoding. */ while ((size_t) (loc - htab->elf.srelrdyn->contents) < htab->elf.srelrdyn->size)