Copyright 1999, 2000, 2001, 2002, 2003 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_check_relocs ppc64_elf_check_relocs
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. */
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;
/* 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. */
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 (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;
- }
+ 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;
}
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)
{
p->count -= 1;
if (p->count == 0)
- *pp = p->next;
+ *pp = p->next;
break;
}
pp = &p->next;
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
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;
}
}
- 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;
}
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 (h, sym_hashes, r_symndx,
+ symtab_hdr, relocation, sec,
+ unresolved_reloc, info,
+ 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