summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2008-02-02 10:01:53 +0000
committerUlrich Drepper <drepper@redhat.com>2008-02-02 10:01:53 +0000
commit712d78eced9020ccfa1c27d32c2929cf16398293 (patch)
treeb9e59971c3e485d9971cca8b92f1d7ee37827511
parent7cd1bf7b762f0b7d3b1158e52c800d299ed5333f (diff)
Start implementation of x86 TLS implementation in ld.
-rw-r--r--src/ChangeLog12
-rw-r--r--src/elf32-i386.script14
-rw-r--r--src/i386_ld.c23
-rw-r--r--src/ld.h5
-rw-r--r--src/ldgeneric.c96
5 files changed, 122 insertions, 28 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index a532a98e..d3e49e41 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -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
diff --git a/src/elf32-i386.script b/src/elf32-i386.script
index 1c46b690..a6cfffa1 100644
--- a/src/elf32-i386.script
+++ b/src/elf32-i386.script
@@ -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
diff --git a/src/i386_ld.c b/src/i386_ld.c
index a3182442..c123d823 100644
--- a/src/i386_ld.c
+++ b/src/i386_ld.c
@@ -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
diff --git a/src/ld.h b/src/ld.h
index 860bcdb4..535f7cd2 100644
--- 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;
diff --git a/src/ldgeneric.c b/src/ldgeneric.c
index bf0d06e3..e0cc4b48 100644
--- a/src/ldgeneric.c
+++ b/src/ldgeneric.c
@@ -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;