2004-02-26 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / bfd / elfxx-ia64.c
index c9da05fd547343dadbe530cd4ed1cd0041b3357b..878511bb828c3f14baadb4e89ea52faa09014a39 100644 (file)
@@ -92,6 +92,9 @@ struct elfNN_ia64_dyn_sym_info
     asection *srel;
     int type;
     int count;
+
+    /* Is this reloc against readonly section? */
+    bfd_boolean reltext;
   } *reloc_entries;
 
   /* TRUE when the section contents have been updated.  */
@@ -243,9 +246,6 @@ static asection *get_pltoff
 static asection *get_reloc_section
   PARAMS ((bfd *abfd, struct elfNN_ia64_link_hash_table *ia64_info,
           asection *sec, bfd_boolean create));
-static bfd_boolean count_dyn_reloc
-  PARAMS ((bfd *abfd, struct elfNN_ia64_dyn_sym_info *dyn_i,
-          asection *srel, int type));
 static bfd_boolean elfNN_ia64_check_relocs
   PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *sec,
           const Elf_Internal_Rela *relocs));
@@ -672,6 +672,36 @@ bfd_elfNN_ia64_after_parse (int itanium)
   oor_branch_size = itanium ? sizeof (oor_ip) : sizeof (oor_brl);
 }
 
+static void
+elfNN_ia64_relax_brl (bfd *abfd, bfd_byte *contents, bfd_vma off)
+{
+  int template;
+  bfd_byte *hit_addr;
+  bfd_vma t0, t1, i0, i1, i2;
+
+  hit_addr = (bfd_byte *) (contents + off);
+  hit_addr -= (long) hit_addr & 0x3;
+  t0 = bfd_get_64 (abfd, hit_addr);
+  t1 = bfd_get_64 (abfd, hit_addr + 8);
+
+  /* Keep the instruction in slot 0. */
+  i0 = (t0 >> 5) & 0x1ffffffffffLL;
+  /* Use nop.b for slot 1. */
+  i1 = 0x4000000000LL;
+  /* For slot 2, turn brl into br by masking out bit 40.  */
+  i2 = (t1 >> 23) & 0x0ffffffffffLL;
+
+  /* Turn a MLX bundle into a MBB bundle with the same stop-bit
+     variety.  */
+  template = 0x12;
+  if ((t0 & 0x1fLL) == 5)
+    template += 1;
+  t0 = (i1 << 46) | (i0 << 5) | template;
+  t1 = (i2 << 23) | (i1 >> 18);
+
+  bfd_put_64 (abfd, t0, hit_addr);
+  bfd_put_64 (abfd, t1, hit_addr + 8);
+}
 \f
 /* These functions do relaxation for IA-64 ELF.  */
 
@@ -714,7 +744,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
      the relax finalize pass.  */
   if ((sec->flags & SEC_RELOC) == 0
       || sec->reloc_count == 0
-      || (link_info->relax_finalizing
+      || (!link_info->need_relax_finalize
          && sec->need_finalize_relax == 0))
     return TRUE;
 
@@ -765,14 +795,31 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
        case R_IA64_PCREL21BI:
        case R_IA64_PCREL21M:
        case R_IA64_PCREL21F:
-         if (link_info->relax_finalizing)
+         /* In the finalize pass, all br relaxations are done. We can
+            skip it. */
+         if (!link_info->need_relax_finalize)
            continue;
          is_branch = TRUE;
          break;
 
+       case R_IA64_PCREL60B:
+         /* We can't optimize brl to br before the finalize pass since
+            br relaxations will increase the code size. Defer it to
+            the finalize pass.  */
+         if (link_info->need_relax_finalize)
+           {
+             sec->need_finalize_relax = 1;
+             continue;
+           }
+         is_branch = TRUE;
+         break;
+
        case R_IA64_LTOFF22X:
        case R_IA64_LDXMOV:
-         if (!link_info->relax_finalizing)
+         /* We can't relax ldx/mov before the finalize pass since
+            br relaxations will increase the code size. Defer it to
+            the finalize pass.  */
+         if (link_info->need_relax_finalize)
            {
              sec->need_finalize_relax = 1;
              continue;
@@ -876,6 +923,8 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
 
       if (is_branch)
        {
+         bfd_signed_vma offset;
+
          reladdr = (sec->output_section->vma
                     + sec->output_offset
                     + roff) & (bfd_vma) -4;
@@ -883,6 +932,25 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
          /* If the branch is in range, no need to do anything.  */
          if ((bfd_signed_vma) (symaddr - reladdr) >= -0x1000000
              && (bfd_signed_vma) (symaddr - reladdr) <= 0x0FFFFF0)
+           {
+             /* If the 60-bit branch is in 21-bit range, optimize it. */
+             if (r_type == R_IA64_PCREL60B)
+               {
+                 elfNN_ia64_relax_brl (abfd, contents, roff);
+
+                 irel->r_info
+                   = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                   R_IA64_PCREL21B);
+
+                 /* If the original relocation offset points to slot
+                    1, change it to slot 2.  */
+                 if ((irel->r_offset & 3) == 1)
+                   irel->r_offset += 1;
+               }
+
+             continue;
+           }
+         else if (r_type == R_IA64_PCREL60B)
            continue;
 
          /* If the branch and target are in the same section, you've
@@ -911,6 +979,13 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
 
              /* Resize the current section to make room for the new branch. */
              trampoff = (sec->_cooked_size + 15) & (bfd_vma) -16;
+
+             /* If trampoline is out of range, there is nothing we
+                can do.  */
+             offset = trampoff - (roff & (bfd_vma) -4);
+             if (offset < -0x1000000 || offset > 0x0FFFFF0)
+               continue;
+
              amt = trampoff + size;
              contents = (bfd_byte *) bfd_realloc (contents, amt);
              if (contents == NULL)
@@ -957,14 +1032,18 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
            }
          else
            {
+             /* If trampoline is out of range, there is nothing we
+                can do.  */
+             offset = f->trampoff - (roff & (bfd_vma) -4);
+             if (offset < -0x1000000 || offset > 0x0FFFFF0)
+               continue;
+
              /* Nop out the reloc, since we're finalizing things here.  */
              irel->r_info = ELFNN_R_INFO (0, R_IA64_NONE);
            }
 
-         /* Fix up the existing branch to hit the trampoline.  Hope like
-            hell this doesn't overflow too.  */
-         if (elfNN_ia64_install_value (abfd, contents + roff,
-                                       f->trampoff - (roff & (bfd_vma) -4),
+         /* Fix up the existing branch to hit the trampoline.  */
+         if (elfNN_ia64_install_value (abfd, contents + roff, offset,
                                        r_type) != bfd_reloc_ok)
            goto error_return;
 
@@ -1073,7 +1152,7 @@ elfNN_ia64_relax_section (abfd, sec, link_info, again)
       /* ??? Resize .rela.got too.  */
     }
 
-  if (link_info->relax_finalizing)
+  if (!link_info->need_relax_finalize)
     sec->need_finalize_relax = 0;
 
   *again = changed_contents || changed_relocs;
@@ -2096,18 +2175,12 @@ get_reloc_section (abfd, ia64_info, sec, create)
        return NULL;
     }
 
-  if (sec->flags & SEC_READONLY)
-    ia64_info->reltext = 1;
-
   return srel;
 }
 
 static bfd_boolean
