bfd/
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 0c81e5e31c67df05ea6468e49141a85d8d16f251..23b54c4cf1b03274ac08fa0ca2df7388057c062c 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC64-specific support for 64-bit ELF.
-   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
@@ -25,6 +25,7 @@
    http://www.linuxbase.org/spec/ELF/ppc64/PPC-elf64abi.txt, and
    http://www.linuxbase.org/spec/ELF/ppc64/spec/book1.html  */
 
+#include <stdarg.h>
 #include "bfd.h"
 #include "sysdep.h"
 #include "bfdlink.h"
@@ -72,9 +73,11 @@ static bfd_vma opd_entry_value
 #define elf_backend_can_gc_sections 1
 #define elf_backend_can_refcount 1
 #define elf_backend_rela_normal 1
+#define elf_backend_default_execstack 0
 
 #define bfd_elf64_mkobject                   ppc64_elf_mkobject
 #define bfd_elf64_bfd_reloc_type_lookup              ppc64_elf_reloc_type_lookup
+#define bfd_elf64_bfd_reloc_name_lookup ppc64_elf_reloc_name_lookup
 #define bfd_elf64_bfd_merge_private_bfd_data  ppc64_elf_merge_private_bfd_data
 #define bfd_elf64_new_section_hook           ppc64_elf_new_section_hook
 #define bfd_elf64_bfd_link_hash_table_create  ppc64_elf_link_hash_table_create
@@ -84,10 +87,12 @@ static bfd_vma opd_entry_value
 #define elf_backend_object_p                 ppc64_elf_object_p
 #define elf_backend_grok_prstatus            ppc64_elf_grok_prstatus
 #define elf_backend_grok_psinfo                      ppc64_elf_grok_psinfo
+#define elf_backend_write_core_note          ppc64_elf_write_core_note
 #define elf_backend_create_dynamic_sections   ppc64_elf_create_dynamic_sections
 #define elf_backend_copy_indirect_symbol      ppc64_elf_copy_indirect_symbol
 #define elf_backend_add_symbol_hook          ppc64_elf_add_symbol_hook
 #define elf_backend_check_directives         ppc64_elf_check_directives
+#define elf_backend_as_needed_cleanup        ppc64_elf_as_needed_cleanup
 #define elf_backend_archive_symbol_lookup     ppc64_elf_archive_symbol_lookup
 #define elf_backend_check_relocs             ppc64_elf_check_relocs
 #define elf_backend_gc_mark_dynamic_ref       ppc64_elf_gc_mark_dynamic_ref
@@ -1194,7 +1199,7 @@ static reloc_howto_type ppc64_elf_howto_raw[] = {
 
   /* Like R_PPC64_PLTGOT16, but for instructions with a DS field.  */
   /* FIXME: R_PPC64_PLTGOT16_DS not implemented.  */
-    HOWTO (R_PPC64_PLTGOT16_DS,        /* type */
+  HOWTO (R_PPC64_PLTGOT16_DS,  /* type */
         0,                     /* rightshift */
         1,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -2107,6 +2112,22 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
   return ppc64_elf_howto_table[r];
 };
 
+static reloc_howto_type *
+ppc64_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                            const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
+       i++)
+    if (ppc64_elf_howto_raw[i].name != NULL
+       && strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
+      return &ppc64_elf_howto_raw[i];
+
+  return NULL;
+}
+
 /* Set the howto pointer for a PowerPC ELF reloc.  */
 
 static void
@@ -2483,6 +2504,53 @@ ppc64_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
   return TRUE;
 }
 
+static char *
+ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type,
+                          ...)
+{
+  switch (note_type)
+    {
+    default:
+      return NULL;
+
+    case NT_PRPSINFO:
+      {
+       char data[136];
+       va_list ap;
+
+       va_start (ap, note_type);
+       memset (data, 0, 40);
+       strncpy (data + 40, va_arg (ap, const char *), 16);
+       strncpy (data + 56, va_arg (ap, const char *), 80);
+       va_end (ap);
+       return elfcore_write_note (abfd, buf, bufsiz,
+                                  "CORE", note_type, data, sizeof (data));
+      }
+
+    case NT_PRSTATUS:
+      {
+       char data[504];
+       va_list ap;
+       long pid;
+       int cursig;
+       const void *greg;
+
+       va_start (ap, note_type);
+       memset (data, 0, 112);
+       pid = va_arg (ap, long);
+       bfd_put_32 (abfd, pid, data + 32);
+       cursig = va_arg (ap, int);
+       bfd_put_16 (abfd, cursig, data + 12);
+       greg = va_arg (ap, const void *);
+       memcpy (data + 112, greg, 384);
+       memset (data + 496, 0, 8);
+       va_end (ap);
+       return elfcore_write_note (abfd, buf, bufsiz,
+                                  "CORE", note_type, data, sizeof (data));
+      }
+    }
+}
+
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 
@@ -3768,45 +3836,22 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info)
     return FALSE;
 
   /* Create branch lookup table for plt_branch stubs.  */
