diff options
author | obrien <obrien@FreeBSD.org> | 2002-10-11 06:10:28 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2002-10-11 06:10:28 +0000 |
commit | 8fe243d22d92c90fa21ca2cd2016f2b9e95ab639 (patch) | |
tree | d28e685449bfff897e191b5b3375d91329beef63 /contrib | |
parent | f81eaaf0a0e79c52bab9370cde97ecfbfb5e51a8 (diff) | |
download | FreeBSD-src-8fe243d22d92c90fa21ca2cd2016f2b9e95ab639.zip FreeBSD-src-8fe243d22d92c90fa21ca2cd2016f2b9e95ab639.tar.gz |
Merge rev 1.8 ("ELF_DYNAMIC_INTERPRETER") into Binutils 2.13_20021110 snap.
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/binutils/bfd/elf32-i386.c | 1146 |
1 files changed, 1033 insertions, 113 deletions
diff --git a/contrib/binutils/bfd/elf32-i386.c b/contrib/binutils/bfd/elf32-i386.c index 13ac62e..da63a07 100644 --- a/contrib/binutils/bfd/elf32-i386.c +++ b/contrib/binutils/bfd/elf32-i386.c @@ -47,12 +47,20 @@ static boolean create_got_section static boolean elf_i386_create_dynamic_sections PARAMS((bfd *, struct bfd_link_info *)); static void elf_i386_copy_indirect_symbol - PARAMS ((struct elf_link_hash_entry *, struct elf_link_hash_entry *)); + PARAMS ((struct elf_backend_data *, struct elf_link_hash_entry *, + struct elf_link_hash_entry *)); +static int elf_i386_tls_transition + PARAMS ((struct bfd_link_info *, int, int)); + +static boolean elf_i386_mkobject + PARAMS((bfd *)); +static boolean elf_i386_object_p + PARAMS((bfd *)); static boolean elf_i386_check_relocs PARAMS ((bfd *, struct bfd_link_info *, asection *, const Elf_Internal_Rela *)); static asection *elf_i386_gc_mark_hook - PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *, + PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, struct elf_link_hash_entry *, Elf_Internal_Sym *)); static boolean elf_i386_gc_sweep_hook PARAMS ((bfd *, struct bfd_link_info *, asection *, @@ -67,6 +75,10 @@ static boolean elf_i386_fake_sections PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *)); static boolean elf_i386_size_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *)); +static bfd_vma dtpoff_base + PARAMS ((struct bfd_link_info *)); +static bfd_vma tpoff + PARAMS ((struct bfd_link_info *, bfd_vma)); static boolean elf_i386_relocate_section PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); @@ -123,9 +135,27 @@ static reloc_howto_type elf_howto_table[]= R_386_ext_offset is the value to subtract from a reloc type of R_386_16 thru R_386_PC8 to form an index into this table. */ #define R_386_standard ((unsigned int) R_386_GOTPC + 1) -#define R_386_ext_offset ((unsigned int) R_386_16 - R_386_standard) +#define R_386_ext_offset ((unsigned int) R_386_TLS_TPOFF - R_386_standard) - /* The remaining relocs are a GNU extension. */ + /* These relocs are a GNU extension. */ + HOWTO(R_386_TLS_TPOFF, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_TPOFF", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_IE, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_IE", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_GOTIE, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_GOTIE", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_LE, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_LE", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_GD, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_GD", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_LDM, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_LDM", + true, 0xffffffff, 0xffffffff, false), HOWTO(R_386_16, 0, 1, 16, false, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_386_16", true, 0xffff, 0xffff, false), @@ -139,9 +169,31 @@ static reloc_howto_type elf_howto_table[]= bfd_elf_generic_reloc, "R_386_PC8", true, 0xff, 0xff, true), - /* Another gap. */ #define R_386_ext ((unsigned int) R_386_PC8 + 1 - R_386_ext_offset) -#define R_386_vt_offset ((unsigned int) R_386_GNU_VTINHERIT - R_386_ext) +#define R_386_tls_offset ((unsigned int) R_386_TLS_LDO_32 - R_386_ext) + /* These are common with Solaris TLS implementation. */ + HOWTO(R_386_TLS_LDO_32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_LDO_32", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_IE_32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_IE_32", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_LE_32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_LE_32", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_DTPMOD32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_DTPMOD32", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_DTPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_DTPOFF32", + true, 0xffffffff, 0xffffffff, false), + HOWTO(R_386_TLS_TPOFF32, 0, 2, 32, false, 0, complain_overflow_bitfield, + bfd_elf_generic_reloc, "R_386_TLS_TPOFF32", + true, 0xffffffff, 0xffffffff, false), + + /* Another gap. */ +#define R_386_tls ((unsigned int) R_386_TLS_TPOFF32 + 1 - R_386_tls_offset) +#define R_386_vt_offset ((unsigned int) R_386_GNU_VTINHERIT - R_386_tls) /* GNU extension to record C++ vtable hierarchy. */ HOWTO (R_386_GNU_VTINHERIT, /* type */ @@ -156,7 +208,7 @@ static reloc_howto_type elf_howto_table[]= false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ - false), + false), /* pcrel_offset */ /* GNU extension to record C++ vtable member usage. */ HOWTO (R_386_GNU_VTENTRY, /* type */ @@ -171,7 +223,7 @@ static reloc_howto_type elf_howto_table[]= false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ - false) + false) /* pcrel_offset */ #define R_386_vt ((unsigned int) R_386_GNU_VTENTRY + 1 - R_386_vt_offset) @@ -238,7 +290,31 @@ elf_i386_reloc_type_lookup (abfd, code) TRACE ("BFD_RELOC_386_GOTPC"); return &elf_howto_table[(unsigned int) R_386_GOTPC ]; - /* The remaining relocs are a GNU extension. */ + /* These relocs are a GNU extension. */ + case BFD_RELOC_386_TLS_TPOFF: + TRACE ("BFD_RELOC_386_TLS_TPOFF"); + return &elf_howto_table[(unsigned int) R_386_TLS_TPOFF - R_386_ext_offset]; + + case BFD_RELOC_386_TLS_IE: + TRACE ("BFD_RELOC_386_TLS_IE"); + return &elf_howto_table[(unsigned int) R_386_TLS_IE - R_386_ext_offset]; + + case BFD_RELOC_386_TLS_GOTIE: + TRACE ("BFD_RELOC_386_TLS_GOTIE"); + return &elf_howto_table[(unsigned int) R_386_TLS_GOTIE - R_386_ext_offset]; + + case BFD_RELOC_386_TLS_LE: + TRACE ("BFD_RELOC_386_TLS_LE"); + return &elf_howto_table[(unsigned int) R_386_TLS_LE - R_386_ext_offset]; + + case BFD_RELOC_386_TLS_GD: + TRACE ("BFD_RELOC_386_TLS_GD"); + return &elf_howto_table[(unsigned int) R_386_TLS_GD - R_386_ext_offset]; + + case BFD_RELOC_386_TLS_LDM: + TRACE ("BFD_RELOC_386_TLS_LDM"); + return &elf_howto_table[(unsigned int) R_386_TLS_LDM - R_386_ext_offset]; + case BFD_RELOC_16: TRACE ("BFD_RELOC_16"); return &elf_howto_table[(unsigned int) R_386_16 - R_386_ext_offset]; @@ -255,6 +331,31 @@ elf_i386_reloc_type_lookup (abfd, code) TRACE ("BFD_RELOC_8_PCREL"); return &elf_howto_table[(unsigned int) R_386_PC8 - R_386_ext_offset]; + /* Common with Sun TLS implementation. */ + case BFD_RELOC_386_TLS_LDO_32: + TRACE ("BFD_RELOC_386_TLS_LDO_32"); + return &elf_howto_table[(unsigned int) R_386_TLS_LDO_32 - R_386_tls_offset]; + + case BFD_RELOC_386_TLS_IE_32: + TRACE ("BFD_RELOC_386_TLS_IE_32"); + return &elf_howto_table[(unsigned int) R_386_TLS_IE_32 - R_386_tls_offset]; + + case BFD_RELOC_386_TLS_LE_32: + TRACE ("BFD_RELOC_386_TLS_LE_32"); + return &elf_howto_table[(unsigned int) R_386_TLS_LE_32 - R_386_tls_offset]; + + case BFD_RELOC_386_TLS_DTPMOD32: + TRACE ("BFD_RELOC_386_TLS_DTPMOD32"); + return &elf_howto_table[(unsigned int) R_386_TLS_DTPMOD32 - R_386_tls_offset]; + + case BFD_RELOC_386_TLS_DTPOFF32: + TRACE ("BFD_RELOC_386_TLS_DTPOFF32"); + return &elf_howto_table[(unsigned int) R_386_TLS_DTPOFF32 - R_386_tls_offset]; + + case BFD_RELOC_386_TLS_TPOFF32: + TRACE ("BFD_RELOC_386_TLS_TPOFF32"); + return &elf_howto_table[(unsigned int) R_386_TLS_TPOFF32 - R_386_tls_offset]; + case BFD_RELOC_VTABLE_INHERIT: TRACE ("BFD_RELOC_VTABLE_INHERIT"); return &elf_howto_table[(unsigned int) R_386_GNU_VTINHERIT @@ -294,8 +395,10 @@ elf_i386_info_to_howto_rel (abfd, cache_ptr, dst) if ((indx = r_type) >= R_386_standard && ((indx = r_type - R_386_ext_offset) - R_386_standard >= R_386_ext - R_386_standard) - && ((indx = r_type - R_386_vt_offset) - R_386_ext - >= R_386_vt - R_386_ext)) + && ((indx = r_type - R_386_tls_offset) - R_386_ext + >= R_386_tls - R_386_ext) + && ((indx = r_type - R_386_vt_offset) - R_386_tls + >= R_386_vt - R_386_tls)) { (*_bfd_error_handler) (_("%s: invalid relocation type %d"), bfd_archive_filename (abfd), (int) r_type); @@ -487,8 +590,59 @@ struct elf_i386_link_hash_entry /* Track dynamic relocs copied for this symbol. */ struct elf_i386_dyn_relocs *dyn_relocs; + +#define GOT_UNKNOWN 0 +#define GOT_NORMAL 1 +#define GOT_TLS_GD 2 +#define GOT_TLS_IE 4 +#define GOT_TLS_IE_POS 5 +#define GOT_TLS_IE_NEG 6 +#define GOT_TLS_IE_BOTH 7 + unsigned char tls_type; }; +#define elf_i386_hash_entry(ent) ((struct elf_i386_link_hash_entry *)(ent)) + +struct elf_i386_obj_tdata +{ + struct elf_obj_tdata root; + + /* tls_type for each local got entry. */ + char *local_got_tls_type; +}; + +#define elf_i386_tdata(abfd) \ + ((struct elf_i386_obj_tdata *) (abfd)->tdata.any) + +#define elf_i386_local_got_tls_type(abfd) \ + (elf_i386_tdata (abfd)->local_got_tls_type) + +static boolean +elf_i386_mkobject (abfd) + bfd *abfd; +{ + bfd_size_type amt = sizeof (struct elf_i386_obj_tdata); + abfd->tdata.any = bfd_zalloc (abfd, amt); + if (abfd->tdata.any == NULL) + return false; + return true; +} + +static boolean +elf_i386_object_p (abfd) + bfd *abfd; +{ + /* Allocate our special target data. */ + struct elf_i386_obj_tdata *new_tdata; + bfd_size_type amt = sizeof (struct elf_i386_obj_tdata); + new_tdata = bfd_zalloc (abfd, amt); + if (new_tdata == NULL) + return false; + new_tdata->root = *abfd->tdata.elf_obj_data; + abfd->tdata.any = new_tdata; + return true; +} + /* i386 ELF linker hash table. */ struct elf_i386_link_hash_table @@ -504,6 +658,11 @@ struct elf_i386_link_hash_table asection *sdynbss; asection *srelbss; + union { + bfd_signed_vma refcount; + bfd_vma offset; + } tls_ldm_got; + /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; }; @@ -539,6 +698,7 @@ link_hash_newfunc (entry, table, string) eh = (struct elf_i386_link_hash_entry *) entry; eh->dyn_relocs = NULL; + eh->tls_type = GOT_UNKNOWN; } return entry; @@ -553,13 +713,13 @@ elf_i386_link_hash_table_create (abfd) struct elf_i386_link_hash_table *ret; bfd_size_type amt = sizeof (struct elf_i386_link_hash_table); - ret = (struct elf_i386_link_hash_table *) bfd_alloc (abfd, amt); + ret = (struct elf_i386_link_hash_table *) bfd_malloc (amt); if (ret == NULL) return NULL; if (! _bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc)) { - bfd_release (abfd, ret); + free (ret); return NULL; } @@ -570,6 +730,7 @@ elf_i386_link_hash_table_create (abfd) ret->srelplt = NULL; ret->sdynbss = NULL; ret->srelbss = NULL; + ret->tls_ldm_got.refcount = 0; ret->sym_sec.abfd = NULL; return &ret->elf.root; @@ -639,7 +800,8 @@ elf_i386_create_dynamic_sections (dynobj, info) /* Copy the extra info we tack onto an elf_link_hash_entry. */ static void -elf_i386_copy_indirect_symbol (dir, ind) +elf_i386_copy_indirect_symbol (bed, dir, ind) + struct elf_backend_data *bed; struct elf_link_hash_entry *dir, *ind; { struct elf_i386_link_hash_entry *edir, *eind; @@ -681,7 +843,41 @@ elf_i386_copy_indirect_symbol (dir, ind) eind->dyn_relocs = NULL; } - _bfd_elf_link_hash_copy_indirect (dir, ind); + if (ind->root.type == bfd_link_hash_indirect + && dir->got.refcount <= 0) + { + edir->tls_type = eind->tls_type; + eind->tls_type = GOT_UNKNOWN; + } + _bfd_elf_link_hash_copy_indirect (bed, dir, ind); +} + +static int +elf_i386_tls_transition (info, r_type, is_local) + struct bfd_link_info *info; + int r_type; + int is_local; +{ + if (info->shared) + return r_type; + + switch (r_type) + { + case R_386_TLS_GD: + case R_386_TLS_IE_32: + if (is_local) + return R_386_TLS_LE_32; + return R_386_TLS_IE_32; + case R_386_TLS_IE: + case R_386_TLS_GOTIE: + if (is_local) + return R_386_TLS_LE_32; + return r_type; + case R_386_TLS_LDM: + return R_386_TLS_LE_32; + } + + return r_type; } /* Look through the relocs for a section during the first phase, and @@ -714,10 +910,12 @@ elf_i386_check_relocs (abfd, info, sec, relocs) rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { + unsigned int r_type; unsigned long r_symndx; struct elf_link_hash_entry *h; r_symndx = ELF32_R_SYM (rel->r_info); + r_type = ELF32_R_TYPE (rel->r_info); if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) { @@ -732,46 +930,13 @@ elf_i386_check_relocs (abfd, info, sec, relocs) else h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - switch (ELF32_R_TYPE (rel->r_info)) - { - case R_386_GOT32: - /* This symbol requires a global offset table entry. */ - if (h != NULL) - { - h->got.refcount += 1; - } - else - { - bfd_signed_vma *local_got_refcounts; - - /* This is a global offset table entry for a local symbol. */ - local_got_refcounts = elf_local_got_refcounts (abfd); - if (local_got_refcounts == NULL) - { - bfd_size_type size; + r_type = elf_i386_tls_transition (info, r_type, h == NULL); - size = symtab_hdr->sh_info; - size *= sizeof (bfd_signed_vma); - local_got_refcounts = ((bfd_signed_vma *) - bfd_zalloc (abfd, size)); - if (local_got_refcounts == NULL) - return false; - elf_local_got_refcounts (abfd) = local_got_refcounts; - } - local_got_refcounts[r_symndx] += 1; - } - /* Fall through */ - - case R_386_GOTOFF: - case R_386_GOTPC: - if (htab->sgot == NULL) - { - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; - if (!create_got_section (htab->elf.dynobj, info)) - return false; - } - break; + switch (r_type) + { + case R_386_TLS_LDM: + htab->tls_ldm_got.refcount += 1; + goto create_got; case R_386_PLT32: /* This symbol requires a procedure linkage table entry. We @@ -790,6 +955,116 @@ elf_i386_check_relocs (abfd, info, sec, relocs) h->plt.refcount += 1; break; + case R_386_TLS_IE_32: + case R_386_TLS_IE: + case R_386_TLS_GOTIE: + if (info->shared) + info->flags |= DF_STATIC_TLS; + /* Fall through */ + + case R_386_GOT32: + case R_386_TLS_GD: + /* This symbol requires a global offset table entry. */ + { + int tls_type, old_tls_type; + + switch (r_type) + { + default: + case R_386_GOT32: tls_type = GOT_NORMAL; break; + case R_386_TLS_GD: tls_type = GOT_TLS_GD; break; + case R_386_TLS_IE_32: + if (ELF32_R_TYPE (rel->r_info) == r_type) + tls_type = GOT_TLS_IE_NEG; + else + /* If this is a GD->IE transition, we may use either of + R_386_TLS_TPOFF and R_386_TLS_TPOFF32. */ + tls_type = GOT_TLS_IE; + break; + case R_386_TLS_IE: + case R_386_TLS_GOTIE: + tls_type = GOT_TLS_IE_POS; break; + } + + if (h != NULL) + { + h->got.refcount += 1; + old_tls_type = elf_i386_hash_entry(h)->tls_type; + } + else + { + bfd_signed_vma *local_got_refcounts; + + /* This is a global offset table entry for a local symbol. */ + local_got_refcounts = elf_local_got_refcounts (abfd); + if (local_got_refcounts == NULL) + { + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= (sizeof (bfd_signed_vma) + sizeof(char)); + local_got_refcounts = ((bfd_signed_vma *) + bfd_zalloc (abfd, size)); + if (local_got_refcounts == NULL) + return false; + elf_local_got_refcounts (abfd) = local_got_refcounts; + elf_i386_local_got_tls_type (abfd) + = (char *) (local_got_refcounts + symtab_hdr->sh_info); + } + local_got_refcounts[r_symndx] += 1; + old_tls_type = elf_i386_local_got_tls_type (abfd) [r_symndx]; + } + + if ((old_tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_IE)) + tls_type |= old_tls_type; + /* If a TLS symbol is accessed using IE at least once, + there is no point to use dynamic model for it. */ + else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN + && (old_tls_type != GOT_TLS_GD + || (tls_type & GOT_TLS_IE) == 0)) + { + if ((old_tls_type & GOT_TLS_IE) && tls_type == GOT_TLS_GD) + tls_type = old_tls_type; + else + { + (*_bfd_error_handler) + (_("%s: `%s' accessed both as normal and thread local symbol"), + bfd_archive_filename (abfd), + h ? h->root.root.string : "<local>"); + return false; + } + } + + if (old_tls_type != tls_type) + { + if (h != NULL) + elf_i386_hash_entry (h)->tls_type = tls_type; + else + elf_i386_local_got_tls_type (abfd) [r_symndx] = tls_type; + } + } + /* Fall through */ + + case R_386_GOTOFF: + case R_386_GOTPC: + create_got: + if (htab->sgot == NULL) + { + if (htab->elf.dynobj == NULL) + htab->elf.dynobj = abfd; + if (!create_got_section (htab->elf.dynobj, info)) + return false; + } + if (r_type != R_386_TLS_IE) + break; + /* Fall through */ + + case R_386_TLS_LE_32: + case R_386_TLS_LE: + if (!info->shared) + break; + /* Fall through */ + case R_386_32: case R_386_PC32: if (h != NULL && !info->shared) @@ -830,7 +1105,7 @@ elf_i386_check_relocs (abfd, info, sec, relocs) symbol. */ if ((info->shared && (sec->flags & SEC_ALLOC) != 0 - && (ELF32_R_TYPE (rel->r_info) != R_386_PC32 + && (r_type != R_386_PC32 || (h != NULL && (! info->symbolic || h->root.type == bfd_link_hash_defweak @@ -929,7 +1204,7 @@ elf_i386_check_relocs (abfd, info, sec, relocs) } p->count += 1; - if (ELF32_R_TYPE (rel->r_info) == R_386_PC32) + if (r_type == R_386_PC32) p->pc_count += 1; } break; @@ -960,8 +1235,8 @@ elf_i386_check_relocs (abfd, info, sec, relocs) relocation. */ static asection * -elf_i386_gc_mark_hook (abfd, info, rel, h, sym) - bfd *abfd; +elf_i386_gc_mark_hook (sec, info, rel, h, sym) + asection *sec; struct bfd_link_info *info ATTRIBUTE_UNUSED; Elf_Internal_Rela *rel; struct elf_link_hash_entry *h; @@ -991,9 +1266,7 @@ elf_i386_gc_mark_hook (abfd, info, rel, h, sym) } } else - { - return bfd_section_from_elf_index (abfd, sym->st_shndx); - } + return bfd_section_from_elf_index (sec->owner, sym->st_shndx); return NULL; } @@ -1012,6 +1285,7 @@ elf_i386_gc_sweep_hook (abfd, info, sec, relocs) bfd_signed_vma *local_got_refcounts; const Elf_Internal_Rela *rel, *relend; unsigned long r_symndx; + int r_type; struct elf_link_hash_entry *h; elf_section_data (sec)->local_dynrel = NULL; @@ -1022,11 +1296,21 @@ elf_i386_gc_sweep_hook (abfd, info, sec, relocs) relend = relocs + sec->reloc_count; for (rel = relocs; rel < relend; rel++) - switch (ELF32_R_TYPE (rel->r_info)) + switch ((r_type = elf_i386_tls_transition (info, + ELF32_R_TYPE (rel->r_info), + ELF32_R_SYM (rel->r_info) + >= symtab_hdr->sh_info))) { + case R_386_TLS_LDM: + if (elf_i386_hash_table (info)->tls_ldm_got.refcount > 0) + elf_i386_hash_table (info)->tls_ldm_got.refcount -= 1; + break; + + case R_386_TLS_GD: + case R_386_TLS_IE_32: + case R_386_TLS_IE: + case R_386_TLS_GOTIE: case R_386_GOT32: - case R_386_GOTOFF: - case R_386_GOTPC: r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx >= symtab_hdr->sh_info) { @@ -1039,7 +1323,15 @@ elf_i386_gc_sweep_hook (abfd, info, sec, relocs) if (local_got_refcounts[r_symndx] > 0) local_got_refcounts[r_symndx] -= 1; } - break; + if (r_type != R_386_TLS_IE) + break; + /* Fall through */ + + case R_386_TLS_LE_32: + case R_386_TLS_LE: + if (!info->shared) + break; + /* Fall through */ case R_386_32: case R_386_PC32: @@ -1179,7 +1471,7 @@ elf_i386_adjust_dynamic_symbol (info, h) } /* If we didn't find any dynamic relocs in read-only sections, then - we'll be keeping the dynamic relocs and avoiding the copy reloc. */ + we'll be keeping the dynamic relocs and avoiding the copy reloc. */ if (p == NULL) { h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF; @@ -1325,10 +1617,18 @@ allocate_dynrelocs (h, inf) h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT; } - if (h->got.refcount > 0) + /* If R_386_TLS_{IE_32,IE,GOTIE} symbol is now local to the binary, + make it a R_386_TLS_LE_32 requiring no TLS entry. */ + if (h->got.refcount > 0 + && !info->shared + && h->dynindx == -1 + && (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE)) + h->got.offset = (bfd_vma) -1; + else if (h->got.refcount > 0) { asection *s; boolean dyn; + int tls_type = elf_i386_hash_entry(h)->tls_type; /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ @@ -1342,8 +1642,23 @@ allocate_dynrelocs (h, inf) s = htab->sgot; h->got.offset = s->_raw_size; s->_raw_size += 4; + /* R_386_TLS_GD needs 2 consecutive GOT slots. */ + if (tls_type == GOT_TLS_GD || tls_type == GOT_TLS_IE_BOTH) + s->_raw_size += 4; dyn = htab->elf.dynamic_sections_created; - if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)) + /* R_386_TLS_IE_32 needs one dynamic relocation, + R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation, + (but if both R_386_TLS_IE_32 and R_386_TLS_IE is present, we + need two), R_386_TLS_GD needs one if local symbol and two if + global. */ + if (tls_type == GOT_TLS_IE_BOTH) + htab->srelgot->_raw_size += 2 * sizeof (Elf32_External_Rel); + else if ((tls_type == GOT_TLS_GD && h->dynindx == -1) + || (tls_type & GOT_TLS_IE)) + htab->srelgot->_raw_size += sizeof (Elf32_External_Rel); + else if (tls_type == GOT_TLS_GD) + htab->srelgot->_raw_size += 2 * sizeof (Elf32_External_Rel); + else if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)) htab->srelgot->_raw_size += sizeof (Elf32_External_Rel); } else @@ -1489,6 +1804,7 @@ elf_i386_size_dynamic_sections (output_bfd, info) { bfd_signed_vma *local_got; bfd_signed_vma *end_local_got; + char *local_tls_type; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; @@ -1530,22 +1846,44 @@ elf_i386_size_dynamic_sections (output_bfd, info) symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; + local_tls_type = elf_i386_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; - for (; local_got < end_local_got; ++local_got) + for (; local_got < end_local_got; ++local_got, ++local_tls_type) { if (*local_got > 0) { *local_got = s->_raw_size; s->_raw_size += 4; - if (info->shared) - srel->_raw_size += sizeof (Elf32_External_Rel); + if (*local_tls_type == GOT_TLS_GD + || *local_tls_type == GOT_TLS_IE_BOTH) + s->_raw_size += 4; + if (info->shared + || *local_tls_type == GOT_TLS_GD + || (*local_tls_type & GOT_TLS_IE)) + { + if (*local_tls_type == GOT_TLS_IE_BOTH) + srel->_raw_size += 2 * sizeof (Elf32_External_Rel); + else + srel->_raw_size += sizeof (Elf32_External_Rel); + } } else *local_got = (bfd_vma) -1; } } + if (htab->tls_ldm_got.refcount > 0) + { + /* Allocate 2 got entries and 1 dynamic reloc for R_386_TLS_LDM + relocs. */ + htab->tls_ldm_got.offset = htab->sgot->_raw_size; + htab->sgot->_raw_size += 8; + htab->srelgot->_raw_size += sizeof (Elf32_External_Rel); + } + else + htab->tls_ldm_got.offset = -1; + /* Allocate global sym .plt and .got entries, and space for global sym dynamic relocs. */ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info); @@ -1691,6 +2029,38 @@ elf_i386_fake_sections (abfd, hdr, sec) return true; } +/* Return the base VMA address which should be subtracted from real addresses + when resolving @dtpoff relocation. + This is PT_TLS segment p_vaddr. */ + +static bfd_vma +dtpoff_base (info) + struct bfd_link_info *info; +{ + /* If tls_segment is NULL, we should have signalled an error already. */ + if (elf_hash_table (info)->tls_segment == NULL) + return 0; + return elf_hash_table (info)->tls_segment->start; +} + +/* Return the relocation value for @tpoff relocation + if STT_TLS virtual address is ADDRESS. */ + +static bfd_vma +tpoff (info, address) + struct bfd_link_info *info; + bfd_vma address; +{ + struct elf_link_tls_segment *tls_segment + = elf_hash_table (info)->tls_segment; + + /* If tls_segment is NULL, we should have signalled an error already. */ + if (tls_segment == NULL) + return 0; + return (align_power (tls_segment->size, tls_segment->align) + + tls_segment->start - address); +} + /* Relocate an i386 ELF section. */ static boolean @@ -1721,7 +2091,7 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) { - int r_type; + unsigned int r_type; reloc_howto_type *howto; unsigned long r_symndx; struct elf_link_hash_entry *h; @@ -1732,6 +2102,7 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, boolean unresolved_reloc; bfd_reloc_status_type r; unsigned int indx; + int tls_type; r_type = ELF32_R_TYPE (rel->r_info); if (r_type == (int) R_386_GNU_VTINHERIT @@ -1739,8 +2110,10 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, continue; if ((indx = (unsigned) r_type) >= R_386_standard - && ((indx = (unsigned) r_type - R_386_ext_offset) - R_386_standard - >= R_386_ext - R_386_standard)) + && ((indx = r_type - R_386_ext_offset) - R_386_standard + >= R_386_ext - R_386_standard) + && ((indx = r_type - R_386_tls_offset) - R_386_ext + >= R_386_tls - R_386_ext)) { bfd_set_error (bfd_error_bad_value); return false; @@ -1751,22 +2124,43 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, if (info->relocateable) { + bfd_vma val; + bfd_byte *where; + /* This is a relocatable link. We don't have to change anything, unless the reloc is against a section symbol, in which case we have to adjust according to where the section symbol winds up in the output section. */ - if (r_symndx < symtab_hdr->sh_info) - { - sym = local_syms + r_symndx; - if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) - { - bfd_vma val; + if (r_symndx >= symtab_hdr->sh_info) + continue; - sec = local_sections[r_symndx]; - val = bfd_get_32 (input_bfd, contents + rel->r_offset); - val += sec->output_offset + sym->st_value; - bfd_put_32 (input_bfd, val, contents + rel->r_offset); - } + sym = local_syms + r_symndx; + if (ELF_ST_TYPE (sym->st_info) != STT_SECTION) + continue; + + sec = local_sections[r_symndx]; + val = sec->output_offset; + if (val == 0) + continue; + + where = contents + rel->r_offset; + switch (howto->size) + { + /* FIXME: overflow checks. */ + case 0: + val += bfd_get_8 (input_bfd, where); + bfd_put_8 (input_bfd, val, where); + break; + case 1: + val += bfd_get_16 (input_bfd, where); + bfd_put_16 (input_bfd, val, where); + break; + case 2: + val += bfd_get_32 (input_bfd, where); + bfd_put_32 (input_bfd, val, where); + break; + default: + abort (); } continue; } @@ -1788,24 +2182,62 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, { asection *msec; bfd_vma addend; + bfd_byte *where = contents + rel->r_offset; - if (howto->src_mask != 0xffffffff) + switch (howto->size) { - (*_bfd_error_handler) - (_("%s(%s+0x%lx): %s relocation against SEC_MERGE section"), - bfd_archive_filename (input_bfd), - bfd_get_section_name (input_bfd, input_section), - (long) rel->r_offset, howto->name); - return false; + case 0: + addend = bfd_get_8 (input_bfd, where); + if (howto->pc_relative) + { + addend = (addend ^ 0x80) - 0x80; + addend += 1; + } + break; + case 1: + addend = bfd_get_16 (input_bfd, where); + if (howto->pc_relative) + { + addend = (addend ^ 0x8000) - 0x8000; + addend += 2; + } + break; + case 2: + addend = bfd_get_32 (input_bfd, where); + if (howto->pc_relative) + { + addend = (addend ^ 0x80000000) - 0x80000000; + addend += 4; + } + break; + default: + abort (); } - addend = bfd_get_32 (input_bfd, contents + rel->r_offset); msec = sec; - addend = - _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend) - - relocation; + addend = _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend); + addend -= relocation; addend += msec->output_section->vma + msec->output_offset; - bfd_put_32 (input_bfd, addend, contents + rel->r_offset); + + switch (howto->size) + { + case 0: + /* FIXME: overflow checks. */ + if (howto->pc_relative) + addend -= 1; + bfd_put_8 (input_bfd, addend, where); + break; + case 1: + if (howto->pc_relative) + addend -= 2; + bfd_put_16 (input_bfd, addend, where); + break; + case 2: + if (howto->pc_relative) + addend -= 4; + bfd_put_32 (input_bfd, addend, where); + break; + } } } else @@ -2065,25 +2497,505 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, } break; + case R_386_TLS_IE: + if (info->shared) + { + Elf_Internal_Rel outrel; + asection *sreloc; + Elf32_External_Rel *loc; + + outrel.r_offset = rel->r_offset + + input_section->output_section->vma + + input_section->output_offset; + outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE); + sreloc = elf_section_data (input_section)->sreloc; + if (sreloc == NULL) + abort (); + loc = (Elf32_External_Rel *) sreloc->contents; + loc += sreloc->reloc_count++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + } + /* Fall through */ + + case R_386_TLS_GD: + case R_386_TLS_IE_32: + case R_386_TLS_GOTIE: + r_type = elf_i386_tls_transition (info, r_type, h == NULL); + tls_type = GOT_UNKNOWN; + if (h == NULL && local_got_offsets) + tls_type = elf_i386_local_got_tls_type (input_bfd) [r_symndx]; + else if (h != NULL) + { + tls_type = elf_i386_hash_entry(h)->tls_type; + if (!info->shared && h->dynindx == -1 && (tls_type & GOT_TLS_IE)) + r_type = R_386_TLS_LE_32; + } + if (tls_type == GOT_TLS_IE) + tls_type = GOT_TLS_IE_NEG; + if (r_type == R_386_TLS_GD) + { + if (tls_type == GOT_TLS_IE_POS) + r_type = R_386_TLS_GOTIE; + else if (tls_type & GOT_TLS_IE) + r_type = R_386_TLS_IE_32; + } + + if (r_type == R_386_TLS_LE_32) + { + BFD_ASSERT (! unresolved_reloc); + if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GD) + { + unsigned int val, type; + bfd_vma roff; + + /* GD->LE transition. */ + BFD_ASSERT (rel->r_offset >= 2); + type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2); + BFD_ASSERT (type == 0x8d || type == 0x04); + BFD_ASSERT (rel->r_offset + 9 <= input_section->_raw_size); + BFD_ASSERT (bfd_get_8 (input_bfd, + contents + rel->r_offset + 4) + == 0xe8); + BFD_ASSERT (rel + 1 < relend); + BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32); + roff = rel->r_offset + 5; + val = bfd_get_8 (input_bfd, + contents + rel->r_offset - 1); + if (type == 0x04) + { + /* leal foo(,%reg,1), %eax; call ___tls_get_addr + Change it into: + movl %gs:0, %eax; subl $foo@tpoff, %eax + (6 byte form of subl). */ + BFD_ASSERT (rel->r_offset >= 3); + BFD_ASSERT (bfd_get_8 (input_bfd, + contents + rel->r_offset - 3) + == 0x8d); + BFD_ASSERT ((val & 0xc7) == 0x05 && val != (4 << 3)); + memcpy (contents + rel->r_offset - 3, + "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + } + else + { + BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4); + if (rel->r_offset + 10 <= input_section->_raw_size + && bfd_get_8 (input_bfd, + contents + rel->r_offset + 9) == 0x90) + { + /* leal foo(%reg), %eax; call ___tls_get_addr; nop + Change it into: + movl %gs:0, %eax; subl $foo@tpoff, %eax + (6 byte form of subl). */ + memcpy (contents + rel->r_offset - 2, + "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + roff = rel->r_offset + 6; + } + else + { + /* leal foo(%reg), %eax; call ___tls_get_addr + Change it into: + movl %gs:0, %eax; subl $foo@tpoff, %eax + (5 byte form of subl). */ + memcpy (contents + rel->r_offset - 2, + "\x65\xa1\0\0\0\0\x2d\0\0\0", 11); + } + } + bfd_put_32 (output_bfd, tpoff (info, relocation), + contents + roff); + /* Skip R_386_PLT32. */ + rel++; + continue; + } + else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_IE) + { + unsigned int val, type; + + /* IE->LE transition: + Originally it can be one of: + movl foo, %eax + movl foo, %reg + addl foo, %reg + We change it into: + movl $foo, %eax + movl $foo, %reg + addl $foo, %reg. */ + BFD_ASSERT (rel->r_offset >= 1); + val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1); + BFD_ASSERT (rel->r_offset + 4 <= input_section->_raw_size); + if (val != 0xa1) + { + BFD_ASSERT (rel->r_offset >= 2); + type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2); + } + if (val == 0xa1) + { + /* movl foo, %eax. */ + bfd_put_8 (output_bfd, 0xb8, contents + rel->r_offset - 2); + } + else if (type == 0x8b) + { + /* movl */ + BFD_ASSERT ((val & 0xc7) == 0x05); + bfd_put_8 (output_bfd, 0xc7, + contents + rel->r_offset - 2); + bfd_put_8 (output_bfd, 0xc0 | ((val >> 3) & 7), + contents + rel->r_offset - 1); + } + else if (type == 0x03) + { + /* addl */ + BFD_ASSERT ((val & 0xc7) == 0x05); + bfd_put_8 (output_bfd, 0x81, + contents + rel->r_offset - 2); + bfd_put_8 (output_bfd, 0xc0 | ((val >> 3) & 7), + contents + rel->r_offset - 1); + } + else + BFD_FAIL (); + bfd_put_32 (output_bfd, -tpoff (info, relocation), + contents + rel->r_offset); + continue; + } + else + { + unsigned int val, type; + + /* {IE_32,GOTIE}->LE transition: + Originally it can be one of: + subl foo(%reg1), %reg2 + movl foo(%reg1), %reg2 + addl foo(%reg1), %reg2 + We change it into: + subl $foo, %reg2 + movl $foo, %reg2 (6 byte form) + addl $foo, %reg2. */ + BFD_ASSERT (rel->r_offset >= 2); + type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2); + val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1); + BFD_ASSERT (rel->r_offset + 4 <= input_section->_raw_size); + BFD_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4); + if (type == 0x8b) + { + /* movl */ + bfd_put_8 (output_bfd, 0xc7, + contents + rel->r_offset - 2); + bfd_put_8 (output_bfd, 0xc0 | ((val >> 3) & 7), + contents + rel->r_offset - 1); + } + else if (type == 0x2b) + { + /* subl */ + bfd_put_8 (output_bfd, 0x81, + contents + rel->r_offset - 2); + bfd_put_8 (output_bfd, 0xe8 | ((val >> 3) & 7), + contents + rel->r_offset - 1); + } + else if (type == 0x03) + { + /* addl */ + bfd_put_8 (output_bfd, 0x81, + contents + rel->r_offset - 2); + bfd_put_8 (output_bfd, 0xc0 | ((val >> 3) & 7), + contents + rel->r_offset - 1); + } + else + BFD_FAIL (); + if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GOTIE) + bfd_put_32 (output_bfd, -tpoff (info, relocation), + contents + rel->r_offset); + else + bfd_put_32 (output_bfd, tpoff (info, relocation), + contents + rel->r_offset); + continue; + } + } + + if (htab->sgot == NULL) + abort (); + + if (h != NULL) + off = h->got.offset; + else + { + if (local_got_offsets == NULL) + abort (); + + off = local_got_offsets[r_symndx]; + } + + if ((off & 1) != 0) + off &= ~1; + else + { + Elf_Internal_Rel outrel; + Elf32_External_Rel *loc; + int dr_type, indx; + + if (htab->srelgot == NULL) + abort (); + + outrel.r_offset = (htab->sgot->output_section->vma + + htab->sgot->output_offset + off); + + indx = h && h->dynindx != -1 ? h->dynindx : 0; + if (r_type == R_386_TLS_GD) + dr_type = R_386_TLS_DTPMOD32; + else if (tls_type == GOT_TLS_IE_POS) + dr_type = R_386_TLS_TPOFF; + else + dr_type = R_386_TLS_TPOFF32; + if (dr_type == R_386_TLS_TPOFF && indx == 0) + bfd_put_32 (output_bfd, relocation - dtpoff_base (info), + htab->sgot->contents + off); + else if (dr_type == R_386_TLS_TPOFF32 && indx == 0) + bfd_put_32 (output_bfd, dtpoff_base (info) - relocation, + htab->sgot->contents + off); + else + bfd_put_32 (output_bfd, 0, + htab->sgot->contents + off); + outrel.r_info = ELF32_R_INFO (indx, dr_type); + loc = (Elf32_External_Rel *) htab->srelgot->contents; + loc += htab->srelgot->reloc_count++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + + if (r_type == R_386_TLS_GD) + { + if (indx == 0) + { + BFD_ASSERT (! unresolved_reloc); + bfd_put_32 (output_bfd, + relocation - dtpoff_base (info), + htab->sgot->contents + off + 4); + } + else + { + bfd_put_32 (output_bfd, 0, + htab->sgot->contents + off + 4); + outrel.r_info = ELF32_R_INFO (indx, + R_386_TLS_DTPOFF32); + outrel.r_offset += 4; + htab->srelgot->reloc_count++; + loc++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, + loc); + } + } + else if (tls_type == GOT_TLS_IE_BOTH) + { + bfd_put_32 (output_bfd, + indx == 0 ? relocation - dtpoff_base (info) : 0, + htab->sgot->contents + off + 4); + outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_TPOFF); + outrel.r_offset += 4; + htab->srelgot->reloc_count++; + loc++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + } + + if (h != NULL) + h->got.offset |= 1; + else + local_got_offsets[r_symndx] |= 1; + } + + if (off >= (bfd_vma) -2) + abort (); + if (r_type == ELF32_R_TYPE (rel->r_info)) + { + relocation = htab->sgot->output_offset + off; + if ((r_type == R_386_TLS_IE || r_type == R_386_TLS_GOTIE) + && tls_type == GOT_TLS_IE_BOTH) + relocation += 4; + if (r_type == R_386_TLS_IE) + relocation += htab->sgot->output_section->vma; + unresolved_reloc = false; + } + else + { + unsigned int val, type; + bfd_vma roff; + + /* GD->IE transition. */ + BFD_ASSERT (rel->r_offset >= 2); + type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2); + BFD_ASSERT (type == 0x8d || type == 0x04); + BFD_ASSERT (rel->r_offset + 9 <= input_section->_raw_size); + BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4) + == 0xe8); + BFD_ASSERT (rel + 1 < relend); + BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32); + roff = rel->r_offset - 3; + val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1); + if (type == 0x04) + { + /* leal foo(,%reg,1), %eax; call ___tls_get_addr + Change it into: + movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax. */ + BFD_ASSERT (rel->r_offset >= 3); + BFD_ASSERT (bfd_get_8 (input_bfd, + contents + rel->r_offset - 3) + == 0x8d); + BFD_ASSERT ((val & 0xc7) == 0x05 && val != (4 << 3)); + val >>= 3; + } + else + { + /* leal foo(%reg), %eax; call ___tls_get_addr; nop + Change it into: + movl %gs:0, %eax; subl $foo@gottpoff(%reg), %eax. */ + BFD_ASSERT (rel->r_offset + 10 <= input_section->_raw_size); + BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4); + BFD_ASSERT (bfd_get_8 (input_bfd, + contents + rel->r_offset + 9) + == 0x90); + roff = rel->r_offset - 2; + } + memcpy (contents + roff, + "\x65\xa1\0\0\0\0\x2b\x80\0\0\0", 12); + contents[roff + 7] = 0x80 | (val & 7); + /* If foo is used only with foo@gotntpoff(%reg) and + foo@indntpoff, but not with foo@gottpoff(%reg), change + subl $foo@gottpoff(%reg), %eax + into: + addl $foo@gotntpoff(%reg), %eax. */ + if (r_type == R_386_TLS_GOTIE) + { + contents[roff + 6] = 0x03; + if (tls_type == GOT_TLS_IE_BOTH) + off += 4; + } + bfd_put_32 (output_bfd, htab->sgot->output_offset + off, + contents + roff + 8); + /* Skip R_386_PLT32. */ + rel++; + continue; + } + break; + + case R_386_TLS_LDM: + if (! info->shared) + { + unsigned int val; + + /* LD->LE transition: + Ensure it is: + leal foo(%reg), %eax; call ___tls_get_addr. + We change it into: + movl %gs:0, %eax; nop; leal 0(%esi,1), %esi. */ + BFD_ASSERT (rel->r_offset >= 2); + BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2) + == 0x8d); + val = bfd_get_8 (input_bfd, contents + rel->r_offset - 1); + BFD_ASSERT ((val & 0xf8) == 0x80 && (val & 7) != 4); + BFD_ASSERT (rel->r_offset + 9 <= input_section->_raw_size); + BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4) + == 0xe8); + BFD_ASSERT (rel + 1 < relend); + BFD_ASSERT (ELF32_R_TYPE (rel[1].r_info) == R_386_PLT32); + memcpy (contents + rel->r_offset - 2, + "\x65\xa1\0\0\0\0\x90\x8d\x74\x26", 11); + /* Skip R_386_PLT32. */ + rel++; + continue; + } + + if (htab->sgot == NULL) + abort (); + + off = htab->tls_ldm_got.offset; + if (off & 1) + off &= ~1; + else + { + Elf_Internal_Rel outrel; + Elf32_External_Rel *loc; + + if (htab->srelgot == NULL) + abort (); + + outrel.r_offset = (htab->sgot->output_section->vma + + htab->sgot->output_offset + off); + + bfd_put_32 (output_bfd, 0, + htab->sgot->contents + off); + bfd_put_32 (output_bfd, 0, + htab->sgot->contents + off + 4); + outrel.r_info = ELF32_R_INFO (0, R_386_TLS_DTPMOD32); + loc = (Elf32_External_Rel *) htab->srelgot->contents; + loc += htab->srelgot->reloc_count++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + htab->tls_ldm_got.offset |= 1; + } + relocation = htab->sgot->output_offset + off; + unresolved_reloc = false; + break; + + case R_386_TLS_LDO_32: + if (info->shared) + relocation -= dtpoff_base (info); + else + /* When converting LDO to LE, we must negate. */ + relocation = -tpoff (info, relocation); + break; + + case R_386_TLS_LE_32: + case R_386_TLS_LE: + if (info->shared) + { + Elf_Internal_Rel outrel; + asection *sreloc; + Elf32_External_Rel *loc; + int indx; + + outrel.r_offset = rel->r_offset + + input_section->output_section->vma + + input_section->output_offset; + if (h != NULL && h->dynindx != -1) + indx = h->dynindx; + else + indx = 0; + if (r_type == R_386_TLS_LE_32) + outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_TPOFF32); + else + outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_TPOFF); + sreloc = elf_section_data (input_section)->sreloc; + if (sreloc == NULL) + abort (); + loc = (Elf32_External_Rel *) sreloc->contents; + loc += sreloc->reloc_count++; + bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc); + if (indx) + continue; + else if (r_type == R_386_TLS_LE_32) + relocation = dtpoff_base (info) - relocation; + else + relocation -= dtpoff_base (info); + } + else if (r_type == R_386_TLS_LE_32) + relocation = tpoff (info, relocation); + else + relocation = -tpoff (info, relocation); + break; + default: break; } - /* FIXME: Why do we allow debugging sections to escape this error? - More importantly, why do we not emit dynamic relocs for - R_386_32 above in debugging sections (which are ! SEC_ALLOC)? - If we had emitted the dynamic reloc, we could remove the - fudge here. */ + /* Dynamic relocs are not propagated for SEC_DEBUGGING sections + because such sections are not SEC_ALLOC and thus ld.so will + not process them. */ if (unresolved_reloc - && !(info->shared - && (input_section->flags & SEC_DEBUGGING) != 0 + && !((input_section->flags & SEC_DEBUGGING) != 0 && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)) - (*_bfd_error_handler) - (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"), - bfd_archive_filename (input_bfd), - bfd_get_section_name (input_bfd, input_section), - (long) rel->r_offset, - h->root.root.string); + { + (*_bfd_error_handler) + (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"), + bfd_archive_filename (input_bfd), + bfd_get_section_name (input_bfd, input_section), + (long) rel->r_offset, + h->root.root.string); + return false; + } r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, @@ -2108,7 +3020,6 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section, if (r == bfd_reloc_overflow) { - if (! ((*info->callbacks->reloc_overflow) (info, name, howto->name, (bfd_vma) 0, input_bfd, input_section, rel->r_offset))) @@ -2216,12 +3127,14 @@ elf_i386_finish_dynamic_symbol (output_bfd, info, h, sym) the .plt section. Leave the value alone. This is a clue for the dynamic linker, to make function pointer comparisons work between an application and shared - library. */ + library. */ sym->st_shndx = SHN_UNDEF; } } - if (h->got.offset != (bfd_vma) -1) + if (h->got.offset != (bfd_vma) -1 + && elf_i386_hash_entry(h)->tls_type != GOT_TLS_GD + && (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE) == 0) { Elf_Internal_Rel rel; Elf32_External_Rel *loc; @@ -2436,11 +3349,13 @@ elf_i386_finish_dynamic_sections (output_bfd, info) return true; } +#ifndef ELF_ARCH #define TARGET_LITTLE_SYM bfd_elf32_i386_vec #define TARGET_LITTLE_NAME "elf32-i386" #define ELF_ARCH bfd_arch_i386 #define ELF_MACHINE_CODE EM_386 #define ELF_MAXPAGESIZE 0x1000 +#endif /* ELF_ARCH */ #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 @@ -2453,6 +3368,9 @@ elf_i386_finish_dynamic_sections (output_bfd, info) #define elf_info_to_howto elf_i386_info_to_howto #define elf_info_to_howto_rel elf_i386_info_to_howto_rel +#define bfd_elf32_mkobject elf_i386_mkobject +#define elf_backend_object_p elf_i386_object_p + #define bfd_elf32_bfd_is_local_label_name elf_i386_is_local_label_name #define bfd_elf32_bfd_link_hash_table_create elf_i386_link_hash_table_create #define bfd_elf32_bfd_reloc_type_lookup elf_i386_reloc_type_lookup @@ -2472,4 +3390,6 @@ elf_i386_finish_dynamic_sections (output_bfd, info) #define elf_backend_relocate_section elf_i386_relocate_section #define elf_backend_size_dynamic_sections elf_i386_size_dynamic_sections +#ifndef ELF32_I386_C_INCLUDED #include "elf32-target.h" +#endif |