/* PowerPC64-specific support for 64-bit ELF.
- Copyright 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004
+ 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 <amodra@bigpond.net.au>
This file is part of BFD, the Binary File Descriptor library.
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
-#include "elf/ppc.h"
#include "elf/ppc64.h"
#include "elf64-ppc.h"
#define elf_backend_plt_not_loaded 1
#define elf_backend_got_symbol_offset 0
#define elf_backend_got_header_size 8
-#define elf_backend_plt_header_size PLT_INITIAL_ENTRY_SIZE
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_rela_normal 1
#define bfd_elf64_bfd_link_hash_table_free ppc64_elf_link_hash_table_free
#define elf_backend_object_p ppc64_elf_object_p
+#define elf_backend_grok_prstatus ppc64_elf_grok_prstatus
+#define elf_backend_grok_psinfo ppc64_elf_grok_psinfo
#define elf_backend_create_dynamic_sections ppc64_elf_create_dynamic_sections
#define elf_backend_copy_indirect_symbol ppc64_elf_copy_indirect_symbol
+#define elf_backend_add_symbol_hook ppc64_elf_add_symbol_hook
#define elf_backend_check_relocs ppc64_elf_check_relocs
#define elf_backend_gc_mark_hook ppc64_elf_gc_mark_hook
#define elf_backend_gc_sweep_hook ppc64_elf_gc_sweep_hook
#define elf_backend_finish_dynamic_symbol ppc64_elf_finish_dynamic_symbol
#define elf_backend_reloc_type_class ppc64_elf_reloc_type_class
#define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections
+#define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook
#define elf_backend_special_sections ppc64_elf_special_sections
/* The name of the dynamic interpreter. This is put in the .interp
return TRUE;
}
+/* Support for core dump NOTE sections. */
+
+static bfd_boolean
+ppc64_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+ size_t offset, raw_size;
+
+ if (note->descsz != 504)
+ return FALSE;
+
+ /* pr_cursig */
+ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+
+ /* pr_pid */
+ elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 32);
+
+ /* pr_reg */
+ offset = 112;
+ raw_size = 384;
+
+ /* Make a ".reg/999" section. */
+ return _bfd_elfcore_make_pseudosection (abfd, ".reg",
+ raw_size, note->descpos + offset);
+}
+
+static bfd_boolean
+ppc64_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ if (note->descsz != 136)
+ return FALSE;
+
+ elf_tdata (abfd)->core_program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 40, 16);
+ elf_tdata (abfd)->core_command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 56, 80);
+
+ return TRUE;
+}
+
/* Merge backend specific data from an object file to the output
object file when linking. */
static struct bfd_elf_special_section const ppc64_elf_special_sections[]=
{
- { ".sdata", 0, NULL, 0,
- SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
- { ".sbss", 0, NULL, 0,
- SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
- { ".plt", 0, NULL, 0,
- SHT_NOBITS, 0 },
- { ".toc", 0, NULL, 0,
- SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
- { ".toc1", 0, NULL, 0,
- SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
- { ".tocbss", 0, NULL, 0,
- SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
- { NULL, 0, NULL, 0,
- 0, 0 }
+ { ".sdata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { ".sbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+ { ".plt", 4, 0, SHT_NOBITS, 0 },
+ { ".toc", 4, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { ".toc1", 5, 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { ".tocbss", 7, 0, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+ { NULL, 0, 0, 0, 0 }
};
struct _ppc64_elf_section_data
pointers must reference the descriptor. Thus, a function pointer
initialized to the address of a function in a shared library will
either require a copy reloc, or a dynamic reloc. Using a copy reloc
- redefines the function desctriptor symbol to point to the copy. This
+ redefines the function descriptor symbol to point to the copy. This
presents a problem as a plt entry for that function is also
initialized from the function descriptor symbol and the copy reloc
may not be initialized first. */
unsigned int is_func_descriptor:1;
unsigned int is_entry:1;
+ /* Whether global opd sym has been adjusted or not. */
+ unsigned int adjust_done:1;
+
/* Contexts in which symbol is used in the GOT (or TOC).
TLS_GD .. TLS_EXPLICIT bits are or'd into the mask as the
corresponding relocs are encountered during check_relocs.
asection *brlt;
asection *relbrlt;
- /* Short-cut to first output tls section. */
- asection *tls_sec;
-
/* Shortcut to .__tls_get_addr. */
struct elf_link_hash_entry *tls_get_addr;
eh->is_func = 0;
eh->is_func_descriptor = 0;
eh->is_entry = 0;
+ eh->adjust_done = 0;
eh->tls_mask = 0;
}
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
-ppc64_elf_copy_indirect_symbol (struct elf_backend_data *bed ATTRIBUTE_UNUSED,
- struct elf_link_hash_entry *dir,
- struct elf_link_hash_entry *ind)
+ppc64_elf_copy_indirect_symbol
+ (const struct elf_backend_data *bed ATTRIBUTE_UNUSED,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
{
struct ppc_link_hash_entry *edir, *eind;
flagword mask;
edir->tls_mask |= eind->tls_mask;
mask = (ELF_LINK_HASH_REF_DYNAMIC | ELF_LINK_HASH_REF_REGULAR
- | ELF_LINK_HASH_REF_REGULAR_NONWEAK | ELF_LINK_NON_GOT_REF);
+ | ELF_LINK_HASH_REF_REGULAR_NONWEAK | ELF_LINK_NON_GOT_REF
+ | ELF_LINK_HASH_NEEDS_PLT);
/* If called to transfer flags for a weakdef during processing
of elf_adjust_dynamic_symbol, don't copy ELF_LINK_NON_GOT_REF.
We clear it ourselves for ELIMINATE_COPY_RELOCS. */
return TRUE;
}
+/* Hack symbols defined in .opd sections to be function type. */
+
+static bfd_boolean
+ppc64_elf_add_symbol_hook (bfd *ibfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ Elf_Internal_Sym *isym,
+ const char **name ATTRIBUTE_UNUSED,
+ flagword *flags ATTRIBUTE_UNUSED,
+ asection **sec,
+ bfd_vma *value ATTRIBUTE_UNUSED)
+{
+ if (strcmp (bfd_get_section_name (ibfd, *sec), ".opd") == 0)
+ isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+ return TRUE;
+}
+
static bfd_boolean
update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
unsigned long r_symndx, bfd_vma r_addend, int tls_type)
if (info->relocatable)
return TRUE;
+ /* Don't do anything special with non-loaded, non-alloced sections.
+ In particular, any relocs in such sections should not affect GOT
+ and PLT reference counting (ie. we don't allow them to create GOT
+ or PLT entries), there's no possibility or desire to optimize TLS
+ relocs, and there's not much point in propagating relocs to shared
+ libs that the dynamic linker won't relocate. */
+ if ((sec->flags & SEC_ALLOC) == 0)
+ return TRUE;
+
htab = ppc_hash_table (info);
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_PPC64_GNU_VTINHERIT:
- if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
return FALSE;
break;
/* This relocation describes which C++ vtable entries are actually
used. Record for later use during GC. */
case R_PPC64_GNU_VTENTRY:
- if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+ if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
return FALSE;
break;
if (NO_OPD_RELOCS && opd_sym_map != NULL)
break;
- /* Don't propagate relocs that the dynamic linker won't relocate. */
- if ((sec->flags & SEC_ALLOC) == 0)
- 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
struct got_entry **local_got_ents;
const Elf_Internal_Rela *rel, *relend;
+ if ((sec->flags & SEC_ALLOC) == 0)
+ return TRUE;
+
elf_section_data (sec)->local_dynrel = NULL;
htab = ppc_hash_table (info);
&& ELF_ST_VISIBILITY (fdh->elf.other) == STV_DEFAULT)))
{
if (fdh->elf.dynindx == -1)
- if (! bfd_elf64_link_record_dynamic_symbol (info, &fdh->elf))
+ if (! bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
return FALSE;
fdh->elf.elf_link_hash_flags
|= (fh->elf.elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
type suitable for optimization, and 1 otherwise. */
static int
-get_tls_mask (char **tls_maskp, Elf_Internal_Sym **locsymsp,
+get_tls_mask (char **tls_maskp, unsigned long *toc_symndx,
+ Elf_Internal_Sym **locsymsp,
const Elf_Internal_Rela *rel, bfd *ibfd)
{
unsigned long r_symndx;
- unsigned int next_r;
+ int next_r;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
asection *sec;
next_r = ppc64_elf_section_data (sec)->t_symndx[off / 8 + 1];
if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
return 0;
- if (h == NULL
- || h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
+ if (toc_symndx != NULL)
+ *toc_symndx = r_symndx;
+ if ((h == NULL
+ || ((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
+ && (next_r == -1 || next_r == -2))
+ return 1 - next_r;
+ return 1;
+}
+
+/* Adjust all global syms defined in opd sections. In gcc generated
+ code these will already have been done, but I suppose we have to
+ cater for all sorts of hand written assembly. */
+
+static bfd_boolean
+adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
+{
+ struct ppc_link_hash_entry *eh;
+ asection *sym_sec;
+ long *opd_adjust;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ if (h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ return TRUE;
+
+ eh = (struct ppc_link_hash_entry *) h;
+ if (eh->adjust_done)
+ return TRUE;
+
+ sym_sec = eh->elf.root.u.def.section;
+ if (sym_sec != NULL
+ && elf_section_data (sym_sec) != NULL
+ && (opd_adjust = ppc64_elf_section_data (sym_sec)->opd.adjust) != NULL)
{
- if (next_r == (unsigned) -1)
- return 2;
- if (next_r == (unsigned) -2
- && (h == NULL
- || !(h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)))
- return 3;
+ eh->elf.root.u.def.value += opd_adjust[eh->elf.root.u.def.value / 24];
+ eh->adjust_done = 1;
}
- return 1;
+ return TRUE;
}
+/* Remove unused Official Procedure Descriptor entries. Currently we
+ only remove those associated with functions in discarded link-once
+ sections, or weakly defined functions that have been overridden. It
+ would be possible to remove many more entries for statically linked
+ applications. */
+
bfd_boolean
ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
{
bfd *ibfd;
+ bfd_boolean some_edited = FALSE;
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
{
r_symndx = ELF64_R_SYM (rel->r_info);
if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
- r_symndx, ibfd))
+ r_symndx, ibfd))
goto error_ret;
if (rel->r_offset == offset)
if (h != NULL)
{
- /* Redefine the function descriptor symbol
- to this location in the opd section.
- We've checked above that opd relocs are
- ordered. */
+ /* 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 - sec->contents;
+ fdh->adjust_done = 1;
}
- else
- {
- /* 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[rel->r_offset / 24] = wptr - rptr;
- }
+
+ /* 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[rel->r_offset / 24] = wptr - rptr;
if (wptr != rptr)
memcpy (wptr, rptr, 24);
{
p->count -= 1;
if (p->count == 0)
- *pp = p->next;
+ *pp = p->next;
break;
}
pp = &p->next;
elf_section_data (sec)->rel_hdr.sh_size
= sec->reloc_count * elf_section_data (sec)->rel_hdr.sh_entsize;
BFD_ASSERT (elf_section_data (sec)->rel_hdr2 == NULL);
+ some_edited = TRUE;
}
else if (elf_section_data (sec)->relocs != relstart)
free (relstart);
}
}
+ if (some_edited)
+ elf_link_hash_traverse (elf_hash_table (info), adjust_opd_syms, NULL);
+
return TRUE;
}
-/* Set htab->tls_sec. */
+/* Set htab->tls_get_addr and call the generic ELF tls_setup function. */
-bfd_boolean
+asection *
ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
{
- asection *tls;
struct ppc_link_hash_table *htab;
- for (tls = obfd->sections; tls != NULL; tls = tls->next)
- if ((tls->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
- == (SEC_THREAD_LOCAL | SEC_LOAD))
- break;
-
htab = ppc_hash_table (info);
- htab->tls_sec = tls;
-
if (htab->tls_get_addr != NULL)
{
struct elf_link_hash_entry *h = htab->tls_get_addr;
htab->tls_get_addr = h;
}
- return tls != NULL;
+ return _bfd_elf_tls_setup (obfd, info);
}
/* Run through all the TLS relocs looking for optimization
value = h->root.u.def.value;
}
else
- value = sym->st_value;
+ {
+ value = sym->st_value;
+
+ if (elf_section_data (sym_sec) != NULL)
+ {
+ long *adjust;
+ adjust = ppc64_elf_section_data (sym_sec)->opd.adjust;
+ if (adjust != NULL)
+ value += adjust[value / 24];
+ }
+ }
ok_tprel = FALSE;
is_local = FALSE;
is_local = TRUE;
value += sym_sec->output_offset;
value += sym_sec->output_section->vma;
- value -= htab->tls_sec->vma;
+ value -= htab->elf.tls_sec->vma;
ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31)
< (bfd_vma) 1 << 32);
}
char *toc_tls;
int retval;
- retval = get_tls_mask (&toc_tls, &locsyms,
+ retval = get_tls_mask (&toc_tls, NULL, &locsyms,
rel - 1, ibfd);
if (retval == 0)
goto err_free_rel;
return TRUE;
}
-/* This is the condition under which ppc64_elf_finish_dynamic_symbol
- will be called from elflink.h. If elflink.h doesn't call our
- finish_dynamic_symbol routine, we'll need to do something about
- initializing any .plt and .got entries in ppc64_elf_relocate_section. */
-#define WILL_CALL_FINISH_DYNAMIC_SYMBOL(DYN, SHARED, H) \
- ((DYN) \
- && ((SHARED) \
- || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0) \
- && ((H)->dynindx != -1 \
- || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0))
-
/* Allocate space in .plt, .got and associated reloc sections for
dynamic relocs. */
if (h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
{
- if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
if (h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
{
- if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
}
/* .plt is in the bss section. We don't initialise it. */
- if ((s->flags & SEC_LOAD) == 0)
+ if (s == htab->plt)
continue;
/* Allocate memory for the section contents. We use bfd_zalloc
the .dynamic section. The DT_DEBUG entry is filled in by the
dynamic linker and used by the debugger. */
#define add_dynamic_entry(TAG, VAL) \
- bfd_elf64_add_dynamic_entry (info, (TAG), (VAL))
+ _bfd_elf_add_dynamic_entry (info, TAG, VAL)
if (info->executable)
{
}
}
- if (h->elf.root.type != bfd_link_hash_defined
- && h->elf.root.type != bfd_link_hash_defweak)
+ if (!(h->elf.root.type == bfd_link_hash_defined
+ || h->elf.root.type == bfd_link_hash_defweak)
+ || h->elf.root.u.def.section->output_section == NULL)
return ppc_stub_none;
}
ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
struct ppc_stub_hash_entry *stub_entry;
+ struct bfd_link_info *info;
struct ppc_link_hash_table *htab;
bfd_vma off;
int size;
/* Massage our args to the form they really have. */
stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
- htab = in_arg;
+ info = in_arg;
+
+ htab = ppc_hash_table (info);
if (stub_entry->stub_type == ppc_stub_plt_call)
{
br_entry->iter = htab->stub_iteration;
br_entry->offset = htab->brlt->_raw_size;
htab->brlt->_raw_size += 8;
+
+ if (info->shared)
+ htab->relbrlt->_raw_size += sizeof (Elf64_External_Rela);
}
stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
if ((isec->flags & SEC_LINKER_CREATED) != 0)
return 0;
+ if (isec->_raw_size == 0)
+ return 0;
+
/* Hack for linux kernel. .fixup contains branches, but only back to
the function that hit an exception. */
branch_ok = strcmp (isec->name, ".fixup") == 0;
/* If a code section has a function that uses the TOC then we need
to use the right TOC (obviously). Also, make sure that .opd gets
- the correct TOC value. */
+ the correct TOC value for R_PPC64_TOC relocs that don't have or
+ can't find their function symbol (shouldn't ever happen now). */
if (isec->has_gp_reloc || (isec->flags & SEC_CODE) == 0)
{
if (elf_gp (isec->owner) != 0)
/* Get tls info. */
char *tls_mask;
- if (!get_tls_mask (&tls_mask, &local_syms,
+ if (!get_tls_mask (&tls_mask, NULL, &local_syms,
irela - 1, input_bfd))
goto error_ret_free_internal;
if (*tls_mask != 0)
}
htab->brlt->_raw_size = 0;
htab->brlt->_cooked_size = 0;
+ if (info->shared)
+ {
+ htab->relbrlt->_raw_size = 0;
+ htab->relbrlt->_cooked_size = 0;
+ }
- bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, htab);
+ bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
/* Ask the linker to do its stuff. */
(*htab->layout_sections_again) ();
if (htab->brlt->contents == NULL)
return FALSE;
}
+ if (info->shared && htab->relbrlt->_raw_size != 0)
+ {
+ htab->relbrlt->contents = bfd_zalloc (htab->relbrlt->owner,
+ htab->relbrlt->_raw_size);
+ if (htab->relbrlt->contents == NULL)
+ return FALSE;
+ }
/* Build the stubs as directed by the stub hash table. */
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
struct elf_link_hash_entry *h;
struct elf_link_hash_entry *fdh;
const char *sym_name;
- unsigned long r_symndx;
+ unsigned long r_symndx, toc_symndx;
char tls_mask, tls_gd, tls_type;
+ char sym_type;
bfd_vma relocation;
bfd_boolean unresolved_reloc;
bfd_boolean warned;
r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info);
+
+ /* For old style R_PPC64_TOC relocs with a zero symbol, use the
+ symbol of the previous ADDR64 reloc. The symbol gives us the
+ proper TOC base to use. */
+ if (rel->r_info == ELF64_R_INFO (0, R_PPC64_TOC)
+ && rel != relocs
+ && ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_ADDR64
+ && is_opd)
+ r_symndx = ELF64_R_SYM (rel[-1].r_info);
+
sym = NULL;
sec = NULL;
h = NULL;
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
sym_name = bfd_elf_local_sym_name (input_bfd, sym);
- relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
+ sym_type = ELF64_ST_TYPE (sym->st_info);
+ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
if (elf_section_data (sec) != NULL)
{
long *opd_sym_adjust;
opd_sym_adjust = ppc64_elf_section_data (sec)->opd.adjust;
- if (opd_sym_adjust != NULL && sym->st_value % 24 == 0)
+ if (opd_sym_adjust != NULL)
relocation += opd_sym_adjust[sym->st_value / 24];
}
}
else
{
- /* It's a global symbol. */
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+ r_symndx, symtab_hdr, sym_hashes,
+ h, sec, relocation,
+ unresolved_reloc, warned);
sym_name = h->root.root.string;
- relocation = 0;
- if (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
- {
- sec = h->root.u.def.section;
- if (sec->output_section == NULL)
- /* Set a flag that will be cleared later if we find a
- relocation value for this symbol. output_section
- is typically NULL for symbols satisfied by a shared
- library. */
- unresolved_reloc = TRUE;
- else
- relocation = (h->root.u.def.value
- + sec->output_section->vma
- + sec->output_offset);
- }
- else if (h->root.type == bfd_link_hash_undefweak)
- ;
- else if (!info->executable
- && !info->no_undefined
- && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
- ;
- else
- {
- if (! ((*info->callbacks->undefined_symbol)
- (info, h->root.root.string, input_bfd, input_section,
- rel->r_offset, (!info->shared
- || info->no_undefined
- || ELF_ST_VISIBILITY (h->other)))))
- return FALSE;
- warned = TRUE;
- }
+ sym_type = h->type;
}
/* TLS optimizations. Replace instruction sequences and relocs
for the final instruction stream. */
tls_mask = 0;
tls_gd = 0;
+ toc_symndx = 0;
if (IS_PPC64_TLS_RELOC (r_type))
{
if (h != NULL)
lgot_masks = (char *) (local_got_ents + symtab_hdr->sh_info);
tls_mask = lgot_masks[r_symndx];
}
+ if (tls_mask == 0 && r_type == R_PPC64_TLS)
+ {
+ /* Check for toc tls entries. */
+ char *toc_tls;
+
+ if (!get_tls_mask (&toc_tls, &toc_symndx, &local_syms,
+ rel, input_bfd))
+ return FALSE;
+
+ if (toc_tls)
+ tls_mask = *toc_tls;
+ }
+ }
+
+ /* Check that tls relocs are used with tls syms, and non-tls
+ relocs are used with non-tls syms. */
+ if (r_symndx != 0
+ && r_type != R_PPC64_NONE
+ && (h == NULL
+ || h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && IS_PPC64_TLS_RELOC (r_type) != (sym_type == STT_TLS))
+ {
+ if (r_type == R_PPC64_TLS && tls_mask != 0)
+ /* R_PPC64_TLS is OK against a symbol in the TOC. */
+ ;
+ else
+ (*_bfd_error_handler)
+ (sym_type == STT_TLS
+ ? _("%s(%s+0x%lx): %s used with TLS symbol %s")
+ : _("%s(%s+0x%lx): %s used with non-TLS symbol %s"),
+ bfd_archive_filename (input_bfd),
+ input_section->name,
+ (long) rel->r_offset,
+ ppc64_elf_howto_table[r_type]->name,
+ sym_name);
}
/* Ensure reloc mapping code below stays sane. */
|| (R_PPC64_GOT_TLSLD16_HI & 3) != (R_PPC64_GOT_TPREL16_HI & 3)
|| (R_PPC64_GOT_TLSLD16_HA & 3) != (R_PPC64_GOT_TPREL16_HA & 3))
abort ();
+
switch (r_type)
{
default:
char *toc_tls;
int retval;
- retval = get_tls_mask (&toc_tls, &local_syms, rel, input_bfd);
+ retval = get_tls_mask (&toc_tls, &toc_symndx, &local_syms,
+ rel, input_bfd);
if (retval == 0)
return FALSE;
insn |= 0x3c0d0000; /* addis 0,13,0 */
bfd_put_32 (output_bfd, insn, contents + rel->r_offset - 2);
r_type = R_PPC64_TPREL16_HA;
- rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+ if (toc_symndx != 0)
+ {
+ rel->r_info = ELF64_R_INFO (toc_symndx, r_type);
+ /* We changed the symbol. Start over in order to
+ get h, sym, sec etc. right. */
+ rel--;
+ continue;
+ }
+ else
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
}
break;
case R_PPC64_TLS:
- if (tls_mask == 0)
- {
- /* Check for toc tls entries. */
- char *toc_tls;
-
- if (!get_tls_mask (&toc_tls, &local_syms, rel, input_bfd))
- return FALSE;
-
- if (toc_tls)
- tls_mask = *toc_tls;
- }
if (tls_mask != 0
&& (tls_mask & TLS_TPREL) == 0)
{
abort ();
insn |= rtra;
bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
- r_type = R_PPC64_TPREL16_LO;
- rel->r_info = ELF64_R_INFO (r_symndx, r_type);
/* Was PPC64_TLS which sits on insn boundary, now
PPC64_TPREL16_LO which is at insn+2. */
rel->r_offset += 2;
+ r_type = R_PPC64_TPREL16_LO;
+ if (toc_symndx != 0)
+ {
+ rel->r_info = ELF64_R_INFO (toc_symndx, r_type);
+ /* We changed the symbol. Start over in order to
+ get h, sym, sec etc. right. */
+ rel--;
+ continue;
+ }
+ else
+ rel->r_info = ELF64_R_INFO (r_symndx, r_type);
}
break;
{
/* Was an LD reloc. */
r_symndx = 0;
- rel->r_addend = htab->tls_sec->vma + DTP_OFFSET;
- rel[1].r_addend = htab->tls_sec->vma + DTP_OFFSET;
+ rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
+ rel[1].r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
}
+ else if (toc_symndx != 0)
+ r_symndx = toc_symndx;
r_type = R_PPC64_TPREL16_HA;
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
rel[1].r_info = ELF64_R_INFO (r_symndx,
bfd_put_32 (output_bfd, insn1, contents + rel->r_offset - 2);
bfd_put_32 (output_bfd, insn2, contents + offset);
bfd_put_32 (output_bfd, insn3, contents + offset + 4);
- if (tls_gd == 0)
+ if (tls_gd == 0 || toc_symndx != 0)
{
- /* We changed the symbol on an LD reloc. Start over
- in order to get h, sym, sec etc. right. */
+ /* We changed the symbol. Start over in order
+ to get h, sym, sec etc. right. */
rel--;
continue;
}
if ((insn & 1) == 0)
can_plt_call = 1;
}
+ else if (h != NULL
+ && strcmp (h->root.root.string,
+ ".__libc_start_main") == 0)
+ {
+ /* Allow crt1 branch to go via a toc adjusting stub. */
+ can_plt_call = 1;
+ }
else
{
if (strcmp (input_section->output_section->name,
{
outrel.r_addend += relocation;
if (tls_type & (TLS_GD | TLS_DTPREL | TLS_TPREL))
- outrel.r_addend -= htab->tls_sec->vma;
+ outrel.r_addend -= htab->elf.tls_sec->vma;
}
loc = relgot->contents;
loc += (relgot->reloc_count++
relocation = 1;
else if (tls_type != 0)
{
- relocation -= htab->tls_sec->vma + DTP_OFFSET;
+ relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
if (tls_type == (TLS_TLS | TLS_TPREL))
relocation += DTP_OFFSET - TP_OFFSET;
relocation = TOCstart;
if (r_symndx == 0)
relocation += htab->stub_group[input_section->id].toc_off;
- else if (sec != NULL && !unresolved_reloc)
+ else if (unresolved_reloc)
+ ;
+ else if (sec != NULL && sec->id <= htab->top_id)
relocation += htab->stub_group[sec->id].toc_off;
else
unresolved_reloc = TRUE;
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
- addend -= htab->tls_sec->vma + TP_OFFSET;
+ 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->tls_sec->vma + DTP_OFFSET;
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
break;
case R_PPC64_DTPMOD64:
goto dodyn;
case R_PPC64_TPREL64:
- addend -= htab->tls_sec->vma + TP_OFFSET;
+ addend -= htab->elf.tls_sec->vma + TP_OFFSET;
goto dodyn;
case R_PPC64_DTPREL64:
- addend -= htab->tls_sec->vma + DTP_OFFSET;
+ addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
/* Fall thru */
/* Relocations that may need to be propagated if this is a
return ret;
}
+/* Adjust the value of any local symbols in opd sections. */
+
+static bfd_boolean
+ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
+ const char *name ATTRIBUTE_UNUSED,
+ Elf_Internal_Sym *elfsym,
+ asection *input_sec,
+ struct elf_link_hash_entry *h)
+{
+ long *adjust;
+ bfd_vma value;
+
+ if (h != NULL
+ || input_sec == NULL
+ || ppc64_elf_section_data (input_sec) == NULL
+ || (adjust = ppc64_elf_section_data (input_sec)->opd.adjust) == NULL)
+ return TRUE;
+
+ value = elfsym->st_value - input_sec->output_offset;
+ if (!info->relocatable)
+ value -= input_sec->output_section->vma;
+
+ elfsym->st_value += adjust[value / 24];
+ return TRUE;
+}
+
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */