]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Start implementation of x86 TLS implementation in ld.
authorUlrich Drepper <drepper@redhat.com>
Sat, 2 Feb 2008 10:01:53 +0000 (10:01 +0000)
committerUlrich Drepper <drepper@redhat.com>
Sat, 2 Feb 2008 10:01:53 +0000 (10:01 +0000)
src/ChangeLog
src/elf32-i386.script
src/i386_ld.c
src/ld.h
src/ldgeneric.c

index a532a98e3f9e11f8f31aa76b1c78bf254050a020..d3e49e41684cb922bfd2465a18d8c61d0f551f0f 100644 (file)
@@ -1,3 +1,15 @@
+2008-02-02  Ulrich Drepper  <drepper@redhat.com>
+
+       * elf32-i386.script: Add .eh_frame_hdr, .tdata, and .tbss sections.
+       * i386_ld.c (elf_i386_count_relocations): Handle R_386_TLS_LDO_32
+       and R_386_TLS_LE.
+       (elf_i386_create_relocations): Likewise.
+       * ld.h (struct ld_state): Add need_tls, tls_start, and tls_tcb
+       elements.
+       * ldgeneric.c (add_section): If TLS section is used, set need_tls flag.
+       (ld_generic_create_outfile): Add PT_TLS entry to program  header.
+       Fix generation of PT_GNU_STACK entry.
+
 2008-02-01  Ulrich Drepper  <drepper@redhat.com>
 
        * ld.c (replace_args): Prevent loop over replacements if the parameter
index 1c46b690d1fd268b5bb7c7a975c5ded88354c7b1..a6cfffa1fdd9960e388bb9b91c619b0f13be63cb 100644 (file)
@@ -50,6 +50,7 @@ SEGMENT [RX]
       *(.gnu.linkonce.r.*)
     }
   .rodata1;
+  .eh_frame_hdr;
   . = ALIGN(32 / 8);
   PROVIDE (__preinit_array_start = .);
   .preinit_array
@@ -93,6 +94,19 @@ SEGMENT [RW]
       KEEP (*(.eh_frame))
     }
   .gcc_except_table;
