/* 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];
}
}
-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)
{
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)
{
&& 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;
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);
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)
{
&& 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;
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;
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;
}
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))
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;
base += 63 * 8;
}
}
+ free (relr_addr);
}
for (group = htab->group; group != NULL; group = group->next)
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++;
{
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)
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)