X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf64-x86-64.c;h=58be143f0660cf5777c788b66d26fdaf735085ea;hb=c4fb387b7c34ff560a22e719f4c0ca53242b2409;hp=4a56d713daee7e9bcdf9f81346aff9028bcae525;hpb=2f3bf80a30d2c892f60087a246e8b4b4b33498d2;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 4a56d713da..58be143f06 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -1,5 +1,5 @@ /* X86-64 specific support for 64-bit ELF - Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. Contributed by Jan Hubicka . @@ -25,6 +25,7 @@ #include "bfdlink.h" #include "libbfd.h" #include "elf-bfd.h" +#include "bfd_stdint.h" #include "elf/x86-64.h" @@ -456,6 +457,18 @@ struct elf64_x86_64_obj_tdata #define elf64_x86_64_local_tlsdesc_gotent(abfd) \ (elf64_x86_64_tdata (abfd)->local_tlsdesc_gotent) +#define is_x86_64_elf(bfd) \ + (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ + && elf_tdata (bfd) != NULL \ + && elf_object_id (bfd) == X86_64_ELF_TDATA) + +static bfd_boolean +elf64_x86_64_mkobject (bfd *abfd) +{ + return bfd_elf_allocate_object (abfd, sizeof (struct elf64_x86_64_obj_tdata), + X86_64_ELF_TDATA); +} + /* x86-64 ELF linker hash table. */ struct elf64_x86_64_link_hash_table @@ -490,6 +503,9 @@ struct elf64_x86_64_link_hash_table /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; + + /* _TLS_MODULE_BASE_ symbol. */ + struct bfd_link_hash_entry *tls_module_base; }; /* Get the x86-64 ELF linker hash table from a link_info structure. */ @@ -562,6 +578,7 @@ elf64_x86_64_link_hash_table_create (bfd *abfd) ret->tlsdesc_got = 0; ret->tls_ld_got.refcount = 0; ret->sgotplt_jump_table_size = 0; + ret->tls_module_base = NULL; return &ret->elf.root; } @@ -691,19 +708,6 @@ elf64_x86_64_copy_indirect_symbol (struct bfd_link_info *info, _bfd_elf_link_hash_copy_indirect (info, dir, ind); } -static bfd_boolean -elf64_x86_64_mkobject (bfd *abfd) -{ - if (abfd->tdata.any == NULL) - { - bfd_size_type amt = sizeof (struct elf64_x86_64_obj_tdata); - abfd->tdata.any = bfd_zalloc (abfd, amt); - if (abfd->tdata.any == NULL) - return FALSE; - } - return bfd_elf_mkobject (abfd); -} - static bfd_boolean elf64_x86_64_elf_object_p (bfd *abfd) { @@ -712,27 +716,265 @@ elf64_x86_64_elf_object_p (bfd *abfd) return TRUE; } -static int -elf64_x86_64_tls_transition (struct bfd_link_info *info, int r_type, - struct elf_link_hash_entry *h) +typedef union + { + unsigned char c[2]; + uint16_t i; + } +x86_64_opcode16; + +typedef union + { + unsigned char c[4]; + uint32_t i; + } +x86_64_opcode32; + +/* Return TRUE if the TLS access code sequence support transition + from R_TYPE. */ + +static bfd_boolean +elf64_x86_64_check_tls_transition (bfd *abfd, asection *sec, + bfd_byte *contents, + Elf_Internal_Shdr *symtab_hdr, + struct elf_link_hash_entry **sym_hashes, + unsigned int r_type, + const Elf_Internal_Rela *rel, + const Elf_Internal_Rela *relend) { - if (info->shared) - return r_type; + unsigned int val; + unsigned long r_symndx; + struct elf_link_hash_entry *h; + bfd_vma offset; + + /* Get the section contents. */ + if (contents == NULL) + { + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else + { + /* FIXME: How to better handle error condition? */ + if (!bfd_malloc_and_get_section (abfd, sec, &contents)) + return FALSE; + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + + offset = rel->r_offset; switch (r_type) + { + case R_X86_64_TLSGD: + case R_X86_64_TLSLD: + if ((rel + 1) >= relend) + return FALSE; + + if (r_type == R_X86_64_TLSGD) + { + /* Check transition from GD access model. Only + .byte 0x66; leaq foo@tlsgd(%rip), %rdi + .word 0x6666; rex64; call __tls_get_addr + can transit to different access model. */ + + static x86_64_opcode32 leaq = { { 0x66, 0x48, 0x8d, 0x3d } }, + call = { { 0x66, 0x66, 0x48, 0xe8 } }; + if (offset < 4 + || (offset + 12) > sec->size + || bfd_get_32 (abfd, contents + offset - 4) != leaq.i + || bfd_get_32 (abfd, contents + offset + 4) != call.i) + return FALSE; + } + else + { + /* Check transition from LD access model. Only + leaq foo@tlsld(%rip), %rdi; + call __tls_get_addr + can transit to different access model. */ + + static x86_64_opcode32 ld = { { 0x48, 0x8d, 0x3d, 0xe8 } }; + x86_64_opcode32 op; + + if (offset < 3 || (offset + 9) > sec->size) + return FALSE; + + op.i = bfd_get_32 (abfd, contents + offset - 3); + op.c[3] = bfd_get_8 (abfd, contents + offset + 4); + if (op.i != ld.i) + return FALSE; + } + + r_symndx = ELF64_R_SYM (rel[1].r_info); + if (r_symndx < symtab_hdr->sh_info) + return FALSE; + + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + /* Use strncmp to check __tls_get_addr since __tls_get_addr + may be versioned. */ + return (h != NULL + && h->root.root.string != NULL + && (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32 + || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32) + && (strncmp (h->root.root.string, + "__tls_get_addr", 14) == 0)); + + case R_X86_64_GOTTPOFF: + /* Check transition from IE access model: + movq foo@gottpoff(%rip), %reg + addq foo@gottpoff(%rip), %reg + */ + + if (offset < 3 || (offset + 4) > sec->size) + return FALSE; + + val = bfd_get_8 (abfd, contents + offset - 3); + if (val != 0x48 && val != 0x4c) + return FALSE; + + val = bfd_get_8 (abfd, contents + offset - 2); + if (val != 0x8b && val != 0x03) + return FALSE; + + val = bfd_get_8 (abfd, contents + offset - 1); + return (val & 0xc7) == 5; + + case R_X86_64_GOTPC32_TLSDESC: + /* Check transition from GDesc access model: + leaq x@tlsdesc(%rip), %rax + + Make sure it's a leaq adding rip to a 32-bit offset + into any register, although it's probably almost always + going to be rax. */ + + if (offset < 3 || (offset + 4) > sec->size) + return FALSE; + + val = bfd_get_8 (abfd, contents + offset - 3); + if ((val & 0xfb) != 0x48) + return FALSE; + + if (bfd_get_8 (abfd, contents + offset - 2) != 0x8d) + return FALSE; + + val = bfd_get_8 (abfd, contents + offset - 1); + return (val & 0xc7) == 0x05; + + case R_X86_64_TLSDESC_CALL: + /* Check transition from GDesc access model: + call *x@tlsdesc(%rax) + */ + if (offset + 2 <= sec->size) + { + /* Make sure that it's a call *x@tlsdesc(%rax). */ + static x86_64_opcode16 call = { { 0xff, 0x10 } }; + return bfd_get_16 (abfd, contents + offset) == call.i; + } + + return FALSE; + + default: + abort (); + } +} + +/* Return TRUE if the TLS access transition is OK or no transition + will be performed. Update R_TYPE if there is a transition. */ + +static bfd_boolean +elf64_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd, + asection *sec, bfd_byte *contents, + Elf_Internal_Shdr *symtab_hdr, + struct elf_link_hash_entry **sym_hashes, + unsigned int *r_type, int tls_type, + const Elf_Internal_Rela *rel, + const Elf_Internal_Rela *relend, + struct elf_link_hash_entry *h) +{ + unsigned int from_type = *r_type; + unsigned int to_type = from_type; + bfd_boolean check = TRUE; + + switch (from_type) { case R_X86_64_TLSGD: case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_TLSDESC_CALL: case R_X86_64_GOTTPOFF: - if (h == NULL) - return R_X86_64_TPOFF32; - return R_X86_64_GOTTPOFF; + if (!info->shared) + { + if (h == NULL) + to_type = R_X86_64_TPOFF32; + else + to_type = R_X86_64_GOTTPOFF; + } + + /* When we are called from elf64_x86_64_relocate_section, + CONTENTS isn't NULL and there may be additional transitions + based on TLS_TYPE. */ + if (contents != NULL) + { + unsigned int new_to_type = to_type; + + if (!info->shared + && h != NULL + && h->dynindx == -1 + && tls_type == GOT_TLS_IE) + new_to_type = R_X86_64_TPOFF32; + + if (to_type == R_X86_64_TLSGD + || to_type == R_X86_64_GOTPC32_TLSDESC + || to_type == R_X86_64_TLSDESC_CALL) + { + if (tls_type == GOT_TLS_IE) + new_to_type = R_X86_64_GOTTPOFF; + } + + /* We checked the transition before when we were called from + elf64_x86_64_check_relocs. We only want to check the new + transition which hasn't been checked before. */ + check = new_to_type != to_type && from_type == to_type; + to_type = new_to_type; + } + + break; + case R_X86_64_TLSLD: - return R_X86_64_TPOFF32; + if (!info->shared) + to_type = R_X86_64_TPOFF32; + break; + + default: + return TRUE; } - return r_type; + /* Return TRUE if there is no transition. */ + if (from_type == to_type) + return TRUE; + + /* Check if the transition can be performed. */ + if (check + && ! elf64_x86_64_check_tls_transition (abfd, sec, contents, + symtab_hdr, sym_hashes, + from_type, rel, relend)) + { + reloc_howto_type *from, *to; + + from = elf64_x86_64_rtype_to_howto (abfd, from_type); + to = elf64_x86_64_rtype_to_howto (abfd, to_type); + + (*_bfd_error_handler) + (_("%B: TLS transition from %s to %s against `%s' at 0x%lx " + "in section `%A' failed"), + abfd, sec, from->name, to->name, + h ? h->root.root.string : "a local symbol", + (unsigned long) rel->r_offset); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + *r_type = to_type; + return TRUE; } /* Look through the relocs for a section during the first phase, and @@ -740,7 +982,8 @@ elf64_x86_64_tls_transition (struct bfd_link_info *info, int r_type, linkage table, and dynamic reloc sections. */ static bfd_boolean -elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, +elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, + asection *sec, const Elf_Internal_Rela *relocs) { struct elf64_x86_64_link_hash_table *htab; @@ -753,8 +996,10 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, if (info->relocatable) return TRUE; + BFD_ASSERT (is_x86_64_elf (abfd)); + htab = elf64_x86_64_hash_table (info); - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + symtab_hdr = &elf_symtab_hdr (abfd); sym_hashes = elf_sym_hashes (abfd); sreloc = NULL; @@ -786,7 +1031,12 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, h = (struct elf_link_hash_entry *) h->root.u.i.link; } - r_type = elf64_x86_64_tls_transition (info, r_type, h); + if (! elf64_x86_64_tls_transition (info, abfd, sec, NULL, + symtab_hdr, sym_hashes, + &r_type, GOT_UNKNOWN, + rel, rel_end, h)) + return FALSE; + switch (r_type) { case R_X86_64_TLSLD: @@ -888,7 +1138,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, else { (*_bfd_error_handler) - (_("%B: %s' accessed both as normal and thread local symbol"), + (_("%B: '%s' accessed both as normal and thread local symbol"), abfd, h ? h->root.root.string : ""); return FALSE; } @@ -1009,6 +1259,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, may need to keep relocations for symbols satisfied by a dynamic library if we manage to avoid copy relocs for the symbol. */ + if ((info->shared && (sec->flags & SEC_ALLOC) != 0 && (((r_type != R_X86_64_PC8) @@ -1034,47 +1285,14 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, this reloc. */ if (sreloc == NULL) { - const char *name; - bfd *dynobj; - - name = (bfd_elf_string_from_elf_section - (abfd, - elf_elfheader (abfd)->e_shstrndx, - elf_section_data (sec)->rel_hdr.sh_name)); - if (name == NULL) - return FALSE; - - if (! CONST_STRNEQ (name, ".rela") - || strcmp (bfd_get_section_name (abfd, sec), - name + 5) != 0) - { - (*_bfd_error_handler) - (_("%B: bad relocation section name `%s\'"), - abfd, name); - } - if (htab->elf.dynobj == NULL) htab->elf.dynobj = abfd; - dynobj = htab->elf.dynobj; + sreloc = _bfd_elf_make_dynamic_reloc_section + (sec, htab->elf.dynobj, 3, abfd, /*rela?*/ TRUE); - sreloc = bfd_get_section_by_name (dynobj, name); if (sreloc == NULL) - { - flagword flags; - - flags = (SEC_HAS_CONTENTS | SEC_READONLY - | SEC_IN_MEMORY | SEC_LINKER_CREATED); - if ((sec->flags & SEC_ALLOC) != 0) - flags |= SEC_ALLOC | SEC_LOAD; - sreloc = bfd_make_section_with_flags (dynobj, - name, - flags); - if (sreloc == NULL - || ! bfd_set_section_alignment (dynobj, sreloc, 3)) - return FALSE; - } - elf_section_data (sec)->sreloc = sreloc; + return FALSE; } /* If this is a global symbol, we count the number of @@ -1136,7 +1354,9 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_X86_64_GNU_VTENTRY: - if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + BFD_ASSERT (h != NULL); + if (h != NULL + && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend)) return FALSE; break; @@ -1173,16 +1393,20 @@ elf64_x86_64_gc_mark_hook (asection *sec, static bfd_boolean elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, - asection *sec, const Elf_Internal_Rela *relocs) + asection *sec, + const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; bfd_signed_vma *local_got_refcounts; const Elf_Internal_Rela *rel, *relend; + if (info->relocatable) + return TRUE; + elf_section_data (sec)->local_dynrel = NULL; - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + symtab_hdr = &elf_symtab_hdr (abfd); sym_hashes = elf_sym_hashes (abfd); local_got_refcounts = elf_local_got_refcounts (abfd); @@ -1216,7 +1440,12 @@ elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, } r_type = ELF64_R_TYPE (rel->r_info); - r_type = elf64_x86_64_tls_transition (info, r_type, h); + if (! elf64_x86_64_tls_transition (info, abfd, sec, NULL, + symtab_hdr, sym_hashes, + &r_type, GOT_UNKNOWN, + rel, relend, h)) + return FALSE; + switch (r_type) { case R_X86_64_TLSLD: @@ -1641,7 +1870,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) /* Finally, allocate space. */ for (p = eh->dyn_relocs; p != NULL; p = p->next) { - asection *sreloc = elf_section_data (p->sec)->sreloc; + asection * sreloc; + + sreloc = elf_section_data (p->sec)->sreloc; + + BFD_ASSERT (sreloc != NULL); + sreloc->size += p->count * sizeof (Elf64_External_Rela); } @@ -1719,7 +1953,7 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, Elf_Internal_Shdr *symtab_hdr; asection *srel; - if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) + if (! is_x86_64_elf (ibfd)) continue; for (s = ibfd->sections; s != NULL; s = s->next) @@ -1745,7 +1979,6 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, srel->size += p->count * sizeof (Elf64_External_Rela); if ((p->sec->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; - } } } @@ -1754,7 +1987,7 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (!local_got) continue; - symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; + symtab_hdr = &elf_symtab_hdr (ibfd); locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; local_tls_type = elf64_x86_64_local_got_tls_type (ibfd); @@ -1814,7 +2047,7 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* Allocate global sym .plt and .got entries, and space for global sym dynamic relocs. */ - elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info); + elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info); /* For every jump slot reserved in the sgotplt, reloc_count is incremented. However, when we reserve space for TLS descriptors, @@ -1945,8 +2178,7 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) - elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, - (PTR) info); + elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info); if ((info->flags & DF_TEXTREL) != 0) { @@ -1985,6 +2217,9 @@ elf64_x86_64_always_size_sections (bfd *output_bfd, tls_sec, 0, NULL, FALSE, bed->collect, &bh))) return FALSE; + + elf64_x86_64_hash_table (info)->tls_module_base = bh; + tlsbase = (struct elf_link_hash_entry *)bh; tlsbase->def_regular = 1; tlsbase->other = STV_HIDDEN; @@ -1995,6 +2230,27 @@ elf64_x86_64_always_size_sections (bfd *output_bfd, return TRUE; } +/* _TLS_MODULE_BASE_ needs to be treated especially when linking + executables. Rather than setting it to the beginning of the TLS + section, we have to set it to the end. This function may be called + multiple times, it is idempotent. */ + +static void +set_tls_module_base (struct bfd_link_info *info) +{ + struct bfd_link_hash_entry *base; + + if (!info->executable) + return; + + base = elf64_x86_64_hash_table (info)->tls_module_base; + + if (!base) + return; + + base->u.def.value = elf_hash_table (info)->tls_size; +} + /* Return the base VMA address which should be subtracted from real addresses when resolving @dtpoff relocation. This is PT_TLS segment p_vaddr. */ @@ -2057,12 +2313,16 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, Elf_Internal_Rela *rel; Elf_Internal_Rela *relend; + BFD_ASSERT (is_x86_64_elf (input_bfd)); + htab = elf64_x86_64_hash_table (info); - symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + symtab_hdr = &elf_symtab_hdr (input_bfd); sym_hashes = elf_sym_hashes (input_bfd); local_got_offsets = elf_local_got_offsets (input_bfd); local_tlsdesc_gotents = elf64_x86_64_local_tlsdesc_gotent (input_bfd); + set_tls_module_base (info); + rel = relocs; relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) @@ -2339,30 +2599,63 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, case R_X86_64_PC16: case R_X86_64_PC32: if (info->shared - && !SYMBOL_REFERENCES_LOCAL (info, h) && (input_section->flags & SEC_ALLOC) != 0 && (input_section->flags & SEC_READONLY) != 0 - && (!h->def_regular - || r_type != R_X86_64_PC32 - || h->type != STT_FUNC - || ELF_ST_VISIBILITY (h->other) != STV_PROTECTED - || !is_32bit_relative_branch (contents, - rel->r_offset))) + && h != NULL) { - if (h->def_regular - && r_type == R_X86_64_PC32 - && h->type == STT_FUNC - && ELF_ST_VISIBILITY (h->other) == STV_PROTECTED) - (*_bfd_error_handler) - (_("%B: relocation R_X86_64_PC32 against protected function `%s' can not be used when making a shared object"), - input_bfd, h->root.root.string); + bfd_boolean fail = FALSE; + bfd_boolean branch + = (r_type == R_X86_64_PC32 + && is_32bit_relative_branch (contents, rel->r_offset)); + + if (SYMBOL_REFERENCES_LOCAL (info, h)) + { + /* Symbol is referenced locally. Make sure it is + defined locally or for a branch. */ + fail = !h->def_regular && !branch; + } else - (*_bfd_error_handler) - (_("%B: relocation %s against `%s' can not be used when making a shared object; recompile with -fPIC"), - input_bfd, x86_64_elf_howto_table[r_type].name, - h->root.root.string); - bfd_set_error (bfd_error_bad_value); - return FALSE; + { + /* Symbol isn't referenced locally. We only allow + branch to symbol with non-default visibility. */ + fail = (!branch + || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT); + } + + if (fail) + { + const char *fmt; + const char *v; + const char *pic = ""; + + switch (ELF_ST_VISIBILITY (h->other)) + { + case STV_HIDDEN: + v = _("hidden symbol"); + break; + case STV_INTERNAL: + v = _("internal symbol"); + break; + case STV_PROTECTED: + v = _("protected symbol"); + break; + default: + v = _("symbol"); + pic = _("; recompile with -fPIC"); + break; + } + + if (h->def_regular) + fmt = _("%B: relocation %s against %s `%s' can not be used when making a shared object%s"); + else + fmt = _("%B: relocation %s against undefined %s `%s' can not be used when making a shared object%s"); + + (*_bfd_error_handler) (fmt, input_bfd, + x86_64_elf_howto_table[r_type].name, + v, h->root.root.string, pic); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } } /* Fall through. */ @@ -2481,8 +2774,8 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } sreloc = elf_section_data (input_section)->sreloc; - if (sreloc == NULL) - abort (); + + BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL); loc = sreloc->contents; loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela); @@ -2502,67 +2795,38 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_TLSDESC_CALL: case R_X86_64_GOTTPOFF: - r_type = elf64_x86_64_tls_transition (info, r_type, h); tls_type = GOT_UNKNOWN; if (h == NULL && local_got_offsets) tls_type = elf64_x86_64_local_got_tls_type (input_bfd) [r_symndx]; else if (h != NULL) - { - tls_type = elf64_x86_64_hash_entry (h)->tls_type; - if (!info->shared && h->dynindx == -1 && tls_type == GOT_TLS_IE) - r_type = R_X86_64_TPOFF32; - } - if (r_type == R_X86_64_TLSGD - || r_type == R_X86_64_GOTPC32_TLSDESC - || r_type == R_X86_64_TLSDESC_CALL) - { - if (tls_type == GOT_TLS_IE) - r_type = R_X86_64_GOTTPOFF; - } + tls_type = elf64_x86_64_hash_entry (h)->tls_type; + + if (! elf64_x86_64_tls_transition (info, input_bfd, + input_section, contents, + symtab_hdr, sym_hashes, + &r_type, tls_type, rel, + relend, h)) + return FALSE; if (r_type == R_X86_64_TPOFF32) { + bfd_vma roff = rel->r_offset; + BFD_ASSERT (! unresolved_reloc); + if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD) { - unsigned int i; - static unsigned char tlsgd[8] - = { 0x66, 0x48, 0x8d, 0x3d, 0x66, 0x66, 0x48, 0xe8 }; - unsigned long tls_r_symndx; - struct elf_link_hash_entry *tls_h; - /* GD->LE transition. .byte 0x66; leaq foo@tlsgd(%rip), %rdi .word 0x6666; rex64; call __tls_get_addr Change it into: movq %fs:0, %rax leaq foo@tpoff(%rax), %rax */ - BFD_ASSERT (rel->r_offset >= 4); - for (i = 0; i < 4; i++) - BFD_ASSERT (bfd_get_8 (input_bfd, - contents + rel->r_offset - 4 + i) - == tlsgd[i]); - BFD_ASSERT (rel->r_offset + 12 <= input_section->size); - for (i = 0; i < 4; i++) - BFD_ASSERT (bfd_get_8 (input_bfd, - contents + rel->r_offset + 4 + i) - == tlsgd[i+4]); - BFD_ASSERT (rel + 1 < relend); - tls_r_symndx = ELF64_R_SYM (rel[1].r_info); - BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info); - tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info]; - BFD_ASSERT (tls_h != NULL - && tls_h->root.root.string != NULL - && strcmp (tls_h->root.root.string, - "__tls_get_addr") == 0); - BFD_ASSERT ((! info->shared - && ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32) - || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32); - memcpy (contents + rel->r_offset - 4, + memcpy (contents + roff - 4, "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x8d\x80\0\0\0", 16); bfd_put_32 (output_bfd, tpoff (info, relocation), - contents + rel->r_offset + 8); + contents + roff + 8); /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ rel++; continue; @@ -2575,26 +2839,13 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, Change it to: movl $x@tpoff, %rax - - Registers other than %rax may be set up here. */ + */ unsigned int val, type, type2; - bfd_vma roff; - /* First, make sure it's a leaq adding rip to a - 32-bit offset into any register, although it's - probably almost always going to be rax. */ - roff = rel->r_offset; - BFD_ASSERT (roff >= 3); type = bfd_get_8 (input_bfd, contents + roff - 3); - BFD_ASSERT ((type & 0xfb) == 0x48); type2 = bfd_get_8 (input_bfd, contents + roff - 2); - BFD_ASSERT (type2 == 0x8d); val = bfd_get_8 (input_bfd, contents + roff - 1); - BFD_ASSERT ((val & 0xc7) == 0x05); - BFD_ASSERT (roff + 4 <= input_section->size); - - /* Now modify the instruction as appropriate. */ bfd_put_8 (output_bfd, 0x48 | ((type >> 2) & 1), contents + roff - 3); bfd_put_8 (output_bfd, 0xc7, contents + roff - 2); @@ -2610,29 +2861,13 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, It's originally: call *(%rax) Turn it into: - nop; nop. */ - - unsigned int val, type; - bfd_vma roff; - - /* First, make sure it's a call *(%rax). */ - 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. Use - xchg %ax,%ax instead of 2 nops. */ + xchg %ax,%ax. */ bfd_put_8 (output_bfd, 0x66, contents + roff); bfd_put_8 (output_bfd, 0x90, contents + roff + 1); continue; } - else + else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTTPOFF) { - unsigned int val, type, reg; - /* IE->LE transition: Originally it can be one of: movq foo@gottpoff(%rip), %reg @@ -2641,25 +2876,23 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, movq $foo, %reg leaq foo(%reg), %reg addq $foo, %reg. */ - BFD_ASSERT (rel->r_offset >= 3); - val = bfd_get_8 (input_bfd, contents + rel->r_offset - 3); - BFD_ASSERT (val == 0x48 || val == 0x4c); - type = bfd_get_8 (input_bfd, contents + rel->r_offset - 2); - BFD_ASSERT (type == 0x8b || type == 0x03); - reg = bfd_get_8 (input_bfd, contents + rel->r_offset - 1); - BFD_ASSERT ((reg & 0xc7) == 5); + + unsigned int val, type, reg; + + val = bfd_get_8 (input_bfd, contents + roff - 3); + type = bfd_get_8 (input_bfd, contents + roff - 2); + reg = bfd_get_8 (input_bfd, contents + roff - 1); reg >>= 3; - BFD_ASSERT (rel->r_offset + 4 <= input_section->size); if (type == 0x8b) { /* movq */ if (val == 0x4c) bfd_put_8 (output_bfd, 0x49, - contents + rel->r_offset - 3); + contents + roff - 3); bfd_put_8 (output_bfd, 0xc7, - contents + rel->r_offset - 2); + contents + roff - 2); bfd_put_8 (output_bfd, 0xc0 | reg, - contents + rel->r_offset - 1); + contents + roff - 1); } else if (reg == 4) { @@ -2667,27 +2900,29 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, special */ if (val == 0x4c) bfd_put_8 (output_bfd, 0x49, - contents + rel->r_offset - 3); + contents + roff - 3); bfd_put_8 (output_bfd, 0x81, - contents + rel->r_offset - 2); + contents + roff - 2); bfd_put_8 (output_bfd, 0xc0 | reg, - contents + rel->r_offset - 1); + contents + roff - 1); } else { /* addq -> leaq */ if (val == 0x4c) bfd_put_8 (output_bfd, 0x4d, - contents + rel->r_offset - 3); + contents + roff - 3); bfd_put_8 (output_bfd, 0x8d, - contents + rel->r_offset - 2); + contents + roff - 2); bfd_put_8 (output_bfd, 0x80 | reg | (reg << 3), - contents + rel->r_offset - 1); + contents + roff - 1); } bfd_put_32 (output_bfd, tpoff (info, relocation), - contents + rel->r_offset); + contents + roff); continue; } + else + BFD_ASSERT (FALSE); } if (htab->sgot == NULL) @@ -2814,151 +3049,104 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, + htab->sgot->output_offset + off; unresolved_reloc = FALSE; } - else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD) - { - unsigned int i; - static unsigned char tlsgd[8] - = { 0x66, 0x48, 0x8d, 0x3d, 0x66, 0x66, 0x48, 0xe8 }; - - /* GD->IE transition. - .byte 0x66; leaq foo@tlsgd(%rip), %rdi - .word 0x6666; rex64; call __tls_get_addr@plt - Change it into: - movq %fs:0, %rax - addq foo@gottpoff(%rip), %rax */ - BFD_ASSERT (rel->r_offset >= 4); - for (i = 0; i < 4; i++) - BFD_ASSERT (bfd_get_8 (input_bfd, - contents + rel->r_offset - 4 + i) - == tlsgd[i]); - BFD_ASSERT (rel->r_offset + 12 <= input_section->size); - for (i = 0; i < 4; i++) - BFD_ASSERT (bfd_get_8 (input_bfd, - contents + rel->r_offset + 4 + i) - == tlsgd[i+4]); - BFD_ASSERT (rel + 1 < relend); - BFD_ASSERT (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32); - memcpy (contents + rel->r_offset - 4, - "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0", - 16); - - relocation = (htab->sgot->output_section->vma - + htab->sgot->output_offset + off - - rel->r_offset - - input_section->output_section->vma - - input_section->output_offset - - 12); - bfd_put_32 (output_bfd, relocation, - contents + rel->r_offset + 8); - /* Skip R_X86_64_PLT32. */ - rel++; - continue; - } - else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC) + else { - /* GDesc -> IE transition. - It's originally something like: - leaq x@tlsdesc(%rip), %rax - - Change it to: - movq x@gottpoff(%rip), %rax # before nop; nop - - Registers other than %rax may be set up here. */ - - unsigned int val, type, type2; - bfd_vma roff; - - /* First, make sure it's a leaq adding rip to a 32-bit - offset into any register, although it's probably - almost always going to be rax. */ - roff = rel->r_offset; - BFD_ASSERT (roff >= 3); - type = bfd_get_8 (input_bfd, contents + roff - 3); - BFD_ASSERT ((type & 0xfb) == 0x48); - type2 = bfd_get_8 (input_bfd, contents + roff - 2); - BFD_ASSERT (type2 == 0x8d); - val = bfd_get_8 (input_bfd, contents + roff - 1); - BFD_ASSERT ((val & 0xc7) == 0x05); - BFD_ASSERT (roff + 4 <= input_section->size); - - /* Now modify the instruction as appropriate. */ - /* To turn a leaq into a movq in the form we use it, it - suffices to change the second byte from 0x8d to - 0x8b. */ - bfd_put_8 (output_bfd, 0x8b, contents + roff - 2); + bfd_vma roff = rel->r_offset; - bfd_put_32 (output_bfd, - htab->sgot->output_section->vma - + htab->sgot->output_offset + off - - rel->r_offset - - input_section->output_section->vma - - input_section->output_offset - - 4, - contents + roff); - continue; - } - else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSDESC_CALL) - { - /* GDesc -> IE transition. - It's originally: - call *(%rax) + if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSGD) + { + /* GD->IE transition. + .byte 0x66; leaq foo@tlsgd(%rip), %rdi + .word 0x6666; rex64; call __tls_get_addr@plt + Change it into: + movq %fs:0, %rax + addq foo@gottpoff(%rip), %rax */ + memcpy (contents + roff - 4, + "\x64\x48\x8b\x04\x25\0\0\0\0\x48\x03\x05\0\0\0", + 16); - Change it to: - nop; nop. */ + relocation = (htab->sgot->output_section->vma + + htab->sgot->output_offset + off + - roff + - input_section->output_section->vma + - input_section->output_offset + - 12); + bfd_put_32 (output_bfd, relocation, + contents + roff + 8); + /* Skip R_X86_64_PLT32. */ + rel++; + continue; + } + else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC) + { + /* GDesc -> IE transition. + It's originally something like: + leaq x@tlsdesc(%rip), %rax - unsigned int val, type; - bfd_vma roff; + Change it to: + movq x@gottpoff(%rip), %rax # before xchg %ax,%ax + */ - /* 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); + unsigned int val, type, type2; - /* Now modify the instruction as appropriate. Use - xchg %ax,%ax instead of 2 nops. */ - bfd_put_8 (output_bfd, 0x66, contents + roff); - bfd_put_8 (output_bfd, 0x90, contents + roff + 1); + type = bfd_get_8 (input_bfd, contents + roff - 3); + type2 = bfd_get_8 (input_bfd, contents + roff - 2); + val = bfd_get_8 (input_bfd, contents + roff - 1); - continue; + /* Now modify the instruction as appropriate. To + turn a leaq into a movq in the form we use it, it + suffices to change the second byte from 0x8d to + 0x8b. */ + bfd_put_8 (output_bfd, 0x8b, contents + roff - 2); + + bfd_put_32 (output_bfd, + htab->sgot->output_section->vma + + htab->sgot->output_offset + off + - rel->r_offset + - input_section->output_section->vma + - input_section->output_offset + - 4, + contents + roff); + continue; + } + else if (ELF64_R_TYPE (rel->r_info) == R_X86_64_TLSDESC_CALL) + { + /* GDesc -> IE transition. + It's originally: + call *(%rax) + + Change it to: + xchg %ax,%ax. */ + + unsigned int val, type; + + type = bfd_get_8 (input_bfd, contents + roff); + val = bfd_get_8 (input_bfd, contents + roff + 1); + bfd_put_8 (output_bfd, 0x66, contents + roff); + bfd_put_8 (output_bfd, 0x90, contents + roff + 1); + continue; + } + else + BFD_ASSERT (FALSE); } - else - BFD_ASSERT (FALSE); break; case R_X86_64_TLSLD: - if (! info->shared) - { - unsigned long tls_r_symndx; - struct elf_link_hash_entry *tls_h; + if (! elf64_x86_64_tls_transition (info, input_bfd, + input_section, contents, + symtab_hdr, sym_hashes, + &r_type, GOT_UNKNOWN, + rel, relend, h)) + return FALSE; + if (r_type != R_X86_64_TLSLD) + { /* LD->LE transition: - Ensure it is: leaq foo@tlsld(%rip), %rdi; call __tls_get_addr. We change it into: .word 0x6666; .byte 0x66; movl %fs:0, %rax. */ - BFD_ASSERT (rel->r_offset >= 3); - BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 3) - == 0x48); - BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 2) - == 0x8d); - BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset - 1) - == 0x3d); - BFD_ASSERT (rel->r_offset + 9 <= input_section->size); - BFD_ASSERT (bfd_get_8 (input_bfd, contents + rel->r_offset + 4) - == 0xe8); - BFD_ASSERT (rel + 1 < relend); - tls_r_symndx = ELF64_R_SYM (rel[1].r_info); - BFD_ASSERT (tls_r_symndx >= symtab_hdr->sh_info); - tls_h = sym_hashes[tls_r_symndx - symtab_hdr->sh_info]; - BFD_ASSERT (tls_h != NULL - && tls_h->root.root.string != NULL - && strcmp (tls_h->root.root.string, - "__tls_get_addr") == 0); - BFD_ASSERT (ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PC32 - || ELF64_R_TYPE (rel[1].r_info) == R_X86_64_PLT32); + + BFD_ASSERT (r_type == R_X86_64_TPOFF32); memcpy (contents + rel->r_offset - 3, "\x66\x66\x66\x64\x48\x8b\x04\x25\0\0\0", 12); /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ @@ -3192,6 +3380,8 @@ elf64_x86_64_finish_dynamic_symbol (bfd *output_bfd, if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) { + if (!h->def_regular) + return FALSE; BFD_ASSERT((h->got.offset & 1) != 0); rela.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE); rela.r_addend = (h->root.u.def.value @@ -3603,7 +3793,7 @@ elf64_x86_64_merge_symbol (struct bfd_link_info *info ATTRIBUTE_UNUSED, } else if (sym->st_shndx == SHN_X86_64_LCOMMON && (elf_section_flags (*oldsec) & SHF_X86_64_LARGE) == 0) - *psec = *sec = bfd_com_section_ptr; + *psec = *sec = bfd_com_section_ptr; } return TRUE; @@ -3614,7 +3804,7 @@ elf64_x86_64_additional_program_headers (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED) { asection *s; - int count = 0; + int count = 0; /* Check to see if we need a large readonly segment. */ s = bfd_get_section_by_name (abfd, ".lrodata"); @@ -3644,7 +3834,7 @@ elf64_x86_64_hash_symbol (struct elf_link_hash_entry *h) return _bfd_elf_hash_symbol (h); } -static const struct bfd_elf_special_section +static const struct bfd_elf_special_section elf64_x86_64_special_sections[]= { { STRING_COMMA_LEN (".gnu.linkonce.lb"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, @@ -3681,6 +3871,7 @@ static const struct bfd_elf_special_section elf64_x86_64_reloc_name_lookup #define elf_backend_adjust_dynamic_symbol elf64_x86_64_adjust_dynamic_symbol +#define elf_backend_relocs_compatible _bfd_elf_relocs_compatible #define elf_backend_check_relocs elf64_x86_64_check_relocs #define elf_backend_copy_indirect_symbol elf64_x86_64_copy_indirect_symbol #define elf_backend_create_dynamic_sections elf64_x86_64_create_dynamic_sections