* elf64-ppc.c (ppc64_elf_edit_opd): Support 16 byte long .opd
authorJakub Jelinek <jakub@redhat.com>
Thu, 19 Aug 2004 07:47:25 +0000 (07:47 +0000)
committerJakub Jelinek <jakub@redhat.com>
Thu, 19 Aug 2004 07:47:25 +0000 (07:47 +0000)
entries (where fd_aux overlaps next entry's fd_func).
Add non_overlapping argument, use it.
(ppc64_elf_check_relocs, ppc64_elf_gc_mark_hook, adjust_opd_syms,
ppc64_elf_size_stubs, ppc64_elf_relocate_section,
ppc64_elf_output_symbol_hook): Use address / 8 instead of address / 24
as indexes into opd_sym_map/opd_adjust array.
* elf64-ppc.h (ppc64_elf_edit_opd): Adjust prototype.

* emultempl/ppc64elf.em (non_overlapping_opd): New variable.
(ppc_before_allocation): Pass it to ppc64_elf_edit_opd).
(OPTION_NON_OVERLAPPING_OPD): Define.
(PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add
--non-overlapping-opd option.

bfd/ChangeLog
bfd/elf64-ppc.c
bfd/elf64-ppc.h
ld/ChangeLog
ld/emultempl/ppc64elf.em

index ed6e667bde086abedb9ac9252494f1639c2bf230..612359b4208829ec00aee36a251afca2873850ed 100644 (file)
@@ -1,3 +1,14 @@
+2004-08-19  Jakub Jelinek  <jakub@redhat.com>
+
+       * elf64-ppc.c (ppc64_elf_edit_opd): Support 16 byte long .opd
+       entries (where fd_aux overlaps next entry's fd_func).
+       Add non_overlapping argument, use it.
+       (ppc64_elf_check_relocs, ppc64_elf_gc_mark_hook, adjust_opd_syms,
+       ppc64_elf_size_stubs, ppc64_elf_relocate_section,
+       ppc64_elf_output_symbol_hook): Use address / 8 instead of address / 24
+       as indexes into opd_sym_map/opd_adjust array.
+       * elf64-ppc.h (ppc64_elf_edit_opd): Adjust prototype.
+
 2004-08-18  Alan Modra  <amodra@bigpond.net.au>
 
        * elf64-ppc.c (func_desc_adjust): Give undefined dot-symbols a value
index 33ffd3906c2151b3a460c6cf3831d5f45b575743..d44d8d3d4525e5f5fc8e67d44aed0872fdcd0a01 100644 (file)
@@ -4206,7 +4206,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       };
       bfd_size_type amt;
 
-      amt = sec->size * sizeof (union opd_info) / 24;
+      amt = sec->size * sizeof (union opd_info) / 8;
       opd_sym_map = bfd_zalloc (abfd, amt);
       if (opd_sym_map == NULL)
        return FALSE;
@@ -4513,7 +4513,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  if (s == NULL)
                    return FALSE;
                  else if (s != sec)
-                   opd_sym_map[rel->r_offset / 24] = s;
+                   opd_sym_map[rel->r_offset / 8] = s;
                }
            }
          /* Fall through.  */
@@ -4892,7 +4892,7 @@ ppc64_elf_gc_mark_hook (asection *sec,
          if (!rsec->gc_mark)
            _bfd_elf_gc_mark (info, rsec, ppc64_elf_gc_mark_hook);
 
-         rsec = opd_sym_section[sym->st_value / 24];
+         rsec = opd_sym_section[sym->st_value / 8];
        }
     }
 
