* elf.c (_bfd_elf_make_section_from_shdr): Set SEC_THREAD_LOCAL
[deliverable/binutils-gdb.git] / bfd / elfxx-ia64.c
index f92ab6ae0d84cbc8451e6d4ac03444c8f34449f4..943159b15b52fdc3336b19d8002df6d30f1fcb63 100644 (file)
@@ -79,6 +79,9 @@ struct elfNN_ia64_dyn_sym_info
   bfd_vma pltoff_offset;
   bfd_vma plt_offset;
   bfd_vma plt2_offset;
+  bfd_vma tprel_offset;
+  bfd_vma dtpmod_offset;
+  bfd_vma dtprel_offset;
 
   /* The symbol table entry, if any, that this was derrived from.  */
   struct elf_link_hash_entry *h;
@@ -97,6 +100,9 @@ struct elfNN_ia64_dyn_sym_info
   unsigned got_done : 1;
   unsigned fptr_done : 1;
   unsigned pltoff_done : 1;
+  unsigned tprel_done : 1;
+  unsigned dtpmod_done : 1;
+  unsigned dtprel_done : 1;
 
   /* True for the different kinds of linker data we want created.  */
   unsigned want_got : 1;
@@ -105,6 +111,9 @@ struct elfNN_ia64_dyn_sym_info
   unsigned want_plt : 1;
   unsigned want_plt2 : 1;
   unsigned want_pltoff : 1;
+  unsigned want_tprel : 1;
+  unsigned want_dtpmod : 1;
+  unsigned want_dtprel : 1;
 };
 
 struct elfNN_ia64_local_hash_entry
@@ -285,6 +294,10 @@ static bfd_vma set_pltoff_entry
   PARAMS ((bfd *abfd, struct bfd_link_info *info,
           struct elfNN_ia64_dyn_sym_info *dyn_i,
           bfd_vma value, boolean));
+static bfd_vma elfNN_ia64_tprel_base
+  PARAMS ((struct bfd_link_info *info));
+static bfd_vma elfNN_ia64_dtprel_base
+  PARAMS ((struct bfd_link_info *info));
 static int elfNN_ia64_unwind_entry_compare
   PARAMS ((const PTR, const PTR));
 static boolean elfNN_ia64_final_link
@@ -423,10 +436,25 @@ static reloc_howto_type ia64_howto_table[] =
     IA64_HOWTO (R_IA64_LTOFF22X,    "LTOFF22X",           0, false, true),
     IA64_HOWTO (R_IA64_LDXMOV,     "LDXMOV",      0, false, true),
 
+    IA64_HOWTO (R_IA64_TPREL14,            "TPREL14",     0, false, false),
     IA64_HOWTO (R_IA64_TPREL22,            "TPREL22",     0, false, false),
+    IA64_HOWTO (R_IA64_TPREL64I,    "TPREL64I",           0, false, false),
     IA64_HOWTO (R_IA64_TPREL64MSB,  "TPREL64MSB",  8, false, false),
     IA64_HOWTO (R_IA64_TPREL64LSB,  "TPREL64LSB",  8, false, false),
-    IA64_HOWTO (R_IA64_LTOFF_TP22,  "LTOFF_TP22",  0, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_TPREL22, "LTOFF_TPREL22",  0, false, false),
+
+    IA64_HOWTO (R_IA64_DTPMOD64MSB, "TPREL64MSB",  8, false, false),
+    IA64_HOWTO (R_IA64_DTPMOD64LSB, "TPREL64LSB",  8, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_DTPMOD22, "LTOFF_DTPMOD22", 0, false, false),
+
+    IA64_HOWTO (R_IA64_DTPREL14,    "DTPREL14",           0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL22,    "DTPREL22",           0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64I,   "DTPREL64I",   0, false, false),
+    IA64_HOWTO (R_IA64_DTPREL32MSB, "DTPREL32MSB", 4, false, false),
+    IA64_HOWTO (R_IA64_DTPREL32LSB, "DTPREL32LSB", 4, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64MSB, "DTPREL64MSB", 8, false, false),
+    IA64_HOWTO (R_IA64_DTPREL64LSB, "DTPREL64LSB", 8, false, false),
+    IA64_HOWTO (R_IA64_LTOFF_DTPREL22, "LTOFF_DTPREL22", 0, false, false),
   };
 
 static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1];