+  .tdata
+    {
+      *(.tdata)
+      *(.tdata.*)
+      *(.gnu.linkone.td.*)
+    }
+  .tbss
+    {
+      *(.tbss)
+      *(.tbss.*)
+      *(.gnu.linkone.tb.*)
+      *(.tcommon)
+    }
   .ctors
     {
       /* gcc uses crtbegin.o to find the start of
index a3182442e22047c120ce00957376e03a087dbfd5..c123d823d52e4c72a2ce5bf70e9bb995677bca21 100644 (file)
@@ -646,9 +646,17 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
                }
              break;
 
+           case R_386_TLS_LDO_32:
+             if (statep->file_type != executable_file_type)
+               abort ();
+             break;
+
+           case R_386_TLS_LE:
+             /* We never need a relocation in the output file.  */
+             break;
+
            case R_386_TLS_IE:
            case R_386_TLS_GOTIE:
-           case R_386_TLS_LE:
            case R_386_TLS_GD:
            case R_386_TLS_LDM:
            case R_386_TLS_GD_32:
@@ -659,7 +667,6 @@ elf_i386_count_relocations (struct ld_state *statep, struct scninfo *scninfo)
            case R_386_TLS_LDM_PUSH:
            case R_386_TLS_LDM_CALL:
            case R_386_TLS_LDM_POP:
-           case R_386_TLS_LDO_32:
            case R_386_TLS_IE_32:
            case R_386_TLS_LE_32:
              /* XXX */
@@ -928,11 +935,20 @@ elf_i386_create_relocations (struct ld_state *statep,
              add_4ubyte_unaligned (relloc, value - gotaddr);
              break;
 
+           case R_386_TLS_LE:
+             value = symref[idx]->merge.value - ld_state.tls_tcb;
+             store_4ubyte_unaligned (relloc, value);
+             break;
+
+           case R_386_TLS_LDO_32:
+             value = symref[idx]->merge.value - ld_state.tls_start;
+             store_4ubyte_unaligned (relloc, value);
+             break;
+
            case R_386_32PLT:
            case R_386_TLS_TPOFF:
            case R_386_TLS_IE:
            case R_386_TLS_GOTIE:
-           case R_386_TLS_LE:
            case R_386_TLS_GD:
            case R_386_TLS_LDM:
            case R_386_16:
@@ -947,7 +963,6 @@ elf_i386_create_relocations (struct ld_state *statep,
            case R_386_TLS_LDM_PUSH:
            case R_386_TLS_LDM_CALL:
            case R_386_TLS_LDM_POP:
-           case R_386_TLS_LDO_32:
            case R_386_TLS_IE_32:
            case R_386_TLS_LE_32:
              // XXX For now fall through
index 860bcdb4e9cff80d18203873b3e7010e370a835c..535f7cd2df74dc5ca088caac3f09fe24baa2c1cd 100644 (file)
--- a/src/ld.h
+++ b/src/ld.h
@@ -967,6 +967,11 @@ struct ld_state
   /* Index of next version.  */
   int nextveridx;
 
+  /* TLS segment.  */
+  bool need_tls;
+  XElf_Addr tls_start;
+  XElf_Addr tls_tcb;
+
   /* Hash table for version symbol strings.  Only strings without
      special characters are hashed here.  */
   ld_version_str_tab version_str_tab;
index bf0d06e35e1b1344cf5c5c1abb9530c449b4d2c3..e0cc4b483a9175deab42668004cf7ab9e22b68ac 100644 (file)
@@ -1085,6 +1085,9 @@ add_section (struct usedfiles *fileinfo, struct scninfo *scninfo)
       queued->segment_nr = ~0;
       queued->last = scninfo->next = scninfo;
 
+      /* Check whether we need a TLS segment.  */
+      ld_state.need_tls |= (shdr->sh_flags & SHF_TLS) != 0;
+
       /* Add to the hash table and possibly overwrite existing value.  */
       ld_section_tab_insert (&ld_state.section_tab, hval, queued);
     }
@@ -6209,13 +6212,6 @@ section index too large in dynamic symbol table"));
 
   if (ld_state.file_type != relocatable_file_type)
     {
-      size_t nphdr;
-      XElf_Addr addr;
-      struct output_segment *segment;
-      Elf_Scn *scn;
-      Elf32_Word nsec;
-      XElf_Phdr_vardef (phdr);
-
       /* Every executable needs a program header.  The number of entries
         varies.  One exists for each segment.  Each SHT_NOTE section gets
         one, too.  For dynamically linked executables we have to create
@@ -6223,12 +6219,12 @@ section index too large in dynamic symbol table"));
         section.  First count the number of segments.
 
         XXX Determine whether the segment is non-empty.  */
-      nphdr = 0;
+      size_t nphdr = 0;
 
       /* We always add a PT_GNU_stack entry.  */
       ++nphdr;
 
-      segment = ld_state.output_segments;
+      struct output_segment *segment = ld_state.output_segments;
       while (segment != NULL)
        {
          ++nphdr;
@@ -6249,7 +6245,12 @@ section index too large in dynamic symbol table"));
            nphdr += 2;
        }
 
+      /* If we need a TLS segment we need an entry for that.  */
+      if (ld_state.need_tls)
+       ++nphdr;
+
       /* Create the program header structure.  */
+      XElf_Phdr_vardef (phdr);
       if (xelf_newphdr (ld_state.outelf, nphdr) == 0)
        error (EXIT_FAILURE, 0, gettext ("cannot create program header: %s"),
               elf_errmsg (-1));
@@ -6265,14 +6266,20 @@ section index too large in dynamic symbol table"));
 
       /* Now determine the memory addresses of all the sections and
         segments.  */
-      nsec = 0;
-      scn = elf_getscn (ld_state.outelf, ld_state.allsections[nsec]->scnidx);
+      Elf32_Word nsec = 0;
+      Elf_Scn *scn = elf_getscn (ld_state.outelf,
+                                ld_state.allsections[nsec]->scnidx);
       xelf_getshdr (scn, shdr);
       assert (shdr != NULL);
 
       /* The address we start with is the offset of the first (not
         zeroth) section.  */
-      addr = shdr->sh_offset;
+      XElf_Addr addr = shdr->sh_offset;
+      XElf_Addr tls_offset = 0;
+      XElf_Addr tls_start = ~((XElf_Addr) 0);
+      XElf_Addr tls_end = 0;
+      XElf_Off tls_filesize = 0;
+      XElf_Addr tls_align = 0;
 
       /* The index of the first loadable segment.  */
       nphdr = 0;
@@ -6292,15 +6299,13 @@ section index too large in dynamic symbol table"));
          XElf_Off nobits_size = 0;
          XElf_Off memsize = 0;
 
-         /* the minimum alignment is a page size.  */
+         /* The minimum alignment is a page size.  */
          segment->align = ld_state.pagesize;
 
          for (orule = segment->output_rules; orule != NULL;
               orule = orule->next)
            if (orule->tag == output_section)
              {
-               XElf_Off oldoff;
-
                /* See whether this output rule corresponds to the next
                   section.  Yes, this is a pointer comparison.  */
                if (ld_state.allsections[nsec]->name
@@ -6330,6 +6335,22 @@ section index too large in dynamic symbol table"));
 
                    /* Remember the address.  */
                    ld_state.allsections[nsec]->addr = addr;
+
+                   /* Handle TLS sections.  */
+                   if (unlikely (shdr->sh_flags & SHF_TLS))
+                     {
+                       if (tls_start > addr)
+                         {
+                           tls_start = addr;
+                           tls_offset = shdr->sh_offset;
+                         }
+                       if (tls_end < addr + shdr->sh_size)
+                         tls_end = addr + shdr->sh_size;
+                       if (shdr->sh_type != SHT_NOBITS)
+                         tls_filesize += shdr->sh_size;
+                       if (shdr->sh_addralign > tls_align)
+                         tls_align = shdr->sh_addralign;
+                     }
                  }
 
                if (first_section)
@@ -6352,12 +6373,19 @@ section index too large in dynamic symbol table"));
                    first_section = false;
                  }
 
-               memsize = shdr->sh_offset - segment->offset + shdr->sh_size;
-               if (nobits_size != 0 && shdr->sh_type != SHT_NOTE)
-                 error (EXIT_FAILURE, 0, gettext ("\
-internal error: nobits section follows nobits section"));
-               if (shdr->sh_type == SHT_NOBITS)
-                 nobits_size += shdr->sh_size;
+               /* NOBITS TLS sections are not laid out in address space
+                  along with the other sections.  */
+               if (shdr->sh_type != SHT_NOBITS
+                   || (shdr->sh_flags & SHF_TLS) == 0)
+                 {
+                   memsize = (shdr->sh_offset - segment->offset
+                              + shdr->sh_size);
+                   if (nobits_size != 0 && shdr->sh_type != SHT_NOTE)
+                     error (EXIT_FAILURE, 0, gettext ("\
+internal error: non-nobits section follows nobits section"));
+                   if (shdr->sh_type == SHT_NOBITS)
+                     nobits_size += shdr->sh_size;
+                 }
 
                /* Determine the new address which is computed using
                   the difference of the offsets on the sections.  Note
@@ -6365,7 +6393,7 @@ internal error: nobits section follows nobits section"));
                   other in the section header table are also
                   consecutive in the file.  This is true here because
                   libelf constructs files this way.  */
-               oldoff = shdr->sh_offset;
+               XElf_Off oldoff = shdr->sh_offset;
 
                if (++nsec >= ld_state.nallsections)
                  break;
@@ -6440,6 +6468,25 @@ internal error: nobits section follows nobits section"));
       xelf_getehdr (ld_state.outelf, ehdr);
       assert (ehdr != NULL);
 
+      /* Add the TLS information.  */
+      if (ld_state.need_tls)
+       {
+         xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr);
+         phdr->p_type = PT_TLS;
+         phdr->p_offset = tls_offset;
+         phdr->p_vaddr = tls_start;
+         phdr->p_paddr = tls_start;
+         phdr->p_filesz = tls_filesize;
+         phdr->p_memsz = tls_end - tls_start;
+         phdr->p_flags = PF_R;
+         phdr->p_align = tls_align;
+         ld_state.tls_tcb = tls_end;
+         ld_state.tls_start = tls_start;
+
+         (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr);
+         ++nphdr;
+       }
+
       /* Add the stack information.  */
       xelf_getphdr_ptr (ld_state.outelf, nphdr, phdr);
       phdr->p_type = PT_GNU_STACK;
@@ -6448,8 +6495,9 @@ internal error: nobits section follows nobits section"));
       phdr->p_paddr = 0;
       phdr->p_filesz = 0;
       phdr->p_memsz = 0;
-      phdr->p_flags = ld_state.execstack == execstack_true ? PF_X : 0;
-      phdr->p_align = 0;
+      phdr->p_flags = (PF_R | PF_W
+                      | (ld_state.execstack == execstack_true ? PF_X : 0));
+      phdr->p_align = xelf_fsize (ld_state.outelf, ELF_T_ADDR, 1);
 
       (void) xelf_update_phdr (ld_state.outelf, nphdr, phdr);
       ++nphdr;