Add ELFv2 .localentry support.
authorAlan Modra <amodra@gmail.com>
Tue, 29 Oct 2013 05:07:43 +0000 (15:37 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 30 Oct 2013 03:10:21 +0000 (13:40 +1030)
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.

bfd/ChangeLog
bfd/elf64-ppc.c
binutils/ChangeLog
binutils/readelf.c
gas/ChangeLog
gas/config/tc-ppc.c
include/elf/ChangeLog
include/elf/ppc64.h

index 2c4bb314ff93f2e208eccecfa031a93712ffcf67..d9551a1d8be4dc811ffbcf03856981a45371d28e 100644 (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.
index 0067295922d244a8947cf0a12483ef9584372503..1d03f84901187f363b2c3289f0223f5b2d25b00e 100644 (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)
index 8457dc7ac1b2812aaf968a4f228ce983c75b666d..9774deadb3a2413a5cd5db337b27ec66e70af4f7 100644 (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.
index ab2943def142548e644b6af019e2f19035e3500e..c99b46e536ae77186063e2ebc7741dbb07cd35ec 100644 (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;
     }
index 25e87d166ea1686d69537fb4158cf09bd3e43277..88f5f91ef6d1a201abe32a778b1eafa4d4039017 100644 (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.
index c249cec9ab9bc7ed91cef450ac1a9b9aed9cedee..822f5a2c764ff09d79c9ac4014dfa2cd68195684 100644 (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
index 2999bc8a49f2521932a9b4693d44d233ed7697a0..7e98ddbf3c25cedc64c90787019ec329c8fab1ed 100644 (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.
index 112164935af528a015c9243b14d668bb3900246f..d3cfdfb82cee32d0943cb91ce8597be1ca59e6e6 100644 (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