@@ -541,10 +569,25 @@ elfNN_ia64_reloc_type_lookup (abfd, bfd_code)
     case BFD_RELOC_IA64_LTOFF22X:      rtype = R_IA64_LTOFF22X; break;
     case BFD_RELOC_IA64_LDXMOV:                rtype = R_IA64_LDXMOV; break;
 
+    case BFD_RELOC_IA64_TPREL14:       rtype = R_IA64_TPREL14; break;
     case BFD_RELOC_IA64_TPREL22:       rtype = R_IA64_TPREL22; break;
+    case BFD_RELOC_IA64_TPREL64I:      rtype = R_IA64_TPREL64I; break;
     case BFD_RELOC_IA64_TPREL64MSB:    rtype = R_IA64_TPREL64MSB; break;
     case BFD_RELOC_IA64_TPREL64LSB:    rtype = R_IA64_TPREL64LSB; break;
-    case BFD_RELOC_IA64_LTOFF_TP22:    rtype = R_IA64_LTOFF_TP22; break;
+    case BFD_RELOC_IA64_LTOFF_TPREL22: rtype = R_IA64_LTOFF_TPREL22; break;
+
+    case BFD_RELOC_IA64_DTPMOD64MSB:   rtype = R_IA64_DTPMOD64MSB; break;
+    case BFD_RELOC_IA64_DTPMOD64LSB:   rtype = R_IA64_DTPMOD64LSB; break;
+    case BFD_RELOC_IA64_LTOFF_DTPMOD22:        rtype = R_IA64_LTOFF_DTPMOD22; break;
+
+    case BFD_RELOC_IA64_DTPREL14:      rtype = R_IA64_DTPREL14; break;
+    case BFD_RELOC_IA64_DTPREL22:      rtype = R_IA64_DTPREL22; break;
+    case BFD_RELOC_IA64_DTPREL64I:     rtype = R_IA64_DTPREL64I; break;
+    case BFD_RELOC_IA64_DTPREL32MSB:   rtype = R_IA64_DTPREL32MSB; break;
+    case BFD_RELOC_IA64_DTPREL32LSB:   rtype = R_IA64_DTPREL32LSB; break;
+    case BFD_RELOC_IA64_DTPREL64MSB:   rtype = R_IA64_DTPREL64MSB; break;
+    case BFD_RELOC_IA64_DTPREL64LSB:   rtype = R_IA64_DTPREL64LSB; break;
+    case BFD_RELOC_IA64_LTOFF_DTPREL22:        rtype = R_IA64_LTOFF_DTPREL22; break;
 
     default: return 0;
     }
@@ -2097,6 +2140,9 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
        NEED_FULL_PLT = 16,
        NEED_DYNREL = 32,
        NEED_LTOFF_FPTR = 64,
+       NEED_TPREL = 128,
+       NEED_DTPMOD = 256,
+       NEED_DTPREL = 512
       };
 
       struct elf_link_hash_entry *h = NULL;
