X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf64-x86-64.c;h=b82bcd11b768993134da4b75507fafd3254d3573;hb=d8045f234d8865a7a7bfce71e81fcbeaf4098a7e;hp=504a19b7b9833f1ee059760c2eba068b4fde1c0c;hpb=66eb6687c96edd3c969bd644574c649fd0be23cd;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 504a19b7b9..b82bcd11b7 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 + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. Contributed by Jan Hubicka . @@ -7,7 +7,7 @@ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -17,13 +17,15 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ -#include "bfd.h" #include "sysdep.h" +#include "bfd.h" #include "bfdlink.h" #include "libbfd.h" #include "elf-bfd.h" +#include "bfd_stdint.h" #include "elf/x86-64.h" @@ -31,8 +33,8 @@ #define MINUS_ONE (~ (bfd_vma) 0) /* The relocation "howto" table. Order of fields: - type, size, bitsize, pc_relative, complain_on_overflow, - special_function, name, partial_inplace, src_mask, dst_pack, pcrel_offset. */ + type, rightshift, size, bitsize, pc_relative, bitpos, complain_on_overflow, + special_function, name, partial_inplace, src_mask, dst_mask, pcrel_offset. */ static reloc_howto_type x86_64_elf_howto_table[] = { HOWTO(R_X86_64_NONE, 0, 0, 0, FALSE, 0, complain_overflow_dont, @@ -112,11 +114,21 @@ static reloc_howto_type x86_64_elf_howto_table[] = HOWTO(R_X86_64_GOTPC32, 0, 2, 32, TRUE, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_X86_64_GOTPC32", FALSE, 0xffffffff, 0xffffffff, TRUE), - EMPTY_HOWTO (27), - EMPTY_HOWTO (28), - EMPTY_HOWTO (29), - EMPTY_HOWTO (30), - EMPTY_HOWTO (31), + HOWTO(R_X86_64_GOT64, 0, 4, 64, FALSE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_GOT64", FALSE, MINUS_ONE, MINUS_ONE, + FALSE), + HOWTO(R_X86_64_GOTPCREL64, 0, 4, 64, TRUE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_GOTPCREL64", FALSE, MINUS_ONE, + MINUS_ONE, TRUE), + HOWTO(R_X86_64_GOTPC64, 0, 4, 64, TRUE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_GOTPC64", + FALSE, MINUS_ONE, MINUS_ONE, TRUE), + HOWTO(R_X86_64_GOTPLT64, 0, 4, 64, FALSE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_GOTPLT64", FALSE, MINUS_ONE, + MINUS_ONE, FALSE), + HOWTO(R_X86_64_PLTOFF64, 0, 4, 64, FALSE, 0, complain_overflow_signed, + bfd_elf_generic_reloc, "R_X86_64_PLTOFF64", FALSE, MINUS_ONE, + MINUS_ONE, FALSE), EMPTY_HOWTO (32), EMPTY_HOWTO (33), HOWTO(R_X86_64_GOTPC32_TLSDESC, 0, 2, 32, TRUE, 0, @@ -149,6 +161,12 @@ static reloc_howto_type x86_64_elf_howto_table[] = FALSE) }; +#define IS_X86_64_PCREL_TYPE(TYPE) \ + ( ((TYPE) == R_X86_64_PC8) \ + || ((TYPE) == R_X86_64_PC16) \ + || ((TYPE) == R_X86_64_PC32) \ + || ((TYPE) == R_X86_64_PC64)) + /* Map BFD relocs to the x86_64 elf relocs. */ struct elf_reloc_map { @@ -185,6 +203,11 @@ static const struct elf_reloc_map x86_64_reloc_map[] = { BFD_RELOC_64_PCREL, R_X86_64_PC64, }, { BFD_RELOC_X86_64_GOTOFF64, R_X86_64_GOTOFF64, }, { BFD_RELOC_X86_64_GOTPC32, R_X86_64_GOTPC32, }, + { BFD_RELOC_X86_64_GOT64, R_X86_64_GOT64, }, + { BFD_RELOC_X86_64_GOTPCREL64,R_X86_64_GOTPCREL64, }, + { BFD_RELOC_X86_64_GOTPC64, R_X86_64_GOTPC64, }, + { BFD_RELOC_X86_64_GOTPLT64, R_X86_64_GOTPLT64, }, + { BFD_RELOC_X86_64_PLTOFF64, R_X86_64_PLTOFF64, }, { BFD_RELOC_X86_64_GOTPC32_TLSDESC, R_X86_64_GOTPC32_TLSDESC, }, { BFD_RELOC_X86_64_TLSDESC_CALL, R_X86_64_TLSDESC_CALL, }, { BFD_RELOC_X86_64_TLSDESC, R_X86_64_TLSDESC, }, @@ -231,6 +254,23 @@ elf64_x86_64_reloc_type_lookup (bfd *abfd, return 0; } +static reloc_howto_type * +elf64_x86_64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, + const char *r_name) +{ + unsigned int i; + + for (i = 0; + i < (sizeof (x86_64_elf_howto_table) + / sizeof (x86_64_elf_howto_table[0])); + i++) + if (x86_64_elf_howto_table[i].name != NULL + && strcasecmp (x86_64_elf_howto_table[i].name, r_name) == 0) + return &x86_64_elf_howto_table[i]; + + return NULL; +} + /* Given an x86_64 ELF reloc type, fill in an arelent structure. */ static void @@ -335,7 +375,7 @@ static const bfd_byte elf64_x86_64_plt0_entry[PLT_ENTRY_SIZE] = { 0xff, 0x35, 8, 0, 0, 0, /* pushq GOT+8(%rip) */ 0xff, 0x25, 16, 0, 0, 0, /* jmpq *GOT+16(%rip) */ - 0x90, 0x90, 0x90, 0x90 /* pad out to 16 bytes with nops. */ + 0x0f, 0x1f, 0x40, 0x00 /* nopl 0(%rax) */ }; /* Subsequent entries in a procedure linkage table look like this. */ @@ -423,6 +463,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 @@ -457,6 +509,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. */ @@ -529,6 +584,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; } @@ -658,16 +714,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) -{ - 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 TRUE; -} - static bfd_boolean elf64_x86_64_elf_object_p (bfd *abfd) { @@ -676,26 +722,284 @@ 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, int is_local) +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 (is_local) - 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 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; } - return r_type; + *r_type = to_type; + return TRUE; +} + +/* Returns true if the hash entry refers to a symbol + marked for indirect handling during reloc processing. */ + +static bfd_boolean +is_indirect_symbol (bfd * abfd, struct elf_link_hash_entry * h) +{ + const struct elf_backend_data * bed; + + if (abfd == NULL || h == NULL) + return FALSE; + + bed = get_elf_backend_data (abfd); + + return h->type == STT_GNU_IFUNC + && (bed->elf_osabi == ELFOSABI_LINUX + /* GNU/Linux is still using the default value 0. */ + || bed->elf_osabi == ELFOSABI_NONE); } /* Look through the relocs for a section during the first phase, and @@ -703,7 +1007,8 @@ elf64_x86_64_tls_transition (struct bfd_link_info *info, int r_type, int is_loca 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; @@ -716,12 +1021,14 @@ 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; - + rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { @@ -749,7 +1056,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 == NULL); + 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: @@ -777,6 +1089,9 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, case R_X86_64_GOT32: case R_X86_64_GOTPCREL: case R_X86_64_TLSGD: + case R_X86_64_GOT64: + case R_X86_64_GOTPCREL64: + case R_X86_64_GOTPLT64: case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_TLSDESC_CALL: /* This symbol requires a global offset table entry. */ @@ -795,6 +1110,14 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, if (h != NULL) { + if (r_type == R_X86_64_GOTPLT64) + { + /* This relocation indicates that we also need + a PLT entry, as this is a function. We don't need + a PLT entry for local symbols. */ + h->needs_plt = 1; + h->plt.refcount += 1; + } h->got.refcount += 1; old_tls_type = elf64_x86_64_hash_entry (h)->tls_type; } @@ -840,7 +1163,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; } @@ -858,6 +1181,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, case R_X86_64_GOTOFF64: case R_X86_64_GOTPC32: + case R_X86_64_GOTPC64: create_got: if (htab->sgot == NULL) { @@ -885,6 +1209,16 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, h->plt.refcount += 1; break; + case R_X86_64_PLTOFF64: + /* This tries to form the 'address' of a function relative + to GOT. For global symbols we need a PLT entry. */ + if (h != NULL) + { + h->needs_plt = 1; + h->plt.refcount += 1; + } + goto create_got; + case R_X86_64_8: case R_X86_64_16: case R_X86_64_32: @@ -952,12 +1286,9 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, symbol. */ if ((info->shared && (sec->flags & SEC_ALLOC) != 0 - && (((r_type != R_X86_64_PC8) - && (r_type != R_X86_64_PC16) - && (r_type != R_X86_64_PC32) - && (r_type != R_X86_64_PC64)) + && (! IS_X86_64_PCREL_TYPE (r_type) || (h != NULL - && (! info->symbolic + && (! SYMBOLIC_BIND (info, h) || h->root.type == bfd_link_hash_defweak || !h->def_regular)))) || (ELIMINATE_COPY_RELOCS @@ -975,47 +1306,20 @@ 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 (strncmp (name, ".rela", 5) != 0 - || 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; + + /* Create the ifunc section, even if we will not encounter an + indirect function symbol. We may not even see one in the input + object file, but we can still encounter them in libraries. */ + (void) _bfd_elf_make_ifunc_reloc_section + (abfd, sec, htab->elf.dynobj, 2); } /* If this is a global symbol, we count the number of @@ -1047,6 +1351,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, if (p == NULL || p->sec != sec) { bfd_size_type amt = sizeof *p; + p = ((struct elf64_x86_64_dyn_relocs *) bfd_alloc (htab->elf.dynobj, amt)); if (p == NULL) @@ -1059,10 +1364,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, } p->count += 1; - if (r_type == R_X86_64_PC8 - || r_type == R_X86_64_PC16 - || r_type == R_X86_64_PC32 - || r_type == R_X86_64_PC64) + if (IS_X86_64_PCREL_TYPE (r_type)) p->pc_count += 1; } break; @@ -1077,7 +1379,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; @@ -1094,54 +1398,40 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, static asection * elf64_x86_64_gc_mark_hook (asection *sec, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { if (h != NULL) - { - switch (ELF64_R_TYPE (rel->r_info)) - { - case R_X86_64_GNU_VTINHERIT: - case R_X86_64_GNU_VTENTRY: - break; - - default: - switch (h->root.type) - { - case bfd_link_hash_defined: - case bfd_link_hash_defweak: - return h->root.u.def.section; - - case bfd_link_hash_common: - return h->root.u.c.p->section; - - default: - break; - } - } - } - else - return bfd_section_from_elf_index (sec->owner, sym->st_shndx); - - return NULL; + switch (ELF64_R_TYPE (rel->r_info)) + { + case R_X86_64_GNU_VTINHERIT: + case R_X86_64_GNU_VTENTRY: + return NULL; + } + + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } /* Update the got entry reference counts for the section being removed. */ 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); @@ -1175,7 +1465,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 != NULL); + 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: @@ -1189,8 +1484,13 @@ elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, case R_X86_64_GOTTPOFF: case R_X86_64_GOT32: case R_X86_64_GOTPCREL: + case R_X86_64_GOT64: + case R_X86_64_GOTPCREL64: + case R_X86_64_GOTPLT64: if (h != NULL) { + if (r_type == R_X86_64_GOTPLT64 && h->plt.refcount > 0) + h->plt.refcount -= 1; if (h->got.refcount > 0) h->got.refcount -= 1; } @@ -1215,6 +1515,7 @@ elf64_x86_64_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, /* Fall thru */ case R_X86_64_PLT32: + case R_X86_64_PLTOFF64: if (h != NULL) { if (h->plt.refcount > 0) @@ -1242,7 +1543,6 @@ elf64_x86_64_adjust_dynamic_symbol (struct bfd_link_info *info, { struct elf64_x86_64_link_hash_table *htab; asection *s; - unsigned int power_of_two; /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later, @@ -1360,32 +1660,9 @@ elf64_x86_64_adjust_dynamic_symbol (struct bfd_link_info *info, h->needs_copy = 1; } - /* We need to figure out the alignment required for this symbol. I - have no idea how ELF linkers handle this. 16-bytes is the size - of the largest type that requires hard alignment -- long double. */ - /* FIXME: This is VERY ugly. Should be fixed for all architectures using - this construct. */ - power_of_two = bfd_log2 (h->size); - if (power_of_two > 4) - power_of_two = 4; - - /* Apply the required alignment. */ s = htab->sdynbss; - s->size = BFD_ALIGN (s->size, (bfd_size_type) (1 << power_of_two)); - if (power_of_two > bfd_get_section_alignment (htab->elf.dynobj, s)) - { - if (! bfd_set_section_alignment (htab->elf.dynobj, s, power_of_two)) - return FALSE; - } - - /* Define the symbol as being at this point in the section. */ - h->root.u.def.section = s; - h->root.u.def.value = s->size; - - /* Increment the section size to make room for the symbol. */ - s->size += h->size; - return TRUE; + return _bfd_elf_adjust_dynamic_copy (h, s); } /* Allocate space in .plt, .got and associated reloc sections for @@ -1398,6 +1675,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) struct elf64_x86_64_link_hash_table *htab; struct elf64_x86_64_link_hash_entry *eh; struct elf64_x86_64_dyn_relocs *p; + bfd_boolean use_indirect_section = FALSE; if (h->root.type == bfd_link_hash_indirect) return TRUE; @@ -1476,7 +1754,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) && !info->shared && h->dynindx == -1 && elf64_x86_64_hash_entry (h)->tls_type == GOT_TLS_IE) - h->got.offset = (bfd_vma) -1; + { + h->got.offset = (bfd_vma) -1; + } else if (h->got.refcount > 0) { asection *s; @@ -1575,13 +1855,21 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) /* Make sure undefined weak symbols are output as a dynamic symbol in PIEs. */ else if (h->dynindx == -1 - && !h->forced_local) - { - if (! bfd_elf_link_record_dynamic_symbol (info, h)) - return FALSE; - } + && ! h->forced_local + && ! bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; } } + else if (is_indirect_symbol (info->output_bfd, h) + && h->dynindx == -1 + && ! h->forced_local) + { + if (bfd_elf_link_record_dynamic_symbol (info, h) + && h->dynindx != -1) + use_indirect_section = TRUE; + else + return FALSE; + } else if (ELIMINATE_COPY_RELOCS) { /* For the non-shared case, discard space for relocs against @@ -1598,11 +1886,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 - && !h->forced_local) - { - if (! bfd_elf_link_record_dynamic_symbol (info, h)) - return FALSE; - } + && ! h->forced_local + && ! bfd_elf_link_record_dynamic_symbol (info, h)) + return FALSE; /* If that succeeded, we know we'll be keeping all the relocs. */ @@ -1618,7 +1904,15 @@ 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; + + if (use_indirect_section) + sreloc = elf_section_data (p->sec)->indirect_relocs; + else + sreloc = elf_section_data (p->sec)->sreloc; + + BFD_ASSERT (sreloc != NULL); + sreloc->size += p->count * sizeof (Elf64_External_Rela); } @@ -1696,7 +1990,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) @@ -1722,7 +2016,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; - } } } @@ -1731,7 +2024,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); @@ -1791,7 +2084,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, @@ -1837,7 +2130,7 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* Strip this section if we don't need it; see the comment below. */ } - else if (strncmp (bfd_get_section_name (dynobj, s), ".rela", 5) == 0) + else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela")) { if (s->size != 0 && s != htab->srelplt) relocs = TRUE; @@ -1922,8 +2215,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) { @@ -1962,6 +2254,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; @@ -1972,6 +2267,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. */ @@ -2034,15 +2350,16 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, Elf_Internal_Rela *rel; Elf_Internal_Rela *relend; - if (info->relocatable) - return TRUE; + 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++) @@ -2092,15 +2409,42 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, h, sec, relocation, unresolved_reloc, warned); } + + if (sec != NULL && elf_discarded_section (sec)) + { + /* For relocs against symbols from removed linkonce sections, + or sections discarded by a linker script, we just want the + section contents zeroed. Avoid any special processing. */ + _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); + rel->r_info = 0; + rel->r_addend = 0; + continue; + } + + if (info->relocatable) + continue; + /* When generating a shared object, the relocations handled here are copied into the output file to be resolved at run time. */ switch (r_type) { + asection *base_got; case R_X86_64_GOT32: + case R_X86_64_GOT64: /* Relocation is to the entry for this symbol in the global offset table. */ case R_X86_64_GOTPCREL: - /* Use global offset table as symbol value. */ + case R_X86_64_GOTPCREL64: + /* Use global offset table entry as symbol value. */ + case R_X86_64_GOTPLT64: + /* This is the same as GOT64 for relocation purposes, but + indicates the existence of a PLT entry. The difficulty is, + that we must calculate the GOT slot offset from the PLT + offset, if this symbol got a PLT entry (it was global). + Additionally if it's computed from the PLT entry, then that + GOT offset is relative to .got.plt, not to .got. */ + base_got = htab->sgot; + if (htab->sgot == NULL) abort (); @@ -2109,6 +2453,19 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, bfd_boolean dyn; off = h->got.offset; + if (h->needs_plt + && h->plt.offset != (bfd_vma)-1 + && off == (bfd_vma)-1) + { + /* We can't use h->got.offset here to save + state, or even just remember the offset, as + finish_dynamic_symbol would use that as offset into + .got. */ + bfd_vma plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1; + off = (plt_index + 3) * GOT_ENTRY_SIZE; + base_got = htab->sgotplt; + } + dyn = htab->elf.dynamic_sections_created; if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) @@ -2133,7 +2490,9 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, else { bfd_put_64 (output_bfd, relocation, - htab->sgot->contents + off); + base_got->contents + off); + /* Note that this is harmless for the GOTPLT64 case, + as -1 | 1 still is -1. */ h->got.offset |= 1; } } @@ -2155,7 +2514,7 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, else { bfd_put_64 (output_bfd, relocation, - htab->sgot->contents + off); + base_got->contents + off); if (info->shared) { @@ -2169,8 +2528,8 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (s == NULL) abort (); - outrel.r_offset = (htab->sgot->output_section->vma - + htab->sgot->output_offset + outrel.r_offset = (base_got->output_section->vma + + base_got->output_offset + off); outrel.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE); outrel.r_addend = relocation; @@ -2186,9 +2545,9 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (off >= (bfd_vma) -2) abort (); - relocation = htab->sgot->output_section->vma - + htab->sgot->output_offset + off; - if (r_type != R_X86_64_GOTPCREL) + relocation = base_got->output_section->vma + + base_got->output_offset + off; + if (r_type != R_X86_64_GOTPCREL && r_type != R_X86_64_GOTPCREL64) relocation -= htab->sgotplt->output_section->vma - htab->sgotplt->output_offset; @@ -2224,12 +2583,31 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, break; case R_X86_64_GOTPC32: + case R_X86_64_GOTPC64: /* Use global offset table as symbol value. */ relocation = htab->sgotplt->output_section->vma + htab->sgotplt->output_offset; unresolved_reloc = FALSE; break; + case R_X86_64_PLTOFF64: + /* Relocation is PLT entry relative to GOT. For local + symbols it's the symbol itself relative to GOT. */ + if (h != NULL + /* See PLT32 handling. */ + && h->plt.offset != (bfd_vma) -1 + && htab->splt != NULL) + { + relocation = (htab->splt->output_section->vma + + htab->splt->output_offset + + h->plt.offset); + unresolved_reloc = FALSE; + } + + relocation -= htab->sgotplt->output_section->vma + + htab->sgotplt->output_offset; + break; + case R_X86_64_PLT32: /* Relocation is to the entry for this symbol in the procedure linkage table. */ @@ -2258,30 +2636,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. */ @@ -2293,22 +2704,21 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, /* FIXME: The ABI says the linker should make sure the value is the same when it's zeroextended to 64 bit. */ - /* r_symndx will be zero only for relocs against symbols - from removed linkonce sections, or sections discarded by - a linker script. */ - if (r_symndx == 0 - || (input_section->flags & SEC_ALLOC) == 0) + if ((input_section->flags & SEC_ALLOC) == 0) break; if ((info->shared && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak) - && ((r_type != R_X86_64_PC8 - && r_type != R_X86_64_PC16 - && r_type != R_X86_64_PC32 - && r_type != R_X86_64_PC64) - || !SYMBOL_CALLS_LOCAL (info, h))) + && (! IS_X86_64_PCREL_TYPE (r_type) + || ! SYMBOL_CALLS_LOCAL (info, h))) + || (! info->shared + && h != NULL + && h->dynindx != -1 + && ! h->forced_local + && ((struct elf64_x86_64_link_hash_entry *) h)->dyn_relocs != NULL + && is_indirect_symbol (output_bfd, h)) || (ELIMINATE_COPY_RELOCS && !info->shared && h != NULL @@ -2348,13 +2758,10 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, become local. */ else if (h != NULL && h->dynindx != -1 - && (r_type == R_X86_64_PC8 - || r_type == R_X86_64_PC16 - || r_type == R_X86_64_PC32 - || r_type == R_X86_64_PC64 - || !info->shared - || !info->symbolic - || !h->def_regular)) + && (IS_X86_64_PCREL_TYPE (r_type) + || ! info->shared + || ! SYMBOLIC_BIND (info, h) + || ! h->def_regular)) { outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); outrel.r_addend = rel->r_addend; @@ -2383,9 +2790,19 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, { asection *osec; + /* We are turning this relocation into one + against a section symbol. It would be + proper to subtract the symbol's value, + osec->vma, from the emitted reloc addend, + but ld.so expects buggy relocs. */ osec = sec->output_section; sindx = elf_section_data (osec)->dynindx; - BFD_ASSERT (sindx > 0); + if (sindx == 0) + { + asection *oi = htab->elf.text_index_section; + sindx = elf_section_data (oi)->dynindx; + } + BFD_ASSERT (sindx != 0); } outrel.r_info = ELF64_R_INFO (sindx, r_type); @@ -2393,9 +2810,18 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } } - sreloc = elf_section_data (input_section)->sreloc; - if (sreloc == NULL) - abort (); + if (! info->shared + && h != NULL + && h->dynindx != -1 + && ! h->forced_local + && is_indirect_symbol (output_bfd, h) + && elf_section_data (input_section)->indirect_relocs != NULL + && elf_section_data (input_section)->indirect_relocs->contents != NULL) + sreloc = elf_section_data (input_section)->indirect_relocs; + else + sreloc = elf_section_data (input_section)->sreloc; + + BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL); loc = sreloc->contents; loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela); @@ -2415,57 +2841,39 @@ 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 == NULL); 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 }; - /* GD->LE transition. .byte 0x66; leaq foo@tlsgd(%rip), %rdi - .word 0x6666; rex64; call __tls_get_addr@plt + .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); - BFD_ASSERT (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); - /* Skip R_X86_64_PLT32. */ + contents + roff + 8); + /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ rel++; continue; } @@ -2477,26 +2885,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); @@ -2512,28 +2907,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. */ - bfd_put_8 (output_bfd, 0x90, contents + roff); + 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 @@ -2542,25 +2922,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) { @@ -2568,27 +2946,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) @@ -2715,142 +3095,107 @@ 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. */ - bfd_put_8 (output_bfd, 0x90, 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) + 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@plt. + 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); - BFD_ASSERT (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_PLT32. */ + /* Skip R_X86_64_PC32/R_X86_64_PLT32. */ rel++; continue; } @@ -2941,12 +3286,6 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (r == bfd_reloc_overflow) { - if (h != NULL - && h->root.type == bfd_link_hash_undefweak - && howto->pc_relative) - /* Ignore reloc overflow on branches to undefweak syms. */ - continue; - if (! ((*info->callbacks->reloc_overflow) (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0, input_bfd, input_section, @@ -3087,6 +3426,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 @@ -3365,11 +3706,12 @@ elf64_x86_64_section_from_shdr (bfd *abfd, static bfd_boolean elf64_x86_64_add_symbol_hook (bfd *abfd, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, Elf_Internal_Sym *sym, const char **namep ATTRIBUTE_UNUSED, flagword *flagsp ATTRIBUTE_UNUSED, - asection **secp, bfd_vma *valp) + asection **secp, + bfd_vma *valp) { asection *lcomm; @@ -3392,6 +3734,10 @@ elf64_x86_64_add_symbol_hook (bfd *abfd, *valp = sym->st_size; break; } + + if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + return TRUE; } @@ -3498,17 +3844,18 @@ 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; } static int -elf64_x86_64_additional_program_headers (bfd *abfd) +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"); @@ -3525,23 +3872,38 @@ elf64_x86_64_additional_program_headers (bfd *abfd) return count; } -static const struct bfd_elf_special_section +/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ + +static bfd_boolean +elf64_x86_64_hash_symbol (struct elf_link_hash_entry *h) +{ + if (h->plt.offset != (bfd_vma) -1 + && !h->def_regular + && !h->pointer_equality_needed) + return FALSE; + + return _bfd_elf_hash_symbol (h); +} + +static const struct bfd_elf_special_section elf64_x86_64_special_sections[]= { - { ".gnu.linkonce.lb", 16, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, - { ".gnu.linkonce.lr", 16, -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE}, - { ".gnu.linkonce.lt", 16, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR + SHF_X86_64_LARGE}, - { ".lbss", 5, -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, - { ".ldata", 6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, - { ".lrodata", 8, -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE}, - { NULL, 0, 0, 0, 0 } + { STRING_COMMA_LEN (".gnu.linkonce.lb"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, + { STRING_COMMA_LEN (".gnu.linkonce.lr"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE}, + { STRING_COMMA_LEN (".gnu.linkonce.lt"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR + SHF_X86_64_LARGE}, + { STRING_COMMA_LEN (".lbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, + { STRING_COMMA_LEN (".ldata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_X86_64_LARGE}, + { STRING_COMMA_LEN (".lrodata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_X86_64_LARGE}, + { NULL, 0, 0, 0, 0 } }; #define TARGET_LITTLE_SYM bfd_elf64_x86_64_vec #define TARGET_LITTLE_NAME "elf64-x86-64" #define ELF_ARCH bfd_arch_i386 #define ELF_MACHINE_CODE EM_X86_64 -#define ELF_MAXPAGESIZE 0x100000 +#define ELF_MAXPAGESIZE 0x200000 +#define ELF_MINPAGESIZE 0x1000 +#define ELF_COMMONPAGESIZE 0x1000 #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 @@ -3556,8 +3918,11 @@ static const struct bfd_elf_special_section #define bfd_elf64_bfd_link_hash_table_create \ elf64_x86_64_link_hash_table_create #define bfd_elf64_bfd_reloc_type_lookup elf64_x86_64_reloc_type_lookup +#define bfd_elf64_bfd_reloc_name_lookup \ + 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 @@ -3571,6 +3936,7 @@ static const struct bfd_elf_special_section #define elf_backend_relocate_section elf64_x86_64_relocate_section #define elf_backend_size_dynamic_sections elf64_x86_64_size_dynamic_sections #define elf_backend_always_size_sections elf64_x86_64_always_size_sections +#define elf_backend_init_index_section _bfd_elf_init_1_index_section #define elf_backend_plt_sym_val elf64_x86_64_plt_sym_val #define elf_backend_object_p elf64_x86_64_elf_object_p #define bfd_elf64_mkobject elf64_x86_64_mkobject @@ -3596,5 +3962,25 @@ static const struct bfd_elf_special_section elf64_x86_64_special_sections #define elf_backend_additional_program_headers \ elf64_x86_64_additional_program_headers +#define elf_backend_hash_symbol \ + elf64_x86_64_hash_symbol + +#undef elf_backend_post_process_headers +#define elf_backend_post_process_headers _bfd_elf_set_osabi + +#include "elf64-target.h" + +/* FreeBSD support. */ + +#undef TARGET_LITTLE_SYM +#define TARGET_LITTLE_SYM bfd_elf64_x86_64_freebsd_vec +#undef TARGET_LITTLE_NAME +#define TARGET_LITTLE_NAME "elf64-x86-64-freebsd" + +#undef ELF_OSABI +#define ELF_OSABI ELFOSABI_FREEBSD + +#undef elf64_bed +#define elf64_bed elf64_x86_64_fbsd_bed #include "elf64-target.h"