-  if (info->shared)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD
-              | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-      htab->brlt
-       = bfd_make_section_anyway_with_flags (dynobj, ".data.rel.ro.brlt",
-                                             flags);
-    }
-  else
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
-              | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-      htab->brlt
-       = bfd_make_section_anyway_with_flags (dynobj, ".rodata.brlt", flags);
-    }
-
+  flags = (SEC_ALLOC | SEC_LOAD
+          | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  htab->brlt = bfd_make_section_anyway_with_flags (dynobj, ".branch_lt",
+                                                  flags);
   if (htab->brlt == NULL
       || ! bfd_set_section_alignment (dynobj, htab->brlt, 3))
     return FALSE;
 
-  if (info->shared)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
-              | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-      htab->relbrlt
-       = bfd_make_section_anyway_with_flags (dynobj, ".rela.data.rel.ro.brlt",
-                                             flags);
-    }
-  else if (info->emitrelocations)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
-              | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-      htab->relbrlt
-       = bfd_make_section_anyway_with_flags (dynobj, ".rela.rodata.brlt",
-                                             flags);
-    }
-  else
+  if (!info->shared)
     return TRUE;
 
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+          | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  htab->relbrlt = bfd_make_section_anyway_with_flags (dynobj,
+                                                     ".rela.branch_lt",
+                                                     flags);
   if (!htab->relbrlt
       || ! bfd_set_section_alignment (dynobj, htab->relbrlt, 3))
     return FALSE;
@@ -4247,6 +4292,17 @@ ppc64_elf_check_directives (bfd *ibfd, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Undo hash table changes when an --as-needed input file is determined
+   not to be needed.  */
+
+static bfd_boolean
+ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED,
+                            struct bfd_link_info *info)
+{
+  ppc_hash_table (info)->dot_syms = NULL;
+  return TRUE;
+}
+
 static bfd_boolean
 update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr,
                       unsigned long r_symndx, bfd_vma r_addend, int tls_type)
@@ -8333,6 +8389,33 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          rl += htab->relbrlt->reloc_count++ * sizeof (Elf64_External_Rela);
          bfd_elf64_swap_reloca_out (htab->relbrlt->owner, &rela, rl);
        }
+      else if (info->emitrelocations)
+       {
+         Elf_Internal_Rela *relocs, *r;
+         struct bfd_elf_section_data *elfsec_data;
+
+         elfsec_data = elf_section_data (htab->brlt);
+         relocs = elfsec_data->relocs;
+         if (relocs == NULL)
+           {
+             bfd_size_type relsize;
+             relsize = htab->brlt->reloc_count * sizeof (*relocs);
+             relocs = bfd_alloc (htab->brlt->owner, relsize);
+             if (relocs == NULL)
+               return FALSE;
+             elfsec_data->relocs = relocs;
+             elfsec_data->rel_hdr.sh_size = relsize;
+             elfsec_data->rel_hdr.sh_entsize = 24;
+             htab->brlt->reloc_count = 0;
+           }
+         r = relocs + htab->brlt->reloc_count;
+         htab->brlt->reloc_count += 1;
+         r->r_offset = (br_entry->offset
+                        + htab->brlt->output_offset
+                        + htab->brlt->output_section->vma);
+         r->r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+         r->r_addend = off;
+       }
 
       off = (br_entry->offset
             + htab->brlt->output_offset
@@ -8562,6 +8645,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
              if (htab->relbrlt != NULL)
                htab->relbrlt->size += sizeof (Elf64_External_Rela);
+             else if (info->emitrelocations)
+               {
+                 htab->brlt->reloc_count += 1;
+                 htab->brlt->flags |= SEC_RELOC;
+               }
            }
 
          stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
@@ -8569,11 +8657,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          if (stub_entry->stub_type != ppc_stub_plt_branch)
            size = 28;
        }
-
-      if (info->emitrelocations
-         && (stub_entry->stub_type == ppc_stub_long_branch
-             || stub_entry->stub_type == ppc_stub_long_branch_r2off))
-       stub_entry->stub_sec->reloc_count += 1;
+      else if (info->emitrelocations)
+       {
+         stub_entry->stub_sec->reloc_count += 1;
+         stub_entry->stub_sec->flags |= SEC_RELOC;
+       }
     }
 
   stub_entry->stub_sec->size += size;
@@ -9365,9 +9453,12 @@ ppc64_elf_size_stubs (bfd *output_bfd,
            stub_sec->rawsize = stub_sec->size;
            stub_sec->size = 0;
            stub_sec->reloc_count = 0;
+           stub_sec->flags &= ~SEC_RELOC;
          }
 
       htab->brlt->size = 0;
+      htab->brlt->reloc_count = 0;
+      htab->brlt->flags &= ~SEC_RELOC;
       if (htab->relbrlt != NULL)
        htab->relbrlt->size = 0;
 
@@ -9731,6 +9822,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   bfd_boolean is_opd;
   /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
   bfd_boolean is_power4 = FALSE;
+  bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
 
   /* Initialize howto table if needed.  */
   if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
