Add ELFv2 .localentry support.

This defines the ELF symbol st_other field used to encode the number
of instructions between a function "global entry" and its "local entry",
and adds support related to the local entry offset.

include/elf/
	* 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.
bfd/
	* 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.
gas/
	* 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.
binutils/
	* readelf.c (get_ppc64_symbol_other): New function.
	(get_symbol_other): Use it for EM_PPC64.
This commit is contained in:
Alan Modra 2013-10-29 15:37:43 +10:30
parent ee67d69a3f
commit 6911b7dcb8
8 changed files with 268 additions and 9 deletions

View File

@ -1,3 +1,20 @@
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.

View File

@ -118,6 +118,7 @@ static bfd_vma opd_entry_value
#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. */
@ -3746,6 +3747,9 @@ struct ppc_stub_hash_entry {
/* 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 {
@ -4004,7 +4008,9 @@ stub_hash_newfunc (struct bfd_hash_entry *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;
@ -4750,7 +4756,7 @@ static bfd_boolean
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)
@ -4770,9 +4776,35 @@ ppc64_elf_add_symbol_hook (bfd *ibfd,
&& 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
@ -9796,7 +9828,8 @@ ppc_type_of_stub (asection *input_sec,
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;
@ -9865,7 +9898,7 @@ ppc_type_of_stub (asection *input_sec,
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;
@ -10233,9 +10266,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
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
@ -10338,6 +10373,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
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);
@ -10682,6 +10719,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
/* 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
@ -10710,8 +10748,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
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;
@ -11308,7 +11348,10 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
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;
@ -11761,6 +11804,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
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;
@ -11837,12 +11881,16 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
}
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;
@ -11879,7 +11927,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
/* 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)
{
@ -11979,6 +12028,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
}
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;
@ -13357,6 +13407,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
+ 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)

View File

@ -1,3 +1,8 @@
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.

View File

@ -9210,6 +9210,19 @@ get_ia64_symbol_other (unsigned int other)
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)
{
@ -9227,6 +9240,9 @@ 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;
}

View File

@ -1,3 +1,11 @@
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.

View File

@ -134,6 +134,7 @@ static void ppc_vbyte (int);
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
@ -266,6 +267,7 @@ const pseudo_typeS md_pseudo_table[] =
{ "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
@ -2226,6 +2228,68 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
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)
@ -6229,6 +6293,22 @@ ppc_force_relocation (fixS *fix)
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;
}
@ -6243,6 +6323,32 @@ ppc_force_relocation (fixS *fix)
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

View File

@ -1,3 +1,9 @@
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.

View File

@ -180,6 +180,53 @@ END_RELOC_NUMBERS (R_PPC64_max)
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