@@ -2134,11 +2180,42 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
       need_entry = 0;
       switch (ELFNN_R_TYPE (rel->r_info))
        {
-       case R_IA64_TPREL22:
        case R_IA64_TPREL64MSB:
        case R_IA64_TPREL64LSB:
-       case R_IA64_LTOFF_TP22:
-         return false;
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_TPREL64LSB;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_IA64_LTOFF_TPREL22:
+         need_entry = NEED_TPREL;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_IA64_DTPREL64MSB:
+       case R_IA64_DTPREL64LSB:
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_DTPREL64LSB;
+         break;
+
+       case R_IA64_LTOFF_DTPREL22:
+         need_entry = NEED_DTPREL;
+         break;
+
+       case R_IA64_DTPMOD64MSB:
+       case R_IA64_DTPMOD64LSB:
+         if (info->shared || maybe_dynamic)
+           need_entry = NEED_DYNREL;
+         dynrel_type = R_IA64_DTPMOD64LSB;
+         break;
+
+       case R_IA64_LTOFF_DTPMOD22:
+         need_entry = NEED_DTPMOD;
+         break;
 
        case R_IA64_LTOFF_FPTR22:
        case R_IA64_LTOFF_FPTR64I:
@@ -2248,7 +2325,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
       dyn_i->h = h;
 
       /* Create what's needed.  */
-      if (need_entry & NEED_GOT)
+      if (need_entry & (NEED_GOT | NEED_TPREL | NEED_DTPMOD | NEED_DTPREL))
        {
          if (!got)
            {
@@ -2256,7 +2333,14 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
              if (!got)
                return false;
            }
-         dyn_i->want_got = 1;
+         if (need_entry & NEED_GOT)
+           dyn_i->want_got = 1;
+         if (need_entry & NEED_TPREL)
+           dyn_i->want_tprel = 1;
+         if (need_entry & NEED_DTPMOD)
+           dyn_i->want_dtpmod = 1;
+         if (need_entry & NEED_DTPREL)
+           dyn_i->want_dtprel = 1;
        }
       if (need_entry & NEED_FPTR)
        {
@@ -2336,6 +2420,21 @@ allocate_global_data_got (dyn_i, data)
        dyn_i->got_offset = x->ofs;
        x->ofs += 8;
      }
+  if (dyn_i->want_tprel)
+    {
+      dyn_i->tprel_offset = x->ofs;
+      x->ofs += 8;
+    }
+  if (dyn_i->want_dtpmod)
+    {
+      dyn_i->dtpmod_offset = x->ofs;
+      x->ofs += 8;
+    }
+  if (dyn_i->want_dtprel)
+    {
+      dyn_i->dtprel_offset = x->ofs;
+      x->ofs += 8;
+    }
   return true;
 }
 
@@ -2582,6 +2681,10 @@ allocate_dynrel_entries (dyn_i, data)
          if (!dynamic_symbol)
            count *= 2;
          break;
+       case R_IA64_TPREL64LSB:
+       case R_IA64_DTPREL64LSB:
+       case R_IA64_DTPMOD64LSB:
+         break;
        default:
          abort ();
        }
@@ -2593,6 +2696,12 @@ allocate_dynrel_entries (dyn_i, data)
   if (((dynamic_symbol || shared) && dyn_i->want_got)
       || (dyn_i->want_ltoff_fptr && dyn_i->h && dyn_i->h->dynindx != -1))
     ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if ((dynamic_symbol || shared) && dyn_i->want_tprel)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if ((dynamic_symbol || shared) && dyn_i->want_dtpmod)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+  if (dynamic_symbol && dyn_i->want_dtprel)