@@ -5674,7 +5674,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
   opd_adjust = get_opd_info (sym_sec);
   if (opd_adjust != NULL)
     {
-      long adjust = opd_adjust[eh->elf.root.u.def.value / 24];
+      long adjust = opd_adjust[eh->elf.root.u.def.value / 8];
       if (adjust == -1)
        {
          /* This entry has been deleted.  */
@@ -5705,10 +5705,12 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
    applications.  */
 
 bfd_boolean
-ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
+ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info,
+                   bfd_boolean non_overlapping)
 {
   bfd *ibfd;
   bfd_boolean some_edited = FALSE;
+  asection *need_pad = NULL;
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
@@ -5720,13 +5722,14 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
       bfd_vma offset;
       bfd_size_type amt;
       long *opd_adjust;
-      bfd_boolean need_edit;
+      bfd_boolean need_edit, add_aux_fields;
+      bfd_size_type cnt_16b = 0;
 
       sec = bfd_get_section_by_name (ibfd, ".opd");
       if (sec == NULL)
        continue;
 
-      amt = sec->size * sizeof (long) / 24;
+      amt = sec->size * sizeof (long) / 8;
       opd_adjust = get_opd_info (sec);
       if (opd_adjust == NULL)
        {
@@ -5757,6 +5760,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
       /* First run through the relocs to check they are sane, and to
         determine whether we need to edit this opd section.  */
       need_edit = FALSE;
+      need_pad = sec;
       offset = 0;
       relend = relstart + sec->reloc_count;
       for (rel = relstart; rel < relend; )
@@ -5767,7 +5771,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
          struct elf_link_hash_entry *h;
          Elf_Internal_Sym *sym;
 
-         /* .opd contains a regular array of 24 byte entries.  We're
+         /* .opd contains a regular array of 16 or 24 byte entries.  We're
             only interested in the reloc pointing to a function entry
             point.  */
          if (rel->r_offset != offset
@@ -5779,6 +5783,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                 Also, there's nothing to prevent someone putting
                 something silly in .opd with the assembler.  No .opd
                 optimization for them!  */
+           broken_opd:
              (*_bfd_error_handler)
                (_("%B: .opd is not a regular array of opd entries"), ibfd);
              need_edit = FALSE;
@@ -5826,19 +5831,54 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
              || sym_sec->output_section == bfd_abs_section_ptr)
            need_edit = TRUE;
 
-         offset += 24;
          rel += 2;
-         /* Allow for the possibility of a reloc on the third word.  */
-         if (rel < relend
-             && rel->r_offset == offset - 8)
-           rel += 1;
+         if (rel == relend
+             || (rel + 1 == relend && rel->r_offset == offset + 16))
+           {
+             if (sec->size == offset + 24)
+               {
+                 need_pad = NULL;
+                 break;
+               }
+             if (rel == relend && sec->size == offset + 16)
+               {
+                 cnt_16b++;
+                 break;
+               }
+             goto broken_opd;
+           }
+
+         if (rel->r_offset == offset + 24)
+           offset += 24;
+         else if (rel->r_offset != offset + 16)
+           goto broken_opd;
+         else if (rel + 1 < relend
+                  && ELF64_R_TYPE (rel[0].r_info) == R_PPC64_ADDR64
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOC)
+           {
+             offset += 16;
+             cnt_16b++;
+           }
+         else if (rel + 2 < relend
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_ADDR64
+                  && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_TOC)
+           {
+             offset += 24;
+             rel += 1;
+           }
+         else
+           goto broken_opd;
        }
 
-      if (need_edit)
+      add_aux_fields = non_overlapping && cnt_16b > 0;
+
+      if (need_edit || add_aux_fields)
        {
          Elf_Internal_Rela *write_rel;
          bfd_byte *rptr, *wptr;
+         bfd_byte *new_contents = NULL;
          bfd_boolean skip;
+         long opd_ent_size;
 
          /* This seems a waste of time as input .opd sections are all
             zeros as generated by gcc, but I suppose there's no reason
@@ -5867,9 +5907,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
 
          wptr = sec->contents;
          rptr = sec->contents;
+         new_contents = sec->contents;
+
+         if (add_aux_fields)
+           {
+             new_contents = bfd_malloc (sec->size + cnt_16b * 8);
+             if (new_contents == NULL)
+               return FALSE;
+             need_pad = FALSE;
+             wptr = new_contents;
+           }
+
          write_rel = relstart;
          skip = FALSE;
          offset = 0;
+         opd_ent_size = 0;
          for (rel = relstart; rel < relend; rel++)
            {
              unsigned long r_symndx;
@@ -5885,6 +5937,19 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
              if (rel->r_offset == offset)
                {
                  struct ppc_link_hash_entry *fdh = NULL;
+
+                 /* See if the .opd entry is full 24 byte or
+                    16 byte (with fd_aux entry overlapped with next
+                    fd_func).  */
+                 opd_ent_size = 24;
+                 if ((rel + 2 == relend && sec->size == offset + 16)
+                     || (rel + 3 < relend
+                         && rel[2].r_offset == offset + 16
+                         && rel[3].r_offset == offset + 24
+                         && ELF64_R_TYPE (rel[2].r_info) == R_PPC64_ADDR64
+                         && ELF64_R_TYPE (rel[3].r_info) == R_PPC64_TOC))
+                   opd_ent_size = 16;
+
                  if (h != NULL
                      && h->root.root.string[0] == '.')
                    fdh = get_fdh ((struct ppc_link_hash_entry *) h,
@@ -5901,7 +5966,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                          fdh->elf.root.u.def.value = 0;
                          fdh->elf.root.u.def.section = sym_sec;
                        }
-                     opd_adjust[rel->r_offset / 24] = -1;
+                     opd_adjust[rel->r_offset / 8] = -1;
                    }
                  else
                    {
@@ -5916,7 +5981,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                             for local symbols, because various places
                             in the generic ELF code use the value
                             stored in u.def.value.  */
-                         fdh->elf.root.u.def.value = wptr - sec->contents;
+                         fdh->elf.root.u.def.value = wptr - new_contents;
                          fdh->adjust_done = 1;
                        }
 
@@ -5926,14 +5991,20 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                         for the function descriptor sym which we
                         don't have at the moment.  So keep an
                         array of adjustments.  */
-                     opd_adjust[rel->r_offset / 24] = wptr - rptr;
+                     opd_adjust[rel->r_offset / 8]
+                       = (wptr - new_contents) - (rptr - sec->contents);
 
                      if (wptr != rptr)
-                       memcpy (wptr, rptr, 24);
-                     wptr += 24;
+                       memcpy (wptr, rptr, opd_ent_size);
+                     wptr += opd_ent_size;
+                     if (add_aux_fields && opd_ent_size == 16)
+                       {
+                         memset (wptr, '\0', 8);
+                         wptr += 8;
+                       }
                    }
-                 rptr += 24;
-                 offset += 24;
+                 rptr += opd_ent_size;
+                 offset += opd_ent_size;
                }
 
              if (skip)
