X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf64-x86-64.c;h=4bbbb0871ead7bdd9677af621757aceebf3e96ad;hb=30d304de8b0593a62f7aab770f421f91fbea392a;hp=aedd2b8134f18ba57b41e81348df104c799257d1;hpb=5608e05bb4ef95fdd0051137ed7db8d1a29ee5ea;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index aedd2b8134..4bbbb0871e 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -37,29 +37,41 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ static reloc_howto_type x86_64_elf_howto_table[] = { HOWTO(R_X86_64_NONE, 0, 0, 0, false, 0, complain_overflow_dont, - bfd_elf_generic_reloc, "R_X86_64_NONE", false, 0x00000000, 0x00000000, false), + bfd_elf_generic_reloc, "R_X86_64_NONE", false, 0x00000000, 0x00000000, + false), HOWTO(R_X86_64_64, 0, 4, 64, false, 0, complain_overflow_bitfield, - bfd_elf_generic_reloc, "R_X86_64_64", false, MINUS_ONE, MINUS_ONE, false), + bfd_elf_generic_reloc, "R_X86_64_64", false, MINUS_ONE, MINUS_ONE, + false), HOWTO(R_X86_64_PC32, 0, 4, 32, true, 0, complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_PC32", false, 0xffffffff, 0xffffffff, true), + bfd_elf_generic_reloc, "R_X86_64_PC32", false, 0xffffffff, 0xffffffff, + true), HOWTO(R_X86_64_GOT32, 0, 4, 32, false, 0, complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_GOT32", false, 0xffffffff, 0xffffffff, false), + bfd_elf_generic_reloc, "R_X86_64_GOT32", false, 0xffffffff, 0xffffffff, + false), HOWTO(R_X86_64_PLT32, 0, 4, 32, true, 0, complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_PLT32", false, 0xffffffff, 0xffffffff, true), + bfd_elf_generic_reloc, "R_X86_64_PLT32", false, 0xffffffff, 0xffffffff, + true), HOWTO(R_X86_64_COPY, 0, 4, 32, false, 0, complain_overflow_bitfield, - bfd_elf_generic_reloc, "R_X86_64_COPY", false, 0xffffffff, 0xffffffff, false), + bfd_elf_generic_reloc, "R_X86_64_COPY", false, 0xffffffff, 0xffffffff, + false), HOWTO(R_X86_64_GLOB_DAT, 0, 4, 64, false, 0, complain_overflow_bitfield, - bfd_elf_generic_reloc, "R_X86_64_GLOB_DAT", false, MINUS_ONE, MINUS_ONE, false), + bfd_elf_generic_reloc, "R_X86_64_GLOB_DAT", false, MINUS_ONE, + MINUS_ONE, false), HOWTO(R_X86_64_JUMP_SLOT, 0, 4, 64, false, 0, complain_overflow_bitfield, - bfd_elf_generic_reloc, "R_X86_64_JUMP_SLOT", false, MINUS_ONE, MINUS_ONE, false), + bfd_elf_generic_reloc, "R_X86_64_JUMP_SLOT", false, MINUS_ONE, + MINUS_ONE, false), HOWTO(R_X86_64_RELATIVE, 0, 4, 64, false, 0, complain_overflow_bitfield, - bfd_elf_generic_reloc, "R_X86_64_RELATIVE", false, MINUS_ONE, MINUS_ONE, false), + bfd_elf_generic_reloc, "R_X86_64_RELATIVE", false, MINUS_ONE, + MINUS_ONE, false), HOWTO(R_X86_64_GOTPCREL, 0, 4, 32, true,0 , complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_GOTPCREL", false, 0xffffffff, 0xffffffff, true), + bfd_elf_generic_reloc, "R_X86_64_GOTPCREL", false, 0xffffffff, + 0xffffffff, true), HOWTO(R_X86_64_32, 0, 4, 32, false, 0, complain_overflow_unsigned, - bfd_elf_generic_reloc, "R_X86_64_32", false, 0xffffffff, 0xffffffff, false), + bfd_elf_generic_reloc, "R_X86_64_32", false, 0xffffffff, 0xffffffff, + false), HOWTO(R_X86_64_32S, 0, 4, 32, false, 0, complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_32S", false, 0xffffffff, 0xffffffff, false), + bfd_elf_generic_reloc, "R_X86_64_32S", false, 0xffffffff, 0xffffffff, + false), HOWTO(R_X86_64_16, 0, 1, 16, false, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_X86_64_16", false, 0xffff, 0xffff, false), HOWTO(R_X86_64_PC16,0, 1, 16, true, 0, complain_overflow_bitfield, @@ -67,7 +79,16 @@ static reloc_howto_type x86_64_elf_howto_table[] = HOWTO(R_X86_64_8, 0, 0, 8, false, 0, complain_overflow_signed, bfd_elf_generic_reloc, "R_X86_64_8", false, 0xff, 0xff, false), HOWTO(R_X86_64_PC8, 0, 0, 8, true, 0, complain_overflow_signed, - bfd_elf_generic_reloc, "R_X86_64_PC8", false, 0xff, 0xff, true) + bfd_elf_generic_reloc, "R_X86_64_PC8", false, 0xff, 0xff, true), + +/* GNU extension to record C++ vtable hierarchy. */ + HOWTO (R_X86_64_GNU_VTINHERIT, 0, 4, 0, false, 0, complain_overflow_dont, + NULL, "R_X86_64_GNU_VTINHERIT", false, 0, 0, false), + +/* GNU extension to record C++ vtable member usage. */ + HOWTO (R_X86_64_GNU_VTENTRY, 0, 4, 0, false, 0, complain_overflow_dont, + _bfd_elf_rel_vtable_reloc_fn, "R_X86_64_GNU_VTENTRY", false, 0, 0, + false) }; /* Map BFD relocs to the x86_64 elf relocs. */ @@ -77,7 +98,7 @@ struct elf_reloc_map unsigned char elf_reloc_val; }; -static CONST struct elf_reloc_map x86_64_reloc_map[] = +static const struct elf_reloc_map x86_64_reloc_map[] = { { BFD_RELOC_NONE, R_X86_64_NONE, }, { BFD_RELOC_64, R_X86_64_64, }, @@ -95,6 +116,8 @@ static CONST struct elf_reloc_map x86_64_reloc_map[] = { BFD_RELOC_16_PCREL, R_X86_64_PC16, }, { BFD_RELOC_8, R_X86_64_8, }, { BFD_RELOC_8_PCREL, R_X86_64_PC8, }, + { BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, }, + { BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, }, }; static reloc_howto_type *elf64_x86_64_reloc_type_lookup @@ -103,6 +126,17 @@ static void elf64_x86_64_info_to_howto PARAMS ((bfd *, arelent *, Elf64_Internal_Rela *)); static struct bfd_link_hash_table *elf64_x86_64_link_hash_table_create PARAMS ((bfd *)); +static boolean elf64_x86_64_elf_object_p PARAMS ((bfd *abfd)); +static boolean elf64_x86_64_check_relocs + PARAMS ((bfd *, struct bfd_link_info *, asection *sec, + const Elf_Internal_Rela *)); +static asection *elf64_x86_64_gc_mark_hook + PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *, + struct elf_link_hash_entry *, Elf_Internal_Sym *)); + +static boolean elf64_x86_64_gc_sweep_hook + PARAMS ((bfd *, struct bfd_link_info *, asection *, + const Elf_Internal_Rela *)); static struct bfd_hash_entry *elf64_x86_64_link_hash_newfunc PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *)); @@ -119,6 +153,8 @@ static boolean elf64_x86_64_finish_dynamic_symbol Elf_Internal_Sym *sym)); static boolean elf64_x86_64_finish_dynamic_sections PARAMS ((bfd *, struct bfd_link_info *)); +static enum elf_reloc_type_class elf64_x86_64_reloc_type_class + PARAMS ((const Elf_Internal_Rela *)); /* Given a BFD reloc type, return a HOWTO structure. */ static reloc_howto_type * @@ -131,8 +167,7 @@ elf64_x86_64_reloc_type_lookup (abfd, code) i++) { if (x86_64_reloc_map[i].bfd_reloc_val == code) - return &x86_64_elf_howto_table[(int) - x86_64_reloc_map[i].elf_reloc_val]; + return &x86_64_elf_howto_table[i]; } return 0; } @@ -145,11 +180,20 @@ elf64_x86_64_info_to_howto (abfd, cache_ptr, dst) arelent *cache_ptr; Elf64_Internal_Rela *dst; { - unsigned r_type; + unsigned r_type, i; r_type = ELF64_R_TYPE (dst->r_info); - BFD_ASSERT (r_type < (unsigned int) R_X86_64_max); - cache_ptr->howto = &x86_64_elf_howto_table[r_type]; + if (r_type < (unsigned int) R_X86_64_GNU_VTINHERIT) + { + BFD_ASSERT (r_type <= (unsigned int) R_X86_64_PC8); + i = r_type; + } + else + { + BFD_ASSERT (r_type < (unsigned int) R_X86_64_max); + i = r_type - ((unsigned int) R_X86_64_GNU_VTINHERIT - R_X86_64_PC8 - 1); + } + cache_ptr->howto = &x86_64_elf_howto_table[i]; BFD_ASSERT (r_type == cache_ptr->howto->type); } @@ -173,18 +217,18 @@ elf64_x86_64_info_to_howto (abfd, cache_ptr, dst) static const bfd_byte elf64_x86_64_plt0_entry[PLT_ENTRY_SIZE] = { - 0xff, 0xb3, 8, 0, 0, 0, /* pushq GOT+8(%rip) */ - 0xff, 0xa3, 16, 0, 0, 0, /* jmp GOT+16(%rip) */ - 0, 0, 0, 0 /* pad out to 16 bytes. */ + 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. */ }; /* Subsequent entries in a procedure linkage table look like this. */ static const bfd_byte elf64_x86_64_plt_entry[PLT_ENTRY_SIZE] = { - 0xff, 0xa3, /* jmp *name@GOTPC(%rip) */ + 0xff, 0x25, /* jmpq *name@GOTPC(%rip) */ 0, 0, 0, 0, /* replaced with offset to this symbol in .got. */ - 0x68, /* pushq immediate */ + 0x68, /* pushq immediate */ 0, 0, 0, 0, /* replaced with index into relocation table. */ 0xe9, /* jmp relative */ 0, 0, 0, 0 /* replaced with offset to start of .plt0. */ @@ -283,9 +327,9 @@ elf64_x86_64_link_hash_table_create (abfd) bfd *abfd; { struct elf64_x86_64_link_hash_table *ret; + bfd_size_type amt = sizeof (struct elf64_x86_64_link_hash_table); - ret = ((struct elf64_x86_64_link_hash_table *) - bfd_alloc (abfd, sizeof (struct elf64_x86_64_link_hash_table))); + ret = ((struct elf64_x86_64_link_hash_table *) bfd_alloc (abfd, amt)); if (ret == (struct elf64_x86_64_link_hash_table *) NULL) return NULL; @@ -299,7 +343,7 @@ elf64_x86_64_link_hash_table_create (abfd) return &ret->root.root; } -boolean +static boolean elf64_x86_64_elf_object_p (abfd) bfd *abfd; { @@ -397,10 +441,8 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) if (h != NULL) { - if (h->got.refcount == -1) + if (h->got.refcount == 0) { - h->got.refcount = 1; - /* Make sure this symbol is output as a dynamic symbol. */ if (h->dynindx == -1) { @@ -411,28 +453,25 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) sgot->_raw_size += GOT_ENTRY_SIZE; srelgot->_raw_size += sizeof (Elf64_External_Rela); } - else - h->got.refcount += 1; + h->got.refcount += 1; } else { /* This is a global offset table entry for a local symbol. */ if (local_got_refcounts == NULL) { - size_t size; + bfd_size_type size; - size = symtab_hdr->sh_info * sizeof (bfd_signed_vma); + size = symtab_hdr->sh_info; + size *= sizeof (bfd_signed_vma); local_got_refcounts = ((bfd_signed_vma *) - bfd_alloc (abfd, size)); + bfd_zalloc (abfd, size)); if (local_got_refcounts == NULL) return false; elf_local_got_refcounts (abfd) = local_got_refcounts; - memset (local_got_refcounts, -1, size); } - if (local_got_refcounts[r_symndx] == -1) + if (local_got_refcounts[r_symndx] == 0) { - local_got_refcounts[r_symndx] = 1; - sgot->_raw_size += GOT_ENTRY_SIZE; if (info->shared) { @@ -442,8 +481,7 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) srelgot->_raw_size += sizeof (Elf64_External_Rela); } } - else - local_got_refcounts[r_symndx] += 1; + local_got_refcounts[r_symndx] += 1; } break; @@ -460,16 +498,14 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) if (h == NULL) continue; - if (h->plt.refcount == -1) - { - h->plt.refcount = 1; - h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; - } - else - h->plt.refcount += 1; + h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT; + h->plt.refcount += 1; break; + case R_X86_64_8: + case R_X86_64_16: case R_X86_64_32: + case R_X86_64_64: case R_X86_64_32S: case R_X86_64_PC32: if (h != NULL) @@ -491,7 +527,9 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) and symbol visibility changes render the symbol local. */ if (info->shared && (sec->flags & SEC_ALLOC) != 0 - && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC32 + && (((ELF64_R_TYPE (rel->r_info) != R_X86_64_PC8) + && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC16) + && (ELF64_R_TYPE (rel->r_info) != R_X86_64_PC32)) || (h != NULL && (! info->symbolic || (h->elf_link_hash_flags @@ -527,9 +565,11 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) flags |= SEC_ALLOC | SEC_LOAD; if (sreloc == NULL || ! bfd_set_section_flags (dynobj, sreloc, flags) - || ! bfd_set_section_alignment (dynobj, sreloc, 2)) + || ! bfd_set_section_alignment (dynobj, sreloc, 3)) return false; } + if (sec->flags & SEC_READONLY) + info->flags |= DF_TEXTREL; } sreloc->_raw_size += sizeof (Elf64_External_Rela); @@ -541,7 +581,9 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) elf64_x86_64 linker hash table, which means that h is really a pointer to an elf64_x86_64_link_hash_entry. */ if (h != NULL - && ELF64_R_TYPE (rel->r_info) == R_X86_64_PC32) + && ((ELF64_R_TYPE (rel->r_info) == R_X86_64_PC8) + || (ELF64_R_TYPE (rel->r_info) == R_X86_64_PC16) + || (ELF64_R_TYPE (rel->r_info) == R_X86_64_PC32))) { struct elf64_x86_64_link_hash_entry *eh; struct elf64_x86_64_pcrel_relocs_copied *p; @@ -555,7 +597,7 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) if (p == NULL) { p = ((struct elf64_x86_64_pcrel_relocs_copied *) - bfd_alloc (dynobj, sizeof *p)); + bfd_alloc (dynobj, (bfd_size_type) sizeof *p)); if (p == NULL) return false; p->next = eh->pcrel_relocs_copied; @@ -568,6 +610,20 @@ elf64_x86_64_check_relocs (abfd, info, sec, relocs) } } break; + + /* This relocation describes the C++ object vtable hierarchy. + Reconstruct it for later use during GC. */ + case R_X86_64_GNU_VTINHERIT: + if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) + return false; + break; + + /* This relocation describes which C++ vtable entries are actually + used. Record for later use during GC. */ + case R_X86_64_GNU_VTENTRY: + if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_addend)) + return false; + break; } } @@ -587,28 +643,30 @@ elf64_x86_64_gc_mark_hook (abfd, info, rel, h, sym) { if (h != NULL) { - switch (h->root.type) + switch (ELF64_R_TYPE (rel->r_info)) { - 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; + case R_X86_64_GNU_VTINHERIT: + case R_X86_64_GNU_VTENTRY: + break; default: - break; + 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 { - if (!(elf_bad_symtab (abfd) - && ELF_ST_BIND (sym->st_info) != STB_LOCAL) - && ! ((sym->st_shndx <= 0 || sym->st_shndx >= SHN_LORESERVE) - && sym->st_shndx != SHN_COMMON)) - { - return bfd_section_from_elf_index (abfd, sym->st_shndx); - } + return bfd_section_from_elf_index (abfd, sym->st_shndx); } return NULL; @@ -790,6 +848,8 @@ elf64_x86_64_adjust_dynamic_symbol (info, h) return true; } + else + h->plt.offset = (bfd_vma) -1; /* If this is a weak symbol, and there is a real definition, the processor independent code will have arranged for us to see the @@ -876,14 +936,13 @@ elf64_x86_64_adjust_dynamic_symbol (info, h) static boolean elf64_x86_64_size_dynamic_sections (output_bfd, info) - bfd *output_bfd; + bfd *output_bfd ATTRIBUTE_UNUSED; struct bfd_link_info *info; { bfd *dynobj; asection *s; boolean plt; boolean relocs; - boolean reltext; dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); @@ -923,7 +982,7 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info) /* The check_relocs and adjust_dynamic_symbol entry points have determined the sizes of the various dynamic sections. Allocate memory for them. */ - plt = relocs = reltext = false; + plt = relocs = false; for (s = dynobj->sections; s != NULL; s = s->next) { const char *name; @@ -968,29 +1027,8 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info) } else { - asection *target; - - /* Remember whether there are any reloc sections other - than .rela.plt. */ if (strcmp (name, ".rela.plt") != 0) - { - const char *outname; - - relocs = true; - - /* If this relocation section applies to a read only - section, then we probably need a DT_TEXTREL - entry. The entries in the .rela.plt section - really apply to the .got section, which we - created ourselves and so know is not readonly. */ - outname = bfd_get_section_name (output_bfd, - s->output_section); - target = bfd_get_section_by_name (output_bfd, outname + 5); - if (target != NULL - && (target->flags & SEC_READONLY) != 0 - && (target->flags & SEC_ALLOC) != 0) - reltext = true; - } + relocs = true; /* We use the reloc_count field as a counter if we need to copy relocs into the output file. */ @@ -1026,37 +1064,39 @@ elf64_x86_64_size_dynamic_sections (output_bfd, info) must add the entries now so that we get the correct size for the .dynamic section. The DT_DEBUG entry is filled in by the dynamic linker and used by the debugger. */ +#define add_dynamic_entry(TAG, VAL) \ + bfd_elf64_add_dynamic_entry (info, (bfd_vma) (TAG), (bfd_vma) (VAL)) + if (! info->shared) { - if (! bfd_elf64_add_dynamic_entry (info, DT_DEBUG, 0)) + if (!add_dynamic_entry (DT_DEBUG, 0)) return false; } if (plt) { - if (! bfd_elf64_add_dynamic_entry (info, DT_PLTGOT, 0) - || ! bfd_elf64_add_dynamic_entry (info, DT_PLTRELSZ, 0) - || ! bfd_elf64_add_dynamic_entry (info, DT_PLTREL, DT_RELA) - || ! bfd_elf64_add_dynamic_entry (info, DT_JMPREL, 0)) + if (!add_dynamic_entry (DT_PLTGOT, 0) + || !add_dynamic_entry (DT_PLTRELSZ, 0) + || !add_dynamic_entry (DT_PLTREL, DT_RELA) + || !add_dynamic_entry (DT_JMPREL, 0)) return false; } if (relocs) { - if (! bfd_elf64_add_dynamic_entry (info, DT_RELA, 0) - || ! bfd_elf64_add_dynamic_entry (info, DT_RELASZ, 0) - || ! bfd_elf64_add_dynamic_entry (info, DT_RELAENT, - sizeof (Elf64_External_Rela))) + if (!add_dynamic_entry (DT_RELA, 0) + || !add_dynamic_entry (DT_RELASZ, 0) + || !add_dynamic_entry (DT_RELAENT, sizeof (Elf64_External_Rela))) return false; } - if (reltext) + if ((info->flags & DF_TEXTREL) != 0) { - if (! bfd_elf64_add_dynamic_entry (info, DT_TEXTREL, 0)) + if (!add_dynamic_entry (DT_TEXTREL, 0)) return false; - info->flags |= DF_TEXTREL; } } +#undef add_dynamic_entry return true; } @@ -1096,7 +1136,7 @@ elf64_x86_64_discard_copies (h, inf) static boolean elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, - contents, relocs, local_syms, local_sections) + contents, relocs, local_syms, local_sections) bfd *output_bfd; struct bfd_link_info *info; bfd *input_bfd; @@ -1143,6 +1183,9 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, unsigned int indx; r_type = ELF64_R_TYPE (rela->r_info); + if (r_type == (int) R_X86_64_GNU_VTINHERIT + || r_type == (int) R_X86_64_GNU_VTENTRY) + continue; if ((indx = (unsigned) r_type) >= R_X86_64_max) { @@ -1180,9 +1223,7 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, { sym = local_syms + r_symndx; sec = local_sections[r_symndx]; - relocation = (sec->output_section->vma - + sec->output_offset - + sym->st_value); + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rela); } else { @@ -1194,11 +1235,45 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, || h->root.type == bfd_link_hash_defweak) { sec = h->root.u.def.section; - if (sec->output_section == NULL) + if ((r_type == R_X86_64_PLT32 + && splt != NULL + && h->plt.offset != (bfd_vma) -1) + || ((r_type == R_X86_64_GOT32 || r_type == R_X86_64_GOTPCREL) + && elf_hash_table (info)->dynamic_sections_created + && (!info->shared + || (! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) + || (info->shared + && ((! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0) + && (r_type == R_X86_64_8 + || r_type == R_X86_64_16 + || r_type == R_X86_64_32 + || r_type == R_X86_64_64 + || r_type == R_X86_64_PC8 + || r_type == R_X86_64_PC16 + || r_type == R_X86_64_PC32) + && ((input_section->flags & SEC_ALLOC) != 0 + /* DWARF will emit R_X86_64_32 relocations in its + sections against symbols defined externally + in shared libraries. We can't do anything + with them here. */ + || ((input_section->flags & SEC_DEBUGGING) != 0 + && (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_DYNAMIC) != 0)))) + { + /* In these cases, we don't need the relocation + value. We check specially because in some + obscure cases sec->output_section will be NULL. */ + relocation = 0; + } + else if (sec->output_section == NULL) { (*_bfd_error_handler) (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"), - bfd_get_filename (input_bfd), h->root.root.string, + bfd_archive_filename (input_bfd), h->root.root.string, bfd_get_section_name (input_bfd, input_section)); relocation = 0; } @@ -1209,7 +1284,9 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, } else if (h->root.type == bfd_link_hash_undefweak) relocation = 0; - else if (info->shared && !info->symbolic && !info->no_undefined + else if (info->shared + && (!info->symbolic || info->allow_shlib_undefined) + && !info->no_undefined && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) relocation = 0; else @@ -1231,92 +1308,10 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, case R_X86_64_GOT32: /* Relocation is to the entry for this symbol in the global offset table. */ - BFD_ASSERT (sgot != NULL); - - if (h != NULL) - { - bfd_vma off = h->got.offset; - BFD_ASSERT (off != (bfd_vma) -1); - - if (! elf_hash_table (info)->dynamic_sections_created - || (info->shared - && (info->symbolic || h->dynindx == -1) - && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))) - { - /* This is actually a static link, or it is a -Bsymbolic - link and the symbol is defined locally, or the symbol - was forced to be local because of a version file. We - must initialize this entry in the global offset table. - Since the offset must always be a multiple of 8, we - use the least significant bit to record whether we - have initialized it already. - - When doing a dynamic link, we create a .rela.got - relocation entry to initialize the value. This is - done in the finish_dynamic_symbol routine. */ - if ((off & 1) != 0) - off &= ~1; - else - { - bfd_put_64 (output_bfd, relocation, - sgot->contents + off); - h->got.offset |= 1; - } - } - relocation = sgot->output_offset + off; - } - else - { - bfd_vma off; - - BFD_ASSERT (local_got_offsets != NULL - && local_got_offsets[r_symndx] != (bfd_vma) -1); - - off = local_got_offsets[r_symndx]; - - /* The offset must always be a multiple of 8. We use - the least significant bit to record whether we have - already generated the necessary reloc. */ - if ((off & 1) != 0) - off &= ~1; - else - { - bfd_put_64 (output_bfd, relocation, sgot->contents + off); - - if (info->shared) - { - asection *srelgot; - Elf_Internal_Rela outrel; - - /* We need to generate a R_X86_64_RELATIVE reloc - for the dynamic linker. */ - srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); - BFD_ASSERT (srelgot != NULL); - - outrel.r_offset = (sgot->output_section->vma - + sgot->output_offset - + off); - outrel.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE); - outrel.r_addend = relocation; - bfd_elf64_swap_reloca_out (output_bfd, &outrel, - (((Elf64_External_Rela *) - srelgot->contents) - + srelgot->reloc_count)); - ++srelgot->reloc_count; - } - - local_got_offsets[r_symndx] |= 1; - } - - relocation = sgot->output_offset + off; - } - - break; - case R_X86_64_GOTPCREL: /* Use global offset table as symbol value. */ - BFD_ASSERT (sgot != NULL); + if (h != NULL) { bfd_vma off = h->got.offset; @@ -1347,7 +1342,10 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, h->got.offset |= 1; } } - relocation = sgot->output_offset + off; + if (r_type == R_X86_64_GOTPCREL) + relocation = sgot->output_section->vma + sgot->output_offset + off; + else + relocation = sgot->output_offset + off; } else { @@ -1392,8 +1390,12 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, local_got_offsets[r_symndx] |= 1; } - relocation = sgot->output_section->vma + off; + if (r_type == R_X86_64_GOTPCREL) + relocation = sgot->output_section->vma + sgot->output_offset + off; + else + relocation = sgot->output_offset + off; } + break; case R_X86_64_PLT32: @@ -1418,23 +1420,23 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, + h->plt.offset); break; - case R_X86_64_8: - case R_X86_64_16: - case R_X86_64_32: case R_X86_64_PC8: case R_X86_64_PC16: case R_X86_64_PC32: - /* FIXME: The abi says the linker should make sure the value is + if (h == NULL || h->dynindx == -1 + || (info->symbolic + && h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)) + break; + /* Fall through. */ + case R_X86_64_8: + case R_X86_64_16: + case R_X86_64_32: + case R_X86_64_64: + /* FIXME: The ABI says the linker should make sure the value is the same when it's zeroextended to 64 bit. */ if (info->shared - && (input_section->flags & SEC_ALLOC) != 0 - && ((r_type != R_X86_64_PC8 && r_type != R_X86_64_PC16 - && r_type != R_X86_64_PC32) - || (h != NULL - && h->dynindx != -1 - && (! info->symbolic - || (h->elf_link_hash_flags - & ELF_LINK_HASH_DEF_REGULAR) == 0)))) + && r_symndx != 0 + && (input_section->flags & SEC_ALLOC) != 0) { Elf_Internal_Rela outrel; boolean skip, relocate; @@ -1465,21 +1467,11 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, skip = false; - if (elf_section_data (input_section)->stab_info == NULL) - outrel.r_offset = rela->r_offset; - else - { - bfd_vma off; - - off = (_bfd_stab_section_offset - (output_bfd, &elf_hash_table (info)->stab_info, - input_section, - &elf_section_data (input_section)->stab_info, - rela->r_offset)); - if (off == (bfd_vma) -1) - skip = true; - outrel.r_offset = off; - } + outrel.r_offset = + _bfd_elf_section_offset (output_bfd, info, input_section, + rela->r_offset); + if (outrel.r_offset == (bfd_vma) -1) + skip = true; outrel.r_offset += (input_section->output_section->vma + input_section->output_offset); @@ -1489,22 +1481,21 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, memset (&outrel, 0, sizeof outrel); relocate = false; } - else if ((r_type == R_X86_64_PC8) || (r_type == R_X86_64_PC16) - || (r_type == R_X86_64_PC32)) + /* h->dynindx may be -1 if this symbol was marked to + become local. */ + else if (h != NULL + && ((! info->symbolic && h->dynindx != -1) + || (h->elf_link_hash_flags + & ELF_LINK_HASH_DEF_REGULAR) == 0)) { - BFD_ASSERT (h != NULL && h->dynindx != -1); + BFD_ASSERT (h->dynindx != -1); relocate = false; outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); outrel.r_addend = relocation + rela->r_addend; } else { - /* h->dynindx may be -1 if this symbol was marked to - become local. */ - if (h == NULL - || ((info->symbolic || h->dynindx == -1) - && (h->elf_link_hash_flags - & ELF_LINK_HASH_DEF_REGULAR) != 0)) + if (r_type == R_X86_64_64) { relocate = true; outrel.r_info = ELF64_R_INFO (0, R_X86_64_RELATIVE); @@ -1512,11 +1503,38 @@ elf64_x86_64_relocate_section (output_bfd, info, input_bfd, input_section, } else { - BFD_ASSERT (h->dynindx != -1); + long sindx; + + if (h == NULL) + sec = local_sections[r_symndx]; + else + { + BFD_ASSERT (h->root.type == bfd_link_hash_defined + || (h->root.type + == bfd_link_hash_defweak)); + sec = h->root.u.def.section; + } + if (sec != NULL && bfd_is_abs_section (sec)) + sindx = 0; + else if (sec == NULL || sec->owner == NULL) + { + bfd_set_error (bfd_error_bad_value); + return false; + } + else + { + asection *osec; + + osec = sec->output_section; + sindx = elf_section_data (osec)->dynindx; + BFD_ASSERT (sindx > 0); + } + relocate = false; - outrel.r_info = ELF64_R_INFO (h->dynindx, R_X86_64_32); + outrel.r_info = ELF64_R_INFO (sindx, r_type); outrel.r_addend = relocation + rela->r_addend; } + } bfd_elf64_swap_reloca_out (output_bfd, &outrel, @@ -1620,7 +1638,7 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym) /* Get the offset into the .got table of the entry that corresponds to this function. Each .got entry is GOT_ENTRY_SIZE - bytes. The first three are reserved. */ + bytes. The first three are reserved for the dynamic linker. */ got_offset = (plt_index + 3) * GOT_ENTRY_SIZE; /* Fill in the entry in the procedure linkage table. */ @@ -1630,13 +1648,26 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym) /* Insert the relocation positions of the plt section. The magic numbers at the end of the statements are the positions of the relocations in the plt section. */ - bfd_put_64 (output_bfd, got_offset, splt->contents + h->plt.offset + 2); - bfd_put_64 (output_bfd, plt_index * sizeof (Elf64_External_Rela), + /* Put offset for jmp *name@GOTPCREL(%rip), since the + instruction uses 6 bytes, subtract this value. */ + bfd_put_32 (output_bfd, + (sgot->output_section->vma + + sgot->output_offset + + got_offset + - splt->output_section->vma + - splt->output_offset + - h->plt.offset + - 6), + splt->contents + h->plt.offset + 2); + /* Put relocation index. */ + bfd_put_32 (output_bfd, plt_index, splt->contents + h->plt.offset + 7); - bfd_put_64 (output_bfd, - (h->plt.offset + PLT_ENTRY_SIZE), + /* Put offset for jmp .PLT0. */ + bfd_put_32 (output_bfd, - (h->plt.offset + PLT_ENTRY_SIZE), splt->contents + h->plt.offset + 12); - /* Fill in the entry in the global offset table. */ + /* Fill in the entry in the global offset table, initially this + points to the pushq instruction in the PLT which is at offset 6. */ bfd_put_64 (output_bfd, (splt->output_section->vma + splt->output_offset + h->plt.offset + 6), sgot->contents + got_offset); @@ -1666,6 +1697,53 @@ elf64_x86_64_finish_dynamic_symbol (output_bfd, info, h, sym) } } + if (h->got.offset != (bfd_vma) -1) + { + asection *sgot; + asection *srela; + Elf_Internal_Rela rela; + + /* This symbol has an entry in the global offset table. Set it + up. */ + + sgot = bfd_get_section_by_name (dynobj, ".got"); + srela = bfd_get_section_by_name (dynobj, ".rela.got"); + BFD_ASSERT (sgot != NULL && srela != NULL); + + rela.r_offset = (sgot->output_section->vma + + sgot->output_offset + + (h->got.offset &~ (bfd_vma) 1)); + + /* If this is a static link, or it is a -Bsymbolic link and the + symbol is defined locally or was forced to be local because + of a version file, we just want to emit a RELATIVE reloc. + The entry in the global offset table will already have been + initialized in the relocate_section function. */ + if (! elf_hash_table (info)->dynamic_sections_created + || (info->shared + && (info->symbolic || h->dynindx == -1) + && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))) + { + 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 + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + BFD_ASSERT((h->got.offset & 1) == 0); + bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset); + rela.r_info = ELF64_R_INFO (h->dynindx, R_X86_64_GLOB_DAT); + rela.r_addend = 0; + } + + bfd_elf64_swap_reloca_out (output_bfd, &rela, + ((Elf64_External_Rela *) srela->contents + + srela->reloc_count)); + ++srela->reloc_count; + } + if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0) { asection *s; @@ -1713,6 +1791,8 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) dynobj = elf_hash_table (info)->dynobj; + sgot = bfd_get_section_by_name (dynobj, ".got.plt"); + BFD_ASSERT (sgot != NULL); sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); if (elf_hash_table (info)->dynamic_sections_created) @@ -1735,7 +1815,7 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) switch (dyn.d_tag) { default: - break; + continue; case DT_PLTGOT: name = ".got"; @@ -1773,7 +1853,6 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) (s->_cooked_size != 0 ? s->_cooked_size : s->_raw_size); break; } - bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon); } @@ -1782,7 +1861,29 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) BFD_ASSERT (splt != NULL); if (splt->_raw_size > 0) { + /* Fill in the first entry in the procedure linkage table. */ memcpy (splt->contents, elf64_x86_64_plt0_entry, PLT_ENTRY_SIZE); + /* Add offset for pushq GOT+8(%rip), since the instruction + uses 6 bytes subtract this value. */ + bfd_put_32 (output_bfd, + (sgot->output_section->vma + + sgot->output_offset + + 8 + - splt->output_section->vma + - splt->output_offset + - 6), + splt->contents + 2); + /* Add offset for jmp *GOT+16(%rip). The 12 is the offset to + the end of the instruction. */ + bfd_put_32 (output_bfd, + (sgot->output_section->vma + + sgot->output_offset + + 16 + - splt->output_section->vma + - splt->output_offset + - 12), + splt->contents + 8); + } elf_section_data (splt->output_section)->this_hdr.sh_entsize = @@ -1791,8 +1892,6 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) /* Set the first entry in the global offset table to the address of the dynamic section. */ - sgot = bfd_get_section_by_name (dynobj, ".got.plt"); - BFD_ASSERT (sgot != NULL); if (sgot->_raw_size > 0) { if (sdyn == NULL) @@ -1801,7 +1900,7 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) bfd_put_64 (output_bfd, sdyn->output_section->vma + sdyn->output_offset, sgot->contents); - /* Write GOT[1] and GOT[2], needed for the linker. */ + /* Write GOT[1] and GOT[2], needed for the dynamic linker. */ bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + GOT_ENTRY_SIZE); bfd_put_64 (output_bfd, (bfd_vma) 0, sgot->contents + GOT_ENTRY_SIZE*2); } @@ -1812,40 +1911,22 @@ elf64_x86_64_finish_dynamic_sections (output_bfd, info) return true; } -/* - * Why was the hash table entry size definition changed from - * ARCH_SIZE/8 to 4? This breaks the 64 bit dynamic linker and - * this is the only reason for the elf64_x86_64_size_info structure. - */ - -const struct elf_size_info elf64_86_64_size_info = +static enum elf_reloc_type_class +elf64_x86_64_reloc_type_class (rela) + const Elf_Internal_Rela *rela; { - sizeof (Elf64_External_Ehdr), - sizeof (Elf64_External_Phdr), - sizeof (Elf64_External_Shdr), - sizeof (Elf64_External_Rel), - sizeof (Elf64_External_Rela), - sizeof (Elf64_External_Sym), - sizeof (Elf64_External_Dyn), - sizeof (Elf_External_Note), - 8, /* hash-table entry size */ - 1, /* internal relocations per external relocations */ - 64, /* arch_size */ - 8, /* file_align */ - ELFCLASS64, EV_CURRENT, - bfd_elf64_write_out_phdrs, - bfd_elf64_write_shdrs_and_ehdr, - bfd_elf64_write_relocs, - bfd_elf64_swap_symbol_out, - bfd_elf64_slurp_reloc_table, - bfd_elf64_slurp_symbol_table, - bfd_elf64_swap_dyn_in, - bfd_elf64_swap_dyn_out, - NULL, - NULL, - NULL, - NULL -}; + switch ((int) ELF64_R_TYPE (rela->r_info)) + { + case R_X86_64_RELATIVE: + return reloc_class_relative; + case R_X86_64_JUMP_SLOT: + return reloc_class_plt; + case R_X86_64_COPY: + return reloc_class_copy; + default: + return reloc_class_normal; + } +} #define TARGET_LITTLE_SYM bfd_elf64_x86_64_vec #define TARGET_LITTLE_NAME "elf64-x86-64" @@ -1853,9 +1934,8 @@ const struct elf_size_info elf64_86_64_size_info = #define ELF_MACHINE_CODE EM_X86_64 #define ELF_MAXPAGESIZE 0x100000 -#define elf_backend_size_info elf64_86_64_size_info - #define elf_backend_can_gc_sections 1 +#define elf_backend_can_refcount 1 #define elf_backend_want_got_plt 1 #define elf_backend_plt_readonly 1 #define elf_backend_want_plt_sym 0 @@ -1880,5 +1960,6 @@ const struct elf_size_info elf64_86_64_size_info = #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_object_p elf64_x86_64_elf_object_p +#define elf_backend_reloc_type_class elf64_x86_64_reloc_type_class #include "elf64-target.h"