From 0aac2413d39d9f6d9e6879a0daa6bd5dea3e0fe3 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 3 Mar 2022 00:04:57 +1030 Subject: [PATCH] PowerPC64 DT_RELR relative reloc addresses Section addresses can change between ppc64_elf_size_stubs and ppc64_elf_build_stubs due to .eh_frame editing. The idea of stashing r_offset final addresses calculated in ppc64_elf_size_stubs for use by ppc64_elf_build_stubs was never a good idea. Instead, we need to keep section/offset pairs. * elf64-ppc.c (struct ppc_link_hash_table): Delete relr_addr. Add relr section/offset array. (append_relr_off): Rewrite. Update all callers. (sort_relr): New function. (ppc64_elf_size_stubs): Adjust to suit new relative reloc stash. (ppc64_elf_build_stubs): Likewise. --- bfd/elf64-ppc.c | 125 +++++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index dd79c05bb61..89ce4cfba0d 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -3269,10 +3269,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]; @@ -13410,16 +13414,11 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec) } } -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) { @@ -13427,16 +13426,51 @@ append_relr_off (struct ppc_link_hash_table *htab, bfd_vma off) 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 + all input files, stashing info about needed relative relocs. */ + static bool got_and_plt_relr_for_local_syms (struct bfd_link_info *info) { @@ -13484,10 +13518,7 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info) && 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; @@ -13502,10 +13533,7 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info) 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); @@ -13525,6 +13553,9 @@ got_and_plt_relr_for_local_syms (struct bfd_link_info *info) 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) { @@ -13556,10 +13587,7 @@ got_and_plt_relr (struct elf_link_hash_entry *h, void *inf) && 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; @@ -13571,10 +13599,7 @@ got_and_plt_relr (struct elf_link_hash_entry *h, void *inf) 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; @@ -13852,9 +13877,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) 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; } @@ -14205,9 +14228,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) 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)) @@ -14216,28 +14237,28 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) 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; @@ -14245,6 +14266,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) base += 63 * 8; } } + free (relr_addr); } for (group = htab->group; group != NULL; group = group->next) @@ -15184,17 +15206,21 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, 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++; @@ -15204,10 +15230,10 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, { 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) @@ -15217,6 +15243,7 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, 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) -- 2.30.2