Add ELFv2 .localentry support.
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
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)
This page took 0.029052 seconds and 4 git commands to generate.