+    ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
 
   if (dyn_i->want_pltoff)
     {
@@ -2903,7 +3012,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
 
       /* Instruction relocations.  */
 
-    case R_IA64_IMM14:         opnd = IA64_OPND_IMM14; break;
+    case R_IA64_IMM14:
+    case R_IA64_TPREL14:
+    case R_IA64_DTPREL14:
+      opnd = IA64_OPND_IMM14;
+      break;
 
     case R_IA64_PCREL21F:      opnd = IA64_OPND_TGT25; break;
     case R_IA64_PCREL21M:      opnd = IA64_OPND_TGT25b; break;
@@ -2920,6 +3033,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_PLTOFF22:
     case R_IA64_PCREL22:
     case R_IA64_LTOFF_FPTR22:
+    case R_IA64_TPREL22:
+    case R_IA64_DTPREL22:
+    case R_IA64_LTOFF_TPREL22:
+    case R_IA64_LTOFF_DTPMOD22:
+    case R_IA64_LTOFF_DTPREL22:
       opnd = IA64_OPND_IMM22;
       break;
 
@@ -2930,6 +3048,8 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_PCREL64I:
     case R_IA64_FPTR64I:
     case R_IA64_LTOFF_FPTR64I:
+    case R_IA64_TPREL64I:
+    case R_IA64_DTPREL64I:
       opnd = IA64_OPND_IMMU64;
       break;
 
@@ -2943,6 +3063,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_SEGREL32MSB:
     case R_IA64_SECREL32MSB:
     case R_IA64_LTV32MSB:
+    case R_IA64_DTPREL32MSB:
       size = 4; bigendian = 1;
       break;
 
@@ -2954,6 +3075,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_SEGREL32LSB:
     case R_IA64_SECREL32LSB:
     case R_IA64_LTV32LSB:
+    case R_IA64_DTPREL32LSB:
       size = 4; bigendian = 0;
       break;
 
@@ -2966,6 +3088,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_SEGREL64MSB:
     case R_IA64_SECREL64MSB:
     case R_IA64_LTV64MSB:
+    case R_IA64_TPREL64MSB:
+    case R_IA64_DTPMOD64MSB:
+    case R_IA64_DTPREL64MSB:
       size = 8; bigendian = 1;
       break;
 
@@ -2978,6 +3103,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
     case R_IA64_SEGREL64LSB:
     case R_IA64_SECREL64LSB:
     case R_IA64_LTV64LSB:
+    case R_IA64_TPREL64LSB:
+    case R_IA64_DTPMOD64LSB:
+    case R_IA64_DTPREL64LSB:
       size = 8; bigendian = 0;
       break;
 
@@ -3132,26 +3260,53 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
 {
   struct elfNN_ia64_link_hash_table *ia64_info;
   asection *got_sec;
+  boolean done;
+  bfd_vma got_offset;
 
   ia64_info = elfNN_ia64_hash_table (info);
   got_sec = ia64_info->got_sec;
 
-  BFD_ASSERT ((dyn_i->got_offset & 7) == 0);
-
-  if (! dyn_i->got_done)
+  switch (dyn_r_type)
     {
+    case R_IA64_TPREL64LSB:
+      done = dyn_i->tprel_done;
+      dyn_i->tprel_done = true;
+      got_offset = dyn_i->tprel_offset;
+      break;
+    case R_IA64_DTPMOD64LSB:
+      done = dyn_i->dtpmod_done;
+      dyn_i->dtpmod_done = true;
+      got_offset = dyn_i->dtpmod_offset;
+      break;
+    case R_IA64_DTPREL64LSB:
+      done = dyn_i->dtprel_done;
+      dyn_i->dtprel_done = true;
+      got_offset = dyn_i->dtprel_offset;
+      break;
+    default:
+      done = dyn_i->got_done;
       dyn_i->got_done = true;
+      got_offset = dyn_i->got_offset;
+      break;
+    }
 
+  BFD_ASSERT ((got_offset & 7) == 0);
+
+  if (! done)
+    {
       /* Store the target address in the linkage table entry.  */
-      bfd_put_64 (abfd, value, got_sec->contents + dyn_i->got_offset);
+      bfd_put_64 (abfd, value, got_sec->contents + got_offset);
 
       /* Install a dynamic relocation if needed.  */
-      if (info->shared
+      if ((info->shared && dyn_r_type != R_IA64_DTPREL64LSB)
           || elfNN_ia64_dynamic_symbol_p (dyn_i->h, info)
          || elfNN_ia64_aix_vec (abfd->xvec)
          || (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB))
        {
-         if (dynindx == -1)
+         if (dynindx == -1
+             && dyn_r_type != R_IA64_TPREL64LSB
+             && dyn_r_type != R_IA64_DTPMOD64LSB
+             && dyn_r_type != R_IA64_DTPREL64LSB)
            {
              dyn_r_type = R_IA64_REL64LSB;
              dynindx = 0;
@@ -3171,6 +3326,15 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
                case R_IA64_FPTR64LSB:
                  dyn_r_type = R_IA64_FPTR64MSB;
                  break;
+               case R_IA64_TPREL64LSB:
+                 dyn_r_type = R_IA64_TPREL64MSB;
+                 break;
+               case R_IA64_DTPMOD64LSB:
+                 dyn_r_type = R_IA64_DTPMOD64MSB;
+                 break;
+               case R_IA64_DTPREL64LSB:
+                 dyn_r_type = R_IA64_DTPREL64MSB;
+                 break;
                default:
                  BFD_ASSERT (false);
                  break;
@@ -3179,7 +3343,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
 
          elfNN_ia64_install_dyn_reloc (abfd, NULL, got_sec,
                                        ia64_info->rel_got_sec,
-                                       dyn_i->got_offset, dyn_r_type,
+                                       got_offset, dyn_r_type,
                                        dynindx, addend);
        }
     }
@@ -3187,7 +3351,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
   /* Return the address of the linkage table entry.  */
   value = (got_sec->output_section->vma
           + got_sec->output_offset
-          + dyn_i->got_offset);
+          + got_offset);
 
   return value;
 }
@@ -3285,6 +3449,35 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
   return value;
 }
 
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @tprel() relocation.
+   Main program TLS (whose template starts at PT_TLS p_vaddr)
+   is assigned offset round(16, PT_TLS p_align).  */
+
+static bfd_vma
+elfNN_ia64_tprel_base (info)
+     struct bfd_link_info *info;
+{
+  struct elf_link_tls_segment *tls_segment
+    = elf_hash_table (info)->tls_segment;
+
+  BFD_ASSERT (tls_segment != NULL);
+  return (tls_segment->start
+         - align_power ((bfd_vma) 16, tls_segment->align));
+}
+
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving @dtprel() relocation.
+   This is PT_TLS segment p_vaddr.  */
+
+static bfd_vma
+elfNN_ia64_dtprel_base (info)
+     struct bfd_link_info *info;
+{
+  BFD_ASSERT (elf_hash_table (info)->tls_segment != NULL);
+  return elf_hash_table (info)->tls_segment->start;
+}
+
 /* Called through qsort to sort the .IA_64.unwind section during a
    non-relocatable link.  Set elfNN_ia64_unwind_entry_compare_bfd
    to the output bfd so we can do proper endianness frobbing.  */
@@ -4030,6 +4223,55 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
                                        r_type);
          break;
 
