summaryrefslogtreecommitdiff
path: root/bfd/elf64-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r--bfd/elf64-ppc.c897
1 files changed, 710 insertions, 187 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index ea9e60217bc..29607bdb9fb 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -75,7 +75,6 @@ static bfd_vma opd_entry_value
#define ELF_MACHINE_CODE EM_PPC64
#define ELF_MAXPAGESIZE 0x10000
#define ELF_COMMONPAGESIZE 0x1000
-#define ELF_RELROPAGESIZE ELF_MAXPAGESIZE
#define elf_info_to_howto ppc64_elf_info_to_howto
#define elf_backend_want_got_sym 0
@@ -3094,7 +3093,7 @@ struct ppc_branch_hash_entry
unsigned int iter;
};
-/* Used to track dynamic relocations for local symbols. */
+/* Used to track dynamic relocations. */
struct ppc_dyn_relocs
{
struct ppc_dyn_relocs *next;
@@ -3103,7 +3102,27 @@ struct ppc_dyn_relocs
asection *sec;
/* Total number of relocs copied for the input section. */
- unsigned int count : 31;
+ unsigned int count;
+
+ /* Number of pc-relative relocs copied for the input section. */
+ unsigned int pc_count;
+
+ /* Number of relocs that might become R_PPC64_RELATIVE. */
+ unsigned int rel_count;
+};
+
+struct ppc_local_dyn_relocs
+{
+ struct ppc_local_dyn_relocs *next;
+
+ /* The input section of the reloc. */
+ asection *sec;
+
+ /* Total number of relocs copied for the input section. */
+ unsigned int count;
+
+ /* Number of relocs that might become R_PPC64_RELATIVE. */
+ unsigned int rel_count : 31;
/* Whether this entry is for STT_GNU_IFUNC symbols. */
unsigned int ifunc : 1;
@@ -3250,6 +3269,15 @@ struct ppc_link_hash_table
/* The size of reliplt used by got entry relocs. */
bfd_size_type got_reli_size;
+ /* DT_RELR array of section/r_offset. */
+ size_t relr_alloc;
+ size_t relr_count;
+ struct
+ {
+ asection *sec;
+ bfd_vma off;
+ } *relr;
+
/* Statistics. */
unsigned long stub_count[ppc_stub_save_res];
@@ -3271,6 +3299,9 @@ struct ppc_link_hash_table
/* Set if inline plt calls should be converted to direct calls. */
unsigned int can_convert_all_inline_plt:1;
+ /* Set if a stub_offset changed. */
+ unsigned int stub_changed:1;
+
/* Set on error. */
unsigned int stub_error:1;
@@ -3288,6 +3319,13 @@ struct ppc_link_hash_table
/* Incremented every time we size stubs. */
unsigned int stub_iteration;
+
+/* After 20 iterations of stub sizing we no longer allow stubs to
+ shrink. This is to break out of a pathological case where adding
+ stubs or increasing their size on one iteration decreases section
+ gaps (perhaps due to alignment), which then results in smaller
+ stubs on the next iteration. */
+#define STUB_SHRINK_ITER 20
};
/* Rename some of the generic section flags to better document how they
@@ -4068,27 +4106,32 @@ ppc64_elf_copy_indirect_symbol (struct bfd_link_info *info,
{
if (dir->dyn_relocs != NULL)
{
- struct elf_dyn_relocs **pp;
- struct elf_dyn_relocs *p;
+ struct ppc_dyn_relocs **pp;
+ struct ppc_dyn_relocs *p;
/* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
- for (pp = &ind->dyn_relocs; (p = *pp) != NULL; )
+ for (pp = (struct ppc_dyn_relocs **) &ind->dyn_relocs;
+ (p = *pp) != NULL;
+ )
{
- struct elf_dyn_relocs *q;
+ struct ppc_dyn_relocs *q;
- for (q = dir->dyn_relocs; q != NULL; q = q->next)
+ for (q = (struct ppc_dyn_relocs *) dir->dyn_relocs;
+ q != NULL;
+ q = q->next)
if (q->sec == p->sec)
{
- q->pc_count += p->pc_count;
q->count += p->count;
+ q->pc_count += p->pc_count;
+ q->rel_count += p->rel_count;
*pp = p->next;
break;
}
if (q == NULL)
pp = &p->next;
}
- *pp = dir->dyn_relocs;
+ *pp = (struct ppc_dyn_relocs *) dir->dyn_relocs;
}
dir->dyn_relocs = ind->dyn_relocs;
@@ -4739,6 +4782,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
{
unsigned long r_symndx;
struct elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
enum elf_ppc64_reloc_type r_type;
int tls_type;
struct _ppc64_elf_section_data *ppc64_sec;
@@ -4746,9 +4790,15 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
r_symndx = ELF64_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
- h = NULL;
+ {
+ h = NULL;
+ isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd, r_symndx);
+ if (isym == NULL)
+ return false;
+ }
else
{
+ isym = NULL;
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
h = elf_follow_link (h);
@@ -4819,11 +4869,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
}
else
{
- Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return false;
-
if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
@@ -5087,16 +5132,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
dest = h->root.u.def.section;
}
else
- {
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return false;
-
- dest = bfd_section_from_elf_index (abfd, isym->st_shndx);
- }
+ dest = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (dest != sec)
ppc64_elf_section_data (sec)->has_14bit_branch = 1;
@@ -5288,35 +5324,21 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (NO_OPD_RELOCS && is_opd)
break;
- /* If we are creating a shared library, and this is a reloc
- against a global symbol, or a non PC relative reloc
- against a local symbol, then we need to copy the reloc
- into the shared library. However, if we are linking with
- -Bsymbolic, we do not need to copy a reloc against a
- global symbol which is defined in an object we are
- including in the link (i.e., DEF_REGULAR is set). At
- this point we have not seen all the input files, so it is
- possible that DEF_REGULAR is not set now but will be set
- later (it is never cleared). In case of a weak definition,
- DEF_REGULAR may be cleared later by a strong definition in
- a shared library. We account for that possibility below by
- storing information in the dyn_relocs field of the hash
- table entry. A similar situation occurs when creating
- shared libraries and symbol visibility changes render the
- symbol local.
-
- If on the other hand, we are creating an executable, we
- may need to keep relocations for symbols satisfied by a
- dynamic library if we manage to avoid copy relocs for the
- symbol. */
+ /* Set up information for symbols that might need dynamic
+ relocations. At this point in linking we have read all
+ the input files and resolved most symbols, but have not
+ yet decided whether symbols are dynamic or finalized
+ symbol flags. In some cases we might be setting dynamic
+ reloc info for symbols that do not end up needing such.
+ That's OK, adjust_dynamic_symbol and allocate_dynrelocs
+ work together with this code. */
dodyn:
if ((h != NULL
- && (h->root.type == bfd_link_hash_defweak
- || !h->def_regular))
- || (h != NULL
- && !bfd_link_executable (info)
- && !SYMBOLIC_BIND (info, h))
+ && !SYMBOL_REFERENCES_LOCAL (info, h))
|| (bfd_link_pic (info)
+ && (h != NULL
+ ? !bfd_is_abs_symbol (&h->root)
+ : isym->st_shndx != SHN_ABS)
&& must_be_dyn_reloc (info, r_type))
|| (!bfd_link_pic (info)
&& ifunc != NULL))
@@ -5337,10 +5359,10 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
relocations we need for this symbol. */
if (h != NULL)
{
- struct elf_dyn_relocs *p;
- struct elf_dyn_relocs **head;
+ struct ppc_dyn_relocs *p;
+ struct ppc_dyn_relocs **head;
- head = &h->dyn_relocs;
+ head = (struct ppc_dyn_relocs **) &h->dyn_relocs;
p = *head;
if (p == NULL || p->sec != sec)
{
@@ -5352,34 +5374,31 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
p->sec = sec;
p->count = 0;
p->pc_count = 0;
+ p->rel_count = 0;
}
p->count += 1;
if (!must_be_dyn_reloc (info, r_type))
p->pc_count += 1;
+ if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+ && rel->r_offset % 2 == 0
+ && sec->alignment_power != 0)
+ p->rel_count += 1;
}
else
{
- /* Track dynamic relocs needed for local syms too.
- We really need local syms available to do this
- easily. Oh well. */
- struct ppc_dyn_relocs *p;
- struct ppc_dyn_relocs **head;
+ /* Track dynamic relocs needed for local syms too. */
+ struct ppc_local_dyn_relocs *p;
+ struct ppc_local_dyn_relocs **head;
bool is_ifunc;
asection *s;
void *vpp;
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return false;
s = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (s == NULL)
s = sec;
vpp = &elf_section_data (s)->local_dynrel;
- head = (struct ppc_dyn_relocs **) vpp;
+ head = (struct ppc_local_dyn_relocs **) vpp;
is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC;
p = *head;
if (p != NULL && p->sec == sec && p->ifunc != is_ifunc)
@@ -5392,10 +5411,15 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
p->next = *head;
*head = p;
p->sec = sec;
- p->ifunc = is_ifunc;
p->count = 0;
+ p->rel_count = 0;
+ p->ifunc = is_ifunc;
}
p->count += 1;
+ if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+ && rel->r_offset % 2 == 0
+ && sec->alignment_power != 0)
+ p->rel_count += 1;
}
}
break;
@@ -6576,9 +6600,9 @@ alias_readonly_dynrelocs (struct elf_link_hash_entry *h)
static bool
pc_dynrelocs (struct ppc_link_hash_entry *eh)
{
- struct elf_dyn_relocs *p;
+ struct ppc_dyn_relocs *p;
- for (p = eh->elf.dyn_relocs; p != NULL; p = p->next)
+ for (p = (struct ppc_dyn_relocs *) eh->elf.dyn_relocs; p != NULL; p = p->next)
if (p->pc_count != 0)
return true;
return false;
@@ -7113,7 +7137,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
have already been determined. */
static bool
-dec_dynrel_count (bfd_vma r_info,
+dec_dynrel_count (const Elf_Internal_Rela *rel,
asection *sec,
struct bfd_link_info *info,
Elf_Internal_Sym **local_syms,
@@ -7125,7 +7149,7 @@ dec_dynrel_count (bfd_vma r_info,
/* Can this reloc be dynamic? This switch, and later tests here
should be kept in sync with the code in check_relocs. */
- r_type = ELF64_R_TYPE (r_info);
+ r_type = ELF64_R_TYPE (rel->r_info);
switch (r_type)
{
default:
@@ -7199,18 +7223,17 @@ dec_dynrel_count (bfd_vma r_info,
unsigned long r_symndx;
bfd *ibfd = sec->owner;
- r_symndx = ELF64_R_SYM (r_info);
+ r_symndx = ELF64_R_SYM (rel->r_info);
if (!get_sym_h (&h, &sym, &sym_sec, NULL, local_syms, r_symndx, ibfd))
return false;
}
if ((h != NULL
- && (h->root.type == bfd_link_hash_defweak
- || !h->def_regular))
- || (h != NULL
- && !bfd_link_executable (info)
- && !SYMBOLIC_BIND (info, h))
+ && !SYMBOL_REFERENCES_LOCAL (info, h))
|| (bfd_link_pic (info)
+ && (h != NULL
+ ? !bfd_is_abs_symbol (&h->root)
+ : sym_sec != bfd_abs_section_ptr)
&& must_be_dyn_reloc (info, r_type))
|| (!bfd_link_pic (info)
&& (h != NULL
@@ -7222,9 +7245,9 @@ dec_dynrel_count (bfd_vma r_info,
if (h != NULL)
{
- struct elf_dyn_relocs *p;
- struct elf_dyn_relocs **pp;
- pp = &h->dyn_relocs;
+ struct ppc_dyn_relocs *p;
+ struct ppc_dyn_relocs **pp;
+ pp = (struct ppc_dyn_relocs **) &h->dyn_relocs;
/* elf_gc_sweep may have already removed all dyn relocs associated
with local syms for a given section. Also, symbol flags are
@@ -7239,6 +7262,10 @@ dec_dynrel_count (bfd_vma r_info,
{
if (!must_be_dyn_reloc (info, r_type))
p->pc_count -= 1;
+ if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+ && rel->r_offset % 2 == 0
+ && sec->alignment_power != 0)
+ p->rel_count -= 1;
p->count -= 1;
if (p->count == 0)
*pp = p->next;
@@ -7249,8 +7276,8 @@ dec_dynrel_count (bfd_vma r_info,
}
else
{
- struct ppc_dyn_relocs *p;
- struct ppc_dyn_relocs **pp;
+ struct ppc_local_dyn_relocs *p;
+ struct ppc_local_dyn_relocs **pp;
void *vpp;
bool is_ifunc;
@@ -7260,7 +7287,7 @@ dec_dynrel_count (bfd_vma r_info,
sym_sec = sec;
vpp = &elf_section_data (sym_sec)->local_dynrel;
- pp = (struct ppc_dyn_relocs **) vpp;
+ pp = (struct ppc_local_dyn_relocs **) vpp;
if (*pp == NULL && info->gc_sections)
return true;
@@ -7270,6 +7297,10 @@ dec_dynrel_count (bfd_vma r_info,
{
if (p->sec == sec && p->ifunc == is_ifunc)
{
+ if ((r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+ && rel->r_offset % 2 == 0
+ && sec->alignment_power != 0)
+ p->rel_count -= 1;
p->count -= 1;
if (p->count == 0)
*pp = p->next;
@@ -7567,7 +7598,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info)
else
while (1)
{
- if (!dec_dynrel_count (rel->r_info, sec, info,
+ if (!dec_dynrel_count (rel, sec, info,
NULL, h, sym))
goto error_ret;
@@ -8587,13 +8618,13 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info)
{
/* If we got rid of a DTPMOD/DTPREL reloc pair then
we'll lose one or two dyn relocs. */
- if (!dec_dynrel_count (rel->r_info, sec, info,
+ if (!dec_dynrel_count (rel, sec, info,
NULL, h, sym))
return false;
if (tls_set == (TLS_EXPLICIT | TLS_GD))
{
- if (!dec_dynrel_count ((rel + 1)->r_info, sec, info,
+ if (!dec_dynrel_count (rel + 1, sec, info,
NULL, h, sym))
return false;
}
@@ -9007,7 +9038,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
|| discarded_section (sym_sec))
continue;
- if (!SYMBOL_REFERENCES_LOCAL (info, h))
+ if (!SYMBOL_REFERENCES_LOCAL (info, h)
+ || (bfd_link_pic (info)
+ && sym_sec == bfd_abs_section_ptr))
continue;
if (h != NULL)
@@ -9419,7 +9452,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
wrel->r_addend = rel->r_addend;
++wrel;
}
- else if (!dec_dynrel_count (rel->r_info, toc, info,
+ else if (!dec_dynrel_count (rel, toc, info,
&local_syms, NULL, NULL))
goto error_ret;
@@ -9589,7 +9622,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
if ((h ? h->type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
continue;
- if (!SYMBOL_REFERENCES_LOCAL (info, h))
+ if (!SYMBOL_REFERENCES_LOCAL (info, h)
+ || (bfd_link_pic (info)
+ && sym_sec == bfd_abs_section_ptr))
continue;
if (h != NULL)
@@ -9720,9 +9755,11 @@ allocate_got (struct elf_link_hash_entry *h,
htab->got_reli_size += rentsize;
}
else if (((bfd_link_pic (info)
- && !(gent->tls_type != 0
- && bfd_link_executable (info)
- && SYMBOL_REFERENCES_LOCAL (info, h)))
+ && (gent->tls_type == 0
+ ? !info->enable_dt_relr
+ : !(bfd_link_executable (info)
+ && SYMBOL_REFERENCES_LOCAL (info, h)))
+ && !bfd_is_abs_symbol (&h->root))
|| (htab->elf.dynamic_sections_created
&& h->dynindx != -1
&& !SYMBOL_REFERENCES_LOCAL (info, h)))
@@ -9884,7 +9921,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
if (h->dyn_relocs != NULL)
{
- struct elf_dyn_relocs *p, **pp;
+ struct ppc_dyn_relocs *p, **pp;
/* In the shared -Bsymbolic case, discard space allocated for
dynamic pc-relative relocs against symbols which turn out to
@@ -9902,7 +9939,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
avoid writing weird assembly. */
if (SYMBOL_CALLS_LOCAL (info, h))
{
- for (pp = &h->dyn_relocs; (p = *pp) != NULL; )
+ for (pp = (struct ppc_dyn_relocs **) &h->dyn_relocs;
+ (p = *pp) != NULL;
+ )
{
p->count -= p->pc_count;
p->pc_count = 0;
@@ -9948,12 +9987,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
}
/* Finally, allocate space. */
- for (p = h->dyn_relocs; p != NULL; p = p->next)
+ for (p = (struct ppc_dyn_relocs *) h->dyn_relocs; p != NULL; p = p->next)
{
+ unsigned int count;
asection *sreloc = elf_section_data (p->sec)->sreloc;
if (eh->elf.type == STT_GNU_IFUNC)
sreloc = htab->elf.irelplt;
- sreloc->size += p->count * sizeof (Elf64_External_Rela);
+ count = p->count;
+ if (info->enable_dt_relr
+ && ((!NO_OPD_RELOCS
+ && ppc64_elf_section_data (p->sec)->sec_type == sec_opd)
+ || (eh->elf.type != STT_GNU_IFUNC
+ && SYMBOL_REFERENCES_LOCAL (info, h))))
+ count -= p->rel_count;
+ sreloc->size += count * sizeof (Elf64_External_Rela);
}
}
@@ -9994,7 +10041,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
s = htab->pltlocal;
pent->plt.offset = s->size;
s->size += LOCAL_PLT_ENTRY_SIZE (htab);
- s = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+ s = NULL;
+ if (bfd_link_pic (info)
+ && !(info->enable_dt_relr && !htab->opd_abi))
+ s = htab->relpltlocal;
}
}
else
@@ -10174,13 +10224,15 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
unsigned char *lgot_masks;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Sym *local_syms;
+ Elf_Internal_Sym *isym;
if (!is_ppc64_elf (ibfd))
continue;
for (s = ibfd->sections; s != NULL; s = s->next)
{
- struct ppc_dyn_relocs *p;
+ struct ppc_local_dyn_relocs *p;
for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
{
@@ -10194,10 +10246,20 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
}
else if (p->count != 0)
{
- asection *srel = elf_section_data (p->sec)->sreloc;
+ unsigned int count;
+ asection *srel;
+
+ count = p->count;
+ if (info->enable_dt_relr
+ && ((!NO_OPD_RELOCS
+ && (ppc64_elf_section_data (p->sec)->sec_type
+ == sec_opd))
+ || !p->ifunc))
+ count -= p->rel_count;
+ srel = elf_section_data (p->sec)->sreloc;
if (p->ifunc)
srel = htab->elf.irelplt;
- srel->size += p->count * sizeof (Elf64_External_Rela);
+ srel->size += count * sizeof (Elf64_External_Rela);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
}
@@ -10214,8 +10276,18 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
local_plt = (struct plt_entry **) end_lgot_ents;
end_local_plt = local_plt + locsymcount;
lgot_masks = (unsigned char *) end_local_plt;
+ local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (local_syms == NULL && locsymcount != 0)
+ {
+ local_syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, locsymcount,
+ 0, NULL, NULL, NULL);
+ if (local_syms == NULL)
+ return false;
+ }
s = ppc64_elf_tdata (ibfd)->got;
- for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
+ for (isym = local_syms;
+ lgot_ents < end_lgot_ents;
+ ++lgot_ents, ++lgot_masks, isym++)
{
struct got_entry **pent, *ent;
@@ -10246,8 +10318,10 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
htab->got_reli_size += rel_size;
}
else if (bfd_link_pic (info)
- && !(ent->tls_type != 0
- && bfd_link_executable (info)))
+ && (ent->tls_type == 0
+ ? !info->enable_dt_relr
+ : !bfd_link_executable (info))
+ && isym->st_shndx != SHN_ABS)
{
asection *srel = ppc64_elf_tdata (ibfd)->relgot;
srel->size += rel_size;
@@ -10258,6 +10332,14 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
else
*pent = ent->next;
}
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ {
+ if (!info->keep_memory)
+ free (local_syms);
+ else
+ symtab_hdr->contents = (unsigned char *) local_syms;
+ }
/* Allocate space for plt calls to local syms. */
lgot_masks = (unsigned char *) end_local_plt;
@@ -10283,7 +10365,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
s = htab->pltlocal;
ent->plt.offset = s->size;
s->size += LOCAL_PLT_ENTRY_SIZE (htab);
- if (bfd_link_pic (info))
+ if (bfd_link_pic (info)
+ && !(info->enable_dt_relr && !htab->opd_abi))
htab->relpltlocal->size += sizeof (Elf64_External_Rela);
}
}
@@ -10342,7 +10425,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd,
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
- if (s == htab->brlt || s == htab->relbrlt)
+ if (s == htab->brlt || s == htab->relbrlt || s == htab->elf.srelrdyn)
/* These haven't been allocated yet; don't strip. */
continue;
else if (s == htab->elf.sgot
@@ -11090,12 +11173,12 @@ plt_stub_size (struct ppc_link_hash_table *htab,
static inline unsigned int
plt_stub_pad (struct ppc_link_hash_table *htab,
struct ppc_stub_hash_entry *stub_entry,
+ bfd_vma stub_off,
bfd_vma plt_off,
unsigned int odd)
{
int stub_align;
unsigned stub_size;
- bfd_vma stub_off = stub_entry->group->stub_sec->size;
if (htab->params->plt_stub_align >= 0)
{
@@ -11693,7 +11776,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
br_entry->iter = 0;
- if (htab->relbrlt != NULL)
+ if (htab->relbrlt != NULL && !info->enable_dt_relr)
{
/* Create a reloc for the branch lookup table entry. */
Elf_Internal_Rela rela;
@@ -12090,6 +12173,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
asection *plt;
bfd_vma targ, off, r2off;
unsigned int size, extra, lr_used, delta, odd;
+ bfd_vma stub_offset;
/* Massage our args to the form they really have. */
stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -12119,7 +12203,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
stub_entry->target_section);
/* Make a note of the offset within the stubs for this entry. */
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset = stub_entry->group->stub_sec->size;
+ if (htab->stub_iteration > STUB_SHRINK_ITER
+ && stub_entry->stub_offset > stub_offset)
+ stub_offset = stub_entry->stub_offset;
if (stub_entry->h != NULL
&& stub_entry->h->save_res
@@ -12149,7 +12236,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
@@ -12198,7 +12285,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
br_entry->offset = htab->brlt->size;
htab->brlt->size += 8;
- if (htab->relbrlt != NULL)
+ if (htab->relbrlt != NULL && !info->enable_dt_relr)
htab->relbrlt->size += sizeof (Elf64_External_Rela);
else if (info->emitrelocations)
{
@@ -12248,7 +12335,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
}
else if (stub_entry->type.main == ppc_stub_long_branch)
{
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
size = 0;
@@ -12287,7 +12374,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
/* After the bcl, lr has been modified so we need to emit
.eh_frame info saying the return address is in r12. */
- lr_used = stub_entry->stub_offset + 8;
+ lr_used = stub_offset + 8;
if (stub_entry->type.r2save)
lr_used += 4;
/* The eh_frame info will consist of a DW_CFA_advance_loc or
@@ -12336,7 +12423,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
plt = htab->pltlocal;
}
targ += plt->output_offset + plt->output_section->vma;
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma
+ lr_used);
@@ -12345,10 +12432,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (htab->params->plt_stub_align != 0)
{
- unsigned pad = plt_stub_pad (htab, stub_entry, off, odd);
+ unsigned pad = plt_stub_pad (htab, stub_entry, stub_offset, off, odd);
- stub_entry->group->stub_sec->size += pad;
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset += pad;
off -= pad;
odd ^= pad & 4;
}
@@ -12370,7 +12456,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
/* After the bcl, lr has been modified so we need to emit
.eh_frame info saying the return address is in r12. */
- lr_used += stub_entry->stub_offset + 8;
+ lr_used += stub_offset + 8;
/* The eh_frame info will consist of a DW_CFA_advance_loc or
variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
DW_CFA_restore_extended 65. */
@@ -12384,20 +12470,18 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
if (!htab->params->no_tls_get_addr_regsave)
{
- unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+ unsigned int cfa_updt = stub_offset + 18 * 4;
delta = cfa_updt - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta);
stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
- stub_entry->group->lr_restore
- = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
else if (stub_entry->type.r2save)
{
- lr_used = stub_entry->stub_offset + size - 20;
+ lr_used = stub_offset + size - 20;
delta = lr_used - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta) + 6;
- stub_entry->group->lr_restore
- = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
}
}
@@ -12422,10 +12506,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (htab->params->plt_stub_align != 0)
{
- unsigned pad = plt_stub_pad (htab, stub_entry, off, 0);
+ unsigned pad = plt_stub_pad (htab, stub_entry, stub_offset, off, 0);
- stub_entry->group->stub_sec->size += pad;
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset += pad;
}
if (info->emitrelocations)
@@ -12449,21 +12532,21 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (!htab->params->no_tls_get_addr_regsave)
{
/* Adjustments to r1 need to be described. */
- unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+ unsigned int cfa_updt = stub_offset + 18 * 4;
delta = cfa_updt - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta);
stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
}
else
{
- lr_used = stub_entry->stub_offset + size - 20;
+ lr_used = stub_offset + size - 20;
/* The eh_frame info will consist of a DW_CFA_advance_loc
or variant, DW_CFA_offset_externed_sf, 65, -stackoff,
DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65. */
delta = lr_used - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta) + 6;
}
- stub_entry->group->lr_restore = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
}
else
@@ -12472,7 +12555,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
return false;
}
- stub_entry->group->stub_sec->size += size;
+ if (stub_entry->stub_offset != stub_offset)
+ htab->stub_changed = true;
+ stub_entry->stub_offset = stub_offset;
+ stub_entry->group->stub_sec->size = stub_offset + size;
return true;
}
@@ -12706,6 +12792,8 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *s;
+ Elf_Internal_Sym *local_syms;
+ Elf_Internal_Sym *isym;
if (!is_ppc64_elf (ibfd))
continue;
@@ -12720,8 +12808,18 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
local_plt = (struct plt_entry **) end_lgot_ents;
end_local_plt = local_plt + locsymcount;
lgot_masks = (unsigned char *) end_local_plt;
+ local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (local_syms == NULL && locsymcount != 0)
+ {
+ local_syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, locsymcount,
+ 0, NULL, NULL, NULL);
+ if (local_syms == NULL)
+ return false;
+ }
s = ppc64_elf_tdata (ibfd)->got;
- for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks)
+ for (isym = local_syms;
+ lgot_ents < end_lgot_ents;
+ ++lgot_ents, ++lgot_masks, isym++)
{
struct got_entry *ent;
@@ -12743,8 +12841,10 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info)
htab->got_reli_size += rel_size;
}
else if (bfd_link_pic (info)
- && !(ent->tls_type != 0
- && bfd_link_executable (info)))
+ && (ent->tls_type == 0
+ ? !info->enable_dt_relr
+ : !bfd_link_executable (info))
+ && isym->st_shndx != SHN_ABS)
{
asection *srel = ppc64_elf_tdata (ibfd)->relgot;
srel->size += rel_size;
@@ -13128,8 +13228,10 @@ check_pasted_section (struct bfd_link_info *info, const char *name)
bool
ppc64_elf_check_init_fini (struct bfd_link_info *info)
{
- return (check_pasted_section (info, ".init")
- & check_pasted_section (info, ".fini"));
+ bool ret1 = check_pasted_section (info, ".init");
+ bool ret2 = check_pasted_section (info, ".fini");
+
+ return ret1 && ret2;
}
/* See whether we can group stub sections together. Grouping stub
@@ -13293,6 +13395,201 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec)
}
}
+/* 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, asection *sec, bfd_vma off)
+{
+ if (htab->relr_count >= htab->relr_alloc)
+ {
+ if (htab->relr_alloc == 0)
+ htab->relr_alloc = 4096;
+ else
+ htab->relr_alloc *= 2;
+ htab->relr = bfd_realloc (htab->relr,
+ htab->relr_alloc * sizeof (*htab->relr));
+ if (htab->relr == NULL)
+ return false;
+ }
+ 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)
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ bfd *ibfd;
+
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ struct got_entry **lgot_ents, **lgot, **end_lgot_ents;
+ struct plt_entry **local_plt, **lplt, **end_local_plt;
+ Elf_Internal_Shdr *symtab_hdr;
+ bfd_size_type locsymcount;
+ Elf_Internal_Sym *local_syms;
+ Elf_Internal_Sym *isym;
+ struct plt_entry *pent;
+ struct got_entry *gent;
+
+ if (!is_ppc64_elf (ibfd))
+ continue;
+
+ lgot_ents = elf_local_got_ents (ibfd);
+ if (!lgot_ents)
+ continue;
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+ locsymcount = symtab_hdr->sh_info;
+ local_syms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (local_syms == NULL && locsymcount != 0)
+ {
+ local_syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, locsymcount,
+ 0, NULL, NULL, NULL);
+ if (local_syms == NULL)
+ return false;
+ }
+ end_lgot_ents = lgot_ents + locsymcount;
+ local_plt = (struct plt_entry **) end_lgot_ents;
+ end_local_plt = local_plt + locsymcount;
+ for (lgot = lgot_ents, isym = local_syms;
+ lgot < end_lgot_ents;
+ ++lgot, ++isym)
+ for (gent = *lgot; gent != NULL; gent = gent->next)
+ if (!gent->is_indirect
+ && gent->tls_type == 0
+ && gent->got.offset != (bfd_vma) -1
+ && isym->st_shndx != SHN_ABS)
+ {
+ asection *got = ppc64_elf_tdata (gent->owner)->got;
+ if (!append_relr_off (htab, got, gent->got.offset))
+ {
+ htab->stub_error = true;
+ return false;
+ }
+ }
+
+ if (!htab->opd_abi)
+ for (lplt = local_plt, isym = local_syms;
+ lplt < end_local_plt;
+ ++lplt, ++isym)
+ for (pent = *lplt; pent != NULL; pent = pent->next)
+ if (pent->plt.offset != (bfd_vma) -1
+ && ELF_ST_TYPE (isym->st_info) != STT_GNU_IFUNC)
+ {
+ if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset))
+ {
+ if (symtab_hdr->contents != (unsigned char *) local_syms)
+ free (local_syms);
+ return false;
+ }
+ }
+
+ if (local_syms != NULL
+ && symtab_hdr->contents != (unsigned char *) local_syms)
+ {
+ if (!info->keep_memory)
+ free (local_syms);
+ else
+ symtab_hdr->contents = (unsigned char *) 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)
+{
+ struct bfd_link_info *info;
+ struct ppc_link_hash_table *htab;
+ struct plt_entry *pent;
+ struct got_entry *gent;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return true;
+
+ info = (struct bfd_link_info *) inf;
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return false;
+
+ if (h->type != STT_GNU_IFUNC
+ && h->def_regular
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak))
+ {
+ if ((!htab->elf.dynamic_sections_created
+ || h->dynindx == -1
+ || SYMBOL_REFERENCES_LOCAL (info, h))
+ && !bfd_is_abs_symbol (&h->root))
+ for (gent = h->got.glist; gent != NULL; gent = gent->next)
+ if (!gent->is_indirect
+ && gent->tls_type == 0
+ && gent->got.offset != (bfd_vma) -1)
+ {
+ asection *got = ppc64_elf_tdata (gent->owner)->got;
+ if (!append_relr_off (htab, got, gent->got.offset))
+ {
+ htab->stub_error = true;
+ return false;
+ }
+ }
+
+ if (!htab->opd_abi
+ && use_local_plt (info, h))
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+ if (pent->plt.offset != (bfd_vma) -1)
+ {
+ if (!append_relr_off (htab, htab->pltlocal, pent->plt.offset))
+ {
+ htab->stub_error = true;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
/* Determine and set the size of the stub section for a final link.
The basic idea here is to examine all the relocations looking for
@@ -13399,12 +13696,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
_bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, true);
}
-#define STUB_SHRINK_ITER 20
/* Loop until no stubs added. After iteration 20 of this loop we may
- exit on a stub section shrinking. This is to break out of a
- pathological case where adding stubs on one iteration decreases
- section gaps (perhaps due to alignment), which then requires
- fewer or smaller stubs on the next iteration. */
+ exit on a stub section shrinking. */
while (1)
{
@@ -13413,6 +13706,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
struct map_stub *group;
htab->stub_iteration += 1;
+ htab->relr_count = 0;
for (input_bfd = info->input_bfds, bfd_indx = 0;
input_bfd != NULL;
@@ -13436,16 +13730,20 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
section = section->next)
{
Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+ bool is_opd;
/* If there aren't any relocs, then there's nothing more
to do. */
if ((section->flags & SEC_RELOC) == 0
|| (section->flags & SEC_ALLOC) == 0
|| (section->flags & SEC_LOAD) == 0
- || (section->flags & SEC_CODE) == 0
|| section->reloc_count == 0)
continue;
+ if (!info->enable_dt_relr
+ && (section->flags & SEC_CODE) == 0)
+ continue;
+
/* If this section is a link-once section that will be
discarded, then don't create any stubs. */
if (section->output_section == NULL
@@ -13459,6 +13757,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
if (internal_relocs == NULL)
goto error_ret_free_local;
+ is_opd = ppc64_elf_section_data (section)->sec_type == sec_opd;
+
/* Now examine each relocation. */
irela = internal_relocs;
irelaend = irela + section->reloc_count;
@@ -13492,21 +13792,78 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
}
/* Only look for stubs on branch instructions. */
- if (r_type != R_PPC64_REL24
- && r_type != R_PPC64_REL24_NOTOC
- && r_type != R_PPC64_REL24_P9NOTOC
- && r_type != R_PPC64_REL14
- && r_type != R_PPC64_REL14_BRTAKEN
- && r_type != R_PPC64_REL14_BRNTAKEN)
- continue;
+ switch (r_type)
+ {
+ default:
+ continue;
+
+ case R_PPC64_REL24:
+ case R_PPC64_REL24_NOTOC:
+ case R_PPC64_REL24_P9NOTOC:
+ case R_PPC64_REL14:
+ case R_PPC64_REL14_BRTAKEN:
+ case R_PPC64_REL14_BRNTAKEN:
+ if ((section->flags & SEC_CODE) != 0)
+ break;
+ continue;
+
+ case R_PPC64_ADDR64:
+ case R_PPC64_TOC:
+ if (info->enable_dt_relr
+ && irela->r_offset % 2 == 0
+ && section->alignment_power != 0)
+ break;
+ continue;
+ }
/* Now determine the call target, its name, value,
section. */
if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
r_indx, input_bfd))
goto error_ret_free_internal;
- hash = ppc_elf_hash_entry (h);
+ if (r_type == R_PPC64_ADDR64 || r_type == R_PPC64_TOC)
+ {
+ /* Only locally defined symbols can possibly use
+ relative relocations. */
+ bfd_vma r_offset;
+ if ((sym_sec == NULL
+ || sym_sec->output_section == NULL)
+ /* No symbol is OK too. */
+ && !(sym != NULL && sym->st_shndx == 0)
+ /* Hack for __ehdr_start, which is undefined
+ at this point. */
+ && !(h != NULL && h->root.linker_def))
+ continue;
+ if (NO_OPD_RELOCS && is_opd)
+ continue;
+ if (!is_opd
+ && r_type == R_PPC64_ADDR64)
+ {
+ if (h != NULL
+ ? h->type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ continue;
+ if (h != NULL
+ ? bfd_is_abs_symbol (&h->root)
+ : sym->st_shndx == SHN_ABS)
+ continue;
+ if (h != NULL
+ && !SYMBOL_REFERENCES_LOCAL (info, h))
+ continue;
+ }
+ r_offset = _bfd_elf_section_offset (info->output_bfd,
+ info,
+ section,
+ irela->r_offset);
+ if (r_offset >= (bfd_vma) -2)
+ continue;
+ if (!append_relr_off (htab, section, r_offset))
+ goto error_ret_free_internal;
+ continue;
+ }
+
+ hash = ppc_elf_hash_entry (h);
ok_dest = false;
fdh = NULL;
sym_value = 0;
@@ -13777,10 +14134,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
{
asection *stub_sec = group->stub_sec;
- if (htab->stub_iteration <= STUB_SHRINK_ITER
- || stub_sec->rawsize < stub_sec->size)
- /* Past STUB_SHRINK_ITER, rawsize is the max size seen. */
- stub_sec->rawsize = stub_sec->size;
+ stub_sec->rawsize = stub_sec->size;
stub_sec->size = 0;
stub_sec->reloc_count = 0;
stub_sec->flags &= ~SEC_RELOC;
@@ -13795,15 +14149,20 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
htab->tga_group->stub_sec->size = 24 * 4;
}
- if (htab->stub_iteration <= STUB_SHRINK_ITER
- || htab->brlt->rawsize < htab->brlt->size)
- htab->brlt->rawsize = htab->brlt->size;
+ htab->brlt->rawsize = htab->brlt->size;
htab->brlt->size = 0;
htab->brlt->reloc_count = 0;
htab->brlt->flags &= ~SEC_RELOC;
if (htab->relbrlt != NULL)
htab->relbrlt->size = 0;
+ if (htab->elf.srelrdyn != NULL)
+ {
+ htab->elf.srelrdyn->rawsize = htab->elf.srelrdyn->size;
+ htab->elf.srelrdyn->size = 0;
+ }
+
+ htab->stub_changed = false;
bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
for (group = htab->group; group != NULL; group = group->next)
@@ -13845,6 +14204,52 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
= (group->stub_sec->size + (1 << align) - 1) & -(1 << align);
}
+ if (htab->elf.srelrdyn != NULL)
+ {
+ bfd_vma r_offset;
+
+ for (r_offset = 0; r_offset < htab->brlt->size; r_offset += 8)
+ if (!append_relr_off (htab, htab->brlt, r_offset))
+ return false;
+
+ if (!got_and_plt_relr_for_local_syms (info))
+ return false;
+ elf_link_hash_traverse (&htab->elf, got_and_plt_relr, info);
+ if (htab->stub_error)
+ return false;
+
+ 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 = 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
+ && relr_addr[i] == base)
+ i++;
+ base += 8;
+ while (1)
+ {
+ size_t start_i = i;
+ while (i < htab->relr_count
+ && relr_addr[i] - base < 63 * 8
+ && (relr_addr[i] - base) % 8 == 0)
+ i++;
+ if (i == start_i)
+ break;
+ htab->elf.srelrdyn->size += 8;
+ base += 63 * 8;
+ }
+ }
+ free (relr_addr);
+ }
+
for (group = htab->group; group != NULL; group = group->next)
if (group->stub_sec != NULL
&& group->stub_sec->rawsize != group->stub_sec->size
@@ -13853,15 +14258,36 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
break;
if (group == NULL
+ && (!htab->stub_changed
+ || htab->stub_iteration > STUB_SHRINK_ITER)
&& (htab->brlt->rawsize == htab->brlt->size
|| (htab->stub_iteration > STUB_SHRINK_ITER
&& htab->brlt->rawsize > htab->brlt->size))
+ && (htab->elf.srelrdyn == NULL
+ || htab->elf.srelrdyn->rawsize == htab->elf.srelrdyn->size
+ || (htab->stub_iteration > STUB_SHRINK_ITER
+ && htab->elf.srelrdyn->rawsize > htab->elf.srelrdyn->size))
&& (htab->glink_eh_frame == NULL
|| htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
&& (htab->tga_group == NULL
|| htab->stub_iteration > 1))
break;
+ if (htab->stub_iteration > STUB_SHRINK_ITER)
+ {
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL
+ && group->stub_sec->size < group->stub_sec->rawsize)
+ group->stub_sec->size = group->stub_sec->rawsize;
+
+ if (htab->brlt->size < htab->brlt->rawsize)
+ htab->brlt->size = htab->brlt->rawsize;
+
+ if (htab->elf.srelrdyn != NULL
+ && htab->elf.srelrdyn->size < htab->elf.srelrdyn->rawsize)
+ htab->elf.srelrdyn->size = htab->elf.srelrdyn->rawsize;
+ }
+
/* Ask the linker to do its stuff. */
(*htab->params->layout_sections_again) ();
}
@@ -13959,6 +14385,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
maybe_strip_output (info, htab->relbrlt);
if (htab->glink_eh_frame != NULL)
maybe_strip_output (info, htab->glink_eh_frame);
+ if (htab->elf.srelrdyn != NULL)
+ maybe_strip_output (info, htab->elf.srelrdyn);
return true;
}
@@ -14120,7 +14548,9 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
else
{
plt = htab->pltlocal;
- if (bfd_link_pic (info))
+ relplt = NULL;
+ if (bfd_link_pic (info)
+ && !(info->enable_dt_relr && !htab->opd_abi))
{
relplt = htab->relpltlocal;
if (htab->opd_abi)
@@ -14128,8 +14558,6 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
else
rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
}
- else
- relplt = NULL;
}
rela.r_addend = defined_sym_val (h) + ent->addend;
@@ -14311,7 +14739,10 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
else
{
plt = htab->pltlocal;
- relplt = bfd_link_pic (info) ? htab->relpltlocal : NULL;
+ relplt = NULL;
+ if (bfd_link_pic (info)
+ && !(info->enable_dt_relr && !htab->opd_abi))
+ relplt = htab->relpltlocal;
}
if (relplt == NULL)
@@ -14749,6 +15180,60 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
}
}
+ if (htab->elf.srelrdyn != NULL && htab->elf.srelrdyn->size != 0)
+ {
+ htab->elf.srelrdyn->contents
+ = bfd_alloc (htab->elf.dynobj, htab->elf.srelrdyn->size);
+ 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 = relr_addr[i];
+ BFD_ASSERT (base % 2 == 0);
+ bfd_put_64 (htab->elf.dynobj, base, loc);
+ loc += 8;
+ i++;
+ while (i < htab->relr_count
+ && relr_addr[i] == base)
+ {
+ htab->stub_error = true;
+ i++;
+ }
+ base += 8;
+ while (1)
+ {
+ bfd_vma bits = 0;
+ while (i < htab->relr_count
+ && relr_addr[i] - base < 63 * 8
+ && (relr_addr[i] - base) % 8 == 0)
+ {
+ bits |= (bfd_vma) 1 << ((relr_addr[i] - base) / 8);
+ i++;
+ }
+ if (bits == 0)
+ break;
+ bfd_put_64 (htab->elf.dynobj, (bits << 1) | 1, loc);
+ loc += 8;
+ 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)
+ {
+ bfd_put_64 (htab->elf.dynobj, 1, loc);
+ loc += 8;
+ }
+ }
+
for (group = htab->group; group != NULL; group = group->next)
if ((stub_sec = group->stub_sec) != NULL)
{
@@ -14760,14 +15245,14 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
}
if (group != NULL)
+ htab->stub_error = true;
+
+ if (htab->stub_error)
{
- htab->stub_error = true;
_bfd_error_handler (_("stubs don't match calculated size"));
+ return false;
}
- if (htab->stub_error)
- return false;
-
if (stats != NULL)
{
char *groupmsg;
@@ -16113,6 +16598,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT16_DS:
if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+ || (bfd_link_pic (info)
+ && sec == bfd_abs_section_ptr)
|| !htab->do_toc_opt)
break;
from = TOCstart + htab->sec_info[input_section->id].toc_off;
@@ -16137,6 +16624,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT16_LO_DS:
case R_PPC64_GOT16_HA:
if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+ || (bfd_link_pic (info)
+ && sec == bfd_abs_section_ptr)
|| !htab->do_toc_opt)
break;
from = TOCstart + htab->sec_info[input_section->id].toc_off;
@@ -16167,6 +16656,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT_PCREL34:
if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+ || (bfd_link_pic (info)
+ && sec == bfd_abs_section_ptr)
|| !htab->do_toc_opt)
break;
from = (rel->r_offset
@@ -16406,7 +16897,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
&& bfd_link_executable (info)
&& (h == NULL
|| SYMBOL_REFERENCES_LOCAL (info,
- &h->elf)))))
+ &h->elf)))
+ && (h != NULL
+ ? !bfd_is_abs_symbol (&h->elf.root)
+ : sym->st_shndx != SHN_ABS)))
+
relgot = ppc64_elf_tdata (ent->owner)->relgot;
if (relgot != NULL)
{
@@ -16462,10 +16957,14 @@ ppc64_elf_relocate_section (bfd *output_bfd,
outrel.r_addend -= htab->elf.tls_sec->vma;
}
}
- loc = relgot->contents;
- loc += (relgot->reloc_count++
- * sizeof (Elf64_External_Rela));
- bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ if (!(info->enable_dt_relr
+ && ELF64_R_TYPE (outrel.r_info) == R_PPC64_RELATIVE))
+ {
+ loc = relgot->contents;
+ loc += (relgot->reloc_count++
+ * sizeof (Elf64_External_Rela));
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ }
}
/* Init the .got section contents here if we're not
@@ -16590,6 +17089,15 @@ ppc64_elf_relocate_section (bfd *output_bfd,
relocation += htab->sec_info[sec->id].toc_off;
else
unresolved_reloc = true;
+ if (unresolved_reloc
+ || (!is_opd
+ && h != NULL
+ && !SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+ info->callbacks->einfo
+ /* xgettext:c-format */
+ (_("%H: %s against %pT is not supported\n"),
+ input_bfd, input_section, rel->r_offset,
+ ppc64_elf_howto_table[r_type]->name, sym_name);
goto dodyn;
/* TOC16 relocs. We want the offset relative to the TOC base,
@@ -16924,24 +17432,31 @@ ppc64_elf_relocate_section (bfd *output_bfd,
}
}
- sreloc = elf_section_data (input_section)->sreloc;
- if (h != NULL
- ? h->elf.type == STT_GNU_IFUNC
- : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ if (!(info->enable_dt_relr
+ && ELF64_R_TYPE (outrel.r_info) == R_PPC64_RELATIVE
+ && rel->r_offset % 2 == 0
+ && input_section->alignment_power != 0
+ && ELF64_R_TYPE (orig_rel.r_info) != R_PPC64_UADDR64))
{
- sreloc = htab->elf.irelplt;
- if (indx == 0 || is_static_defined (&h->elf))
- htab->elf.ifunc_resolvers = true;
+ sreloc = elf_section_data (input_section)->sreloc;
+ if (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ sreloc = htab->elf.irelplt;
+ if (indx == 0 || is_static_defined (&h->elf))
+ htab->elf.ifunc_resolvers = true;
+ }
+ if (sreloc == NULL)
+ abort ();
+
+ if (sreloc->reloc_count * sizeof (Elf64_External_Rela)
+ >= sreloc->size)
+ abort ();
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
}
- if (sreloc == NULL)
- abort ();
-
- if (sreloc->reloc_count * sizeof (Elf64_External_Rela)
- >= sreloc->size)
- abort ();
- loc = sreloc->contents;
- loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
- bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
if (!warned_dynamic
&& !ppc64_glibc_dynamic_reloc (ELF64_R_TYPE (outrel.r_info)))
@@ -17095,7 +17610,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_GOT16_HA:
case R_PPC64_TOC16_HA:
if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000
- && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn)
+ && !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn
+ && !(bfd_link_pic (info)
+ && (h != NULL
+ ? bfd_is_abs_symbol (&h->elf.root)
+ : sec == bfd_abs_section_ptr)))
{
bfd_byte *p;
nop_it:
@@ -17126,6 +17645,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
case R_PPC64_TOC16_LO_DS:
if (htab->do_toc_opt && relocation + addend + 0x8000 < 0x10000
&& !ppc64_elf_tdata (input_bfd)->unexpected_toc_insn
+ && !(bfd_link_pic (info)
+ && (h != NULL
+ ? bfd_is_abs_symbol (&h->elf.root)
+ : sec == bfd_abs_section_ptr))
&& offset_in_range (input_section, rel->r_offset & ~3, 4))
{
bfd_byte *p = contents + (rel->r_offset & ~3);