/* PowerPC64-specific support for 64-bit ELF.
- Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2015 Free Software Foundation, Inc.
Written by Linus Nordberg, Swox AB <info@swox.com>,
based on elf32-ppc.c by Ian Lance Taylor.
Largely rewritten by Alan Modra.
#define ELF_TARGET_ID PPC64_ELF_DATA
#define ELF_MACHINE_CODE EM_PPC64
#define ELF_MAXPAGESIZE 0x10000
-#define ELF_COMMONPAGESIZE 0x1000
+#define ELF_COMMONPAGESIZE 0x10000
#define elf_info_to_howto ppc64_elf_info_to_howto
#define elf_backend_want_got_sym 0
/* TOC base pointers offset from start of TOC. */
#define TOC_BASE_OFF 0x8000
+/* TOC base alignment. */
+#define TOC_BASE_ALIGN 256
/* Offset of tp and dtp pointers from start of TLS block. */
#define TP_OFFSET 0x7000
#ifndef NO_OPD_RELOCS
#define NO_OPD_RELOCS 0
#endif
+
+static inline int
+abiversion (bfd *abfd)
+{
+ return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI;
+}
+
+static inline void
+set_abiversion (bfd *abfd, int ver)
+{
+ elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI;
+ elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI;
+}
\f
#define ONES(n) (((bfd_vma) 1 << ((n) - 1) << 1) - 1)
/* This reloc does nothing. */
HOWTO (R_PPC64_NONE, /* type */
0, /* rightshift */
- 2, /* size (0 = byte, 1 = short, 2 = long) */
- 32, /* bitsize */
+ 3, /* size (0 = byte, 1 = short, 2 = long) */
+ 0, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
complain_overflow_dont, /* complain_on_overflow */
+ symbol->section->output_section->vma
+ symbol->section->output_offset);
}
+ else
+ {
+ elf_symbol_type *elfsym = (elf_symbol_type *) symbol;
+
+ if (symbol->section->owner != abfd
+ && abiversion (symbol->section->owner) >= 2)
+ {
+ unsigned int i;
+
+ for (i = 0; i < symbol->section->owner->symcount; ++i)
+ {
+ asymbol *symdef = symbol->section->owner->outsymbols[i];
+
+ if (strcmp (symdef->name, symbol->name) == 0)
+ {
+ elfsym = (elf_symbol_type *) symdef;
+ break;
+ }
+ }
+ }
+ reloc_entry->addend
+ += PPC64_LOCAL_ENTRY_OFFSET (elfsym->internal_elf_sym.st_other);
+ }
return bfd_reloc_continue;
}
union
{
- /* An array with one entry for each opd function descriptor. */
+ /* An array with one entry for each opd function descriptor,
+ and some spares since opd entries may be either 16 or 24 bytes. */
+#define OPD_NDX(OFF) ((OFF) >> 4)
struct _opd_sec_data
{
/* Points to the function code section for local opd entries. */
return &ppc64_elf_section_data (sec)->u.opd;
return NULL;
}
-
-static inline int
-abiversion (bfd *abfd)
-{
- return elf_elfheader (abfd)->e_flags & EF_PPC64_ABI;
-}
-
-static inline void
-set_abiversion (bfd *abfd, int ver)
-{
- elf_elfheader (abfd)->e_flags &= ~EF_PPC64_ABI;
- elf_elfheader (abfd)->e_flags |= ver & EF_PPC64_ABI;
-}
\f
/* Parameters for the qsort hook. */
static bfd_boolean synthetic_relocatable;
}
/* Create synthetic symbols, effectively restoring "dot-symbol" function
- entry syms. Also generate @plt symbols for the glink branch table. */
+ entry syms. Also generate @plt symbols for the glink branch table.
+ Returns count of synthetic symbols in RET or -1 on error. */
static long
ppc64_elf_get_synthetic_symtab (bfd *abfd,
}
}
+ if (size == 0)
+ goto done;
s = *ret = bfd_malloc (size);
if (s == NULL)
{
if (opd != NULL && !bfd_malloc_and_get_section (abfd, opd, &contents))
{
+ free_contents_and_exit_err:
+ count = -1;
free_contents_and_exit:
if (contents)
free (contents);
- count = -1;
goto done;
}
void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
if (!bfd_malloc_and_get_section (abfd, dynamic, &dynbuf))
- goto free_contents_and_exit;
+ goto free_contents_and_exit_err;
extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
{
slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
if (! (*slurp_relocs) (abfd, relplt, dyn_syms, TRUE))
- goto free_contents_and_exit;
+ goto free_contents_and_exit_err;
plt_count = relplt->size / sizeof (Elf64_External_Rela);
size += plt_count * sizeof (asymbol);
}
}
+ if (size == 0)
+ goto free_contents_and_exit;
s = *ret = bfd_malloc (size);
if (s == NULL)
- goto free_contents_and_exit;
+ goto free_contents_and_exit_err;
names = (char *) (s + count + plt_count + (resolv_vma != 0));
ppc_stub_plt_branch,
ppc_stub_plt_branch_r2off,
ppc_stub_plt_call,
- ppc_stub_plt_call_r2save
+ ppc_stub_plt_call_r2save,
+ ppc_stub_global_entry
};
struct ppc_stub_hash_entry {
bfd_size_type got_reli_size;
/* Statistics. */
- unsigned long stub_count[ppc_stub_plt_call_r2save];
+ unsigned long stub_count[ppc_stub_global_entry];
/* Number of stubs against global syms. */
unsigned long stub_globals;
const char **name,
flagword *flags ATTRIBUTE_UNUSED,
asection **sec,
- bfd_vma *value ATTRIBUTE_UNUSED)
+ bfd_vma *value)
{
- if ((ibfd->flags & DYNAMIC) == 0
- && ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
+ if ((ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
+ && (ibfd->flags & DYNAMIC) == 0
+ && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ if (*sec != NULL
+ && strcmp ((*sec)->name, ".opd") == 0)
{
- if ((ibfd->flags & DYNAMIC) == 0)
- elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+ asection *code_sec;
+
+ if (!(ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_TYPE (isym->st_info) == STT_FUNC))
+ isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+
+ /* If the symbol is a function defined in .opd, and the function
+ code is in a discarded group, let it appear to be undefined. */
+ if (!info->relocatable
+ && (*sec)->reloc_count != 0
+ && opd_entry_value (*sec, *value, &code_sec, NULL,
+ FALSE) != (bfd_vma) -1
+ && discarded_section (code_sec))
+ {
+ *sec = bfd_und_section_ptr;
+ isym->st_shndx = SHN_UNDEF;
+ }
}
- else if (ELF_ST_TYPE (isym->st_info) == STT_FUNC)
- ;
else if (*sec != NULL
- && strcmp ((*sec)->name, ".opd") == 0)
- isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+ && strcmp ((*sec)->name, ".toc") == 0
+ && ELF_ST_TYPE (isym->st_info) == STT_OBJECT)
+ {
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+ if (htab != NULL)
+ htab->params->object_in_toc = 1;
+ }
if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
{
{
struct ppc_link_hash_table *htab;
struct ppc_link_hash_entry **p, *eh;
+ asection *opd = bfd_get_section_by_name (ibfd, ".opd");
- if (!is_ppc64_elf (info->output_bfd))
- return TRUE;
- htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
-
- if (is_ppc64_elf (ibfd))
+ if (opd != NULL && opd->size != 0)
{
- asection *opd = bfd_get_section_by_name (ibfd, ".opd");
-
- if (opd != NULL && opd->size != 0)
+ if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, 1);
+ else if (abiversion (ibfd) == 2)
{
- if (abiversion (ibfd) == 0)
- set_abiversion (ibfd, 1);
- else if (abiversion (ibfd) == 2)
- {
- info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
- " version %d\n"),
- ibfd, abiversion (ibfd));
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
-
- if ((ibfd->flags & DYNAMIC) == 0
- && (opd->flags & SEC_RELOC) != 0
- && opd->reloc_count != 0
- && !bfd_is_abs_section (opd->output_section))
- {
- /* Garbage collection needs some extra help with .opd sections.
- We don't want to necessarily keep everything referenced by
- relocs in .opd, as that would keep all functions. Instead,
- if we reference an .opd symbol (a function descriptor), we
- want to keep the function code symbol's section. This is
- easy for global symbols, but for local syms we need to keep
- information about the associated function section. */
- bfd_size_type amt;
- asection **opd_sym_map;
-
- amt = opd->size * sizeof (*opd_sym_map) / 8;
- opd_sym_map = bfd_zalloc (ibfd, amt);
- if (opd_sym_map == NULL)
- return FALSE;
- ppc64_elf_section_data (opd)->u.opd.func_sec = opd_sym_map;
- BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
- ppc64_elf_section_data (opd)->sec_type = sec_opd;
- }
+ info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
+ " version %d\n"),
+ ibfd, abiversion (ibfd));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
}
- /* For input files without an explicit abiversion in e_flags
- we should have flagged any with symbol st_other bits set
- as ELFv1 and above flagged those with .opd as ELFv2.
- Set the output abiversion if not yet set, and for any input
- still ambiguous, take its abiversion from the output.
- Differences in ABI are reported later. */
- if (abiversion (info->output_bfd) == 0)
- set_abiversion (info->output_bfd, abiversion (ibfd));
- else if (abiversion (ibfd) == 0)
- set_abiversion (ibfd, abiversion (info->output_bfd));
-
- p = &htab->dot_syms;
- while ((eh = *p) != NULL)
+ if ((ibfd->flags & DYNAMIC) == 0
+ && (opd->flags & SEC_RELOC) != 0
+ && opd->reloc_count != 0
+ && !bfd_is_abs_section (opd->output_section))
{
- *p = NULL;
- if (&eh->elf == htab->elf.hgot)
- ;
- else if (htab->elf.hgot == NULL
- && strcmp (eh->elf.root.root.string, ".TOC.") == 0)
- htab->elf.hgot = &eh->elf;
- else if (!add_symbol_adjust (eh, info))
+ /* Garbage collection needs some extra help with .opd sections.
+ We don't want to necessarily keep everything referenced by
+ relocs in .opd, as that would keep all functions. Instead,
+ if we reference an .opd symbol (a function descriptor), we
+ want to keep the function code symbol's section. This is
+ easy for global symbols, but for local syms we need to keep
+ information about the associated function section. */
+ bfd_size_type amt;
+ asection **opd_sym_map;
+
+ amt = OPD_NDX (opd->size) * sizeof (*opd_sym_map);
+ opd_sym_map = bfd_zalloc (ibfd, amt);
+ if (opd_sym_map == NULL)
return FALSE;
- p = &eh->u.next_dot_sym;
+ ppc64_elf_section_data (opd)->u.opd.func_sec = opd_sym_map;
+ BFD_ASSERT (ppc64_elf_section_data (opd)->sec_type == sec_normal);
+ ppc64_elf_section_data (opd)->sec_type = sec_opd;
}
}
+ if (!is_ppc64_elf (info->output_bfd))
+ return TRUE;
+ htab = ppc_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ /* For input files without an explicit abiversion in e_flags
+ we should have flagged any with symbol st_other bits set
+ as ELFv1 and above flagged those with .opd as ELFv2.
+ Set the output abiversion if not yet set, and for any input
+ still ambiguous, take its abiversion from the output.
+ Differences in ABI are reported later. */
+ if (abiversion (info->output_bfd) == 0)
+ set_abiversion (info->output_bfd, abiversion (ibfd));
+ else if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, abiversion (info->output_bfd));
+
+ p = &htab->dot_syms;
+ while ((eh = *p) != NULL)
+ {
+ *p = NULL;
+ if (&eh->elf == htab->elf.hgot)
+ ;
+ else if (htab->elf.hgot == NULL
+ && strcmp (eh->elf.root.root.string, ".TOC.") == 0)
+ htab->elf.hgot = &eh->elf;
+ else if (!add_symbol_adjust (eh, info))
+ return FALSE;
+ p = &eh->u.next_dot_sym;
+ }
+
/* Clear the list for non-ppc64 input files. */
p = &htab->dot_syms;
while ((eh = *p) != NULL)
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_TPREL16_HA:
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
tls_type = TLS_TLS | TLS_TPREL;
goto dogottls;
case R_PPC64_TPREL64:
tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
- if (!info->executable)
+ if (info->shared)
info->flags |= DF_STATIC_TLS;
goto dotlstoc;
case R_PPC64_TPREL16_HIGHESTA:
if (info->shared)
{
- if (!info->executable)
- info->flags |= DF_STATIC_TLS;
+ info->flags |= DF_STATIC_TLS;
goto dodyn;
}
break;
s = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (s != NULL && s != sec)
- opd_sym_map[rel->r_offset / 8] = s;
+ opd_sym_map[OPD_NDX (rel->r_offset)] = s;
}
}
/* Fall through. */
}
/* OFFSET in OPD_SEC specifies a function descriptor. Return the address
- of the code entry point, and its section. */
+ of the code entry point, and its section, which must be in the same
+ object as OPD_SEC. Returns (bfd_vma) -1 on error. */
static bfd_vma
opd_entry_value (asection *opd_sec,
ppc64_elf_tdata (opd_bfd)->opd.contents = contents;
}
+ /* PR 17512: file: 64b9dfbb. */
+ if (offset + 7 >= opd_sec->size || offset + 7 < offset)
+ return (bfd_vma) -1;
+
val = bfd_get_64 (opd_bfd, contents + offset);
if (code_sec != NULL)
{
relocs = ppc64_elf_tdata (opd_bfd)->opd.relocs;
if (relocs == NULL)
relocs = _bfd_elf_link_read_relocs (opd_bfd, opd_sec, NULL, NULL, TRUE);
+ /* PR 17512: file: df8e1fd6. */
+ if (relocs == NULL)
+ return (bfd_vma) -1;
/* Go find the opd reloc at the sym address. */
lo = relocs;
- BFD_ASSERT (lo != NULL);
hi = lo + opd_sec->reloc_count - 1; /* ignore last reloc */
val = (bfd_vma) -1;
while (lo < hi)
&& ELF64_R_TYPE ((look + 1)->r_info) == R_PPC64_TOC)
{
unsigned long symndx = ELF64_R_SYM (look->r_info);
- asection *sec;
+ asection *sec = NULL;
- if (symndx < symtab_hdr->sh_info
- || elf_sym_hashes (opd_bfd) == NULL)
- {
- Elf_Internal_Sym *sym;
-
- sym = (Elf_Internal_Sym *) symtab_hdr->contents;
- if (sym == NULL)
- {
- size_t symcnt = symtab_hdr->sh_info;
- if (elf_sym_hashes (opd_bfd) == NULL)
- symcnt = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
- sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, symcnt,
- 0, NULL, NULL, NULL);
- if (sym == NULL)
- break;
- symtab_hdr->contents = (bfd_byte *) sym;
- }
-
- sym += symndx;
- val = sym->st_value;
- sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
- BFD_ASSERT ((sec->flags & SEC_MERGE) == 0);
- }
- else
+ if (symndx >= symtab_hdr->sh_info
+ && elf_sym_hashes (opd_bfd) != NULL)
{
struct elf_link_hash_entry **sym_hashes;
struct elf_link_hash_entry *rh;
|| rh->root.type == bfd_link_hash_defweak);
val = rh->root.u.def.value;
sec = rh->root.u.def.section;
+ if (sec->owner != opd_bfd)
+ {
+ sec = NULL;
+ val = (bfd_vma) -1;
+ }
+ }
+ }
+
+ if (sec == NULL)
+ {
+ Elf_Internal_Sym *sym;
+
+ if (symndx < symtab_hdr->sh_info)
+ {
+ sym = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (sym == NULL)
+ {
+ size_t symcnt = symtab_hdr->sh_info;
+ sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+ symcnt, 0,
+ NULL, NULL, NULL);
+ if (sym == NULL)
+ break;
+ symtab_hdr->contents = (bfd_byte *) sym;
+ }
+ sym += symndx;
}
else
{
- /* Handle the odd case where we can be called
- during bfd_elf_link_add_symbols before the
- symbol hashes have been fully populated. */
- Elf_Internal_Sym *sym;
-
- sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, 1,
- symndx, NULL, NULL, NULL);
+ sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
+ 1, symndx,
+ NULL, NULL, NULL);
if (sym == NULL)
break;
-
- val = sym->st_value;
- sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
- free (sym);
}
+ sec = bfd_section_from_elf_index (opd_bfd, sym->st_shndx);
+ if (sec == NULL)
+ break;
+ BFD_ASSERT ((sec->flags & SEC_MERGE) == 0);
+ val = sym->st_value;
}
+
val += look->r_addend;
if (code_off != NULL)
*code_off = val;
else
*code_sec = sec;
}
- if (sec != NULL && sec->output_section != NULL)
+ if (sec->output_section != NULL)
val += sec->output_section->vma + sec->output_offset;
}
break;
if ((eh->elf.root.type == bfd_link_hash_defined
|| eh->elf.root.type == bfd_link_hash_defweak)
&& (eh->elf.ref_dynamic
- || (eh->elf.def_regular
+ || ((eh->elf.def_regular || ELF_COMMON_DEF_P (&eh->elf))
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_INTERNAL
&& ELF_ST_VISIBILITY (eh->elf.other) != STV_HIDDEN
&& (!info->executable
{
rsec->gc_mark = 1;
- rsec = opd->func_sec[(sym->st_value + rel->r_addend) / 8];
+ rsec = opd->func_sec[OPD_NDX (sym->st_value + rel->r_addend)];
}
}
{
h->plt.plist = NULL;
h->needs_plt = 0;
+ h->pointer_equality_needed = 0;
}
else if (abiversion (info->output_bfd) == 2)
{
+ /* Taking a function's address in a read/write section
+ doesn't require us to define the function symbol in the
+ executable on a global entry stub. A dynamic reloc can
+ be used instead. */
+ if (h->pointer_equality_needed
+ && h->type != STT_GNU_IFUNC
+ && !readonly_dynrelocs (h))
+ {
+ h->pointer_equality_needed = 0;
+ h->non_got_ref = 0;
+ }
+
/* After adjust_dynamic_symbol, non_got_ref set in the
non-shared case means that we have allocated space in
.dynbss for the symbol and thus dyn_relocs for this
relocations against this symbol to the PLT entry. Allow
dynamic relocs if the reference is weak, and the dynamic
relocs will not cause text relocation. */
- if (!h->ref_regular_nonweak
- && h->non_got_ref
- && h->type != STT_GNU_IFUNC
- && !readonly_dynrelocs (h))
+ else if (!h->ref_regular_nonweak
+ && h->non_got_ref
+ && h->type != STT_GNU_IFUNC
+ && !readonly_dynrelocs (h))
h->non_got_ref = 0;
/* If making a plt entry, then we don't need copy relocs. */
if (!h->def_dynamic || !h->ref_regular || h->def_regular)
return TRUE;
+ /* If -z nocopyreloc was given, don't generate them either. */
+ if (info->nocopyreloc)
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
/* If we didn't find any dynamic relocs in read-only sections, then
we'll be keeping the dynamic relocs and avoiding the copy reloc. */
if (ELIMINATE_COPY_RELOCS && !readonly_dynrelocs (h))
return TRUE;
}
+ /* Protected variables do not work with .dynbss. The copy in
+ .dynbss won't be used by the shared library with the protected
+ definition for the variable. Text relocations are preferable
+ to an incorrect program. */
+ if (h->protected_def)
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
if (h->plt.plist != NULL)
{
/* We should never get here, but unfortunately there are versions
s = htab->dynbss;
- return _bfd_elf_adjust_dynamic_copy (h, s);
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
/* If given a function descriptor symbol, hide both the function code
opd = get_opd_info (sym_sec);
if (opd != NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[eh->elf.root.u.def.value / 8];
+ long adjust = opd->adjust[OPD_NDX (eh->elf.root.u.def.value)];
if (adjust == -1)
{
/* This entry has been deleted. */
Elf_Internal_Rela *relstart, *rel, *relend;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Sym *local_syms;
- bfd_vma offset;
struct _opd_sec_data *opd;
- bfd_boolean need_edit, add_aux_fields;
+ bfd_boolean need_edit, add_aux_fields, broken;
bfd_size_type cnt_16b = 0;
if (!is_ppc64_elf (ibfd))
/* First run through the relocs to check they are sane, and to
determine whether we need to edit this opd section. */
need_edit = FALSE;
+ broken = FALSE;
need_pad = sec;
- offset = 0;
relend = relstart + sec->reloc_count;
for (rel = relstart; rel < relend; )
{
asection *sym_sec;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
+ bfd_vma offset;
- /* .opd contains a regular array of 16 or 24 byte entries. We're
+ /* .opd contains an array of 16 or 24 byte entries. We're
only interested in the reloc pointing to a function entry
point. */
- if (rel->r_offset != offset
- || rel + 1 >= relend
- || (rel + 1)->r_offset != offset + 8)
+ offset = rel->r_offset;
+ if (rel + 1 == relend
+ || rel[1].r_offset != offset + 8)
{
/* If someone messes with .opd alignment then after a
"ld -r" we might have padding in the middle of .opd.
broken_opd:
(*_bfd_error_handler)
(_("%B: .opd is not a regular array of opd entries"), ibfd);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
(*_bfd_error_handler)
(_("%B: unexpected reloc type %u in .opd section"),
ibfd, r_type);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
(*_bfd_error_handler)
(_("%B: undefined sym `%s' in .opd section"),
ibfd, sym_name);
- need_edit = FALSE;
+ broken = TRUE;
break;
}
need_edit = TRUE;
rel += 2;
- if (rel == relend
- || (rel + 1 == relend && rel->r_offset == offset + 16))
+ if (rel + 1 == relend
+ || (rel + 2 < relend
+ && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC))
+ ++rel;
+
+ if (rel == relend)
{
if (sec->size == offset + 24)
{
need_pad = NULL;
break;
}
- if (rel == relend && sec->size == offset + 16)
+ if (sec->size == offset + 16)
{
cnt_16b++;
break;
}
goto broken_opd;
}
-
- if (rel->r_offset == offset + 24)
- offset += 24;
- else if (rel->r_offset != offset + 16)
- goto broken_opd;
else if (rel + 1 < relend
&& ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
&& ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
{
- offset += 16;
- cnt_16b++;
- }
- else if (rel + 2 < relend
- && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
- && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
- {
- offset += 24;
- rel += 1;
+ if (rel[0].r_offset == offset + 16)
+ cnt_16b++;
+ else if (rel[0].r_offset != offset + 24)
+ goto broken_opd;
}
else
goto broken_opd;
add_aux_fields = htab->params->non_overlapping_opd && cnt_16b > 0;
- if (need_edit || add_aux_fields)
+ if (!broken && (need_edit || add_aux_fields))
{
Elf_Internal_Rela *write_rel;
Elf_Internal_Shdr *rel_hdr;
bfd_byte *rptr, *wptr;
bfd_byte *new_contents;
- bfd_boolean skip;
- long opd_ent_size;
bfd_size_type amt;
new_contents = NULL;
- amt = sec->size * sizeof (long) / 8;
+ amt = OPD_NDX (sec->size) * sizeof (long);
opd = &ppc64_elf_section_data (sec)->u.opd;
opd->adjust = bfd_zalloc (sec->owner, amt);
if (opd->adjust == NULL)
new_contents = bfd_malloc (sec->size + cnt_16b * 8);
if (new_contents == NULL)
return FALSE;
- need_pad = FALSE;
+ need_pad = NULL;
}
wptr = new_contents;
rptr = sec->contents;
-
write_rel = relstart;
- skip = FALSE;
- offset = 0;
- opd_ent_size = 0;
- for (rel = relstart; rel < relend; rel++)
+ for (rel = relstart; rel < relend; )
{
unsigned long r_symndx;
asection *sym_sec;
struct elf_link_hash_entry *h;
+ struct ppc_link_hash_entry *fdh = NULL;
Elf_Internal_Sym *sym;
+ long opd_ent_size;
+ Elf_Internal_Rela *next_rel;
+ bfd_boolean skip;
r_symndx = ELF64_R_SYM (rel->r_info);
if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
r_symndx, ibfd))
goto error_ret;
- if (rel->r_offset == offset)
+ next_rel = rel + 2;
+ if (next_rel + 1 == relend
+ || (next_rel + 2 < relend
+ && ELF64_R_TYPE (next_rel[2].r_info) == R_PPC64_TOC))
+ ++next_rel;
+
+ /* See if the .opd entry is full 24 byte or
+ 16 byte (with fd_aux entry overlapped with next
+ fd_func). */
+ opd_ent_size = 24;
+ if (next_rel == relend)
{
- struct ppc_link_hash_entry *fdh = NULL;
-
- /* See if the .opd entry is full 24 byte or
- 16 byte (with fd_aux entry overlapped with next
- fd_func). */
- opd_ent_size = 24;
- if ((rel + 2 == relend && sec->size == offset + 16)
- || (rel + 3 < relend
- && rel[2].r_offset == offset + 16
- && rel[3].r_offset == offset + 24
- && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
- && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+ if (sec->size == rel->r_offset + 16)
opd_ent_size = 16;
+ }
+ else if (next_rel->r_offset == rel->r_offset + 16)
+ opd_ent_size = 16;
- if (h != NULL
- && h->root.root.string[0] == '.')
- {
- fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
- if (fdh != NULL
- && fdh->elf.root.type != bfd_link_hash_defined
- && fdh->elf.root.type != bfd_link_hash_defweak)
- fdh = NULL;
- }
+ if (h != NULL
+ && h->root.root.string[0] == '.')
+ {
+ fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
+ if (fdh != NULL
+ && fdh->elf.root.type != bfd_link_hash_defined
+ && fdh->elf.root.type != bfd_link_hash_defweak)
+ fdh = NULL;
+ }
- skip = (sym_sec->owner != ibfd
- || sym_sec->output_section == bfd_abs_section_ptr);
- if (skip)
+ skip = (sym_sec->owner != ibfd
+ || sym_sec->output_section == bfd_abs_section_ptr);
+ if (skip)
+ {
+ if (fdh != NULL && sym_sec->owner == ibfd)
{
- if (fdh != NULL && sym_sec->owner == ibfd)
- {
- /* Arrange for the function descriptor sym
- to be dropped. */
- fdh->elf.root.u.def.value = 0;
- fdh->elf.root.u.def.section = sym_sec;
- }
- opd->adjust[rel->r_offset / 8] = -1;
+ /* Arrange for the function descriptor sym
+ to be dropped. */
+ fdh->elf.root.u.def.value = 0;
+ fdh->elf.root.u.def.section = sym_sec;
}
- else
- {
- /* We'll be keeping this opd entry. */
+ opd->adjust[OPD_NDX (rel->r_offset)] = -1;
- if (fdh != NULL)
- {
- /* Redefine the function descriptor symbol to
- this location in the opd section. It is
- necessary to update the value here rather
- than using an array of adjustments as we do
- for local symbols, because various places
- in the generic ELF code use the value
- stored in u.def.value. */
- fdh->elf.root.u.def.value = wptr - new_contents;
- fdh->adjust_done = 1;
- }
+ if (NO_OPD_RELOCS || info->relocatable)
+ rel = next_rel;
+ else
+ while (1)
+ {
+ if (!dec_dynrel_count (rel->r_info, sec, info,
+ NULL, h, sym))
+ goto error_ret;
- /* Local syms are a bit tricky. We could
- tweak them as they can be cached, but
- we'd need to look through the local syms
- for the function descriptor sym which we
- don't have at the moment. So keep an
- array of adjustments. */
- opd->adjust[rel->r_offset / 8]
- = (wptr - new_contents) - (rptr - sec->contents);
-
- if (wptr != rptr)
- memcpy (wptr, rptr, opd_ent_size);
- wptr += opd_ent_size;
- if (add_aux_fields && opd_ent_size == 16)
- {
- memset (wptr, '\0', 8);
- wptr += 8;
- }
- }
- rptr += opd_ent_size;
- offset += opd_ent_size;
- }
+ if (++rel == next_rel)
+ break;
- if (skip)
- {
- if (!NO_OPD_RELOCS
- && !info->relocatable
- && !dec_dynrel_count (rel->r_info, sec, info,
- NULL, h, sym))
- goto error_ret;
+ r_symndx = ELF64_R_SYM (rel->r_info);
+ if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+ r_symndx, ibfd))
+ goto error_ret;
+ }
}
else
{
+ /* We'll be keeping this opd entry. */
+ long adjust;
+
+ if (fdh != NULL)
+ {
+ /* Redefine the function descriptor symbol to
+ this location in the opd section. It is
+ necessary to update the value here rather
+ than using an array of adjustments as we do
+ for local symbols, because various places
+ in the generic ELF code use the value
+ stored in u.def.value. */
+ fdh->elf.root.u.def.value = wptr - new_contents;
+ fdh->adjust_done = 1;
+ }
+
+ /* Local syms are a bit tricky. We could
+ tweak them as they can be cached, but
+ we'd need to look through the local syms
+ for the function descriptor sym which we
+ don't have at the moment. So keep an
+ array of adjustments. */
+ adjust = (wptr - new_contents) - (rptr - sec->contents);
+ opd->adjust[OPD_NDX (rel->r_offset)] = adjust;
+
+ if (wptr != rptr)
+ memcpy (wptr, rptr, opd_ent_size);
+ wptr += opd_ent_size;
+ if (add_aux_fields && opd_ent_size == 16)
+ {
+ memset (wptr, '\0', 8);
+ wptr += 8;
+ }
+
/* We need to adjust any reloc offsets to point to the
- new opd entries. While we're at it, we may as well
- remove redundant relocs. */
- rel->r_offset += opd->adjust[(offset - opd_ent_size) / 8];
- if (write_rel != rel)
- memcpy (write_rel, rel, sizeof (*rel));
- ++write_rel;
+ new opd entries. */
+ for ( ; rel != next_rel; ++rel)
+ {
+ rel->r_offset += adjust;
+ if (write_rel != rel)
+ memcpy (write_rel, rel, sizeof (*rel));
+ ++write_rel;
+ }
}
+
+ rptr += opd_ent_size;
}
sec->size = wptr - new_contents;
else
value = sym->st_value;
value += rel->r_addend;
- BFD_ASSERT (value < toc->size && value % 8 == 0);
+ if (value % 8 != 0)
+ continue;
+ BFD_ASSERT (value < toc->size
+ && toc->output_offset % 8 == 0);
toc_ref_index = (value + toc->output_offset) / 8;
if (r_type == R_PPC64_TLS
|| r_type == R_PPC64_TLSGD
size += 4;
if (htab->params->plt_static_chain)
size += 4;
- if (htab->params->plt_thread_safe)
+ if (htab->params->plt_thread_safe
+ && htab->elf.dynamic_sections_created
+ && stub_entry->h != NULL
+ && stub_entry->h->elf.dynindx != -1)
size += 8;
if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off))
size += 4;
bfd_vma stub_off = stub_entry->stub_sec->size;
if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
- > (stub_size & -stub_align))
+ > ((stub_size - 1) & -stub_align))
return stub_align - (stub_off & (stub_align - 1));
return 0;
}
bfd *obfd = htab->params->stub_bfd;
bfd_boolean plt_load_toc = htab->opd_abi;
bfd_boolean plt_static_chain = htab->params->plt_static_chain;
- bfd_boolean plt_thread_safe = htab->params->plt_thread_safe;
+ bfd_boolean plt_thread_safe = (htab->params->plt_thread_safe
+ && htab->elf.dynamic_sections_created
+ && stub_entry->h != NULL
+ && stub_entry->h->elf.dynindx != -1);
bfd_boolean use_fake_dep = plt_thread_safe;
bfd_vma cmp_branch_off = 0;
if (!ALWAYS_USE_FAKE_DEP
&& plt_load_toc
&& plt_thread_safe
- && !(stub_entry->h != NULL
- && (stub_entry->h == htab->tls_get_addr_fd
- || stub_entry->h == htab->tls_get_addr)
+ && !((stub_entry->h == htab->tls_get_addr_fd
+ || stub_entry->h == htab->tls_get_addr)
&& !htab->params->no_tls_get_addr_opt))
{
bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
p = build_plt_stub (htab, stub_entry, p, offset, r);
bfd_put_32 (obfd, BCTRL, p - 4);
- bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p), p += 4;
bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p), p += 4;
+ bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p), p += 4;
bfd_put_32 (obfd, MTLR_R11, p), p += 4;
bfd_put_32 (obfd, BLR, p), p += 4;
h->ref_regular_nonweak = 1;
h->forced_local = 1;
h->non_elf = 0;
+ h->root.linker_def = 1;
}
}
addr = (htab->toc_first_sec->output_offset
+ htab->toc_first_sec->output_section->vma);
htab->toc_curr = addr;
+ htab->toc_curr &= -TOC_BASE_ALIGN;
}
/* toc_curr is the base address of this toc group. Set elf_gp
{
long adjust;
- adjust = opd->adjust[sym->st_value / 8];
+ adjust = opd->adjust[OPD_NDX (sym->st_value)];
if (adjust == -1)
/* Assume deleted functions won't ever be called. */
continue;
bfd_boolean suppress_size_errors;
suppress_size_errors = FALSE;
- stub14_group_size = stub_group_size;
+ stub14_group_size = stub_group_size >> 10;
if (stub_group_size == 1)
{
/* Default values. */
65, /* RA reg. */
1, /* Augmentation size. */
DW_EH_PE_pcrel | DW_EH_PE_sdata4, /* FDE encoding. */
- DW_CFA_def_cfa, 1, 0 /* def_cfa: r1 offset 0. */
+ DW_CFA_def_cfa, 1, 0, /* def_cfa: r1 offset 0. */
+ 0, 0, 0, 0
};
/* Stripping output sections is normally done before dynamic section
/* libanl */
"getaddrinfo_a",
/* libgomp */
+ "GOMP_parallel",
"GOMP_parallel_start",
+ "GOMP_parallel_loop_static",
"GOMP_parallel_loop_static_start",
+ "GOMP_parallel_loop_dynamic",
"GOMP_parallel_loop_dynamic_start",
+ "GOMP_parallel_loop_guided",
"GOMP_parallel_loop_guided_start",
+ "GOMP_parallel_loop_runtime",
"GOMP_parallel_loop_runtime_start",
+ "GOMP_parallel_sections",
"GOMP_parallel_sections_start",
+ /* libgo */
+ "__go_go",
};
unsigned i;
if (hash == NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[sym_value / 8];
+ long adjust = opd->adjust[OPD_NDX (sym_value)];
if (adjust == -1)
continue;
code_value += adjust;
stub_sec != NULL;
stub_sec = stub_sec->next)
if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- size += 20;
+ size += 24;
if (htab->glink != NULL && htab->glink->size != 0)
size += 24;
if (size != 0)
(*htab->params->layout_sections_again) ();
}
+ if (htab->glink_eh_frame != NULL
+ && htab->glink_eh_frame->size != 0)
+ {
+ bfd_vma val;
+ bfd_byte *p, *last_fde;
+ size_t last_fde_len, size, align, pad;
+ asection *stub_sec;
+
+ p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
+ if (p == NULL)
+ return FALSE;
+ htab->glink_eh_frame->contents = p;
+ last_fde = p;
+
+ memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
+ /* CIE length (rewrite in case little-endian). */
+ last_fde_len = sizeof (glink_eh_frame_cie) - 4;
+ bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
+ p += sizeof (glink_eh_frame_cie);
+
+ for (stub_sec = htab->params->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ {
+ last_fde = p;
+ last_fde_len = 20;
+ /* FDE length. */
+ bfd_put_32 (htab->elf.dynobj, 20, p);
+ p += 4;
+ /* CIE pointer. */
+ val = p - htab->glink_eh_frame->contents;
+ bfd_put_32 (htab->elf.dynobj, val, p);
+ p += 4;
+ /* Offset to stub section, written later. */
+ p += 4;
+ /* stub section size. */
+ bfd_put_32 (htab->elf.dynobj, stub_sec->size, p);
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Pad. */
+ p += 7;
+ }
+ if (htab->glink != NULL && htab->glink->size != 0)
+ {
+ last_fde = p;
+ last_fde_len = 20;
+ /* FDE length. */
+ bfd_put_32 (htab->elf.dynobj, 20, p);
+ p += 4;
+ /* CIE pointer. */
+ val = p - htab->glink_eh_frame->contents;
+ bfd_put_32 (htab->elf.dynobj, val, p);
+ p += 4;
+ /* Offset to .glink, written later. */
+ p += 4;
+ /* .glink size. */
+ bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p);
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+
+ *p++ = DW_CFA_advance_loc + 1;
+ *p++ = DW_CFA_register;
+ *p++ = 65;
+ *p++ = 12;
+ *p++ = DW_CFA_advance_loc + 4;
+ *p++ = DW_CFA_restore_extended;
+ *p++ = 65;
+ }
+ /* Subsume any padding into the last FDE if user .eh_frame
+ sections are aligned more than glink_eh_frame. Otherwise any
+ zero padding will be seen as a terminator. */
+ size = p - htab->glink_eh_frame->contents;
+ align = 1;
+ align <<= htab->glink_eh_frame->output_section->alignment_power;
+ align -= 1;
+ pad = ((size + align) & ~align) - size;
+ htab->glink_eh_frame->size = size + pad;
+ bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
+ }
+
maybe_strip_output (info, htab->brlt);
if (htab->glink_eh_frame != NULL)
maybe_strip_output (info, htab->glink_eh_frame);
ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd)
{
asection *s;
- bfd_vma TOCstart;
+ bfd_vma TOCstart, adjust;
/* The TOC consists of sections .got, .toc, .tocbss, .plt in that
order. The TOC starts where the first of these sections starts. */
if (s != NULL)
TOCstart = s->output_section->vma + s->output_offset;
+ /* Force alignment. */
+ adjust = TOCstart & (TOC_BASE_ALIGN - 1);
+ TOCstart -= adjust;
_bfd_set_gp_value (obfd, TOCstart);
- if (info != NULL && s != NULL && is_ppc64_elf (obfd))
+ if (info != NULL && s != NULL)
{
struct ppc_link_hash_table *htab = ppc_hash_table (info);
- if (htab != NULL
- && htab->elf.hgot != NULL)
+ if (htab != NULL)
{
- htab->elf.hgot->root.u.def.value = TOC_BASE_OFF;
- htab->elf.hgot->root.u.def.section = s;
+ if (htab->elf.hgot != NULL)
+ {
+ htab->elf.hgot->root.u.def.value = TOC_BASE_OFF - adjust;
+ htab->elf.hgot->root.u.def.section = s;
+ }
+ }
+ else
+ {
+ struct bfd_link_hash_entry *bh = NULL;
+ _bfd_generic_link_add_one_symbol (info, obfd, ".TOC.", BSF_GLOBAL,
+ s, TOC_BASE_OFF - adjust,
+ NULL, FALSE, FALSE, &bh);
}
}
return TOCstart;
htab->stub_error = TRUE;
}
+ htab->stub_count[ppc_stub_global_entry - 1] += 1;
+ if (htab->params->emit_stub_syms)
+ {
+ size_t len = strlen (h->root.root.string);
+ char *name = bfd_malloc (sizeof "12345678.global_entry." + len);
+
+ if (name == NULL)
+ return FALSE;
+
+ sprintf (name, "%08x.global_entry.%s", s->id, h->root.root.string);
+ h = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
+ if (h == NULL)
+ return FALSE;
+ if (h->root.type == bfd_link_hash_new)
+ {
+ h->root.type = bfd_link_hash_defined;
+ h->root.u.def.section = s;
+ h->root.u.def.value = p - s->contents;
+ h->ref_regular = 1;
+ h->def_regular = 1;
+ h->ref_regular_nonweak = 1;
+ h->forced_local = 1;
+ h->non_elf = 0;
+ h->root.linker_def = 1;
+ }
+ }
+
if (PPC_HA (off) != 0)
{
bfd_put_32 (s->owner, ADDIS_R12_R12 | PPC_HA (off), p);
h->ref_regular_nonweak = 1;
h->forced_local = 1;
h->non_elf = 0;
+ h->root.linker_def = 1;
}
}
plt0 = (htab->elf.splt->output_section->vma
elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info);
}
- if (htab->brlt->size != 0)
+ if (htab->brlt != NULL && htab->brlt->size != 0)
{
htab->brlt->contents = bfd_zalloc (htab->brlt->owner,
htab->brlt->size);
return FALSE;
}
- if (htab->glink_eh_frame != NULL
- && htab->glink_eh_frame->size != 0)
- {
- bfd_vma val;
- bfd_byte *last_fde;
- size_t last_fde_len, size, align, pad;
-
- p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
- if (p == NULL)
- return FALSE;
- htab->glink_eh_frame->contents = p;
- last_fde = p;
-
- htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
-
- memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
- /* CIE length (rewrite in case little-endian). */
- last_fde_len = sizeof (glink_eh_frame_cie) - 4;
- bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
- p += sizeof (glink_eh_frame_cie);
-
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- {
- last_fde = p;
- last_fde_len = 16;
- /* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 16, p);
- p += 4;
- /* CIE pointer. */
- val = p - htab->glink_eh_frame->contents;
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* Offset to stub section. */
- val = (stub_sec->output_section->vma
- + stub_sec->output_offset);
- val -= (htab->glink_eh_frame->output_section->vma
- + htab->glink_eh_frame->output_offset);
- val -= p - htab->glink_eh_frame->contents;
- if (val + 0x80000000 > 0xffffffff)
- {
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
- stub_sec->name);
- return FALSE;
- }
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* stub section size. */
- bfd_put_32 (htab->elf.dynobj, stub_sec->rawsize, p);
- p += 4;
- /* Augmentation. */
- p += 1;
- /* Pad. */
- p += 3;
- }
- if (htab->glink != NULL && htab->glink->size != 0)
- {
- last_fde = p;
- last_fde_len = 20;
- /* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 20, p);
- p += 4;
- /* CIE pointer. */
- val = p - htab->glink_eh_frame->contents;
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* Offset to .glink. */
- val = (htab->glink->output_section->vma
- + htab->glink->output_offset
- + 8);
- val -= (htab->glink_eh_frame->output_section->vma
- + htab->glink_eh_frame->output_offset);
- val -= p - htab->glink_eh_frame->contents;
- if (val + 0x80000000 > 0xffffffff)
- {
- info->callbacks->einfo
- (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
- htab->glink->name);
- return FALSE;
- }
- bfd_put_32 (htab->elf.dynobj, val, p);
- p += 4;
- /* .glink size. */
- bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p);
- p += 4;
- /* Augmentation. */
- p += 1;
-
- *p++ = DW_CFA_advance_loc + 1;
- *p++ = DW_CFA_register;
- *p++ = 65;
- *p++ = 12;
- *p++ = DW_CFA_advance_loc + 4;
- *p++ = DW_CFA_restore_extended;
- *p++ = 65;
- }
- /* Subsume any padding into the last FDE if user .eh_frame
- sections are aligned more than glink_eh_frame. Otherwise any
- zero padding will be seen as a terminator. */
- size = p - htab->glink_eh_frame->contents;
- align = 1;
- align <<= htab->glink_eh_frame->output_section->alignment_power;
- align -= 1;
- pad = ((size + align) & ~align) - size;
- htab->glink_eh_frame->size = size + pad;
- bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
- }
-
/* Build the stubs as directed by the stub hash table. */
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
break;
}
+ /* Note that the glink_eh_frame check here is not only testing that
+ the generated size matched the calculated size but also that
+ bfd_elf_discard_info didn't make any changes to the section. */
if (stub_sec != NULL
|| (htab->glink_eh_frame != NULL
&& htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size))
" long branch %lu\n"
" long toc adj %lu\n"
" plt call %lu\n"
- " plt call toc %lu"),
+ " plt call toc %lu\n"
+ " global entry %lu"),
stub_sec_count,
stub_sec_count == 1 ? "" : "s",
htab->stub_count[ppc_stub_long_branch - 1],
htab->stub_count[ppc_stub_plt_branch - 1],
htab->stub_count[ppc_stub_plt_branch_r2off - 1],
htab->stub_count[ppc_stub_plt_call - 1],
- htab->stub_count[ppc_stub_plt_call_r2save - 1]);
+ htab->stub_count[ppc_stub_plt_call_r2save - 1],
+ htab->stub_count[ppc_stub_global_entry - 1]);
}
return TRUE;
}
opd = get_opd_info (sec);
if (opd != NULL && opd->adjust != NULL)
{
- long adjust = opd->adjust[(sym->st_value + rel->r_addend) / 8];
+ long adjust = opd->adjust[OPD_NDX (sym->st_value
+ + rel->r_addend)];
if (adjust == -1)
relocation = 0;
else
htab->tls_get_addr,
htab->tls_get_addr_fd))
offset = rel[1].r_offset;
+ /* We read the low GOT_TLS (or TOC16) insn because we
+ need to keep the destination reg. It may be
+ something other than the usual r3, and moved to r3
+ before the call by intervening code. */
+ insn1 = bfd_get_32 (output_bfd,
+ contents + rel->r_offset - d_offset);
if ((tls_mask & tls_gd) != 0)
{
/* IE */
- insn1 = bfd_get_32 (output_bfd,
- contents + rel->r_offset - d_offset);
- insn1 &= (1 << 26) - (1 << 2);
+ insn1 &= (0x1f << 21) | (0x1f << 16);
insn1 |= 58 << 26; /* ld */
insn2 = 0x7c636a14; /* add 3,3,13 */
if (offset != (bfd_vma) -1)
else
{
/* LE */
- insn1 = 0x3c6d0000; /* addis 3,13,0 */
+ insn1 &= 0x1f << 21;
+ insn1 |= 0x3c0d0000; /* addis r,13,0 */
insn2 = 0x38630000; /* addi 3,3,0 */
if (tls_gd == 0)
{
. addi 2,2,.TOC.@l
if .TOC. is in range. */
if (!info->shared
+ && !info->traditional_format
&& h != NULL && &h->elf == htab->elf.hgot
&& rel + 1 < relend
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
if (!can_plt_call)
{
- info->callbacks->einfo
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
- "recompile with -fPIC\n"),
- input_bfd, input_section, rel->r_offset, sym_name);
+ if (stub_entry->stub_type == ppc_stub_plt_call
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+ info->callbacks->einfo
+ (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ "recompile with -fPIC\n"),
+ input_bfd, input_section, rel->r_offset, sym_name);
+ else
+ info->callbacks->einfo
+ (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ "(-mcmodel=small toc adjust stub)\n"),
+ input_bfd, input_section, rel->r_offset, sym_name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
{
outrel.r_addend += relocation;
if (tls_type & (TLS_GD | TLS_DTPREL | TLS_TPREL))
- outrel.r_addend -= htab->elf.tls_sec->vma;
+ {
+ if (htab->elf.tls_sec == NULL)
+ outrel.r_addend = 0;
+ else
+ outrel.r_addend -= htab->elf.tls_sec->vma;
+ }
}
loc = relgot->contents;
loc += (relgot->reloc_count++
relocation = 1;
else if (tls_type != 0)
{
- relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
- if (tls_type == (TLS_TLS | TLS_TPREL))
- relocation += DTP_OFFSET - TP_OFFSET;
+ if (htab->elf.tls_sec == NULL)
+ relocation = 0;
+ else
+ {
+ relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (tls_type == (TLS_TLS | TLS_TPREL))
+ relocation += DTP_OFFSET - TP_OFFSET;
+ }
if (tls_type == (TLS_TLS | TLS_GD))
{
bfd_put_32 (output_bfd, insn, p);
break;
}
- addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + TP_OFFSET;
if (info->shared)
/* The TPREL16 relocs shouldn't really be used in shared
libs as they will result in DT_TEXTREL being set, but
case R_PPC64_DTPREL16_HIGHERA:
case R_PPC64_DTPREL16_HIGHEST:
case R_PPC64_DTPREL16_HIGHESTA:
- addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
break;
case R_PPC64_ADDR64_LOCAL:
goto dodyn;
case R_PPC64_TPREL64:
- addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + TP_OFFSET;
goto dodyn;
case R_PPC64_DTPREL64:
- addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+ if (htab->elf.tls_sec != NULL)
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
/* Fall thru */
/* Relocations that may need to be propagated if this is a
if (r == bfd_reloc_overflow)
{
- if (warned)
- continue;
- if (h != NULL
- && h->elf.root.type == bfd_link_hash_undefweak
- && howto->pc_relative)
+ /* On code like "if (foo) foo();" don't report overflow
+ on a branch to zero when foo is undefined. */
+ if (!warned
+ && (reloc_dest == DEST_STUB
+ || !(h != NULL
+ && (h->elf.root.type == bfd_link_hash_undefweak
+ || h->elf.root.type == bfd_link_hash_undefined)
+ && is_branch_reloc (r_type))))
{
- /* Assume this is a call protected by other code that
- detects the symbol is undefined. If this is the case,
- we can safely ignore the overflow. If not, the
- program is hosed anyway, and a little warning isn't
- going to help. */
-
- continue;
+ if (!((*info->callbacks->reloc_overflow)
+ (info, &h->elf.root, sym_name,
+ reloc_name, orig_rel.r_addend,
+ input_bfd, input_section, rel->r_offset)))
+ return FALSE;
}
-
- if (!((*info->callbacks->reloc_overflow)
- (info, &h->elf.root, sym_name,
- reloc_name, orig_rel.r_addend,
- input_bfd, input_section, rel->r_offset)))
- return FALSE;
}
else
{
if (!info->relocatable)
value -= input_sec->output_section->vma;
- adjust = opd->adjust[value / 8];
+ adjust = opd->adjust[OPD_NDX (value)];
if (adjust == -1)
return 2;
NULL))
return FALSE;
-
if (htab->glink_eh_frame != NULL
- && htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
- && !_bfd_elf_write_section_eh_frame (output_bfd, info,
- htab->glink_eh_frame,
- htab->glink_eh_frame->contents))
- return FALSE;
+ && htab->glink_eh_frame->size != 0)
+ {
+ bfd_vma val;
+ bfd_byte *p;
+ asection *stub_sec;
+
+ p = htab->glink_eh_frame->contents + sizeof (glink_eh_frame_cie);
+ for (stub_sec = htab->params->stub_bfd->sections;
+ stub_sec != NULL;
+ stub_sec = stub_sec->next)
+ if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ {
+ /* FDE length. */
+ p += 4;
+ /* CIE pointer. */
+ p += 4;
+ /* Offset to stub section. */
+ val = (stub_sec->output_section->vma
+ + stub_sec->output_offset);
+ val -= (htab->glink_eh_frame->output_section->vma
+ + htab->glink_eh_frame->output_offset
+ + (p - htab->glink_eh_frame->contents));
+ if (val + 0x80000000 > 0xffffffff)
+ {
+ info->callbacks->einfo
+ (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ stub_sec->name);
+ return FALSE;
+ }
+ bfd_put_32 (dynobj, val, p);
+ p += 4;
+ /* stub section size. */
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Pad. */
+ p += 7;
+ }
+ if (htab->glink != NULL && htab->glink->size != 0)
+ {
+ /* FDE length. */
+ p += 4;
+ /* CIE pointer. */
+ p += 4;
+ /* Offset to .glink. */
+ val = (htab->glink->output_section->vma
+ + htab->glink->output_offset
+ + 8);
+ val -= (htab->glink_eh_frame->output_section->vma
+ + htab->glink_eh_frame->output_offset
+ + (p - htab->glink_eh_frame->contents));
+ if (val + 0x80000000 > 0xffffffff)
+ {
+ info->callbacks->einfo
+ (_("%P: %s offset too large for .eh_frame sdata4 encoding"),
+ htab->glink->name);
+ return FALSE;
+ }
+ bfd_put_32 (dynobj, val, p);
+ p += 4;
+ /* .glink size. */
+ p += 4;
+ /* Augmentation. */
+ p += 1;
+ /* Ops. */
+ p += 7;
+ }
+
+ if (htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
+ && !_bfd_elf_write_section_eh_frame (output_bfd, info,
+ htab->glink_eh_frame,
+ htab->glink_eh_frame->contents))
+ return FALSE;
+ }
/* We need to handle writing out multiple GOT sections ourselves,
since we didn't add them to DYNOBJ. We know dynobj is the first