@@ -5971,15 +6042,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
                  /* We need to adjust any reloc offsets to point to the
                     new opd entries.  While we're at it, we may as well
                     remove redundant relocs.  */
-                 rel->r_offset += wptr - rptr;
+                 rel->r_offset += opd_adjust[(offset - opd_ent_size) / 8];
                  if (write_rel != rel)
                    memcpy (write_rel, rel, sizeof (*rel));
                  ++write_rel;
                }
            }
 
-         sec->size = wptr - sec->contents;
+         sec->size = wptr - new_contents;
          sec->reloc_count = write_rel - relstart;
+         if (add_aux_fields)
+           {
+             free (sec->contents);
+             sec->contents = new_contents;
+           }
+
          /* Fudge the size too, as this is used later in
             elf_bfd_final_link if we are emitting relocs.  */
          elf_section_data (sec)->rel_hdr.sh_size
@@ -6003,6 +6080,40 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info)
   if (some_edited)
     elf_link_hash_traverse (elf_hash_table (info), adjust_opd_syms, NULL);
 
+  /* If we are doing a final link and the last .opd entry is just 16 byte
+     long, add a 8 byte padding after it.  */
+  if (need_pad != NULL && !info->relocatable)
+    {
+      bfd_byte *p;
+
+      if ((need_pad->flags & SEC_IN_MEMORY) == 0)
+       {
+         BFD_ASSERT (need_pad->size > 0);
+
+         p = bfd_malloc (need_pad->size + 8);
+         if (p == NULL)
+           return FALSE;
+                      
+         if (! bfd_get_section_contents (need_pad->owner, need_pad,
+                                         p, 0, need_pad->size))
+           return FALSE;
+
+         need_pad->contents = p;
+         need_pad->flags |= (SEC_IN_MEMORY | SEC_HAS_CONTENTS);
+       }
+      else
+       {
+         p = bfd_realloc (need_pad->contents, need_pad->size + 8);
+         if (p == NULL)
+           return FALSE;
+
+         need_pad->contents = p;
+       }
+
+      memset (need_pad->contents + need_pad->size, 0, 8);
+      need_pad->size += 8;
+    }
+
   return TRUE;
 }
 