+       case R_IA64_TPREL14:
+       case R_IA64_TPREL22:
+       case R_IA64_TPREL64I:
+         value -= elfNN_ia64_tprel_base (info);
+         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         break;
+
+       case R_IA64_DTPREL14:
+       case R_IA64_DTPREL22:
+       case R_IA64_DTPREL64I:
+         value -= elfNN_ia64_dtprel_base (info);
+         r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+         break;
+
+       case R_IA64_LTOFF_TPREL22:
+       case R_IA64_LTOFF_DTPMOD22:
+       case R_IA64_LTOFF_DTPREL22:
+         {
+           int got_r_type;
+
+           switch (r_type)
+             {
+             default:
+             case R_IA64_LTOFF_TPREL22:
+               if (!dynamic_symbol_p && !info->shared)
+                 value -= elfNN_ia64_tprel_base (info);
+               got_r_type = R_IA64_TPREL64LSB;
+               break;
+             case R_IA64_LTOFF_DTPMOD22:
+               if (!dynamic_symbol_p && !info->shared)
+                 value = 1;
+               got_r_type = R_IA64_DTPMOD64LSB;
+               break;
+             case R_IA64_LTOFF_DTPREL22:
+               if (!dynamic_symbol_p)
+                 value -= elfNN_ia64_dtprel_base (info);
+               got_r_type = R_IA64_DTPREL64LSB;
+               break;
+             }
+           dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, false);
+           value = set_got_entry (input_bfd, info, dyn_i,
+                                  (h ? h->dynindx : -1), rel->r_addend,
+                                  value, got_r_type);
+           value -= gp_val;
+           r = elfNN_ia64_install_value (output_bfd, hit_addr, value,
+                                         r_type);
+         }
+         break;
+
        default:
          r = bfd_reloc_notsupported;
          break;
This page took 0.036249 seconds and 4 git commands to generate.