PR ld/15323
[binutils-gdb.git] / bfd / elf32-ppc.c
index 3f4e4bf27ea87af2ee25eefedb64e992d851ffb7..f356c959fd6bbfe67850734579ff096330e3cb71 100644 (file)
@@ -37,6 +37,7 @@
 #include "elf32-ppc.h"
 #include "elf-vxworks.h"
 #include "dwarf2.h"
+#include "elf-linux-psinfo.h"
 
 typedef enum split16_format_type
 {
@@ -1777,6 +1778,58 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 };
+
+/* External 32-bit PPC structure for PRPSINFO.  This structure is
+   ABI-defined, thus we choose to use char arrays here in order to
+   avoid dealing with different types in different architectures.
+
+   The PPC 32-bit structure uses int for `pr_uid' and `pr_gid' while
+   most non-PPC architectures use `short int'.
+
+   This structure will ultimately be written in the corefile's note
+   section, as the PRPSINFO.  */
+
+struct elf_external_ppc_linux_prpsinfo32
+  {
+    char pr_state;                     /* Numeric process state.  */
+    char pr_sname;                     /* Char for pr_state.  */
+    char pr_zomb;                      /* Zombie.  */
+    char pr_nice;                      /* Nice val.  */
+    char pr_flag[4];                   /* Flags.  */
+    char pr_uid[4];
+    char pr_gid[4];
+    char pr_pid[4];
+    char pr_ppid[4];
+    char pr_pgrp[4];
+    char pr_sid[4];
+    char pr_fname[16];                 /* Filename of executable.  */
+    char pr_psargs[80];                        /* Initial part of arg list.  */
+  };
+
+/* Helper macro to swap (properly handling endianess) things from the
+   `elf_internal_prpsinfo' structure to the `elf_external_ppc_prpsinfo32'
+   structure.
+
+   Note that FROM should be a pointer, and TO should be the explicit type.  */
+
+#define PPC_LINUX_PRPSINFO32_SWAP_FIELDS(abfd, from, to)             \
+  do                                                                 \
+    {                                                                \
+      H_PUT_8 (abfd, from->pr_state, &to.pr_state);                  \
+      H_PUT_8 (abfd, from->pr_sname, &to.pr_sname);                  \
+      H_PUT_8 (abfd, from->pr_zomb, &to.pr_zomb);                    \
+      H_PUT_8 (abfd, from->pr_nice, &to.pr_nice);                    \
+      H_PUT_32 (abfd, from->pr_flag, to.pr_flag);                    \
+      H_PUT_32 (abfd, from->pr_uid, to.pr_uid);                              \
+      H_PUT_32 (abfd, from->pr_gid, to.pr_gid);                              \
+      H_PUT_32 (abfd, from->pr_pid, to.pr_pid);                              \
+      H_PUT_32 (abfd, from->pr_ppid, to.pr_ppid);                    \
+      H_PUT_32 (abfd, from->pr_pgrp, to.pr_pgrp);                    \
+      H_PUT_32 (abfd, from->pr_sid, to.pr_sid);                              \
+      strncpy (to.pr_fname, from->pr_fname, sizeof (to.pr_fname));    \
+      strncpy (to.pr_psargs, from->pr_psargs, sizeof (to.pr_psargs)); \
+    } while (0)
+
 \f
 /* Initialize the ppc_elf_howto_table, so that linear accesses can be done.  */
 
@@ -2163,10 +2216,10 @@ ppc_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 
     case 268:          /* Linux/PPC.  */
       /* pr_cursig */
-      elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+      elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
 
       /* pr_pid */
-      elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 24);
+      elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
 
       /* pr_reg */
       offset = 72;
@@ -2189,11 +2242,11 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
       return FALSE;
 
     case 128:          /* Linux/PPC elf_prpsinfo.  */
-      elf_tdata (abfd)->core_pid
+      elf_tdata (abfd)->core->pid
        = bfd_get_32 (abfd, note->descdata + 16);
-      elf_tdata (abfd)->core_program
+      elf_tdata (abfd)->core->program
        = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
-      elf_tdata (abfd)->core_command
+      elf_tdata (abfd)->core->command
        = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
     }
 
@@ -2202,7 +2255,7 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
      implementations, so strip it off if it exists.  */
 
   {
-    char *command = elf_tdata (abfd)->core_command;
+    char *command = elf_tdata (abfd)->core->command;
     int n = strlen (command);
 
     if (0 < n && command[n - 1] == ' ')
@@ -2212,6 +2265,19 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
   return TRUE;
 }
 
