+2013-10-30 Alan Modra <amodra@gmail.com>
+ Ulrich Weigand <uweigand@de.ibm.com>
+
+ * elf64-ppc.c (struct ppc_stub_hash_entry): Add "other".
+ (stub_hash_newfunc): Init new ppc_stub_hash_entry field, and one
+ we forgot, "plt_ent".
+ (ppc64_elf_add_symbol_hook): Check ELFv1 objects don't have
+ st_other bits only valid in ELFv2.
+ (ppc64_elf_merge_symbol_attribute): New function.
+ (ppc_type_of_stub): Add local_off param to test branch range.
+ (ppc_build_one_stub): Adjust destinations for ELFv2 locals.
+ (ppc_size_one_stub, toc_adjusting_stub_needed): Similarly.
+ (ppc64_elf_size_stubs): Pass local_off to ppc_type_of_stub.
+ Set "other" field.
+ (ppc64_elf_relocate_section): Adjust destination for ELFv2 local
+ calls.
+
2013-10-30 Alan Modra <amodra@gmail.com>
* elf64-ppc.c (abiversion, set_abiversion): New functions.
#define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook
#define elf_backend_special_sections ppc64_elf_special_sections
#define elf_backend_post_process_headers _bfd_elf_set_osabi
+#define elf_backend_merge_symbol_attribute ppc64_elf_merge_symbol_attribute
/* The name of the dynamic interpreter. This is put in the .interp
section. */
/* Where this stub is being called from, or, in the case of combined
stub sections, the first input section in the group. */
asection *id_sec;
+
+ /* Symbol st_other. */
+ unsigned char other;
};
struct ppc_branch_hash_entry {
eh->target_value = 0;
eh->target_section = NULL;
eh->h = NULL;
+ eh->plt_ent = NULL;
eh->id_sec = NULL;
+ eh->other = 0;
}
return entry;
ppc64_elf_add_symbol_hook (bfd *ibfd,
struct bfd_link_info *info,
Elf_Internal_Sym *isym,
- const char **name ATTRIBUTE_UNUSED,
+ const char **name,
flagword *flags ATTRIBUTE_UNUSED,
asection **sec,
bfd_vma *value ATTRIBUTE_UNUSED)
&& strcmp ((*sec)->name, ".opd") == 0)
isym->st_info = ELF_ST_INFO (ELF_ST_BIND (isym->st_info), STT_FUNC);
+ if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
+ {
+ if (abiversion (ibfd) == 0)
+ set_abiversion (ibfd, 2);
+ else if (abiversion (ibfd) == 1)
+ {
+ info->callbacks->einfo (_("%P: symbol '%s' has invalid st_other"
+ " for ABI version 1\n"), name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ }
+
return TRUE;
}
+/* Merge non-visibility st_other attributes: local entry point. */
+
+static void
+ppc64_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+ const Elf_Internal_Sym *isym,
+ bfd_boolean definition,
+ bfd_boolean dynamic)
+{
+ if (definition && !dynamic)
+ h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
+ | ELF_ST_VISIBILITY (h->other));
+}
+
/* This function makes an old ABI object reference to ".bar" cause the
inclusion of a new ABI object archive that defines "bar".
NAME is a symbol defined in an archive. Return a symbol in the hash
const Elf_Internal_Rela *rel,
struct ppc_link_hash_entry **hash,
struct plt_entry **plt_ent,
- bfd_vma destination)
+ bfd_vma destination,
+ unsigned long local_off)
{
struct ppc_link_hash_entry *h = *hash;
bfd_vma location;
if (r_type != R_PPC64_REL24)
max_branch_offset = 1 << 15;
- if (branch_offset + max_branch_offset >= 2 * max_branch_offset)
+ if (branch_offset + max_branch_offset >= 2 * max_branch_offset - local_off)
/* We need a stub. Figure out whether a long_branch or plt_branch
is needed later. */
return ppc_stub_long_branch;
case ppc_stub_long_branch:
case ppc_stub_long_branch_r2off:
/* Branches are relative. This is where we are going to. */
- off = dest = (stub_entry->target_value
- + stub_entry->target_section->output_offset
- + stub_entry->target_section->output_section->vma);
+ dest = (stub_entry->target_value
+ + stub_entry->target_section->output_offset
+ + stub_entry->target_section->output_section->vma);
+ dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+ off = dest;
/* And this is where we are coming from. */
off -= (stub_entry->stub_offset
dest = (stub_entry->target_value
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
+ if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
+ dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
bfd_put_64 (htab->brlt->owner, dest,
htab->brlt->contents + br_entry->offset);
/* ppc_stub_long_branch or ppc_stub_plt_branch, or their r2off
variants. */
bfd_vma r2off = 0;
+ bfd_vma local_off = 0;
off = (stub_entry->target_value
+ stub_entry->target_section->output_offset
off -= size - 4;
}
+ local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+
/* If the branch offset if too big, use a ppc_stub_plt_branch. */
- if (off + (1 << 25) >= (bfd_vma) (1 << 26))
+ if (off + (1 << 25) >= (bfd_vma) (1 << 26) - local_off)
{
struct ppc_branch_hash_entry *br_entry;
need a plt_branch stub. A plt_branch stub uses r2. */
else if (dest - (isec->output_offset
+ isec->output_section->vma
- + rel->r_offset) + (1 << 25) >= (2 << 25))
+ + rel->r_offset) + (1 << 25)
+ >= (2u << 25) - PPC64_LOCAL_ENTRY_OFFSET (h
+ ? h->other
+ : sym->st_other))
{
ret = 1;
break;
asection *sym_sec, *code_sec;
bfd_vma sym_value, code_value;
bfd_vma destination;
+ unsigned long local_off;
bfd_boolean ok_dest;
struct ppc_link_hash_entry *hash;
struct ppc_link_hash_entry *fdh;
}
destination = 0;
+ local_off = 0;
if (ok_dest)
{
sym_value += irela->r_addend;
destination = (sym_value
+ sym_sec->output_offset
+ sym_sec->output_section->vma);
+ local_off = PPC64_LOCAL_ENTRY_OFFSET (hash
+ ? hash->elf.other
+ : sym->st_other);
}
code_sec = sym_sec;
/* Determine what (if any) linker stub is needed. */
plt_ent = NULL;
stub_type = ppc_type_of_stub (section, irela, &hash,
- &plt_ent, destination);
+ &plt_ent, destination,
+ local_off);
if (stub_type != ppc_stub_plt_call)
{
}
stub_entry->h = hash;
stub_entry->plt_ent = plt_ent;
+ stub_entry->other = hash ? hash->elf.other : sym->st_other;
if (stub_entry->h != NULL)
htab->stub_globals += 1;
+ input_section->output_offset
+ input_section->output_section->vma);
+ relocation += PPC64_LOCAL_ENTRY_OFFSET (fdh
+ ? fdh->elf.other
+ : sym->st_other);
+
if (stub_entry != NULL
&& (stub_entry->stub_type == ppc_stub_long_branch
|| stub_entry->stub_type == ppc_stub_plt_branch)
+2013-10-30 Ulrich Weigand <uweigand@de.ibm.com>
+
+ * readelf.c (get_ppc64_symbol_other): New function.
+ (get_symbol_other): Use it for EM_PPC64.
+
2013-10-30 Alan Modra <amodra@gmail.com>
* readelf.c (get_machine_flags): Display ABI version for EM_PPC64.
return NULL;
}
+static const char *
+get_ppc64_symbol_other (unsigned int other)
+{
+ if (PPC64_LOCAL_ENTRY_OFFSET (other) != 0)
+ {
+ static char buf[32];
+ snprintf (buf, sizeof buf, _("<localentry>: %d"),
+ PPC64_LOCAL_ENTRY_OFFSET (other));
+ return buf;
+ }
+ return NULL;
+}
+
static const char *
get_symbol_other (unsigned int other)
{
case EM_IA_64:
result = get_ia64_symbol_other (other);
break;
+ case EM_PPC64:
+ result = get_ppc64_symbol_other (other);
+ break;
default:
break;
}
+2013-10-30 Ulrich Weigand <uweigand@de.ibm.com>
+
+ * config/tc-ppc.c (md_pseudo_table): Add .localentry.
+ (ppc_elf_localentry): New function.
+ (ppc_force_relocation): Force relocs on all branches to localenty
+ symbols.
+ (ppc_fix_adjustable): Don't reduce such symbols to section+offset.
+
2013-10-30 Alan Modra <amodra@gmail.com>
* config/tc-ppc.c: Include elf/ppc64.h.
static void ppc_elf_cons (int);
static void ppc_elf_rdata (int);
static void ppc_elf_lcomm (int);
+static void ppc_elf_localentry (int);
static void ppc_elf_abiversion (int);
#endif
{ "rdata", ppc_elf_rdata, 0 },
{ "rodata", ppc_elf_rdata, 0 },
{ "lcomm", ppc_elf_lcomm, 0 },
+ { "localentry", ppc_elf_localentry, 0 },
{ "abiversion", ppc_elf_abiversion, 0 },
#endif
demand_empty_rest_of_line ();
}
+/* Pseudo op to set symbol local entry point. */
+static void
+ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ char *p;
+ expressionS exp;
+ symbolS *sym;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_bad (_("expected comma after name `%s' in .localentry directive"),
+ name);
+ *p = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ expression (&exp);
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing expression in .localentry directive"));
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ }
+ *p = 0;
+ sym = symbol_find_or_make (name);
+ *p = c;
+
+ if (resolve_expression (&exp)
+ && exp.X_op == O_constant)
+ {
+ unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
+
+ if (exp.X_add_number != PPC64_LOCAL_ENTRY_OFFSET (encoded))
+ as_bad (_(".localentry expression for `%s' "
+ "is not a valid power of 2"), S_GET_NAME (sym));
+ else
+ {
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
+ elfsym->internal_elf_sym.st_other |= encoded;
+ if (ppc_abiversion == 0)
+ ppc_abiversion = 2;
+ }
+ }
+ else
+ as_bad (_(".localentry expression for `%s' "
+ "does not evaluate to a constant"), S_GET_NAME (sym));
+
+ demand_empty_rest_of_line ();
+}
+
/* Pseudo op to set ABI version. */
static void
ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
case BFD_RELOC_24_PLT_PCREL:
case BFD_RELOC_PPC64_TOC:
return 1;
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_BA26:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_BA16:
+ /* All branch fixups targeting a localentry symbol must
+ force a relocation. */
+ if (fix->fx_addsy)
+ {
+ asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+ elf_symbol_type *elfsym
+ = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+ return 1;
+ }
+ break;
default:
break;
}
int
ppc_fix_adjustable (fixS *fix)
{
+ switch (fix->fx_r_type)
+ {
+ /* All branch fixups targeting a localentry symbol must
+ continue using the symbol. */
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_BA26:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_BA16:
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_BA16_BRTAKEN:
+ case BFD_RELOC_PPC_BA16_BRNTAKEN:
+ if (fix->fx_addsy)
+ {
+ asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+ elf_symbol_type *elfsym
+ = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
&& fix->fx_r_type != BFD_RELOC_LO16_GOTOFF
&& fix->fx_r_type != BFD_RELOC_HI16_GOTOFF
+2013-10-30 Alan Modra <amodra@gmail.com>
+
+ * ppc64.h (STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK): Define.
+ (ppc64_decode_local_entry, ppc64_encode_local_entry): New functions.
+ (PPC64_LOCAL_ENTRY_OFFSET, PPC64_SET_LOCAL_ENTRY_OFFSET): Define.
+
2013-10-30 Alan Modra <amodra@gmail.com>
* ppc64.h (EF_PPC64_ABI): Define.
0 for unspecified or not using any features affected by the differences. */
#define EF_PPC64_ABI 3
+/* The ELFv2 ABI uses three bits in the symbol st_other field of a
+ function definition to specify the number of instructions between a
+ function's global entry point and local entry point.
+ The global entry point is used when it is necessary to set up the
+ toc pointer (r2) for the function. Callers must enter the global
+ entry point with r12 set to the global entry point address. On
+ return from the function, r2 may have a different value to that
+ which it had on entry.
+ The local entry point is used when r2 is known to already be valid
+ for the function. There is no requirement on r12 when using the
+ local entry point, and on return r2 will contain the same value as
+ at entry.
+ A value of zero in these bits means that the function has a single
+ entry point with no requirement on r12 or r2, and that on return r2
+ will contain the same value as at entry.
+ Values of one and seven are reserved. */
+#define STO_PPC64_LOCAL_BIT 5
+#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT)
+
+// 3 bit other field to bytes.
+static inline unsigned int
+ppc64_decode_local_entry(unsigned int other)
+{
+ return ((1 << other) >> 2) << 2;
+}
+
+// bytes to field value.
+static inline unsigned int
+ppc64_encode_local_entry(unsigned int val)
+{
+ return (val >= 4 * 4
+ ? (val >= 8 * 4
+ ? (val >= 16 * 4 ? 6 : 5)
+ : 4)
+ : (val >= 2 * 4
+ ? 3
+ : (val >= 1 * 4 ? 2 : 0)));
+}
+
+/* st_other to number of bytes. */
+#define PPC64_LOCAL_ENTRY_OFFSET(other) \
+ ppc64_decode_local_entry (((other) & STO_PPC64_LOCAL_MASK) \
+ >> STO_PPC64_LOCAL_BIT)
+/* number of bytes to st_other. */
+#define PPC64_SET_LOCAL_ENTRY_OFFSET(val) \
+ ppc64_encode_local_entry (val) << STO_PPC64_LOCAL_BIT
+
/* Specify the start of the .glink section. */
#define DT_PPC64_GLINK DT_LOPROC