-count_dyn_reloc (abfd, dyn_i, srel, type)
-     bfd *abfd;
-     struct elfNN_ia64_dyn_sym_info *dyn_i;
-     asection *srel;
-     int type;
+count_dyn_reloc (bfd *abfd, struct elfNN_ia64_dyn_sym_info *dyn_i,
+                asection *srel, int type, bfd_boolean reltext)
 {
   struct elfNN_ia64_dyn_reloc_entry *rent;
 
@@ -2128,6 +2201,7 @@ count_dyn_reloc (abfd, dyn_i, srel, type)
       rent->count = 0;
       dyn_i->reloc_entries = rent;
     }
+  rent->reltext = reltext;
   rent->count++;
 
   return TRUE;
@@ -2412,7 +2486,8 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
              if (!srel)
                return FALSE;
            }
-         if (!count_dyn_reloc (abfd, dyn_i, srel, dynrel_type))
+         if (!count_dyn_reloc (abfd, dyn_i, srel, dynrel_type,
+                               (sec->flags & SEC_READONLY) != 0))
            return FALSE;
        }
     }
@@ -2719,6 +2794,8 @@ allocate_dynrel_entries (dyn_i, data)
        default:
          abort ();
        }
+      if (rent->reltext)
+       ia64_info->reltext = 1;
       rent->srel->_raw_size += sizeof (ElfNN_External_Rela) * count;
     }
 
@@ -3938,6 +4015,24 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
 
              BFD_ASSERT (srel != NULL);
 
+             switch (r_type)
+               {
+               case R_IA64_IMM14:
+               case R_IA64_IMM22:
+               case R_IA64_IMM64:
+                 /* ??? People shouldn't be doing non-pic code in
+                    shared libraries nor dynamic executables.  */
+                 (*_bfd_error_handler)
+                   (_("%s: non-pic code with imm relocation against dynamic symbol `%s'"),
+                    bfd_archive_filename (input_bfd),
+                    h->root.root.string);
+                 ret_val = FALSE;
+                 continue;
+
+               default:
+                 break;
+               }
+
              /* If we don't need dynamic symbol lookup, find a
                 matching RELATIVE relocation.  */
              dyn_r_type = r_type;
@@ -3965,17 +4060,7 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                      break;
 
                    default:
-                     /* We can't represent this without a dynamic symbol.
-                        Adjust the relocation to be against an output
-                        section symbol, which are always present in the
-                        dynamic symbol table.  */
-                     /* ??? People shouldn't be doing non-pic code in
-                        shared libraries.  Hork.  */
-                     (*_bfd_error_handler)
-                       (_("%s: linking non-pic code in a shared library"),
-                        bfd_archive_filename (input_bfd));
-                     ret_val = FALSE;
-                     continue;
+                     break;
                    }
                  dynindx = 0;
                  addend = value;
This page took 0.029223 seconds and 4 git commands to generate.