+char *
+elfcore_write_ppc_linux_prpsinfo32 (bfd *abfd, char *buf, int *bufsiz,
+                                     const struct elf_internal_linux_prpsinfo *prpsinfo)
+{
+  struct elf_external_ppc_linux_prpsinfo32 data;
+
+  memset (&data, 0, sizeof (data));
+  PPC_LINUX_PRPSINFO32_SWAP_FIELDS (abfd, prpsinfo, data);
+
+  return elfcore_write_note (abfd, buf, bufsiz,
+                            "CORE", NT_PRPSINFO, &data, sizeof (data));
+}
+
 static char *
 ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...)
 {
@@ -2370,7 +2436,7 @@ ppc_elf_modify_segment_map (bfd *abfd,
      If we find that case, we split the segment.
      We maintain the original output section order.  */
 
-  for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+  for (m = elf_seg_map (abfd); m != NULL; m = m->next)
     {
       if (m->count == 0)
        continue;
@@ -3017,6 +3083,21 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    shared lib.  */
 #define ELIMINATE_COPY_RELOCS 1
 
+/* Used to track dynamic relocations for local symbols.  */
+struct ppc_dyn_relocs
+{
+  struct ppc_dyn_relocs *next;
+
+  /* The input section of the reloc.  */
+  asection *sec;
+
+  /* Total number of relocs copied for the input section.  */
+  unsigned int count : 31;
+
+  /* Whether this entry is for STT_GNU_IFUNC symbols.  */
+  unsigned int ifunc : 1;
+};
+
 /* PPC ELF linker hash entry.  */
 
 struct ppc_elf_link_hash_entry
@@ -3830,6 +3911,10 @@ ppc_elf_check_relocs (bfd *abfd,
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         /* PR15323, ref flags aren't set for references in the same
+            object.  */
+         h->root.non_ir_ref = 1;
        }
 
       /* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
@@ -3855,13 +3940,11 @@ ppc_elf_check_relocs (bfd *abfd,
          if (isym == NULL)
            return FALSE;
 
-         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
-             && (!info->shared
-                 || is_branch_reloc (r_type)))
+         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              struct plt_entry **ifunc;
-             bfd_vma addend;
 
+             /* Set PLT_IFUNC flag for this sym, no GOT entry yet.  */
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                             PLT_IFUNC);
              if (ifunc == NULL)
@@ -3870,15 +3953,19 @@ ppc_elf_check_relocs (bfd *abfd,
              /* STT_GNU_IFUNC symbols must have a PLT entry;
                 In a non-pie executable even when there are
                 no plt calls.  */
-             addend = 0;
-             if (r_type == R_PPC_PLTREL24)
+             if (!info->shared
+                 || is_branch_reloc (r_type))
                {
-                 ppc_elf_tdata (abfd)->makes_plt_call = 1;
-                 if (info->shared)
-                   addend = rel->r_addend;
+                 bfd_vma addend = 0;
+                 if (r_type == R_PPC_PLTREL24)
+                   {
+                     ppc_elf_tdata (abfd)->makes_plt_call = 1;
+                     if (info->shared)
+                       addend = rel->r_addend;
+                   }
+                 if (!update_plt_info (abfd, ifunc, got2, addend))
+                   return FALSE;
                }
-             if (!update_plt_info (abfd, ifunc, got2, addend))
-               return FALSE;
            }
        }
 
@@ -4346,9 +4433,6 @@ ppc_elf_check_relocs (bfd *abfd,
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
            {
-             struct elf_dyn_relocs *p;
-             struct elf_dyn_relocs **rel_head;
-
 #ifdef DEBUG
              fprintf (stderr,
                       "ppc_elf_check_relocs needs to "
@@ -4372,13 +4456,34 @@ ppc_elf_check_relocs (bfd *abfd,
                 relocations we need for this symbol.  */
              if (h != NULL)
                {
+                 struct elf_dyn_relocs *p;
+                 struct elf_dyn_relocs **rel_head;
+
                  rel_head = &ppc_elf_hash_entry (h)->dyn_relocs;
+                 p = *rel_head;
+                 if (p == NULL || p->sec != sec)
+                   {
+                     p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+                     if (p == NULL)
+                       return FALSE;
+                     p->next = *rel_head;
+                     *rel_head = p;
+                     p->sec = sec;
+                     p->count = 0;
+                     p->pc_count = 0;
+                   }
+                 p->count += 1;
+                 if (!must_be_dyn_reloc (info, r_type))
+                   p->pc_count += 1;
                }
              else
                {
                  /* Track dynamic relocs needed for local syms too.
                     We really need local syms available to do this
                     easily.  Oh well.  */
+                 struct ppc_dyn_relocs *p;
+                 struct ppc_dyn_relocs **rel_head;
+                 bfd_boolean is_ifunc;
                  asection *s;
                  void *vpp;
                  Elf_Internal_Sym *isym;
@@ -4393,25 +4498,24 @@ ppc_elf_check_relocs (bfd *abfd,
                    s = sec;
 
                  vpp = &elf_section_data (s)->local_dynrel;
-                 rel_head = (struct elf_dyn_relocs **) vpp;
-               }
-
-             p = *rel_head;
-             if (p == NULL || p->sec != sec)
-               {
-                 p = bfd_alloc (htab->elf.dynobj, sizeof *p);
-                 if (p == NULL)
-                   return FALSE;
-                 p->next = *rel_head;
-                 *rel_head = p;
-                 p->sec = sec;
-                 p->count = 0;
-                 p->pc_count = 0;
+                 rel_head = (struct ppc_dyn_relocs **) vpp;
+                 is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC;
+                 p = *rel_head;
+                 if (p != NULL && p->sec == sec && p->ifunc != is_ifunc)
+                   p = p->next;
+                 if (p == NULL || p->sec != sec || p->ifunc != is_ifunc)
+                   {
+                     p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+                     if (p == NULL)
+                       return FALSE;
+                     p->next = *rel_head;
+                     *rel_head = p;
+                     p->sec = sec;
+                     p->ifunc = is_ifunc;
+                     p->count = 0;
+                   }
+                 p->count += 1;
                }
-
-             p->count += 1;
-             if (!must_be_dyn_reloc (info, r_type))
-               p->pc_count += 1;
            }
 
          break;
@@ -5835,6 +5939,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                  || eh->elf.root.type != bfd_link_hash_undefweak))
            {
              asection *rsec = htab->relgot;
+
+             if (eh->elf.type == STT_GNU_IFUNC)
+               rsec = htab->reliplt;
              /* All the entries we allocated need relocs.
                 Except LD only needs one.  */
              if ((eh->tls_mask & TLS_LD) != 0
@@ -5952,7 +6059,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
       asection *sreloc = elf_section_data (p->sec)->sreloc;
-      if (!htab->elf.dynamic_sections_created)
+      if (eh->elf.type == STT_GNU_IFUNC)
        sreloc = htab->reliplt;
       sreloc->size += p->count * sizeof (Elf32_External_Rela);
     }
@@ -6045,9 +6152,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       for (s = ibfd->sections; s != NULL; s = s->next)
        {
-         struct elf_dyn_relocs *p;
+         struct ppc_dyn_relocs *p;
 
-         for (p = ((struct elf_dyn_relocs *)
+         for (p = ((struct ppc_dyn_relocs *)
                    elf_section_data (s)->local_dynrel);
               p != NULL;
               p = p->next)
@@ -6070,7 +6177,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              else if (p->count != 0)
                {
                  asection *sreloc = elf_section_data (p->sec)->sreloc;
-                 if (!htab->elf.dynamic_sections_created)
+                 if (p->ifunc)
                    sreloc = htab->reliplt;
                  sreloc->size += p->count * sizeof (Elf32_External_Rela);
                  if ((p->sec->output_section->flags
@@ -6115,8 +6222,12 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              {
                *local_got = allocate_got (htab, need);
                if (info->shared)
-                 htab->relgot->size += (need
-                                        * (sizeof (Elf32_External_Rela) / 4));
+                 {
+                   asection *srel = htab->relgot;
+                   if ((*lgot_masks & PLT_IFUNC) != 0)
+                     srel = htab->reliplt;
+                   srel->size += need * (sizeof (Elf32_External_Rela) / 4);
+                 }
              }
          }
        else
@@ -7315,7 +7426,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
   Elf_Internal_Rela outrel;
-  asection *got2, *sreloc = NULL;
+  asection *got2;
   bfd_vma *local_got_offsets;
   bfd_boolean ret = TRUE;
   bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
@@ -7406,7 +7517,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        {
          if (got2 != NULL
              && r_type == R_PPC_PLTREL24
-             && rel->r_addend >= 32768)
+             && rel->r_addend != 0)
            {
              /* R_PPC_PLTREL24 is rather special.  If non-zero, the
                 addend specifies the GOT pointer offset within .got2.  */
@@ -7921,6 +8032,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                        asection *rsec = htab->relgot;
                        bfd_byte * loc;
 
+                       if (ifunc != NULL)
+                         rsec = htab->reliplt;
                        outrel.r_offset = (htab->got->output_section->vma
                                           + htab->got->output_offset
                                           + off);
@@ -8167,7 +8280,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  && !h->def_regular))
            {
              int skip;
-             bfd_byte * loc;
+             bfd_byte *loc;
+             asection *sreloc;
 #ifdef DEBUG
              fprintf (stderr, "ppc_elf_relocate_section needs to "
                       "create relocation for %s\n",
@@ -8178,14 +8292,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
              /* When generating a shared object, these relocations
                 are copied into the output file to be resolved at run
                 time.  */
+             sreloc = elf_section_data (input_section)->sreloc;
+             if (ifunc)
+               sreloc = htab->reliplt;
              if (sreloc == NULL)
-               {
-                 sreloc = elf_section_data (input_section)->sreloc;
-                 if (!htab->elf.dynamic_sections_created)
-                   sreloc = htab->reliplt;
-                 if (sreloc == NULL)
-                   return FALSE;
-               }
+               return FALSE;
 
              skip = 0;
              outrel.r_offset = _bfd_elf_section_offset (output_bfd, info,
@@ -8411,33 +8522,37 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_PLTREL24:
-         if (h == NULL || ifunc != NULL)
-           break;
-         /* Relocation is to the entry for this symbol in the
-            procedure linkage table.  */
-         {
-           struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
-                                                 info->shared ? addend : 0);
-           addend = 0;
-           if (ent == NULL
-               || htab->plt == NULL)
-             {
-               /* We didn't make a PLT entry for this symbol.  This
-                  happens when statically linking PIC code, or when
-                  using -Bsymbolic.  */
-               break;
-             }
+         if (h != NULL && ifunc == NULL)
+           {
+             struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
+                                                   info->shared ? addend : 0);
+             if (ent == NULL
+                 || htab->plt == NULL)
+               {
+                 /* We didn't make a PLT entry for this symbol.  This
+                    happens when statically linking PIC code, or when
+                    using -Bsymbolic.  */
+               }
+             else
+               {
+                 /* Relocation is to the entry for this symbol in the
+                    procedure linkage table.  */
+                 unresolved_reloc = FALSE;
+                 if (htab->plt_type == PLT_NEW)
+                   relocation = (htab->glink->output_section->vma
+                                 + htab->glink->output_offset
+                                 + ent->glink_offset);
+                 else
+                   relocation = (htab->plt->output_section->vma
+                                 + htab->plt->output_offset
+                                 + ent->plt.offset);
+               }
+           }
 
-           unresolved_reloc = FALSE;
-           if (htab->plt_type == PLT_NEW)
-             relocation = (htab->glink->output_section->vma
-                           + htab->glink->output_offset
-                           + ent->glink_offset);
-           else
-             relocation = (htab->plt->output_section->vma
-                           + htab->plt->output_offset
-                           + ent->plt.offset);
-         }
+         /* R_PPC_PLTREL24 is rather special.  If non-zero, the
+            addend specifies the GOT pointer offset within .got2.
+            Don't apply it to the relocation field.  */
+         addend = 0;
          break;
 
          /* Relocate against _SDA_BASE_.  */
@@ -9281,14 +9396,19 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 }
 \f
 static enum elf_reloc_type_class
-ppc_elf_reloc_type_class (const Elf_Internal_Rela *rela)
+ppc_elf_reloc_type_class (const struct bfd_link_info *info,
+                         const asection *rel_sec,
+                         const Elf_Internal_Rela *rela)
 {
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+  if (rel_sec == htab->reliplt)
+    return reloc_class_ifunc;
+
   switch (ELF32_R_TYPE (rela->r_info))
     {
     case R_PPC_RELATIVE:
       return reloc_class_relative;
-    case R_PPC_REL24:
-    case R_PPC_ADDR24:
     case R_PPC_JMP_SLOT:
       return reloc_class_plt;
     case R_PPC_COPY: