From 712d78eced9020ccfa1c27d32c2929cf16398293 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 2 Feb 2008 10:01:53 +0000 Subject: Start implementation of x86 TLS implementation in ld. --- src/ChangeLog | 12 +++++++ src/elf32-i386.script | 14 ++++++++ src/i386_ld.c | 23 +++++++++--- src/ld.h | 5 +++ src/ldgeneric.c | 96 ++++++++++++++++++++++++++++++++++++++------------- 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 + + * 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 * 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; -- cgit v1.2.3