@@ -7770,7 +7881,7 @@ ppc64_elf_size_stubs (bfd *output_bfd,
 
                      if (hash == NULL)
                        {
-                         long adjust = opd_adjust[sym_value / 24];
+                         long adjust = opd_adjust[sym_value / 8];
                          if (adjust == -1)
                            continue;
                          sym_value += adjust;
@@ -8308,7 +8419,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          opd_adjust = get_opd_info (sec);
          if (opd_adjust != NULL)
            {
-             long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 24];
+             long adjust = opd_adjust[(sym->st_value + rel->r_addend) / 8];
              if (adjust == -1)
                relocation = 0;
              else
@@ -9604,7 +9715,7 @@ ppc64_elf_output_symbol_hook (struct bfd_link_info *info,
   if (!info->relocatable)
     value -= input_sec->output_section->vma;
 
-  adjust = opd_adjust[value / 24];
+  adjust = opd_adjust[value / 8];
   if (adjust == -1)
     elfsym->st_value = 0;
   else
index b1f4343a4ead4445bad9c068125dd9ac257faea9..4a58bbe71b060f15dc1bfe562ba1a050739d187c 100644 (file)
@@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 void ppc64_elf_init_stub_bfd
   (bfd *, struct bfd_link_info *);
 bfd_boolean ppc64_elf_edit_opd
-  (bfd *, struct bfd_link_info *);
+  (bfd *, struct bfd_link_info *, bfd_boolean);
 asection *ppc64_elf_tls_setup
   (bfd *, struct bfd_link_info *);
 bfd_boolean ppc64_elf_tls_optimize
index a8b8ba4793b5d6b81b696f58fd6372a994824563..0bf150a124bf16d2058fc29bb709c7048f8e491f 100644 (file)
@@ -1,3 +1,11 @@
+2004-08-19  Jakub Jelinek  <jakub@redhat.com>
+
+       * emultempl/ppc64elf.em (non_overlapping_opd): New variable.
+       (ppc_before_allocation): Pass it to ppc64_elf_edit_opd).
+       (OPTION_NON_OVERLAPPING_OPD): Define.
+       (PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add
+       --non-overlapping-opd option.
+
 2004-08-18  Alan Modra  <amodra@bigpond.net.au>
 
        PR 338
index 264d63ebf1fe6d45682201946e644368fd800751..094a4fb55ed4363d365b6c8b677600903962a901 100644 (file)
@@ -51,6 +51,9 @@ static int emit_stub_syms = 0;
 
 static asection *toc_section = 0;
 
+/* Whether to canonicalize .opd so that there are no overlapping
+   .opd entries.  */
+static int non_overlapping_opd = 0;
 
 /* This is called before the input files are opened.  We create a new
    fake input file to hold the stub sections.  */
@@ -89,7 +92,7 @@ ppc_before_allocation (void)
 {
   if (stub_file != NULL)
     {
-      if (!ppc64_elf_edit_opd (output_bfd, &link_info))
+      if (!ppc64_elf_edit_opd (output_bfd, &link_info, non_overlapping_opd))
        {
          einfo ("%X%P: can not edit opd %E\n");
          return;
@@ -463,6 +466,7 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_DOTSYMS                 (OPTION_STUBSYMS + 1)
 #define OPTION_NO_DOTSYMS              (OPTION_DOTSYMS + 1)
 #define OPTION_NO_TLS_OPT              (OPTION_NO_DOTSYMS + 1)
+#define OPTION_NON_OVERLAPPING_OPD     (OPTION_NO_TLS_OPT + 1)
 '
 
 PARSE_AND_LIST_LONGOPTS='
@@ -471,6 +475,7 @@ PARSE_AND_LIST_LONGOPTS='
   { "dotsyms", no_argument, NULL, OPTION_DOTSYMS },
   { "no-dotsyms", no_argument, NULL, OPTION_NO_DOTSYMS },
   { "no-tls-optimize", no_argument, NULL, OPTION_NO_TLS_OPT },
+  { "non-overlapping-opd", no_argument, NULL, OPTION_NON_OVERLAPPING_OPD },
 '
 
 PARSE_AND_LIST_OPTIONS='
@@ -498,6 +503,10 @@ PARSE_AND_LIST_OPTIONS='
   fprintf (file, _("\
   --no-tls-optimize     Don'\''t try to optimize TLS accesses.\n"
                   ));
+  fprintf (file, _("\
+  --non-overlapping-opd Canonicalize .opd, so that there are no overlapping\n\
+                          .opd entries.\n"
+                  ));
 '
 
 PARSE_AND_LIST_ARGS_CASES='
@@ -525,6 +534,10 @@ PARSE_AND_LIST_ARGS_CASES='
     case OPTION_NO_TLS_OPT:
       notlsopt = 1;
       break;
+
+    case OPTION_NON_OVERLAPPING_OPD:
+      non_overlapping_opd = 1;
+      break;
 '
 
 # Put these extra ppc64elf routines in ld_${EMULATION_NAME}_emulation