@@ -9821,13 +9913,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    relocation += adjust;
                }
            }
-         if (info->relocatable)
-           continue;
        }
       else
        {
-         if (info->relocatable)
-           continue;
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h_elf, sec, relocation,
@@ -9837,6 +9925,21 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
       h = (struct ppc_link_hash_entry *) h_elf;
 
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce sections,
+            or sections discarded by a linker script, we just want the
+            section contents zeroed.  Avoid any special processing.  */
+         _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd,
+                              contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
+      if (info->relocatable)
+       continue;
+
       /* TLS optimizations.  Replace instruction sequences and relocs
         based on information we collected in tls_optimize.  We edit
         RELOCS so that --emit-relocs will output something sensible
@@ -9960,10 +10063,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              && (tls_mask & TLS_TPREL) == 0)
            {
            toctprel:
-             insn = bfd_get_32 (output_bfd, contents + rel->r_offset - 2);
+             insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
              insn &= 31 << 21;
              insn |= 0x3c0d0000;       /* addis 0,13,0 */
-             bfd_put_32 (output_bfd, insn, contents + rel->r_offset - 2);
+             bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
              r_type = R_PPC64_TPREL16_HA;
              if (toc_symndx != 0)
                {
@@ -10015,8 +10118,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              insn |= rtra;
              bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
              /* Was PPC64_TLS which sits on insn boundary, now
-                PPC64_TPREL16_LO which is at insn+2.  */
-             rel->r_offset += 2;
+                PPC64_TPREL16_LO which is at low-order half-word.  */
+             rel->r_offset += d_offset;
              r_type = R_PPC64_TPREL16_LO;
              if (toc_symndx != 0)
                {
@@ -10049,7 +10152,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              else
                {
                  bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
-                 rel->r_offset -= 2;
+                 rel->r_offset -= d_offset;
                  r_type = R_PPC64_NONE;
                }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
@@ -10098,7 +10201,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  /* OK, it checks out.  Replace the call.  */
                  offset = rel[1].r_offset;
                  insn1 = bfd_get_32 (output_bfd,
-                                     contents + rel->r_offset - 2);
+                                     contents + rel->r_offset - d_offset);
                  insn3 = bfd_get_32 (output_bfd,
                                      contents + offset + 4);
                  if ((tls_mask & tls_gd) != 0)
@@ -10133,7 +10236,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      rel->r_info = ELF64_R_INFO (r_symndx, r_type);
                      rel[1].r_info = ELF64_R_INFO (r_symndx,
                                                    R_PPC64_TPREL16_LO);
-                     rel[1].r_offset += 2;
+                     rel[1].r_offset += d_offset;
                    }
                  if (insn3 == NOP
                      || insn3 == CROR_151515 || insn3 == CROR_313131)
@@ -10142,7 +10245,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      insn2 = NOP;
                      rel[1].r_offset += 4;
                    }
-                 bfd_put_32 (output_bfd, insn1, contents + rel->r_offset - 2);
+                 bfd_put_32 (output_bfd, insn1, contents + rel->r_offset - d_offset);
                  bfd_put_32 (output_bfd, insn2, contents + offset);
                  bfd_put_32 (output_bfd, insn3, contents + offset + 4);
                  if (tls_gd == 0 || toc_symndx != 0)
@@ -10656,7 +10759,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            relocation += htab->stub_group[sec->id].toc_off;
          else
            unresolved_reloc = TRUE;
-         goto dodyn2;
+         goto dodyn;
 
          /* TOC16 relocs.  We want the offset relative to the TOC base,
             which is the address of the start of the TOC plus 0x8000.
@@ -10756,19 +10859,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_UADDR16:
        case R_PPC64_UADDR32:
        case R_PPC64_UADDR64:
-         /* r_symndx will be zero only for relocs against symbols
-            from removed linkonce sections, or sections discarded by
-            a linker script.  */
        dodyn:
-         if (r_symndx == 0)
-           {
-             _bfd_clear_contents (ppc64_elf_howto_table[r_type], input_bfd,
-                                  contents + rel->r_offset);
-             break;
-           }
-         /* Fall thru.  */
-
-       dodyn2:
          if ((input_section->flags & SEC_ALLOC) == 0)
            break;
 
@@ -11380,6 +11471,17 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
        = PLT_ENTRY_SIZE;
     }
 
+  /* brlt is SEC_LINKER_CREATED, so we need to write out relocs for
+     brlt ourselves if emitrelocations.  */
+  if (htab->brlt != NULL
+      && htab->brlt->reloc_count != 0
+      && !_bfd_elf_link_output_relocs (output_bfd,
+                                      htab->brlt,
+                                      &elf_section_data (htab->brlt)->rel_hdr,
+                                      elf_section_data (htab->brlt)->relocs,
+                                      NULL))
+    return FALSE;
+
   /* We need to handle writing out multiple GOT sections ourselves,
      since we didn't add them to DYNOBJ.  We know dynobj is the first
      bfd.  */
This page took 0.031765 seconds and 4 git commands to generate.