From: Alan Modra Date: Tue, 29 Oct 2013 05:07:43 +0000 (+1030) Subject: Add ELFv2 .localentry support. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6911b7dcb8ea17f8b811578dd4ac1ab7bb675e7b;p=binutils-gdb.git 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. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2c4bb314ff9..d9551a1d8be 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,20 @@ +2013-10-30 Alan Modra + Ulrich Weigand + + * 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 * elf64-ppc.c (abiversion, set_abiversion): New functions. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 0067295922d..1d03f849011 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -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) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 8457dc7ac1b..9774deadb3a 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,8 @@ +2013-10-30 Ulrich Weigand + + * readelf.c (get_ppc64_symbol_other): New function. + (get_symbol_other): Use it for EM_PPC64. + 2013-10-30 Alan Modra * readelf.c (get_machine_flags): Display ABI version for EM_PPC64. diff --git a/binutils/readelf.c b/binutils/readelf.c index ab2943def14..c99b46e536a 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -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, _(": %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; } diff --git a/gas/ChangeLog b/gas/ChangeLog index 25e87d166ea..88f5f91ef6d 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 c249cec9ab9..822f5a2c764 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 diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 2999bc8a49f..7e98ddbf3c2 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,9 @@ +2013-10-30 Alan Modra + + * 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 * ppc64.h (EF_PPC64_ABI): Define. diff --git a/include/elf/ppc64.h b/include/elf/ppc64.h index 112164935af..d3cfdfb82ce 100644 --- a/include/elf/ppc64.h +++ b/include/elf/ppc64.h @@ -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