summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog58
-rw-r--r--bfd/bfd-in2.h40
-rw-r--r--bfd/elf-bfd.h11
-rw-r--r--bfd/elf.c71
-rw-r--r--bfd/elf32-i386.c706
-rw-r--r--bfd/elflink.h96
-rw-r--r--bfd/elfxx-ia64.c274
-rw-r--r--bfd/libbfd.h28
-rw-r--r--bfd/reloc.c50
-rw-r--r--bfd/section.c9
-rw-r--r--bfd/syms.c3
-rw-r--r--include/ChangeLog6
-rw-r--r--include/elf/common.h4
-rw-r--r--include/elf/i386.h23
-rw-r--r--include/elf/ia64.h2
15 files changed, 1293 insertions, 88 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 0f2d311a65f..791f2641b14 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,61 @@
+2002-05-23 Jakub Jelinek <jakub@redhat.com>
+
+ * elf.c (_bfd_elf_make_section_from_shdr): Set SEC_THREAD_LOCAL
+ for symbols from SHF_TLS section.
+ (_bfd_elf_print_private_bfd_data): Add PT_TLS.
+ (elf_fake_sections): Set SHF_TLS for SEC_THREAD_LOCAL sections.
+ (map_sections_to_segments): Build PT_TLS segment if necessary.
+ (assign_file_positions_for_segments): Likewise.
+ (get_program_header_size): Account for PT_TLS segment.
+ (swap_out_syms): Set type of BSF_THREAD_LOCAL symbols and symbols from
+ SEC_THREAD_LOCAL sections to STT_TLS.
+ * reloc.c: Add 386 and IA-64 TLS relocs.
+ * section.c (SEC_THREAD_LOCAL): Define.
+ (SEC_CONSTRUCTOR_TEXT, SEC_CONSTRUCTOR_DATA, SEC_CONSTRUCTOR_BSS):
+ Remove.
+ * elflink.h (elf_link_add_object_symbols): Support .tcommon.
+ (size_dynamic_sections): If DF_STATIC_TLS, set DF_FLAGS
+ unconditionally.
+ (struct elf_final_link_info): Add first_tls_sec.
+ (elf_bfd_final_link): Set first_tls_sec.
+ Compute elf_hash_table (info)->tls_segment.
+ (elf_link_output_extsym): Handle STT_TLS symbols.
+ (elf_link_input_bfd): Likewise.
+ * syms.c (BSF_THREAD_LOCAL): Define.
+ * bfd-in2.h: Rebuilt.
+ * libbfd.h: Rebuilt.
+ * elf32-i386.c (elf_i386_tls_transition, dtpoff_base, tpoff,
+ elf_i386_mkobject, elf_i386_object_p): New functions.
+ (elf_howto_table): Add TLS relocs.
+ (elf_i386_reloc_type_lookup): Support TLS relocs.
+ (elf_i386_info_to_howto_rel): Likewise.
+ (struct elf_i386_link_hash_entry): Add tls_type.
+ (struct elf_i386_obj_tdata): New.
+ (elf_i386_hash_entry, elf_i386_tdata, elf_i386_local_got_tls_type):
+ New macros.
+ (struct elf_i386_link_hash_table): Add tls_ldm_got.
+ (link_hash_newfunc): Clear tls_type.
+ (elf_i386_check_relocs): Support TLS relocs.
+ (elf_i386_gc_sweep_hook): Likewise.
+ (allocate_dynrelocs): Likewise.
+ (elf_i386_size_dynamic_sections): Likewise.
+ (elf_i386_relocate_section): Likewise.
+ (elf_i386_finish_dynamic_symbol): Likewise.
+ (bfd_elf32_mkobject, elf_backend_object_p): Define.
+ * elfxx-ia64.c (struct elfNN_ia64_dyn_sym_info): Add tprel_offset,
+ dtpmod_offset, dtprel_offset, tprel_done, dtpmod_done, dtprel_done,
+ want_tprel, want_dtpmod, want_dtprel.
+ (elfNN_ia64_tprel_base, elfNN_ia64_dtprel_base): New functions.
+ (ia64_howto_table): Add TLS relocs, rename R_IA64_LTOFF_TP22 to
+ R_IA64_LTOFF_TPREL22.
+ (elf_code_to_howto_index): Add TLS relocs.
+ (elfNN_ia64_check_relocs): Support TLS relocs.
+ (allocate_global_data_got): Account for TLS .got data.
+ (allocate_dynrel_entries): Account for TLS dynamic relocations.
+ (elfNN_ia64_install_value): Supprt TLS relocs.
+ (set_got_entry): Support TLS relocs.
+ (elfNN_ia64_relocate_section): Likewise.
+
2002-05-23 Nick Clifton <nickc@cambridge.redhat.com>
* elf32-arm.h (elf32_arm_final_link_relocate): For the Thumb
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 0dfc073b03b..8a0c45304fd 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1068,12 +1068,6 @@ typedef struct sec
standard data. */
#define SEC_CONSTRUCTOR 0x100
- /* The section is a constructor, and should be placed at the
- end of the text, data, or bss section(?). */
-#define SEC_CONSTRUCTOR_TEXT 0x1100
-#define SEC_CONSTRUCTOR_DATA 0x2100
-#define SEC_CONSTRUCTOR_BSS 0x3100
-
/* The section has contents - a data section could be
<<SEC_ALLOC>> | <<SEC_HAS_CONTENTS>>; a debug section could be
<<SEC_HAS_CONTENTS>> */
@@ -1094,6 +1088,9 @@ typedef struct sec
sections. */
#define SEC_COFF_SHARED_LIBRARY 0x800
+ /* The section contains thread local data. */
+#define SEC_THREAD_LOCAL 0x1000
+
/* The section has GOT references. This flag is only for the
linker, and is currently only used by the elf32-hppa back end.
It will be set if global offset table references were detected
@@ -2211,6 +2208,15 @@ to compensate for the borrow when the low bits are added. */
BFD_RELOC_386_RELATIVE,
BFD_RELOC_386_GOTOFF,
BFD_RELOC_386_GOTPC,
+ BFD_RELOC_386_TLS_LE,
+ BFD_RELOC_386_TLS_GD,
+ BFD_RELOC_386_TLS_LDM,
+ BFD_RELOC_386_TLS_LDO_32,
+ BFD_RELOC_386_TLS_IE_32,
+ BFD_RELOC_386_TLS_LE_32,
+ BFD_RELOC_386_TLS_DTPMOD32,
+ BFD_RELOC_386_TLS_DTPOFF32,
+ BFD_RELOC_386_TLS_TPOFF32,
/* x86-64/elf relocations */
BFD_RELOC_X86_64_GOT32,
@@ -2922,12 +2928,25 @@ this offset in the reloc's section offset. */
BFD_RELOC_IA64_IPLTMSB,
BFD_RELOC_IA64_IPLTLSB,
BFD_RELOC_IA64_COPY,
+ BFD_RELOC_IA64_LTOFF22X,
+ BFD_RELOC_IA64_LDXMOV,
+ BFD_RELOC_IA64_TPREL14,
BFD_RELOC_IA64_TPREL22,
+ BFD_RELOC_IA64_TPREL64I,
BFD_RELOC_IA64_TPREL64MSB,
BFD_RELOC_IA64_TPREL64LSB,
- BFD_RELOC_IA64_LTOFF_TP22,
- BFD_RELOC_IA64_LTOFF22X,
- BFD_RELOC_IA64_LDXMOV,
+ BFD_RELOC_IA64_LTOFF_TPREL22,
+ BFD_RELOC_IA64_DTPMOD64MSB,
+ BFD_RELOC_IA64_DTPMOD64LSB,
+ BFD_RELOC_IA64_LTOFF_DTPMOD22,
+ BFD_RELOC_IA64_DTPREL14,
+ BFD_RELOC_IA64_DTPREL22,
+ BFD_RELOC_IA64_DTPREL64I,
+ BFD_RELOC_IA64_DTPREL32MSB,
+ BFD_RELOC_IA64_DTPREL32LSB,
+ BFD_RELOC_IA64_DTPREL64MSB,
+ BFD_RELOC_IA64_DTPREL64LSB,
+ BFD_RELOC_IA64_LTOFF_DTPREL22,
/* Motorola 68HC11 reloc.
This is the 8 bits high part of an absolute address. */
@@ -3138,6 +3157,9 @@ typedef struct symbol_cache_entry
as well. */
#define BSF_DEBUGGING_RELOC 0x20000
+ /* This symbol is thread local. Used in ELF. */
+#define BSF_THREAD_LOCAL 0x40000
+
flagword flags;
/* A pointer to the section to which this symbol is
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 625f11b07e8..f2c807529ba 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -234,6 +234,14 @@ enum elf_link_info_type
ELF_INFO_TYPE_LAST
};
+/* Cached start, size and alignment of PT_TLS segment. */
+struct elf_link_tls_segment
+{
+ bfd_vma start;
+ bfd_size_type size;
+ unsigned int align;
+};
+
/* ELF linker hash table. */
struct elf_link_hash_table
@@ -286,6 +294,9 @@ struct elf_link_hash_table
/* A linked list of DT_RPATH/DT_RUNPATH names found in dynamic
objects included in the link. */
struct bfd_link_needed_list *runpath;
+
+ /* Cached start, size and alignment of PT_TLS segment. */
+ struct elf_link_tls_segment *tls_segment;
};
/* Look up an entry in an ELF linker hash table. */
diff --git a/bfd/elf.c b/bfd/elf.c
index c3339f93b7a..30814ae1cc4 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -591,6 +591,8 @@ _bfd_elf_make_section_from_shdr (abfd, hdr, name)
if (hdr->sh_flags & SHF_GROUP)
if (!setup_group (abfd, hdr, newsect))
return false;
+ if ((hdr->sh_flags & SHF_TLS) != 0)
+ flags |= SEC_THREAD_LOCAL;
/* The debugging sections appear to be recognized only by name, not
any sort of flag. */
@@ -883,6 +885,7 @@ _bfd_elf_print_private_bfd_data (abfd, farg)
case PT_NOTE: pt = "NOTE"; break;
case PT_SHLIB: pt = "SHLIB"; break;
case PT_PHDR: pt = "PHDR"; break;
+ case PT_TLS: pt = "TLS"; break;
case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break;
}
@@ -2274,6 +2277,8 @@ elf_fake_sections (abfd, asect, failedptrarg)
}
if (elf_group_name (asect) != NULL)
this_hdr->sh_flags |= SHF_GROUP;
+ if ((asect->flags & SEC_THREAD_LOCAL) != 0)
+ this_hdr->sh_flags |= SHF_TLS;
/* Check for processor-specific section types. */
if (bed->elf_backend_fake_sections
@@ -2956,6 +2961,8 @@ map_sections_to_segments (abfd)
asection **hdrpp;
boolean phdr_in_segment = true;
boolean writable;
+ int tls_count = 0;
+ asection *first_tls = NULL;
asection *dynsec, *eh_frame_hdr;
bfd_size_type amt;
@@ -3194,6 +3201,39 @@ map_sections_to_segments (abfd)
*pm = m;
pm = &m->next;
}
+ if (s->flags & SEC_THREAD_LOCAL)
+ {
+ if (! tls_count)
+ first_tls = s;
+ tls_count++;
+ }
+ }
+
+ /* If there are any SHF_TLS output sections, add PT_TLS segment. */
+ if (tls_count > 0)
+ {
+ int i;
+
+ amt = sizeof (struct elf_segment_map);
+ amt += (tls_count - 1) * sizeof (asection *);
+ m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
+ if (m == NULL)
+ goto error_return;
+ m->next = NULL;
+ m->p_type = PT_TLS;
+ m->count = tls_count;
+ /* Mandated PF_R. */
+ m->p_flags = PF_R;
+ m->p_flags_valid = 1;
+ for (i = 0; i < tls_count; ++i)
+ {
+ BFD_ASSERT (first_tls->flags & SEC_THREAD_LOCAL);
+ m->sections[i] = first_tls;
+ first_tls = first_tls->next;
+ }
+
+ *pm = m;
+ pm = &m->next;
}
/* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
@@ -3618,6 +3658,20 @@ Error: First section in segment (%s) starts at 0x%x whereas the segment starts a
if ((flags & SEC_LOAD) != 0)
p->p_filesz += sec->_raw_size;
+ if (p->p_type == PT_TLS
+ && sec->_raw_size == 0
+ && (sec->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ struct bfd_link_order *o;
+ bfd_vma tbss_size = 0;
+
+ for (o = sec->link_order_head; o != NULL; o = o->next)
+ if (tbss_size < o->offset + o->size)
+ tbss_size = o->offset + o->size;
+
+ p->p_memsz += tbss_size;
+ }
+
if (align > p->p_align
&& (p->p_type != PT_LOAD || (abfd->flags & D_PAGED) == 0))
p->p_align = align;
@@ -3752,6 +3806,16 @@ get_program_header_size (abfd)
}
}
+ for (s = abfd->sections; s != NULL; s = s->next)
+ {
+ if (s->flags & SEC_THREAD_LOCAL)
+ {
+ /* We need a PT_TLS segment. */
+ ++segs;
+ break;
+ }
+ }
+
/* Let the backend count up any program headers it might need. */
if (bed->elf_backend_additional_program_headers)
{
@@ -5045,13 +5109,18 @@ swap_out_syms (abfd, sttp, relocatable_p)
sym.st_shndx = shndx;
}
- if ((flags & BSF_FUNCTION) != 0)
+ if ((flags & BSF_THREAD_LOCAL) != 0)
+ type = STT_TLS;
+ else if ((flags & BSF_FUNCTION) != 0)
type = STT_FUNC;
else if ((flags & BSF_OBJECT) != 0)
type = STT_OBJECT;
else
type = STT_NOTYPE;
+ if (syms[idx]->section->flags & SEC_THREAD_LOCAL)
+ type = STT_TLS;
+
/* Processor-specific types */
if (type_ptr != NULL
&& bed->elf_backend_get_symbol_type)
diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c
index 760762d1723..01e1f21d759 100644
--- a/bfd/elf32-i386.c
+++ b/bfd/elf32-i386.c
@@ -46,6 +46,13 @@ 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 *));
+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 *));
@@ -65,6 +72,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 **));
@@ -121,9 +132,18 @@ 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_LE - R_386_standard)
/* The remaining relocs are a GNU extension. */
+ 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),
@@ -137,9 +157,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 */
@@ -237,6 +279,18 @@ elf_i386_reloc_type_lookup (abfd, code)
return &elf_howto_table[(unsigned int) R_386_GOTPC ];
/* The remaining relocs are a GNU extension. */
+ 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];
@@ -253,6 +307,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
@@ -292,8 +371,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);
@@ -481,8 +562,54 @@ struct elf_i386_link_hash_entry
/* Track dynamic relocs copied for this symbol. */
struct elf_i386_dyn_relocs *dyn_relocs;
+
+ enum {
+ GOT_UNKNOWN = 0, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE
+ } 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
@@ -498,6 +625,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;
};
@@ -533,6 +665,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;
@@ -678,6 +811,29 @@ elf_i386_copy_indirect_symbol (dir, ind)
_bfd_elf_link_hash_copy_indirect (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_LDM:
+ return R_386_TLS_LE_32;
+ }
+
+ return r_type;
+}
+
/* Look through the relocs for a section during the first phase, and
calculate needed space in the global offset table, procedure linkage
table, and dynamic reloc sections. */
@@ -708,10 +864,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))
{
@@ -726,38 +884,86 @@ 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))
+ r_type = elf_i386_tls_transition (info, r_type, h == NULL);
+
+ switch (r_type)
{
+ case R_386_TLS_IE_32:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ /* FALLTHROUGH */
case R_386_GOT32:
+ case R_386_TLS_GD:
/* 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;
-
- 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)
+ {
+ 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: tls_type = GOT_TLS_IE; 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 a TLS symbol is accessed using IE at least once,
+ there is no point to use dynamic model for it. */
+ if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
+ && (old_tls_type != GOT_TLS_GD || tls_type != GOT_TLS_IE))
+ {
+ if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD)
+ tls_type = GOT_TLS_IE;
+ else
+ {
+ (*_bfd_error_handler)
+ (_("%s: `%s' accessed both as normal and thread local symbol"),
+ bfd_archive_filename (abfd), h->root.root.string);
return false;
- elf_local_got_refcounts (abfd) = local_got_refcounts;
- }
- local_got_refcounts[r_symndx] += 1;
- }
+ }
+ }
+
+ 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)
@@ -767,6 +973,10 @@ elf_i386_check_relocs (abfd, info, sec, relocs)
}
break;
+ 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
actually build the entry in adjust_dynamic_symbol,
@@ -824,7 +1034,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
@@ -923,7 +1133,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;
@@ -942,6 +1152,16 @@ elf_i386_check_relocs (abfd, info, sec, relocs)
return false;
break;
+ case R_386_TLS_LE_32:
+ case R_386_TLS_LE:
+ if (info->shared)
+ {
+ (*_bfd_error_handler) (_("%s: TLS local exec code cannot be linked into shared objects"),
+ bfd_archive_filename (abfd));
+ return false;
+ }
+ break;
+
default:
break;
}
@@ -1016,11 +1236,18 @@ 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 (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_GOT32:
- case R_386_GOTOFF:
- case R_386_GOTPC:
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
{
@@ -1319,10 +1546,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 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. */
@@ -1336,8 +1571,18 @@ 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)
+ 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_GD needs one if local symbol and two if global. */
+ 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
@@ -1483,6 +1728,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;
@@ -1524,15 +1770,20 @@ 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)
+ if (*local_tls_type == GOT_TLS_GD)
+ s->_raw_size += 4;
+ if (info->shared
+ || *local_tls_type == GOT_TLS_GD
+ || *local_tls_type == GOT_TLS_IE)
srel->_raw_size += sizeof (Elf32_External_Rel);
}
else
@@ -1540,6 +1791,17 @@ elf_i386_size_dynamic_sections (output_bfd, info)
}
}
+ 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);
@@ -1685,6 +1947,34 @@ 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;
+{
+ BFD_ASSERT (elf_hash_table (info)->tls_segment != NULL);
+ 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;
+
+ BFD_ASSERT (tls_segment != NULL);
+ return (align_power (tls_segment->size, tls_segment->align)
+ + tls_segment->start - address);
+}
+
/* Relocate an i386 ELF section. */
static boolean
@@ -1715,7 +2005,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;
@@ -1726,6 +2016,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
@@ -1733,8 +2024,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;
@@ -2118,6 +2411,330 @@ elf_i386_relocate_section (output_bfd, info, input_bfd, input_section,
}
break;
+ case R_386_TLS_GD:
+ case R_386_TLS_IE_32:
+ 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 (r_type == R_386_TLS_GD && tls_type == GOT_TLS_IE)
+ r_type = R_386_TLS_IE_32;
+
+ if (r_type == R_386_TLS_LE_32)
+ {
+ BFD_ASSERT (unresolved_reloc == false);
+ 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
+ {
+ unsigned int val, type;
+
+ /* IE->LE transition:
+ Originally it can be either:
+ subl foo(%reg1), %reg2
+ or
+ movl foo(%reg1), %reg2
+ We change it into:
+ subl $foo, %reg2
+ or
+ movl $foo, %reg2 (6 byte form) */
+ 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);
+ if (type == 0x8b)
+ {
+ /* movl */
+ BFD_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4);
+ 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_ASSERT ((val & 0xc0) == 0x80 && (val & 7) != 4);
+ 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
+ BFD_FAIL ();
+ 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);
+
+ bfd_put_32 (output_bfd, 0,
+ htab->sgot->contents + off);
+ indx = h && h->dynindx != -1 ? h->dynindx : 0;
+ if (r_type == R_386_TLS_GD)
+ dr_type = R_386_TLS_DTPMOD32;
+ else
+ dr_type = R_386_TLS_TPOFF32;
+ 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 == false);
+ 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);
+ }
+ }
+
+ 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;
+ 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);
+ 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:
+ relocation = tpoff (info, relocation);
+ break;
+
+ case R_386_TLS_LE:
+ relocation = -tpoff (info, relocation);
+ break;
+
default:
break;
}
@@ -2273,7 +2890,9 @@ elf_i386_finish_dynamic_symbol (output_bfd, info, h, sym)
}
}
- 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)
{
Elf_Internal_Rel rel;
Elf32_External_Rel *loc;
@@ -2505,6 +3124,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
diff --git a/bfd/elflink.h b/bfd/elflink.h
index 4c5661b4505..5046416a759 100644
--- a/bfd/elflink.h
+++ b/bfd/elflink.h
@@ -1723,7 +1723,23 @@ elf_link_add_object_symbols (abfd, info)
if (name == (const char *) NULL)
goto error_return;
- if (add_symbol_hook)
+ if (sym.st_shndx == SHN_COMMON && ELF_ST_TYPE (sym.st_info) == STT_TLS)
+ {
+ asection *tcomm = bfd_get_section_by_name (abfd, ".tcommon");
+
+ if (tcomm == NULL)
+ {
+ tcomm = bfd_make_section (abfd, ".tcommon");
+ if (tcomm == NULL
+ || !bfd_set_section_flags (abfd, tcomm, (SEC_ALLOC
+ | SEC_IS_COMMON
+ | SEC_LINKER_CREATED
+ | SEC_THREAD_LOCAL)))
+ goto error_return;
+ }
+ sec = tcomm;
+ }
+ else if (add_symbol_hook)
{
if (! (*add_symbol_hook) (abfd, info, &sym, &name, &flags, &sec,
&value))
@@ -3457,7 +3473,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
elf_tdata (output_bfd)->cverdefs = cdefs;
}
- if (info->new_dtags && info->flags)
+ if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS))
{
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS, info->flags))
return false;
@@ -4481,6 +4497,8 @@ struct elf_final_link_info
asection *hash_sec;
/* symbol version section (.gnu.version). */
asection *symver_sec;
+ /* first SHF_TLS section (if any). */
+ asection *first_tls_sec;
/* Buffer large enough to hold contents of any section. */
bfd_byte *contents;
/* Buffer large enough to hold external relocs of any section. */
@@ -4960,6 +4978,14 @@ elf_bfd_final_link (abfd, info)
finfo.symbuf = NULL;
finfo.symshndxbuf = NULL;
finfo.symbuf_count = 0;
+ finfo.first_tls_sec = NULL;
+ for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+ if ((o->flags & SEC_THREAD_LOCAL) != 0
+ && (o->flags & SEC_LOAD) != 0)
+ {
+ finfo.first_tls_sec = o;
+ break;
+ }
/* Count up the number of relocations we will output for each output
section, so that we know the sizes of the reloc sections. We
@@ -5324,6 +5350,40 @@ elf_bfd_final_link (abfd, info)
goto error_return;
}
+ if (finfo.first_tls_sec)
+ {
+ unsigned int align = 0;
+ bfd_vma base = finfo.first_tls_sec->vma, end = 0;
+ asection *sec;
+
+ for (sec = finfo.first_tls_sec;
+ sec && (sec->flags & SEC_THREAD_LOCAL);
+ sec = sec->next)
+ {
+ bfd_vma size = sec->_raw_size;
+
+ if (bfd_get_section_alignment (abfd, sec) > align)
+ align = bfd_get_section_alignment (abfd, sec);
+ if (sec->_raw_size == 0 && (sec->flags & SEC_HAS_CONTENTS) == 0)
+ {
+ struct bfd_link_order *o;
+
+ size = 0;
+ for (o = sec->link_order_head; o != NULL; o = o->next)
+ if (size < o->offset + o->size)
+ size = o->offset + o->size;
+ }
+ end = sec->vma + size;
+ }
+ elf_hash_table (info)->tls_segment
+ = bfd_zalloc (abfd, sizeof (struct elf_link_tls_segment));
+ if (elf_hash_table (info)->tls_segment == NULL)
+ goto error_return;
+ elf_hash_table (info)->tls_segment->start = base;
+ elf_hash_table (info)->tls_segment->size = end - base;
+ elf_hash_table (info)->tls_segment->align = align;
+ }
+
/* Since ELF permits relocations to be against local symbols, we
must have the local symbols available when we do the relocations.
Since we would rather only read the local symbols once, and we
@@ -6100,7 +6160,16 @@ elf_link_output_extsym (h, data)
addresses. */
sym.st_value = h->root.u.def.value + input_sec->output_offset;
if (! finfo->info->relocateable)
- sym.st_value += input_sec->output_section->vma;
+ {
+ sym.st_value += input_sec->output_section->vma;
+ if (h->type == STT_TLS)
+ {
+ /* STT_TLS symbols are relative to PT_TLS segment
+ base. */
+ BFD_ASSERT (finfo->first_tls_sec != NULL);
+ sym.st_value -= finfo->first_tls_sec->vma;
+ }
+ }
}
else
{
@@ -6564,7 +6633,15 @@ elf_link_input_bfd (finfo, input_bfd)
these requirements. */
osym.st_value += isec->output_offset;
if (! finfo->info->relocateable)
- osym.st_value += isec->output_section->vma;
+ {
+ osym.st_value += isec->output_section->vma;
+ if (ELF_ST_TYPE (osym.st_info) == STT_TLS)
+ {
+ /* STT_TLS symbols are relative to PT_TLS segment base. */
+ BFD_ASSERT (finfo->first_tls_sec != NULL);
+ osym.st_value -= finfo->first_tls_sec->vma;
+ }
+ }
if (! elf_link_output_sym (finfo, name, &osym, isec))
return false;
@@ -6899,7 +6976,16 @@ elf_link_input_bfd (finfo, input_bfd)
isym->st_value += sec->output_offset;
if (! finfo->info->relocateable)
- isym->st_value += osec->vma;
+ {
+ isym->st_value += osec->vma;
+ if (ELF_ST_TYPE (isym->st_info) == STT_TLS)
+ {
+ /* STT_TLS symbols are relative to PT_TLS
+ segment base. */
+ BFD_ASSERT (finfo->first_tls_sec != NULL);
+ isym->st_value -= finfo->first_tls_sec->vma;
+ }
+ }
finfo->indices[r_symndx]
= bfd_get_symcount (output_bfd);
diff --git a/bfd/elfxx-ia64.c b/bfd/elfxx-ia64.c
index f92ab6ae0d8..943159b15b5 100644
--- a/bfd/elfxx-ia64.c
+++ b/bfd/elfxx-ia64.c
@@ -79,6 +79,9 @@ struct elfNN_ia64_dyn_sym_info
bfd_vma pltoff_offset;
bfd_vma plt_offset;
bfd_vma plt2_offset;
+ bfd_vma tprel_offset;
+ bfd_vma dtpmod_offset;
+ bfd_vma dtprel_offset;
/* The symbol table entry, if any, that this was derrived from. */
struct elf_link_hash_entry *h;
@@ -97,6 +100,9 @@ struct elfNN_ia64_dyn_sym_info
unsigned got_done : 1;
unsigned fptr_done : 1;
unsigned pltoff_done : 1;
+ unsigned tprel_done : 1;
+ unsigned dtpmod_done : 1;
+ unsigned dtprel_done : 1;
/* True for the different kinds of linker data we want created. */
unsigned want_got : 1;
@@ -105,6 +111,9 @@ struct elfNN_ia64_dyn_sym_info
unsigned want_plt : 1;
unsigned want_plt2 : 1;
unsigned want_pltoff : 1;
+ unsigned want_tprel : 1;
+ unsigned want_dtpmod : 1;
+ unsigned want_dtprel : 1;
};
struct elfNN_ia64_local_hash_entry
@@ -285,6 +294,10 @@ static bfd_vma set_pltoff_entry
PARAMS ((bfd *abfd, struct bfd_link_info *info,
struct elfNN_ia64_dyn_sym_info *dyn_i,
bfd_vma value, boolean));
+static bfd_vma elfNN_ia64_tprel_base
+ PARAMS ((struct bfd_link_info *info));
+static bfd_vma elfNN_ia64_dtprel_base
+ PARAMS ((struct bfd_link_info *info));
static int elfNN_ia64_unwind_entry_compare
PARAMS ((const PTR, const PTR));
static boolean elfNN_ia64_final_link
@@ -423,10 +436,25 @@ static reloc_howto_type ia64_howto_table[] =
IA64_HOWTO (R_IA64_LTOFF22X, "LTOFF22X", 0, false, true),
IA64_HOWTO (R_IA64_LDXMOV, "LDXMOV", 0, false, true),
+ IA64_HOWTO (R_IA64_TPREL14, "TPREL14", 0, false, false),
IA64_HOWTO (R_IA64_TPREL22, "TPREL22", 0, false, false),
+ IA64_HOWTO (R_IA64_TPREL64I, "TPREL64I", 0, false, false),
IA64_HOWTO (R_IA64_TPREL64MSB, "TPREL64MSB", 8, false, false),
IA64_HOWTO (R_IA64_TPREL64LSB, "TPREL64LSB", 8, false, false),
- IA64_HOWTO (R_IA64_LTOFF_TP22, "LTOFF_TP22", 0, false, false),
+ IA64_HOWTO (R_IA64_LTOFF_TPREL22, "LTOFF_TPREL22", 0, false, false),
+
+ IA64_HOWTO (R_IA64_DTPMOD64MSB, "TPREL64MSB", 8, false, false),
+ IA64_HOWTO (R_IA64_DTPMOD64LSB, "TPREL64LSB", 8, false, false),
+ IA64_HOWTO (R_IA64_LTOFF_DTPMOD22, "LTOFF_DTPMOD22", 0, false, false),
+
+ IA64_HOWTO (R_IA64_DTPREL14, "DTPREL14", 0, false, false),
+ IA64_HOWTO (R_IA64_DTPREL22, "DTPREL22", 0, false, false),
+ IA64_HOWTO (R_IA64_DTPREL64I, "DTPREL64I", 0, false, false),
+ IA64_HOWTO (R_IA64_DTPREL32MSB, "DTPREL32MSB", 4, false, false),
+ IA64_HOWTO (R_IA64_DTPREL32LSB, "DTPREL32LSB", 4, false, false),
+ IA64_HOWTO (R_IA64_DTPREL64MSB, "DTPREL64MSB", 8, false, false),
+ IA64_HOWTO (R_IA64_DTPREL64LSB, "DTPREL64LSB", 8, false, false),
+ IA64_HOWTO (R_IA64_LTOFF_DTPREL22, "LTOFF_DTPREL22", 0, false, false),
};
static unsigned char elf_code_to_howto_index[R_IA64_MAX_RELOC_CODE + 1];
@@ -541,10 +569,25 @@ elfNN_ia64_reloc_type_lookup (abfd, bfd_code)
case BFD_RELOC_IA64_LTOFF22X: rtype = R_IA64_LTOFF22X; break;
case BFD_RELOC_IA64_LDXMOV: rtype = R_IA64_LDXMOV; break;
+ case BFD_RELOC_IA64_TPREL14: rtype = R_IA64_TPREL14; break;
case BFD_RELOC_IA64_TPREL22: rtype = R_IA64_TPREL22; break;
+ case BFD_RELOC_IA64_TPREL64I: rtype = R_IA64_TPREL64I; break;
case BFD_RELOC_IA64_TPREL64MSB: rtype = R_IA64_TPREL64MSB; break;
case BFD_RELOC_IA64_TPREL64LSB: rtype = R_IA64_TPREL64LSB; break;
- case BFD_RELOC_IA64_LTOFF_TP22: rtype = R_IA64_LTOFF_TP22; break;
+ case BFD_RELOC_IA64_LTOFF_TPREL22: rtype = R_IA64_LTOFF_TPREL22; break;
+
+ case BFD_RELOC_IA64_DTPMOD64MSB: rtype = R_IA64_DTPMOD64MSB; break;
+ case BFD_RELOC_IA64_DTPMOD64LSB: rtype = R_IA64_DTPMOD64LSB; break;
+ case BFD_RELOC_IA64_LTOFF_DTPMOD22: rtype = R_IA64_LTOFF_DTPMOD22; break;
+
+ case BFD_RELOC_IA64_DTPREL14: rtype = R_IA64_DTPREL14; break;
+ case BFD_RELOC_IA64_DTPREL22: rtype = R_IA64_DTPREL22; break;
+ case BFD_RELOC_IA64_DTPREL64I: rtype = R_IA64_DTPREL64I; break;
+ case BFD_RELOC_IA64_DTPREL32MSB: rtype = R_IA64_DTPREL32MSB; break;
+ case BFD_RELOC_IA64_DTPREL32LSB: rtype = R_IA64_DTPREL32LSB; break;
+ case BFD_RELOC_IA64_DTPREL64MSB: rtype = R_IA64_DTPREL64MSB; break;
+ case BFD_RELOC_IA64_DTPREL64LSB: rtype = R_IA64_DTPREL64LSB; break;
+ case BFD_RELOC_IA64_LTOFF_DTPREL22: rtype = R_IA64_LTOFF_DTPREL22; break;
default: return 0;
}
@@ -2097,6 +2140,9 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
NEED_FULL_PLT = 16,
NEED_DYNREL = 32,
NEED_LTOFF_FPTR = 64,
+ NEED_TPREL = 128,
+ NEED_DTPMOD = 256,
+ NEED_DTPREL = 512
};
struct elf_link_hash_entry *h = NULL;
@@ -2134,11 +2180,42 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
need_entry = 0;
switch (ELFNN_R_TYPE (rel->r_info))
{
- case R_IA64_TPREL22:
case R_IA64_TPREL64MSB:
case R_IA64_TPREL64LSB:
- case R_IA64_LTOFF_TP22:
- return false;
+ if (info->shared || maybe_dynamic)
+ need_entry = NEED_DYNREL;
+ dynrel_type = R_IA64_TPREL64LSB;
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ break;
+
+ case R_IA64_LTOFF_TPREL22:
+ need_entry = NEED_TPREL;
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ break;
+
+ case R_IA64_DTPREL64MSB:
+ case R_IA64_DTPREL64LSB:
+ if (info->shared || maybe_dynamic)
+ need_entry = NEED_DYNREL;
+ dynrel_type = R_IA64_DTPREL64LSB;
+ break;
+
+ case R_IA64_LTOFF_DTPREL22:
+ need_entry = NEED_DTPREL;
+ break;
+
+ case R_IA64_DTPMOD64MSB:
+ case R_IA64_DTPMOD64LSB:
+ if (info->shared || maybe_dynamic)
+ need_entry = NEED_DYNREL;
+ dynrel_type = R_IA64_DTPMOD64LSB;
+ break;
+
+ case R_IA64_LTOFF_DTPMOD22:
+ need_entry = NEED_DTPMOD;
+ break;
case R_IA64_LTOFF_FPTR22:
case R_IA64_LTOFF_FPTR64I:
@@ -2248,7 +2325,7 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
dyn_i->h = h;
/* Create what's needed. */
- if (need_entry & NEED_GOT)
+ if (need_entry & (NEED_GOT | NEED_TPREL | NEED_DTPMOD | NEED_DTPREL))
{
if (!got)
{
@@ -2256,7 +2333,14 @@ elfNN_ia64_check_relocs (abfd, info, sec, relocs)
if (!got)
return false;
}
- dyn_i->want_got = 1;
+ if (need_entry & NEED_GOT)
+ dyn_i->want_got = 1;
+ if (need_entry & NEED_TPREL)
+ dyn_i->want_tprel = 1;
+ if (need_entry & NEED_DTPMOD)
+ dyn_i->want_dtpmod = 1;
+ if (need_entry & NEED_DTPREL)
+ dyn_i->want_dtprel = 1;
}
if (need_entry & NEED_FPTR)
{
@@ -2336,6 +2420,21 @@ allocate_global_data_got (dyn_i, data)
dyn_i->got_offset = x->ofs;
x->ofs += 8;
}
+ if (dyn_i->want_tprel)
+ {
+ dyn_i->tprel_offset = x->ofs;
+ x->ofs += 8;
+ }
+ if (dyn_i->want_dtpmod)
+ {
+ dyn_i->dtpmod_offset = x->ofs;
+ x->ofs += 8;
+ }
+ if (dyn_i->want_dtprel)
+ {
+ dyn_i->dtprel_offset = x->ofs;
+ x->ofs += 8;
+ }
return true;
}
@@ -2582,6 +2681,10 @@ allocate_dynrel_entries (dyn_i, data)
if (!dynamic_symbol)
count *= 2;
break;
+ case R_IA64_TPREL64LSB:
+ case R_IA64_DTPREL64LSB:
+ case R_IA64_DTPMOD64LSB:
+ break;
default:
abort ();
}
@@ -2593,6 +2696,12 @@ allocate_dynrel_entries (dyn_i, data)
if (((dynamic_symbol || shared) && dyn_i->want_got)
|| (dyn_i->want_ltoff_fptr && dyn_i->h && dyn_i->h->dynindx != -1))
ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+ if ((dynamic_symbol || shared) && dyn_i->want_tprel)
+ ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+ if ((dynamic_symbol || shared) && dyn_i->want_dtpmod)
+ ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
+ if (dynamic_symbol && dyn_i->want_dtprel)
+ ia64_info->rel_got_sec->_raw_size += sizeof (ElfNN_External_Rela);
if (dyn_i->want_pltoff)
{
@@ -2903,7 +3012,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
/* Instruction relocations. */
- case R_IA64_IMM14: opnd = IA64_OPND_IMM14; break;
+ case R_IA64_IMM14:
+ case R_IA64_TPREL14:
+ case R_IA64_DTPREL14:
+ opnd = IA64_OPND_IMM14;
+ break;
case R_IA64_PCREL21F: opnd = IA64_OPND_TGT25; break;
case R_IA64_PCREL21M: opnd = IA64_OPND_TGT25b; break;
@@ -2920,6 +3033,11 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_PLTOFF22:
case R_IA64_PCREL22:
case R_IA64_LTOFF_FPTR22:
+ case R_IA64_TPREL22:
+ case R_IA64_DTPREL22:
+ case R_IA64_LTOFF_TPREL22:
+ case R_IA64_LTOFF_DTPMOD22:
+ case R_IA64_LTOFF_DTPREL22:
opnd = IA64_OPND_IMM22;
break;
@@ -2930,6 +3048,8 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_PCREL64I:
case R_IA64_FPTR64I:
case R_IA64_LTOFF_FPTR64I:
+ case R_IA64_TPREL64I:
+ case R_IA64_DTPREL64I:
opnd = IA64_OPND_IMMU64;
break;
@@ -2943,6 +3063,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_SEGREL32MSB:
case R_IA64_SECREL32MSB:
case R_IA64_LTV32MSB:
+ case R_IA64_DTPREL32MSB:
size = 4; bigendian = 1;
break;
@@ -2954,6 +3075,7 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_SEGREL32LSB:
case R_IA64_SECREL32LSB:
case R_IA64_LTV32LSB:
+ case R_IA64_DTPREL32LSB:
size = 4; bigendian = 0;
break;
@@ -2966,6 +3088,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_SEGREL64MSB:
case R_IA64_SECREL64MSB:
case R_IA64_LTV64MSB:
+ case R_IA64_TPREL64MSB:
+ case R_IA64_DTPMOD64MSB:
+ case R_IA64_DTPREL64MSB:
size = 8; bigendian = 1;
break;
@@ -2978,6 +3103,9 @@ elfNN_ia64_install_value (abfd, hit_addr, v, r_type)
case R_IA64_SEGREL64LSB:
case R_IA64_SECREL64LSB:
case R_IA64_LTV64LSB:
+ case R_IA64_TPREL64LSB:
+ case R_IA64_DTPMOD64LSB:
+ case R_IA64_DTPREL64LSB:
size = 8; bigendian = 0;
break;
@@ -3132,26 +3260,53 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
{
struct elfNN_ia64_link_hash_table *ia64_info;
asection *got_sec;
+ boolean done;
+ bfd_vma got_offset;
ia64_info = elfNN_ia64_hash_table (info);
got_sec = ia64_info->got_sec;
- BFD_ASSERT ((dyn_i->got_offset & 7) == 0);
-
- if (! dyn_i->got_done)
+ switch (dyn_r_type)
{
+ case R_IA64_TPREL64LSB:
+ done = dyn_i->tprel_done;
+ dyn_i->tprel_done = true;
+ got_offset = dyn_i->tprel_offset;
+ break;
+ case R_IA64_DTPMOD64LSB:
+ done = dyn_i->dtpmod_done;
+ dyn_i->dtpmod_done = true;
+ got_offset = dyn_i->dtpmod_offset;
+ break;
+ case R_IA64_DTPREL64LSB:
+ done = dyn_i->dtprel_done;
+ dyn_i->dtprel_done = true;
+ got_offset = dyn_i->dtprel_offset;
+ break;
+ default:
+ done = dyn_i->got_done;
dyn_i->got_done = true;
+ got_offset = dyn_i->got_offset;
+ break;
+ }
+ BFD_ASSERT ((got_offset & 7) == 0);
+
+ if (! done)
+ {
/* Store the target address in the linkage table entry. */
- bfd_put_64 (abfd, value, got_sec->contents + dyn_i->got_offset);
+ bfd_put_64 (abfd, value, got_sec->contents + got_offset);
/* Install a dynamic relocation if needed. */
- if (info->shared
+ if ((info->shared && dyn_r_type != R_IA64_DTPREL64LSB)
|| elfNN_ia64_dynamic_symbol_p (dyn_i->h, info)
|| elfNN_ia64_aix_vec (abfd->xvec)
|| (dynindx != -1 && dyn_r_type == R_IA64_FPTR64LSB))
{
- if (dynindx == -1)
+ if (dynindx == -1
+ && dyn_r_type != R_IA64_TPREL64LSB
+ && dyn_r_type != R_IA64_DTPMOD64LSB
+ && dyn_r_type != R_IA64_DTPREL64LSB)
{
dyn_r_type = R_IA64_REL64LSB;
dynindx = 0;
@@ -3171,6 +3326,15 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
case R_IA64_FPTR64LSB:
dyn_r_type = R_IA64_FPTR64MSB;
break;
+ case R_IA64_TPREL64LSB:
+ dyn_r_type = R_IA64_TPREL64MSB;
+ break;
+ case R_IA64_DTPMOD64LSB:
+ dyn_r_type = R_IA64_DTPMOD64MSB;
+ break;
+ case R_IA64_DTPREL64LSB:
+ dyn_r_type = R_IA64_DTPREL64MSB;
+ break;
default:
BFD_ASSERT (false);
break;
@@ -3179,7 +3343,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
elfNN_ia64_install_dyn_reloc (abfd, NULL, got_sec,
ia64_info->rel_got_sec,
- dyn_i->got_offset, dyn_r_type,
+ got_offset, dyn_r_type,
dynindx, addend);
}
}
@@ -3187,7 +3351,7 @@ set_got_entry (abfd, info, dyn_i, dynindx, addend, value, dyn_r_type)
/* Return the address of the linkage table entry. */
value = (got_sec->output_section->vma
+ got_sec->output_offset
- + dyn_i->got_offset);
+ + got_offset);
return value;
}
@@ -3285,6 +3449,35 @@ set_pltoff_entry (abfd, info, dyn_i, value, is_plt)
return value;
}
+/* Return the base VMA address which should be subtracted from real addresses
+ when resolving @tprel() relocation.
+ Main program TLS (whose template starts at PT_TLS p_vaddr)
+ is assigned offset round(16, PT_TLS p_align). */
+
+static bfd_vma
+elfNN_ia64_tprel_base (info)
+ struct bfd_link_info *info;
+{
+ struct elf_link_tls_segment *tls_segment
+ = elf_hash_table (info)->tls_segment;
+
+ BFD_ASSERT (tls_segment != NULL);
+ return (tls_segment->start
+ - align_power ((bfd_vma) 16, tls_segment->align));
+}
+
+/* Return the base VMA address which should be subtracted from real addresses
+ when resolving @dtprel() relocation.
+ This is PT_TLS segment p_vaddr. */
+
+static bfd_vma
+elfNN_ia64_dtprel_base (info)
+ struct bfd_link_info *info;
+{
+ BFD_ASSERT (elf_hash_table (info)->tls_segment != NULL);
+ return elf_hash_table (info)->tls_segment->start;
+}
+
/* Called through qsort to sort the .IA_64.unwind section during a
non-relocatable link. Set elfNN_ia64_unwind_entry_compare_bfd
to the output bfd so we can do proper endianness frobbing. */
@@ -4030,6 +4223,55 @@ elfNN_ia64_relocate_section (output_bfd, info, input_bfd, input_section,
r_type);
break;
+ case R_IA64_TPREL14:
+ case R_IA64_TPREL22:
+ case R_IA64_TPREL64I:
+ value -= elfNN_ia64_tprel_base (info);
+ r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+ break;
+
+ case R_IA64_DTPREL14:
+ case R_IA64_DTPREL22:
+ case R_IA64_DTPREL64I:
+ value -= elfNN_ia64_dtprel_base (info);
+ r = elfNN_ia64_install_value (output_bfd, hit_addr, value, r_type);
+ break;
+
+ case R_IA64_LTOFF_TPREL22:
+ case R_IA64_LTOFF_DTPMOD22:
+ case R_IA64_LTOFF_DTPREL22:
+ {
+ int got_r_type;
+
+ switch (r_type)
+ {
+ default:
+ case R_IA64_LTOFF_TPREL22:
+ if (!dynamic_symbol_p && !info->shared)
+ value -= elfNN_ia64_tprel_base (info);
+ got_r_type = R_IA64_TPREL64LSB;
+ break;
+ case R_IA64_LTOFF_DTPMOD22:
+ if (!dynamic_symbol_p && !info->shared)
+ value = 1;
+ got_r_type = R_IA64_DTPMOD64LSB;
+ break;
+ case R_IA64_LTOFF_DTPREL22:
+ if (!dynamic_symbol_p)
+ value -= elfNN_ia64_dtprel_base (info);
+ got_r_type = R_IA64_DTPREL64LSB;
+ break;
+ }
+ dyn_i = get_dyn_sym_info (ia64_info, h, input_bfd, rel, false);
+ value = set_got_entry (input_bfd, info, dyn_i,
+ (h ? h->dynindx : -1), rel->r_addend,
+ value, got_r_type);
+ value -= gp_val;
+ r = elfNN_ia64_install_value (output_bfd, hit_addr, value,
+ r_type);
+ }
+ break;
+
default:
r = bfd_reloc_notsupported;
break;
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 2fe674c79fc..c3c8126d490 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -771,6 +771,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_386_RELATIVE",
"BFD_RELOC_386_GOTOFF",
"BFD_RELOC_386_GOTPC",
+ "BFD_RELOC_386_TLS_LE",
+ "BFD_RELOC_386_TLS_GD",
+ "BFD_RELOC_386_TLS_LDM",
+ "BFD_RELOC_386_TLS_LDO_32",
+ "BFD_RELOC_386_TLS_IE_32",
+ "BFD_RELOC_386_TLS_LE_32",
+ "BFD_RELOC_386_TLS_DTPMOD32",
+ "BFD_RELOC_386_TLS_DTPOFF32",
+ "BFD_RELOC_386_TLS_TPOFF32",
"BFD_RELOC_X86_64_GOT32",
"BFD_RELOC_X86_64_PLT32",
"BFD_RELOC_X86_64_COPY",
@@ -1142,12 +1151,25 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_IA64_IPLTMSB",
"BFD_RELOC_IA64_IPLTLSB",
"BFD_RELOC_IA64_COPY",
+ "BFD_RELOC_IA64_LTOFF22X",
+ "BFD_RELOC_IA64_LDXMOV",
+ "BFD_RELOC_IA64_TPREL14",
"BFD_RELOC_IA64_TPREL22",
+ "BFD_RELOC_IA64_TPREL64I",
"BFD_RELOC_IA64_TPREL64MSB",
"BFD_RELOC_IA64_TPREL64LSB",
- "BFD_RELOC_IA64_LTOFF_TP22",
- "BFD_RELOC_IA64_LTOFF22X",
- "BFD_RELOC_IA64_LDXMOV",
+ "BFD_RELOC_IA64_LTOFF_TPREL22",
+ "BFD_RELOC_IA64_DTPMOD64MSB",
+ "BFD_RELOC_IA64_DTPMOD64LSB",
+ "BFD_RELOC_IA64_LTOFF_DTPMOD22",
+ "BFD_RELOC_IA64_DTPREL14",
+ "BFD_RELOC_IA64_DTPREL22",
+ "BFD_RELOC_IA64_DTPREL64I",
+ "BFD_RELOC_IA64_DTPREL32MSB",
+ "BFD_RELOC_IA64_DTPREL32LSB",
+ "BFD_RELOC_IA64_DTPREL64MSB",
+ "BFD_RELOC_IA64_DTPREL64LSB",
+ "BFD_RELOC_IA64_LTOFF_DTPREL22",
"BFD_RELOC_M68HC11_HI8",
"BFD_RELOC_M68HC11_LO8",
"BFD_RELOC_M68HC11_3B",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index c701fe0d403..e60062e12d2 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2072,6 +2072,24 @@ ENUMX
BFD_RELOC_386_GOTOFF
ENUMX
BFD_RELOC_386_GOTPC
+ENUMX
+ BFD_RELOC_386_TLS_LE
+ENUMX
+ BFD_RELOC_386_TLS_GD
+ENUMX
+ BFD_RELOC_386_TLS_LDM
+ENUMX
+ BFD_RELOC_386_TLS_LDO_32
+ENUMX
+ BFD_RELOC_386_TLS_IE_32
+ENUMX
+ BFD_RELOC_386_TLS_LE_32
+ENUMX
+ BFD_RELOC_386_TLS_DTPMOD32
+ENUMX
+ BFD_RELOC_386_TLS_DTPOFF32
+ENUMX
+ BFD_RELOC_386_TLS_TPOFF32
ENUMDOC
i386/elf relocations
@@ -3185,17 +3203,43 @@ ENUMX
ENUMX
BFD_RELOC_IA64_COPY
ENUMX
+ BFD_RELOC_IA64_LTOFF22X
+ENUMX
+ BFD_RELOC_IA64_LDXMOV
+ENUMX
+ BFD_RELOC_IA64_TPREL14
+ENUMX
BFD_RELOC_IA64_TPREL22
ENUMX
+ BFD_RELOC_IA64_TPREL64I
+ENUMX
BFD_RELOC_IA64_TPREL64MSB
ENUMX
BFD_RELOC_IA64_TPREL64LSB
ENUMX
- BFD_RELOC_IA64_LTOFF_TP22
+ BFD_RELOC_IA64_LTOFF_TPREL22
ENUMX
- BFD_RELOC_IA64_LTOFF22X
+ BFD_RELOC_IA64_DTPMOD64MSB
ENUMX
- BFD_RELOC_IA64_LDXMOV
+ BFD_RELOC_IA64_DTPMOD64LSB
+ENUMX
+ BFD_RELOC_IA64_LTOFF_DTPMOD22
+ENUMX
+ BFD_RELOC_IA64_DTPREL14
+ENUMX
+ BFD_RELOC_IA64_DTPREL22
+ENUMX
+ BFD_RELOC_IA64_DTPREL64I
+ENUMX
+ BFD_RELOC_IA64_DTPREL32MSB
+ENUMX
+ BFD_RELOC_IA64_DTPREL32LSB
+ENUMX
+ BFD_RELOC_IA64_DTPREL64MSB
+ENUMX
+ BFD_RELOC_IA64_DTPREL64LSB
+ENUMX
+ BFD_RELOC_IA64_LTOFF_DTPREL22
ENUMDOC
Intel IA64 Relocations.
diff --git a/bfd/section.c b/bfd/section.c
index 076a0122be7..358f5c4f0be 100644
--- a/bfd/section.c
+++ b/bfd/section.c
@@ -230,12 +230,6 @@ CODE_FRAGMENT
. standard data. *}
.#define SEC_CONSTRUCTOR 0x100
.
-. {* The section is a constructor, and should be placed at the
-. end of the text, data, or bss section(?). *}
-.#define SEC_CONSTRUCTOR_TEXT 0x1100
-.#define SEC_CONSTRUCTOR_DATA 0x2100
-.#define SEC_CONSTRUCTOR_BSS 0x3100
-.
. {* The section has contents - a data section could be
. <<SEC_ALLOC>> | <<SEC_HAS_CONTENTS>>; a debug section could be
. <<SEC_HAS_CONTENTS>> *}
@@ -256,6 +250,9 @@ CODE_FRAGMENT
. sections. *}
.#define SEC_COFF_SHARED_LIBRARY 0x800
.
+. {* The section contains thread local data. *}
+.#define SEC_THREAD_LOCAL 0x1000
+.
. {* The section has GOT references. This flag is only for the
. linker, and is currently only used by the elf32-hppa back end.
. It will be set if global offset table references were detected
diff --git a/bfd/syms.c b/bfd/syms.c
index 01f7eee2cce..2a889414774 100644
--- a/bfd/syms.c
+++ b/bfd/syms.c
@@ -285,6 +285,9 @@ CODE_FRAGMENT
. as well. *}
.#define BSF_DEBUGGING_RELOC 0x20000
.
+. {* This symbol is thread local. Used in ELF. *}
+.#define BSF_THREAD_LOCAL 0x40000
+.
. flagword flags;
.
. {* A pointer to the section to which this symbol is
diff --git a/include/ChangeLog b/include/ChangeLog
index 9469b38dbba..f59e44757a1 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,9 @@
+2002-05-23 Jakub Jelinek <jakub@redhat.com>
+
+ * elf/common.h (PT_TLS, SHF_TLS, STT_TLS, DF_STATIC_TLS): Define.
+ * elf/ia64.h (R_IA64_LTOFF_TPREL22): Renamed from R_IA64_LTOFF_TP22.
+ * elf/i386.h: Add TLS relocs.
+
2002-05-21 H.J. Lu (hjl@gnu.org)
* bfdlink.h (bfd_link_info): Add allow_multiple_definition.
diff --git a/include/elf/common.h b/include/elf/common.h
index f8b0832e867..f14f7bb679c 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -254,6 +254,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define PT_NOTE 4 /* Auxiliary information */
#define PT_SHLIB 5 /* Reserved, unspecified semantics */
#define PT_PHDR 6 /* Entry for header table itself */
+#define PT_TLS 7 /* Thread local storage segment */
#define PT_LOOS 0x60000000 /* OS-specific */
#define PT_HIOS 0x6fffffff /* OS-specific */
#define PT_LOPROC 0x70000000 /* Processor-specific */
@@ -322,6 +323,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define SHF_LINK_ORDER (1 << 7) /* Preserve section ordering when linking */
#define SHF_OS_NONCONFORMING (1 << 8) /* OS specific processing required */
#define SHF_GROUP (1 << 9) /* Member of a section group */
+#define SHF_TLS (1 << 10) /* Thread local storage section */
/* #define SHF_MASKOS 0x0F000000 *//* OS-specific semantics */
#define SHF_MASKOS 0x0FF00000 /* New value, Oct 4, 1999 Draft */
@@ -417,6 +419,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define STT_SECTION 3 /* Symbol associated with a section */
#define STT_FILE 4 /* Symbol gives a file name */
#define STT_COMMON 5 /* An uninitialised common block */
+#define STT_TLS 6 /* Thread local data object */
#define STT_LOOS 10 /* OS-specific semantics */
#define STT_HIOS 12 /* OS-specific semantics */
#define STT_LOPROC 13 /* Application-specific semantics */
@@ -585,6 +588,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define DF_SYMBOLIC (1 << 1)
#define DF_TEXTREL (1 << 2)
#define DF_BIND_NOW (1 << 3)
+#define DF_STATIC_TLS (1 << 4)
/* These constants are used for the version number of a Elf32_Verdef
structure. */
diff --git a/include/elf/i386.h b/include/elf/i386.h
index 4a15efc4912..c6e9709a1fe 100644
--- a/include/elf/i386.h
+++ b/include/elf/i386.h
@@ -34,12 +34,31 @@ START_RELOC_NUMBERS (elf_i386_reloc_type)
RELOC_NUMBER (R_386_RELATIVE, 8) /* Adjust by program base */
RELOC_NUMBER (R_386_GOTOFF, 9) /* 32 bit offset to GOT */
RELOC_NUMBER (R_386_GOTPC, 10) /* 32 bit PC relative offset to GOT */
- FAKE_RELOC (FIRST_INVALID_RELOC, 11)
- FAKE_RELOC (LAST_INVALID_RELOC, 19)
+ RELOC_NUMBER (R_386_32PLT, 11) /* Used by Sun */
+ FAKE_RELOC (FIRST_INVALID_RELOC, 12)
+ FAKE_RELOC (LAST_INVALID_RELOC, 16)
+ RELOC_NUMBER (R_386_TLS_LE, 17)
+ RELOC_NUMBER (R_386_TLS_GD, 18)
+ RELOC_NUMBER (R_386_TLS_LDM, 19)
RELOC_NUMBER (R_386_16, 20)
RELOC_NUMBER (R_386_PC16, 21)
RELOC_NUMBER (R_386_8, 22)
RELOC_NUMBER (R_386_PC8, 23)
+ RELOC_NUMBER (R_386_TLS_GD_32, 24)
+ RELOC_NUMBER (R_386_TLS_GD_PUSH, 25)
+ RELOC_NUMBER (R_386_TLS_GD_CALL, 26)
+ RELOC_NUMBER (R_386_TLS_GD_POP, 27)
+ RELOC_NUMBER (R_386_TLS_LDM_32, 28)
+ RELOC_NUMBER (R_386_TLS_LDM_PUSH, 29)
+ RELOC_NUMBER (R_386_TLS_LDM_CALL, 30)
+ RELOC_NUMBER (R_386_TLS_LDM_POP, 31)
+ RELOC_NUMBER (R_386_TLS_LDO_32, 32)
+ RELOC_NUMBER (R_386_TLS_IE_32, 33)
+ RELOC_NUMBER (R_386_TLS_LE_32, 34)
+ RELOC_NUMBER (R_386_TLS_DTPMOD32, 35)
+ RELOC_NUMBER (R_386_TLS_DTPOFF32, 36)
+ RELOC_NUMBER (R_386_TLS_TPOFF32, 37)
+
/* These are GNU extensions to enable C++ vtable garbage collection. */
RELOC_NUMBER (R_386_GNU_VTINHERIT, 250)
RELOC_NUMBER (R_386_GNU_VTENTRY, 251)
diff --git a/include/elf/ia64.h b/include/elf/ia64.h
index 5e632ed010e..310a7f74af0 100644
--- a/include/elf/ia64.h
+++ b/include/elf/ia64.h
@@ -192,7 +192,7 @@ START_RELOC_NUMBERS (elf_ia64_reloc_type)
RELOC_NUMBER (R_IA64_TPREL64MSB, 0x96) /* @tprel(sym+add), data8 MSB */
RELOC_NUMBER (R_IA64_TPREL64LSB, 0x97) /* @tprel(sym+add), data8 LSB */
- RELOC_NUMBER (R_IA64_LTOFF_TP22, 0x9a) /* @ltoff(@tprel(s+a)), add imm22 */
+ RELOC_NUMBER (R_IA64_LTOFF_TPREL22, 0x9a) /* @ltoff(@tprel(s+a)), add imm22 */
RELOC_NUMBER (R_IA64_DTPMOD64MSB, 0xa6) /* @dtpmod(sym+add), data8 MSB */
RELOC_NUMBER (R_IA64_DTPMOD64LSB, 0xa7) /* @dtpmod(sym+add), data8 LSB */