From 6911b7dcb8ea17f8b811578dd4ac1ab7bb675e7b Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 29 Oct 2013 15:37:43 +1030 Subject: 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. --- gas/ChangeLog | 8 ++++ gas/config/tc-ppc.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) (limited to 'gas') diff --git a/gas/ChangeLog b/gas/ChangeLog index 25e87d1..88f5f91 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,11 @@ +2013-10-30 Ulrich Weigand + + * 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 * config/tc-ppc.c: Include elf/ppc64.h. diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index c249cec..822f5a2 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -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 -- cgit v1.1