aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-i386.c')
-rw-r--r--bfd/elf32-i386.c434
1 files changed, 393 insertions, 41 deletions
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 061a9cb..b8e8790 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -1,6 +1,6 @@
/* Intel 80386/80486-specific support for 32-bit ELF
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
@@ -126,9 +126,19 @@ static reloc_howto_type elf_howto_table[]=
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),
+ EMPTY_HOWTO (38),
+ HOWTO(R_386_TLS_GOTDESC, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_386_TLS_GOTDESC",
+ TRUE, 0xffffffff, 0xffffffff, FALSE),
+ HOWTO(R_386_TLS_DESC_CALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+ bfd_elf_generic_reloc, "R_386_TLS_DESC_CALL",
+ FALSE, 0, 0, FALSE),
+ HOWTO(R_386_TLS_DESC, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_386_TLS_DESC",
+ TRUE, 0xffffffff, 0xffffffff, FALSE),
/* Another gap. */
-#define R_386_tls (R_386_TLS_TPOFF32 + 1 - R_386_tls_offset)
+#define R_386_tls (R_386_TLS_DESC + 1 - R_386_tls_offset)
#define R_386_vt_offset (R_386_GNU_VTINHERIT - R_386_tls)
/* GNU extension to record C++ vtable hierarchy. */
@@ -292,6 +302,18 @@ elf_i386_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
TRACE ("BFD_RELOC_386_TLS_TPOFF32");
return &elf_howto_table[R_386_TLS_TPOFF32 - R_386_tls_offset];
+ case BFD_RELOC_386_TLS_GOTDESC:
+ TRACE ("BFD_RELOC_386_TLS_GOTDESC");
+ return &elf_howto_table[R_386_TLS_GOTDESC - R_386_tls_offset];
+
+ case BFD_RELOC_386_TLS_DESC_CALL:
+ TRACE ("BFD_RELOC_386_TLS_DESC_CALL");
+ return &elf_howto_table[R_386_TLS_DESC_CALL - R_386_tls_offset];
+
+ case BFD_RELOC_386_TLS_DESC:
+ TRACE ("BFD_RELOC_386_TLS_DESC");
+ return &elf_howto_table[R_386_TLS_DESC - R_386_tls_offset];
+
case BFD_RELOC_VTABLE_INHERIT:
TRACE ("BFD_RELOC_VTABLE_INHERIT");
return &elf_howto_table[R_386_GNU_VTINHERIT - R_386_vt_offset];
@@ -559,7 +581,20 @@ struct elf_i386_link_hash_entry
#define GOT_TLS_IE_POS 5
#define GOT_TLS_IE_NEG 6
#define GOT_TLS_IE_BOTH 7
+#define GOT_TLS_GDESC 8
+#define GOT_TLS_GD_BOTH_P(type) \
+ ((type) == (GOT_TLS_GD | GOT_TLS_GDESC))
+#define GOT_TLS_GD_P(type) \
+ ((type) == GOT_TLS_GD || GOT_TLS_GD_BOTH_P (type))
+#define GOT_TLS_GDESC_P(type) \
+ ((type) == GOT_TLS_GDESC || GOT_TLS_GD_BOTH_P (type))
+#define GOT_TLS_GD_ANY_P(type) \
+ (GOT_TLS_GD_P (type) || GOT_TLS_GDESC_P (type))
unsigned char tls_type;
+
+ /* Offset of the GOTPLT entry reserved for the TLS descriptor,
+ starting at the end of the jump table. */
+ bfd_vma tlsdesc_got;
};
#define elf_i386_hash_entry(ent) ((struct elf_i386_link_hash_entry *)(ent))
@@ -570,6 +605,9 @@ struct elf_i386_obj_tdata
/* tls_type for each local got entry. */
char *local_got_tls_type;
+
+ /* GOTPLT entries for TLS descriptors. */
+ bfd_vma *local_tlsdesc_gotent;
};
#define elf_i386_tdata(abfd) \
@@ -578,6 +616,9 @@ struct elf_i386_obj_tdata
#define elf_i386_local_got_tls_type(abfd) \
(elf_i386_tdata (abfd)->local_got_tls_type)
+#define elf_i386_local_tlsdesc_gotent(abfd) \
+ (elf_i386_tdata (abfd)->local_tlsdesc_gotent)
+
static bfd_boolean
elf_i386_mkobject (bfd *abfd)
{
@@ -620,6 +661,10 @@ struct elf_i386_link_hash_table
bfd_vma offset;
} tls_ldm_got;
+ /* The amount of space used by the reserved portion of the sgotplt
+ section, plus whatever space is used by the jump slots. */
+ bfd_vma sgotplt_jump_table_size;
+
/* Small local sym to section mapping cache. */
struct sym_sec_cache sym_sec;
};
@@ -629,6 +674,9 @@ struct elf_i386_link_hash_table
#define elf_i386_hash_table(p) \
((struct elf_i386_link_hash_table *) ((p)->hash))
+#define elf_i386_compute_jump_table_size(htab) \
+ ((htab)->srelplt->reloc_count * 4)
+
/* Create an entry in an i386 ELF linker hash table. */
static struct bfd_hash_entry *
@@ -655,6 +703,7 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
eh = (struct elf_i386_link_hash_entry *) entry;
eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
+ eh->tlsdesc_got = (bfd_vma) -1;
}
return entry;
@@ -686,6 +735,7 @@ elf_i386_link_hash_table_create (bfd *abfd)
ret->sdynbss = NULL;
ret->srelbss = NULL;
ret->tls_ldm_got.refcount = 0;
+ ret->sgotplt_jump_table_size = 0;
ret->sym_sec.abfd = NULL;
ret->is_vxworks = 0;
ret->srelplt2 = NULL;
@@ -845,6 +895,8 @@ elf_i386_tls_transition (struct bfd_link_info *info, int r_type, int is_local)
switch (r_type)
{
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
if (is_local)
return R_386_TLS_LE_32;
@@ -949,6 +1001,8 @@ elf_i386_check_relocs (bfd *abfd,
case R_386_GOT32:
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
/* This symbol requires a global offset table entry. */
{
int tls_type, old_tls_type;
@@ -958,6 +1012,9 @@ elf_i386_check_relocs (bfd *abfd,
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_GOTDESC:
+ case R_386_TLS_DESC_CALL:
+ tls_type = GOT_TLS_GDESC; break;
case R_386_TLS_IE_32:
if (ELF32_R_TYPE (rel->r_info) == r_type)
tls_type = GOT_TLS_IE_NEG;
@@ -987,13 +1044,16 @@ elf_i386_check_relocs (bfd *abfd,
bfd_size_type size;
size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof(char));
+ size *= (sizeof (bfd_signed_vma)
+ + sizeof (bfd_vma) + sizeof(char));
local_got_refcounts = bfd_zalloc (abfd, size);
if (local_got_refcounts == NULL)
return FALSE;
elf_local_got_refcounts (abfd) = local_got_refcounts;
+ elf_i386_local_tlsdesc_gotent (abfd)
+ = (bfd_vma *) (local_got_refcounts + symtab_hdr->sh_info);
elf_i386_local_got_tls_type (abfd)
- = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+ = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info);
}
local_got_refcounts[r_symndx] += 1;
old_tls_type = elf_i386_local_got_tls_type (abfd) [r_symndx];
@@ -1004,11 +1064,14 @@ elf_i386_check_relocs (bfd *abfd,
/* 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
+ && (! GOT_TLS_GD_ANY_P (old_tls_type)
|| (tls_type & GOT_TLS_IE) == 0))
{
- if ((old_tls_type & GOT_TLS_IE) && tls_type == GOT_TLS_GD)
+ if ((old_tls_type & GOT_TLS_IE) && GOT_TLS_GD_ANY_P (tls_type))
tls_type = old_tls_type;
+ else if (GOT_TLS_GD_ANY_P (old_tls_type)
+ && GOT_TLS_GD_ANY_P (tls_type))
+ tls_type |= old_tls_type;
else
{
(*_bfd_error_handler)
@@ -1316,6 +1379,8 @@ elf_i386_gc_sweep_hook (bfd *abfd,
break;
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
case R_386_TLS_IE:
case R_386_TLS_GOTIE:
@@ -1579,6 +1644,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* We also need to make an entry in the .rel.plt section. */
htab->srelplt->size += sizeof (Elf32_External_Rel);
+ htab->srelplt->reloc_count++;
if (htab->is_vxworks && !info->shared)
{
@@ -1612,6 +1678,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
h->needs_plt = 0;
}
+ eh = (struct elf_i386_link_hash_entry *) h;
+ eh->tlsdesc_got = (bfd_vma) -1;
+
/* 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
@@ -1635,11 +1704,22 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
}
s = htab->sgot;
- h->got.offset = s->size;
- s->size += 4;
- /* R_386_TLS_GD needs 2 consecutive GOT slots. */
- if (tls_type == GOT_TLS_GD || tls_type == GOT_TLS_IE_BOTH)
- s->size += 4;
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ eh->tlsdesc_got = htab->sgotplt->size
+ - elf_i386_compute_jump_table_size (htab);
+ htab->sgotplt->size += 8;
+ h->got.offset = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (tls_type)
+ || GOT_TLS_GD_P (tls_type))
+ {
+ h->got.offset = s->size;
+ s->size += 4;
+ /* R_386_TLS_GD needs 2 consecutive GOT slots. */
+ if (GOT_TLS_GD_P (tls_type) || tls_type == GOT_TLS_IE_BOTH)
+ s->size += 4;
+ }
dyn = htab->elf.dynamic_sections_created;
/* R_386_TLS_IE_32 needs one dynamic relocation,
R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation,
@@ -1648,21 +1728,23 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
global. */
if (tls_type == GOT_TLS_IE_BOTH)
htab->srelgot->size += 2 * sizeof (Elf32_External_Rel);
- else if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
+ else if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
|| (tls_type & GOT_TLS_IE))
htab->srelgot->size += sizeof (Elf32_External_Rel);
- else if (tls_type == GOT_TLS_GD)
+ else if (GOT_TLS_GD_P (tls_type))
htab->srelgot->size += 2 * sizeof (Elf32_External_Rel);
- else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
- || h->root.type != bfd_link_hash_undefweak)
+ else if (! GOT_TLS_GDESC_P (tls_type)
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
&& (info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
htab->srelgot->size += sizeof (Elf32_External_Rel);
+ if (GOT_TLS_GDESC_P (tls_type))
+ htab->srelplt->size += sizeof (Elf32_External_Rel);
}
else
h->got.offset = (bfd_vma) -1;
- eh = (struct elf_i386_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return TRUE;
@@ -1810,6 +1892,7 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
char *local_tls_type;
+ bfd_vma *local_tlsdesc_gotent;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
@@ -1852,25 +1935,42 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
local_tls_type = elf_i386_local_got_tls_type (ibfd);
+ local_tlsdesc_gotent = elf_i386_local_tlsdesc_gotent (ibfd);
s = htab->sgot;
srel = htab->srelgot;
- for (; local_got < end_local_got; ++local_got, ++local_tls_type)
+ for (; local_got < end_local_got;
+ ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
{
+ *local_tlsdesc_gotent = (bfd_vma) -1;
if (*local_got > 0)
{
- *local_got = s->size;
- s->size += 4;
- if (*local_tls_type == GOT_TLS_GD
- || *local_tls_type == GOT_TLS_IE_BOTH)
- s->size += 4;
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ {
+ *local_tlsdesc_gotent = htab->sgotplt->size
+ - elf_i386_compute_jump_table_size (htab);
+ htab->sgotplt->size += 8;
+ *local_got = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (*local_tls_type)
+ || GOT_TLS_GD_P (*local_tls_type))
+ {
+ *local_got = s->size;
+ s->size += 4;
+ if (GOT_TLS_GD_P (*local_tls_type)
+ || *local_tls_type == GOT_TLS_IE_BOTH)
+ s->size += 4;
+ }
if (info->shared
- || *local_tls_type == GOT_TLS_GD
+ || GOT_TLS_GD_ANY_P (*local_tls_type)
|| (*local_tls_type & GOT_TLS_IE))
{
if (*local_tls_type == GOT_TLS_IE_BOTH)
srel->size += 2 * sizeof (Elf32_External_Rel);
- else
+ else if (GOT_TLS_GD_P (*local_tls_type)
+ || ! GOT_TLS_GDESC_P (*local_tls_type))
srel->size += sizeof (Elf32_External_Rel);
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ htab->srelplt->size += sizeof (Elf32_External_Rel);
}
}
else
@@ -1914,6 +2014,14 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+ /* For every jump slot reserved in the sgotplt, reloc_count is
+ incremented. However, when we reserve space for TLS descriptors,
+ it's not incremented, so in order to compute the space reserved
+ for them, it suffices to multiply the reloc count by the jump
+ slot size. */
+ if (htab->srelplt)
+ htab->sgotplt_jump_table_size = htab->srelplt->reloc_count * 4;
+
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
relocs = FALSE;
@@ -1945,7 +2053,8 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
/* We use the reloc_count field as a counter if we need
to copy relocs into the output file. */
- s->reloc_count = 0;
+ if (s != htab->srelplt)
+ s->reloc_count = 0;
}
else
{
@@ -2032,6 +2141,41 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
return TRUE;
}
+static bfd_boolean
+elf_i386_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ asection *tls_sec = elf_hash_table (info)->tls_sec;
+
+ if (tls_sec)
+ {
+ struct elf_link_hash_entry *tlsbase;
+
+ tlsbase = elf_link_hash_lookup (elf_hash_table (info),
+ "_TLS_MODULE_BASE_",
+ FALSE, FALSE, FALSE);
+
+ if (tlsbase && tlsbase->type == STT_TLS)
+ {
+ struct bfd_link_hash_entry *bh = NULL;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (output_bfd);
+
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+ tls_sec, 0, NULL, FALSE,
+ bed->collect, &bh)))
+ return FALSE;
+ tlsbase = (struct elf_link_hash_entry *)bh;
+ tlsbase->def_regular = 1;
+ tlsbase->other = STV_HIDDEN;
+ (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
/* Set the correct type for an x86 ELF section. We do this by the
section name, which is a hack, but ought to work. */
@@ -2109,6 +2253,7 @@ elf_i386_relocate_section (bfd *output_bfd,
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_vma *local_got_offsets;
+ bfd_vma *local_tlsdesc_gotents;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
@@ -2116,6 +2261,7 @@ elf_i386_relocate_section (bfd *output_bfd,
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
local_got_offsets = elf_local_got_offsets (input_bfd);
+ local_tlsdesc_gotents = elf_i386_local_tlsdesc_gotent (input_bfd);
rel = relocs;
relend = relocs + input_section->reloc_count;
@@ -2127,7 +2273,7 @@ elf_i386_relocate_section (bfd *output_bfd,
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
asection *sec;
- bfd_vma off;
+ bfd_vma off, offplt;
bfd_vma relocation;
bfd_boolean unresolved_reloc;
bfd_reloc_status_type r;
@@ -2549,6 +2695,8 @@ elf_i386_relocate_section (bfd *output_bfd,
/* Fall through */
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
case R_386_TLS_GOTIE:
r_type = elf_i386_tls_transition (info, r_type, h == NULL);
@@ -2563,7 +2711,9 @@ elf_i386_relocate_section (bfd *output_bfd,
}
if (tls_type == GOT_TLS_IE)
tls_type = GOT_TLS_IE_NEG;
- if (r_type == R_386_TLS_GD)
+ if (r_type == R_386_TLS_GD
+ || r_type == R_386_TLS_GOTDESC
+ || r_type == R_386_TLS_DESC_CALL)
{
if (tls_type == GOT_TLS_IE_POS)
r_type = R_386_TLS_GOTIE;
@@ -2637,6 +2787,63 @@ elf_i386_relocate_section (bfd *output_bfd,
rel++;
continue;
}
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GOTDESC)
+ {
+ /* GDesc -> LE transition.
+ It's originally something like:
+ leal x@tlsdesc(%ebx), %eax
+
+ leal x@ntpoff, %eax
+
+ Registers other than %eax may be set up here. */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a leal adding ebx to a
+ 32-bit offset into any register, although it's
+ probably almost always going to be eax. */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff >= 2);
+ type = bfd_get_8 (input_bfd, contents + roff - 2);
+ BFD_ASSERT (type == 0x8d);
+ val = bfd_get_8 (input_bfd, contents + roff - 1);
+ BFD_ASSERT ((val & 0xc7) == 0x83);
+ BFD_ASSERT (roff + 4 <= input_section->size);
+
+ /* Now modify the instruction as appropriate. */
+ /* aoliva FIXME: remove the above and xor the byte
+ below with 0x86. */
+ bfd_put_8 (output_bfd, val ^ 0x86,
+ contents + roff - 1);
+ bfd_put_32 (output_bfd, -tpoff (info, relocation),
+ contents + roff);
+ continue;
+ }
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_DESC_CALL)
+ {
+ /* GDesc -> LE transition.
+ It's originally:
+ call *(%eax)
+ Turn it into:
+ nop; nop */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a call *(%eax). */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff + 2 <= input_section->size);
+ type = bfd_get_8 (input_bfd, contents + roff);
+ BFD_ASSERT (type == 0xff);
+ val = bfd_get_8 (input_bfd, contents + roff + 1);
+ BFD_ASSERT (val == 0x10);
+
+ /* Now modify the instruction as appropriate. */
+ bfd_put_8 (output_bfd, 0x90, contents + roff);
+ bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+ continue;
+ }
else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_IE)
{
unsigned int val, type;
@@ -2751,13 +2958,17 @@ elf_i386_relocate_section (bfd *output_bfd,
abort ();
if (h != NULL)
- off = h->got.offset;
+ {
+ off = h->got.offset;
+ offplt = elf_i386_hash_entry (h)->tlsdesc_got;
+ }
else
{
if (local_got_offsets == NULL)
abort ();
off = local_got_offsets[r_symndx];
+ offplt = local_tlsdesc_gotents[r_symndx];
}
if ((off & 1) != 0)
@@ -2767,35 +2978,77 @@ elf_i386_relocate_section (bfd *output_bfd,
Elf_Internal_Rela outrel;
bfd_byte *loc;
int dr_type, indx;
+ asection *sreloc;
if (htab->srelgot == NULL)
abort ();
+ indx = h && h->dynindx != -1 ? h->dynindx : 0;
+
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_DESC);
+ BFD_ASSERT (htab->sgotplt_jump_table_size + offplt + 8
+ <= htab->sgotplt->size);
+ outrel.r_offset = (htab->sgotplt->output_section->vma
+ + htab->sgotplt->output_offset
+ + offplt
+ + htab->sgotplt_jump_table_size);
+ sreloc = htab->srelplt;
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++
+ * sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ if (indx == 0)
+ {
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_32 (output_bfd,
+ relocation - dtpoff_base (info),
+ htab->sgotplt->contents + offplt
+ + htab->sgotplt_jump_table_size + 4);
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, 0,
+ htab->sgotplt->contents + offplt
+ + htab->sgotplt_jump_table_size + 4);
+ }
+ }
+
+ sreloc = htab->srelgot;
+
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)
+ if (GOT_TLS_GD_P (tls_type))
dr_type = R_386_TLS_DTPMOD32;
+ else if (GOT_TLS_GDESC_P (tls_type))
+ goto dr_done;
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
+ else if (dr_type != R_386_TLS_DESC)
bfd_put_32 (output_bfd, 0,
htab->sgot->contents + off);
outrel.r_info = ELF32_R_INFO (indx, dr_type);
- loc = htab->srelgot->contents;
- loc += htab->srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
+
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
- if (r_type == R_386_TLS_GD)
+ if (GOT_TLS_GD_P (tls_type))
{
if (indx == 0)
{
@@ -2811,8 +3064,10 @@ elf_i386_relocate_section (bfd *output_bfd,
outrel.r_info = ELF32_R_INFO (indx,
R_386_TLS_DTPOFF32);
outrel.r_offset += 4;
- htab->srelgot->reloc_count++;
+ sreloc->reloc_count++;
loc += sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
}
}
@@ -2823,25 +3078,33 @@ elf_i386_relocate_section (bfd *output_bfd,
htab->sgot->contents + off + 4);
outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_TPOFF);
outrel.r_offset += 4;
- htab->srelgot->reloc_count++;
+ sreloc->reloc_count++;
loc += sizeof (Elf32_External_Rel);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
}
+ dr_done:
if (h != NULL)
h->got.offset |= 1;
else
local_got_offsets[r_symndx] |= 1;
}
- if (off >= (bfd_vma) -2)
+ if (off >= (bfd_vma) -2
+ && ! GOT_TLS_GDESC_P (tls_type))
abort ();
- if (r_type == ELF32_R_TYPE (rel->r_info))
+ if (r_type == R_386_TLS_GOTDESC
+ || r_type == R_386_TLS_DESC_CALL)
+ {
+ relocation = htab->sgotplt_jump_table_size + offplt;
+ unresolved_reloc = FALSE;
+ }
+ else if (r_type == ELF32_R_TYPE (rel->r_info))
{
bfd_vma g_o_t = htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset;
relocation = htab->sgot->output_section->vma
- + htab->sgot->output_offset + off - g_o_t;
+ + htab->sgot->output_offset + off - g_o_t;
if ((r_type == R_386_TLS_IE || r_type == R_386_TLS_GOTIE)
&& tls_type == GOT_TLS_IE_BOTH)
relocation += 4;
@@ -2849,7 +3112,7 @@ elf_i386_relocate_section (bfd *output_bfd,
relocation += g_o_t;
unresolved_reloc = FALSE;
}
- else
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GD)
{
unsigned int val, type;
bfd_vma roff;
@@ -2913,6 +3176,94 @@ elf_i386_relocate_section (bfd *output_bfd,
rel++;
continue;
}
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GOTDESC)
+ {
+ /* GDesc -> IE transition.
+ It's originally something like:
+ leal x@tlsdesc(%ebx), %eax
+
+ Change it to:
+ movl x@gotntpoff(%ebx), %eax # before nop; nop
+ or:
+ movl x@gottpoff(%ebx), %eax # before negl %eax
+
+ Registers other than %eax may be set up here. */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a leal adding ebx to a 32-bit
+ offset into any register, although it's probably
+ almost always going to be eax. */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff >= 2);
+ type = bfd_get_8 (input_bfd, contents + roff - 2);
+ BFD_ASSERT (type == 0x8d);
+ val = bfd_get_8 (input_bfd, contents + roff - 1);
+ BFD_ASSERT ((val & 0xc7) == 0x83);
+ BFD_ASSERT (roff + 4 <= input_section->size);
+
+ /* Now modify the instruction as appropriate. */
+ /* To turn a leal into a movl in the form we use it, it
+ suffices to change the first byte from 0x8d to 0x8b.
+ aoliva FIXME: should we decide to keep the leal, all
+ we have to do is remove the statement below, and
+ adjust the relaxation of R_386_TLS_DESC_CALL. */
+ bfd_put_8 (output_bfd, 0x8b, contents + roff - 2);
+
+ if (tls_type == GOT_TLS_IE_BOTH)
+ off += 4;
+
+ bfd_put_32 (output_bfd,
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + off
+ - htab->sgotplt->output_section->vma
+ - htab->sgotplt->output_offset,
+ contents + roff);
+ continue;
+ }
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_DESC_CALL)
+ {
+ /* GDesc -> IE transition.
+ It's originally:
+ call *(%eax)
+
+ Change it to:
+ nop; nop
+ or
+ negl %eax
+ depending on how we transformed the TLS_GOTDESC above.
+ */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a call *(%eax). */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff + 2 <= input_section->size);
+ type = bfd_get_8 (input_bfd, contents + roff);
+ BFD_ASSERT (type == 0xff);
+ val = bfd_get_8 (input_bfd, contents + roff + 1);
+ BFD_ASSERT (val == 0x10);
+
+ /* Now modify the instruction as appropriate. */
+ if (tls_type != GOT_TLS_IE_NEG)
+ {
+ /* nop; nop */
+ bfd_put_8 (output_bfd, 0x90, contents + roff);
+ bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+ }
+ else
+ {
+ /* negl %eax */
+ bfd_put_8 (output_bfd, 0xf7, contents + roff);
+ bfd_put_8 (output_bfd, 0xd8, contents + roff + 1);
+ }
+
+ continue;
+ }
+ else
+ BFD_ASSERT (FALSE);
break;
case R_386_TLS_LDM:
@@ -3220,7 +3571,7 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd,
}
if (h->got.offset != (bfd_vma) -1
- && elf_i386_hash_entry(h)->tls_type != GOT_TLS_GD
+ && ! GOT_TLS_GD_ANY_P (elf_i386_hash_entry(h)->tls_type)
&& (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE) == 0)
{
Elf_Internal_Rela rel;
@@ -3555,6 +3906,7 @@ elf_i386_plt_sym_val (bfd_vma i, const asection *plt,
#define elf_backend_reloc_type_class elf_i386_reloc_type_class
#define elf_backend_relocate_section elf_i386_relocate_section
#define elf_backend_size_dynamic_sections elf_i386_size_dynamic_sections
+#define elf_backend_always_size_sections elf_i386_always_size_sections
#define elf_backend_plt_sym_val elf_i386_plt_sym_val
#include "elf32-target.h"