/* IBM S/390-specific support for 64-bit ELF
- Copyright 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010, 2011, 2012 Free Software Foundation, Inc.
Contributed Martin Schwidefsky (schwidefsky@de.ibm.com).
This file is part of BFD, the Binary File Descriptor library.
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,
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., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, 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"
-
-static reloc_howto_type *elf_s390_reloc_type_lookup
- PARAMS ((bfd *, bfd_reloc_code_real_type));
-static void elf_s390_info_to_howto
- PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-static bfd_boolean elf_s390_is_local_label_name
- PARAMS ((bfd *, const char *));
-static struct bfd_hash_entry *link_hash_newfunc
- PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
-static struct bfd_link_hash_table *elf_s390_link_hash_table_create
- PARAMS ((bfd *));
-static bfd_boolean create_got_section
- PARAMS((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_create_dynamic_sections
- PARAMS((bfd *, struct bfd_link_info *));
-static void elf_s390_copy_indirect_symbol
- PARAMS ((struct elf_backend_data *, struct elf_link_hash_entry *,
- struct elf_link_hash_entry *));
-static bfd_boolean elf_s390_check_relocs
- PARAMS ((bfd *, struct bfd_link_info *, asection *,
- const Elf_Internal_Rela *));
-static asection *elf_s390_gc_mark_hook
- PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
- struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean elf_s390_gc_sweep_hook
- PARAMS ((bfd *, struct bfd_link_info *, asection *,
- const Elf_Internal_Rela *));
-static bfd_boolean elf_s390_adjust_dynamic_symbol
- PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
-static bfd_boolean allocate_dynrelocs
- PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean readonly_dynrelocs
- PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean elf_s390_size_dynamic_sections
- PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_relocate_section
- PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
- Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-static bfd_boolean elf_s390_finish_dynamic_symbol
- PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
- Elf_Internal_Sym *));
-static enum elf_reloc_type_class elf_s390_reloc_type_class
- PARAMS ((const Elf_Internal_Rela *));
-static bfd_boolean elf_s390_finish_dynamic_sections
- PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_object_p PARAMS ((bfd *));
-
#include "elf/s390.h"
/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
from smaller values. Start with zero, widen, *then* decrement. */
#define MINUS_ONE (((bfd_vma)0) - 1)
+static bfd_reloc_status_type
+s390_tls_reloc (bfd *, arelent *, asymbol *, void *,
+ asection *, bfd *, char **);
+static bfd_reloc_status_type
+s390_elf_ldisp_reloc (bfd *, arelent *, asymbol *, void *,
+ asection *, bfd *, char **);
+
/* The relocation "howto" table. */
static reloc_howto_type elf_howto_table[] =
{
0, /* dst_mask */
FALSE), /* pcrel_offset */
- HOWTO(R_390_8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_8", FALSE, 0,0x000000ff, FALSE),
- HOWTO(R_390_12, 0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_12", FALSE, 0,0x00000fff, FALSE),
- HOWTO(R_390_16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_16", FALSE, 0,0x0000ffff, FALSE),
- HOWTO(R_390_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_32", FALSE, 0,0xffffffff, FALSE),
- HOWTO(R_390_PC32, 0, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32", FALSE, 0,0xffffffff, TRUE),
- HOWTO(R_390_GOT12, 0, 1, 12, FALSE, 0, complain_overflow_dont, bfd_elf_generic_reloc, "R_390_GOT12", FALSE, 0,0x00000fff, FALSE),
- HOWTO(R_390_GOT32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT32", FALSE, 0,0xffffffff, FALSE),
- HOWTO(R_390_PLT32, 0, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32", FALSE, 0,0xffffffff, TRUE),
- HOWTO(R_390_COPY, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_COPY", FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_GLOB_DAT, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GLOB_DAT",FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_JMP_SLOT, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_JMP_SLOT",FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_RELATIVE, 0, 4, 64, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_RELATIVE",FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_GOTOFF, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTOFF", FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_GOTPC, 0, 4, 64, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPC", FALSE, 0,MINUS_ONE, TRUE),
- HOWTO(R_390_GOT16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT16", FALSE, 0,0x0000ffff, FALSE),
- HOWTO(R_390_PC16, 0, 1, 16, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16", FALSE, 0,0x0000ffff, TRUE),
- HOWTO(R_390_PC16DBL, 1, 1, 16, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC16DBL", FALSE, 0,0x0000ffff, TRUE),
- HOWTO(R_390_PLT16DBL, 1, 1, 16, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff, TRUE),
- HOWTO(R_390_PC32DBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC32DBL", FALSE, 0,0xffffffff, TRUE),
- HOWTO(R_390_PLT32DBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff, TRUE),
- HOWTO(R_390_GOTPCDBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,MINUS_ONE, TRUE),
- HOWTO(R_390_64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_64", FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_PC64, 0, 4, 64, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PC64", FALSE, 0,MINUS_ONE, TRUE),
- HOWTO(R_390_GOT64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOT64", FALSE, 0,MINUS_ONE, FALSE),
- HOWTO(R_390_PLT64, 0, 4, 64, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_PLT64", FALSE, 0,MINUS_ONE, TRUE),
- HOWTO(R_390_GOTENT, 1, 2, 32, TRUE, 0, complain_overflow_bitfield, bfd_elf_generic_reloc, "R_390_GOTENT", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_8, 0, 0, 8, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_8", FALSE, 0,0x000000ff, FALSE),
+ HOWTO(R_390_12, 0, 1, 12, FALSE, 0, complain_overflow_dont,
+ bfd_elf_generic_reloc, "R_390_12", FALSE, 0,0x00000fff, FALSE),
+ HOWTO(R_390_16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_16", FALSE, 0,0x0000ffff, FALSE),
+ HOWTO(R_390_32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_32", FALSE, 0,0xffffffff, FALSE),
+ HOWTO(R_390_PC32, 0, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC32", FALSE, 0,0xffffffff, TRUE),
+ HOWTO(R_390_GOT12, 0, 1, 12, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOT12", FALSE, 0,0x00000fff, FALSE),
+ HOWTO(R_390_GOT32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOT32", FALSE, 0,0xffffffff, FALSE),
+ HOWTO(R_390_PLT32, 0, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT32", FALSE, 0,0xffffffff, TRUE),
+ HOWTO(R_390_COPY, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_COPY", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_GLOB_DAT, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GLOB_DAT", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_JMP_SLOT, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_JMP_SLOT", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_RELATIVE, 0, 4, 64, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_RELATIVE", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_GOTOFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTOFF32", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_GOTPC, 0, 4, 64, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPC", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_GOT16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOT16", FALSE, 0,0x0000ffff, FALSE),
+ HOWTO(R_390_PC16, 0, 1, 16, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC16", FALSE, 0,0x0000ffff, TRUE),
+ HOWTO(R_390_PC16DBL, 1, 1, 16, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC16DBL", FALSE, 0,0x0000ffff, TRUE),
+ HOWTO(R_390_PLT16DBL, 1, 1, 16, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT16DBL", FALSE, 0,0x0000ffff, TRUE),
+ HOWTO(R_390_PC32DBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC32DBL", FALSE, 0,0xffffffff, TRUE),
+ HOWTO(R_390_PLT32DBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT32DBL", FALSE, 0,0xffffffff, TRUE),
+ HOWTO(R_390_GOTPCDBL, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPCDBL", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_64", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_PC64, 0, 4, 64, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC64", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_GOT64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOT64", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_PLT64, 0, 4, 64, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT64", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_GOTENT, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTENT", FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_GOTOFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTOFF16", FALSE, 0,0x0000ffff, FALSE),
+ HOWTO(R_390_GOTOFF64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTOFF64", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_GOTPLT12, 0, 1, 12, FALSE, 0, complain_overflow_dont,
+ bfd_elf_generic_reloc, "R_390_GOTPLT12", FALSE, 0,0x00000fff, FALSE),
+ HOWTO(R_390_GOTPLT16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPLT16", FALSE, 0,0x0000ffff, FALSE),
+ HOWTO(R_390_GOTPLT32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPLT32", FALSE, 0,0xffffffff, FALSE),
+ HOWTO(R_390_GOTPLT64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPLT64", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_GOTPLTENT, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_GOTPLTENT",FALSE, 0,MINUS_ONE, TRUE),
+ HOWTO(R_390_PLTOFF16, 0, 1, 16, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLTOFF16", FALSE, 0,0x0000ffff, FALSE),
+ HOWTO(R_390_PLTOFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLTOFF32", FALSE, 0,0xffffffff, FALSE),
+ HOWTO(R_390_PLTOFF64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLTOFF64", FALSE, 0,MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_LOAD, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+ s390_tls_reloc, "R_390_TLS_LOAD", FALSE, 0, 0, FALSE),
+ HOWTO(R_390_TLS_GDCALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+ s390_tls_reloc, "R_390_TLS_GDCALL", FALSE, 0, 0, FALSE),
+ HOWTO(R_390_TLS_LDCALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+ s390_tls_reloc, "R_390_TLS_LDCALL", FALSE, 0, 0, FALSE),
+ EMPTY_HOWTO (R_390_TLS_GD32), /* Empty entry for R_390_TLS_GD32. */
+ HOWTO(R_390_TLS_GD64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_GD64", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_GOTIE12, 0, 1, 12, FALSE, 0, complain_overflow_dont,
+ bfd_elf_generic_reloc, "R_390_TLS_GOTIE12", FALSE, 0, 0x00000fff, FALSE),
+ EMPTY_HOWTO (R_390_TLS_GOTIE32), /* Empty entry for R_390_TLS_GOTIE32. */
+ HOWTO(R_390_TLS_GOTIE64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_GOTIE64", FALSE, 0, MINUS_ONE, FALSE),
+ EMPTY_HOWTO (R_390_TLS_LDM32), /* Empty entry for R_390_TLS_LDM32. */
+ HOWTO(R_390_TLS_LDM64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_LDM64", FALSE, 0, MINUS_ONE, FALSE),
+ EMPTY_HOWTO (R_390_TLS_IE32), /* Empty entry for R_390_TLS_IE32. */
+ HOWTO(R_390_TLS_IE64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_IE64", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_IEENT, 1, 2, 32, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_IEENT", FALSE, 0, MINUS_ONE, TRUE),
+ EMPTY_HOWTO (R_390_TLS_LE32), /* Empty entry for R_390_TLS_LE32. */
+ HOWTO(R_390_TLS_LE64, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_LE64", FALSE, 0, MINUS_ONE, FALSE),
+ EMPTY_HOWTO (R_390_TLS_LDO32), /* Empty entry for R_390_TLS_LDO32. */
+ HOWTO(R_390_TLS_LDO64, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_LDO64", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_DTPMOD, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_DTPMOD", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_DTPOFF, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_DTPOFF", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_TLS_TPOFF, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_TLS_TPOFF", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
+ s390_elf_ldisp_reloc, "R_390_20", FALSE, 0,0x0fffff00, FALSE),
+ HOWTO(R_390_GOT20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
+ s390_elf_ldisp_reloc, "R_390_GOT20", FALSE, 0,0x0fffff00, FALSE),
+ HOWTO(R_390_GOTPLT20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
+ s390_elf_ldisp_reloc, "R_390_GOTPLT20", FALSE, 0,0x0fffff00, FALSE),
+ HOWTO(R_390_TLS_GOTIE20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
+ s390_elf_ldisp_reloc, "R_390_TLS_GOTIE20", FALSE, 0,0x0fffff00, FALSE),
+ HOWTO(R_390_IRELATIVE, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_IRELATIVE", FALSE, 0, MINUS_ONE, FALSE),
+
};
/* GNU extension to record C++ vtable hierarchy. */
HOWTO (R_390_GNU_VTENTRY, 0,4,0,FALSE,0,complain_overflow_dont, _bfd_elf_rel_vtable_reloc_fn,"R_390_GNU_VTENTRY", FALSE,0,0, FALSE);
static reloc_howto_type *
-elf_s390_reloc_type_lookup (abfd, code)
- bfd *abfd ATTRIBUTE_UNUSED;
- bfd_reloc_code_real_type code;
+elf_s390_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
{
switch (code)
{
case BFD_RELOC_390_RELATIVE:
return &elf_howto_table[(int) R_390_RELATIVE];
case BFD_RELOC_32_GOTOFF:
- return &elf_howto_table[(int) R_390_GOTOFF];
+ return &elf_howto_table[(int) R_390_GOTOFF32];
case BFD_RELOC_390_GOTPC:
return &elf_howto_table[(int) R_390_GOTPC];
case BFD_RELOC_390_GOT16:
return &elf_howto_table[(int) R_390_PC16DBL];
case BFD_RELOC_390_PLT16DBL:
return &elf_howto_table[(int) R_390_PLT16DBL];
- case BFD_RELOC_VTABLE_INHERIT:
- return &elf64_s390_vtinherit_howto;
- case BFD_RELOC_VTABLE_ENTRY:
- return &elf64_s390_vtentry_howto;
case BFD_RELOC_390_PC32DBL:
return &elf_howto_table[(int) R_390_PC32DBL];
case BFD_RELOC_390_PLT32DBL:
return &elf_howto_table[(int) R_390_PLT64];
case BFD_RELOC_390_GOTENT:
return &elf_howto_table[(int) R_390_GOTENT];
+ case BFD_RELOC_16_GOTOFF:
+ return &elf_howto_table[(int) R_390_GOTOFF16];
+ case BFD_RELOC_390_GOTOFF64:
+ return &elf_howto_table[(int) R_390_GOTOFF64];
+ case BFD_RELOC_390_GOTPLT12:
+ return &elf_howto_table[(int) R_390_GOTPLT12];
+ case BFD_RELOC_390_GOTPLT16:
+ return &elf_howto_table[(int) R_390_GOTPLT16];
+ case BFD_RELOC_390_GOTPLT32:
+ return &elf_howto_table[(int) R_390_GOTPLT32];
+ case BFD_RELOC_390_GOTPLT64:
+ return &elf_howto_table[(int) R_390_GOTPLT64];
+ case BFD_RELOC_390_GOTPLTENT:
+ return &elf_howto_table[(int) R_390_GOTPLTENT];
+ case BFD_RELOC_390_PLTOFF16:
+ return &elf_howto_table[(int) R_390_PLTOFF16];
+ case BFD_RELOC_390_PLTOFF32:
+ return &elf_howto_table[(int) R_390_PLTOFF32];
+ case BFD_RELOC_390_PLTOFF64:
+ return &elf_howto_table[(int) R_390_PLTOFF64];
+ case BFD_RELOC_390_TLS_LOAD:
+ return &elf_howto_table[(int) R_390_TLS_LOAD];
+ case BFD_RELOC_390_TLS_GDCALL:
+ return &elf_howto_table[(int) R_390_TLS_GDCALL];
+ case BFD_RELOC_390_TLS_LDCALL:
+ return &elf_howto_table[(int) R_390_TLS_LDCALL];
+ case BFD_RELOC_390_TLS_GD64:
+ return &elf_howto_table[(int) R_390_TLS_GD64];
+ case BFD_RELOC_390_TLS_GOTIE12:
+ return &elf_howto_table[(int) R_390_TLS_GOTIE12];
+ case BFD_RELOC_390_TLS_GOTIE64:
+ return &elf_howto_table[(int) R_390_TLS_GOTIE64];
+ case BFD_RELOC_390_TLS_LDM64:
+ return &elf_howto_table[(int) R_390_TLS_LDM64];
+ case BFD_RELOC_390_TLS_IE64:
+ return &elf_howto_table[(int) R_390_TLS_IE64];
+ case BFD_RELOC_390_TLS_IEENT:
+ return &elf_howto_table[(int) R_390_TLS_IEENT];
+ case BFD_RELOC_390_TLS_LE64:
+ return &elf_howto_table[(int) R_390_TLS_LE64];
+ case BFD_RELOC_390_TLS_LDO64:
+ return &elf_howto_table[(int) R_390_TLS_LDO64];
+ case BFD_RELOC_390_TLS_DTPMOD:
+ return &elf_howto_table[(int) R_390_TLS_DTPMOD];
+ case BFD_RELOC_390_TLS_DTPOFF:
+ return &elf_howto_table[(int) R_390_TLS_DTPOFF];
+ case BFD_RELOC_390_TLS_TPOFF:
+ return &elf_howto_table[(int) R_390_TLS_TPOFF];
+ case BFD_RELOC_390_20:
+ return &elf_howto_table[(int) R_390_20];
+ case BFD_RELOC_390_GOT20:
+ return &elf_howto_table[(int) R_390_GOT20];
+ case BFD_RELOC_390_GOTPLT20:
+ return &elf_howto_table[(int) R_390_GOTPLT20];
+ case BFD_RELOC_390_TLS_GOTIE20:
+ return &elf_howto_table[(int) R_390_TLS_GOTIE20];
+ case BFD_RELOC_390_IRELATIVE:
+ return &elf_howto_table[(int) R_390_IRELATIVE];
+ case BFD_RELOC_VTABLE_INHERIT:
+ return &elf64_s390_vtinherit_howto;
+ case BFD_RELOC_VTABLE_ENTRY:
+ return &elf64_s390_vtentry_howto;
default:
break;
}
return 0;
}
+static reloc_howto_type *
+elf_s390_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ const char *r_name)
+{
+ unsigned int i;
+
+ for (i = 0;
+ i < sizeof (elf_howto_table) / sizeof (elf_howto_table[0]);
+ i++)
+ if (elf_howto_table[i].name != NULL
+ && strcasecmp (elf_howto_table[i].name, r_name) == 0)
+ return &elf_howto_table[i];
+
+ if (strcasecmp (elf64_s390_vtinherit_howto.name, r_name) == 0)
+ return &elf64_s390_vtinherit_howto;
+ if (strcasecmp (elf64_s390_vtentry_howto.name, r_name) == 0)
+ return &elf64_s390_vtentry_howto;
+
+ return NULL;
+}
+
/* We need to use ELF64_R_TYPE so we have our own copy of this function,
and elf64-s390.c has its own copy. */
static void
-elf_s390_info_to_howto (abfd, cache_ptr, dst)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *cache_ptr;
- Elf_Internal_Rela *dst;
+elf_s390_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr,
+ Elf_Internal_Rela *dst)
{
- switch (ELF64_R_TYPE(dst->r_info))
+ unsigned int r_type = ELF64_R_TYPE(dst->r_info);
+ switch (r_type)
{
case R_390_GNU_VTINHERIT:
cache_ptr->howto = &elf64_s390_vtinherit_howto;
break;
default:
- BFD_ASSERT (ELF64_R_TYPE(dst->r_info) < (unsigned int) R_390_max);
- cache_ptr->howto = &elf_howto_table[ELF64_R_TYPE(dst->r_info)];
+ if (r_type >= sizeof (elf_howto_table) / sizeof (elf_howto_table[0]))
+ {
+ (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
+ abfd, (int) r_type);
+ r_type = R_390_NONE;
+ }
+ cache_ptr->howto = &elf_howto_table[r_type];
+ }
+}
+
+/* A relocation function which doesn't do anything. */
+static bfd_reloc_status_type
+s390_tls_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *reloc_entry,
+ asymbol *symbol ATTRIBUTE_UNUSED,
+ void * data ATTRIBUTE_UNUSED,
+ asection *input_section,
+ bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ if (output_bfd)
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+}
+
+/* Handle the large displacement relocs. */
+static bfd_reloc_status_type
+s390_elf_ldisp_reloc (bfd *abfd,
+ arelent *reloc_entry,
+ asymbol *symbol,
+ void * data,
+ asection *input_section,
+ bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
+{
+ reloc_howto_type *howto = reloc_entry->howto;
+ bfd_vma relocation;
+ bfd_vma insn;
+
+ if (output_bfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (! howto->partial_inplace
+ || reloc_entry->addend == 0))
+ {
+ reloc_entry->address += input_section->output_offset;
+ return bfd_reloc_ok;
+ }
+ if (output_bfd != NULL)
+ return bfd_reloc_continue;
+
+ if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
+ return bfd_reloc_outofrange;
+
+ relocation = (symbol->value
+ + symbol->section->output_section->vma
+ + symbol->section->output_offset);
+ relocation += reloc_entry->addend;
+ if (howto->pc_relative)
+ {
+ relocation -= (input_section->output_section->vma
+ + input_section->output_offset);
+ relocation -= reloc_entry->address;
}
+
+ insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ insn |= (relocation & 0xfff) << 16 | (relocation & 0xff000) >> 4;
+ bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
+
+ if ((bfd_signed_vma) relocation < - 0x80000
+ || (bfd_signed_vma) relocation > 0x7ffff)
+ return bfd_reloc_overflow;
+ else
+ return bfd_reloc_ok;
}
static bfd_boolean
-elf_s390_is_local_label_name (abfd, name)
- bfd *abfd;
- const char *name;
+elf_s390_is_local_label_name (bfd *abfd, const char *name)
{
if (name[0] == '.' && (name[1] == 'X' || name[1] == 'L'))
return TRUE;
/* The name of the dynamic interpreter. This is put in the .interp
section. */
-#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
+#define ELF_DYNAMIC_INTERPRETER "/lib/ld64.so.1"
+
+/* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
+ copying dynamic variables from a shared lib into an app's dynbss
+ section, and instead use a dynamic relocation to point into the
+ shared lib. */
+#define ELIMINATE_COPY_RELOCS 1
/* The size in bytes of the first entry in the procedure linkage table. */
#define PLT_FIRST_ENTRY_SIZE 32
#define GOT_ENTRY_SIZE 8
+#define RELA_ENTRY_SIZE sizeof (Elf64_External_Rela)
+
/* The first three entries in a procedure linkage table are reserved,
and the initial contents are unimportant (we zero them out).
Subsequent entries look like this. See the SVR4 ABI 386
RET1: BASR 1,0 # 2 bytes Return from GOT 1st time
LGF 1,12(1) # 6 bytes Load offset in symbl table in r1
BRCL 15,-x # 6 bytes Jump to start of PLT
- .long ? # 4 bytes offset into symbol table
+ .long ? # 4 bytes offset into .rela.plt
Total = 32 bytes per PLT entry
Fixup at offset 2: relative address to GOT entry
Fixup at offset 22: relative branch to PLT0
- Fixup at offset 28: 32 bit offset into symbol table
-
- A 32 bit offset into the symbol table is enough. It allows for symbol
- tables up to a size of 2 gigabyte. A single dynamic object (the main
- program, any shared library) is limited to 4GB in size and I want to see
- the program that manages to have a symbol table of more than 2 GB with a
- total size of at max 4 GB. */
-
-#define PLT_ENTRY_WORD0 (bfd_vma) 0xc0100000
-#define PLT_ENTRY_WORD1 (bfd_vma) 0x0000e310
-#define PLT_ENTRY_WORD2 (bfd_vma) 0x10000004
-#define PLT_ENTRY_WORD3 (bfd_vma) 0x07f10d10
-#define PLT_ENTRY_WORD4 (bfd_vma) 0xe310100c
-#define PLT_ENTRY_WORD5 (bfd_vma) 0x0014c0f4
-#define PLT_ENTRY_WORD6 (bfd_vma) 0x00000000
-#define PLT_ENTRY_WORD7 (bfd_vma) 0x00000000
+ Fixup at offset 28: 32 bit offset into .rela.plt
+
+ A 32 bit offset into the symbol table is enough. It allows for
+ .rela.plt sections up to a size of 2 gigabyte. A single dynamic
+ object (the main program, any shared library) is limited to 4GB in
+ size. Having a .rela.plt of 2GB would already make the .plt
+ section bigger than 8GB. */
+
+static const bfd_byte elf_s390x_plt_entry[PLT_ENTRY_SIZE] =
+ {
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, /* larl %r1,. */
+ 0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, /* lg %r1,0(%r1) */
+ 0x07, 0xf1, /* br %r1 */
+ 0x0d, 0x10, /* basr %r1,%r0 */
+ 0xe3, 0x10, 0x10, 0x0c, 0x00, 0x14, /* lgf %r1,12(%r1) */
+ 0xc0, 0xf4, 0x00, 0x00, 0x00, 0x00, /* jg first plt */
+ 0x00, 0x00, 0x00, 0x00 /* .long 0x00000000 */
+ };
/* The first PLT entry pushes the offset into the symbol table
- from R1 onto the stack at 8(15) and the loader object info
- at 12(15), loads the loader address in R1 and jumps to it. */
+ from R1 onto the stack at 56(15) and the loader object info
+ at 48(15), loads the loader address in R1 and jumps to it. */
/* The first entry in the PLT:
Fixup at offset 8: relative address to start of GOT. */
-#define PLT_FIRST_ENTRY_WORD0 (bfd_vma) 0xe310f038
-#define PLT_FIRST_ENTRY_WORD1 (bfd_vma) 0x0024c010
-#define PLT_FIRST_ENTRY_WORD2 (bfd_vma) 0x00000000
-#define PLT_FIRST_ENTRY_WORD3 (bfd_vma) 0xd207f030
-#define PLT_FIRST_ENTRY_WORD4 (bfd_vma) 0x1008e310
-#define PLT_FIRST_ENTRY_WORD5 (bfd_vma) 0x10100004
-#define PLT_FIRST_ENTRY_WORD6 (bfd_vma) 0x07f10700
-#define PLT_FIRST_ENTRY_WORD7 (bfd_vma) 0x07000700
-
-/* The s390 linker needs to keep track of the number of relocs that it
- decides to copy as dynamic relocs in check_relocs for each symbol.
- This is so that it can later discard them if they are found to be
- unnecessary. We store the information in a field extending the
- regular ELF linker hash table. */
-
-struct elf_s390_dyn_relocs
-{
- struct elf_s390_dyn_relocs *next;
-
- /* The input section of the reloc. */
- asection *sec;
-
- /* Total number of relocs copied for the input section. */
- bfd_size_type count;
+static const bfd_byte elf_s390x_first_plt_entry[PLT_FIRST_ENTRY_SIZE] =
+ {
+ 0xe3, 0x10, 0xf0, 0x38, 0x00, 0x24, /* stg %r1,56(%r15) */
+ 0xc0, 0x10, 0x00, 0x00, 0x00, 0x00, /* larl %r1,. */
+ 0xd2, 0x07, 0xf0, 0x30, 0x10, 0x08, /* mvc 48(8,%r15),8(%r1) */
+ 0xe3, 0x10, 0x10, 0x10, 0x00, 0x04, /* lg %r1,16(%r1) */
+ 0x07, 0xf1, /* br %r1 */
+ 0x07, 0x00, /* nopr %r0 */
+ 0x07, 0x00, /* nopr %r0 */
+ 0x07, 0x00 /* nopr %r0 */
+ };
- /* Number of pc-relative relocs copied for the input section. */
- bfd_size_type pc_count;
-};
/* s390 ELF linker hash entry. */
struct elf_link_hash_entry elf;
/* Track dynamic relocs copied for this symbol. */
- struct elf_s390_dyn_relocs *dyn_relocs;
+ struct elf_dyn_relocs *dyn_relocs;
+
+ /* Number of GOTPLT references for a function. */
+ bfd_signed_vma gotplt_refcount;
+
+#define GOT_UNKNOWN 0
+#define GOT_NORMAL 1
+#define GOT_TLS_GD 2
+#define GOT_TLS_IE 3
+#define GOT_TLS_IE_NLT 3
+ unsigned char tls_type;
+
+ /* For pointer equality reasons we might need to change the symbol
+ type from STT_GNU_IFUNC to STT_FUNC together with its value and
+ section entry. So after alloc_dynrelocs only these values should
+ be used. In order to check whether a symbol is IFUNC use
+ s390_is_ifunc_symbol_p. */
+ bfd_vma ifunc_resolver_address;
+ asection *ifunc_resolver_section;
};
+#define elf_s390_hash_entry(ent) \
+ ((struct elf_s390_link_hash_entry *)(ent))
+
+/* This structure represents an entry in the local PLT list needed for
+ local IFUNC symbols. */
+struct plt_entry
+{
+ /* The section of the local symbol.
+ Set in relocate_section and used in finish_dynamic_sections. */
+ asection *sec;
+
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } plt;
+};
+
+/* NOTE: Keep this structure in sync with
+ the one declared in elf32-s390.c. */
+struct elf_s390_obj_tdata
+{
+ struct elf_obj_tdata root;
+
+ /* A local PLT is needed for ifunc symbols. */
+ struct plt_entry *local_plt;
+
+ /* TLS type for each local got entry. */
+ char *local_got_tls_type;
+};
+
+#define elf_s390_tdata(abfd) \
+ ((struct elf_s390_obj_tdata *) (abfd)->tdata.any)
+
+#define elf_s390_local_plt(abfd) \
+ (elf_s390_tdata (abfd)->local_plt)
+
+#define elf_s390_local_got_tls_type(abfd) \
+ (elf_s390_tdata (abfd)->local_got_tls_type)
+
+#define is_s390_elf(bfd) \
+ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+ && elf_tdata (bfd) != NULL \
+ && elf_object_id (bfd) == S390_ELF_DATA)
+
+static bfd_boolean
+elf_s390_mkobject (bfd *abfd)
+{
+ return bfd_elf_allocate_object (abfd, sizeof (struct elf_s390_obj_tdata),
+ S390_ELF_DATA);
+}
+
+static bfd_boolean
+elf_s390_object_p (bfd *abfd)
+{
+ /* Set the right machine number for an s390 elf32 file. */
+ return bfd_default_set_arch_mach (abfd, bfd_arch_s390, bfd_mach_s390_64);
+}
+
/* s390 ELF linker hash table. */
struct elf_s390_link_hash_table
struct elf_link_hash_table elf;
/* Short-cuts to get to dynamic linker sections. */
- asection *sgot;
- asection *sgotplt;
- asection *srelgot;
- asection *splt;
- asection *srelplt;
asection *sdynbss;
asection *srelbss;
+ asection *irelifunc;
+
+ union {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tls_ldm_got;
- /* Small local sym to section mapping cache. */
- struct sym_sec_cache sym_sec;
+ /* Small local sym cache. */
+ struct sym_cache sym_cache;
};
/* Get the s390 ELF linker hash table from a link_info structure. */
#define elf_s390_hash_table(p) \
- ((struct elf_s390_link_hash_table *) ((p)->hash))
+ (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+ == S390_ELF_DATA ? ((struct elf_s390_link_hash_table *) ((p)->hash)) : NULL)
+
+#define ELF64 1
+#include "elf-s390-common.c"
/* Create an entry in an s390 ELF linker hash table. */
static struct bfd_hash_entry *
-link_hash_newfunc (entry, table, string)
- struct bfd_hash_entry *entry;
- struct bfd_hash_table *table;
- const char *string;
+link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
{
/* Allocate the structure if it has not already been allocated by a
subclass. */
eh = (struct elf_s390_link_hash_entry *) entry;
eh->dyn_relocs = NULL;
+ eh->gotplt_refcount = 0;
+ eh->tls_type = GOT_UNKNOWN;
+ eh->ifunc_resolver_address = 0;
+ eh->ifunc_resolver_section = NULL;
}
return entry;
/* Create an s390 ELF linker hash table. */
static struct bfd_link_hash_table *
-elf_s390_link_hash_table_create (abfd)
- bfd *abfd;
+elf_s390_link_hash_table_create (bfd *abfd)
{
struct elf_s390_link_hash_table *ret;
bfd_size_type amt = sizeof (struct elf_s390_link_hash_table);
if (ret == NULL)
return NULL;
- if (! _bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc))
+ if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
+ sizeof (struct elf_s390_link_hash_entry),
+ S390_ELF_DATA))
{
free (ret);
return NULL;
}
- ret->sgot = NULL;
- ret->sgotplt = NULL;
- ret->srelgot = NULL;
- ret->splt = NULL;
- ret->srelplt = NULL;
+ ret->elf.sgot = NULL;
+ ret->elf.sgotplt = NULL;
+ ret->elf.srelgot = NULL;
+ ret->elf.splt = NULL;
+ ret->elf.srelplt = NULL;
ret->sdynbss = NULL;
ret->srelbss = NULL;
- ret->sym_sec.abfd = NULL;
+ ret->tls_ldm_got.refcount = 0;
+ ret->sym_cache.abfd = NULL;
return &ret->elf.root;
}
shortcuts to them in our hash table. */
static bfd_boolean
-create_got_section (dynobj, info)
- bfd *dynobj;
- struct bfd_link_info *info;
+create_got_section (bfd *dynobj,
+ struct bfd_link_info *info)
{
struct elf_s390_link_hash_table *htab;
return FALSE;
htab = elf_s390_hash_table (info);
- htab->sgot = bfd_get_section_by_name (dynobj, ".got");
- htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
- if (!htab->sgot || !htab->sgotplt)
- abort ();
-
- htab->srelgot = bfd_make_section (dynobj, ".rela.got");
- if (htab->srelgot == NULL
- || ! bfd_set_section_flags (dynobj, htab->srelgot,
- (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY | SEC_LINKER_CREATED
- | SEC_READONLY))
- || ! bfd_set_section_alignment (dynobj, htab->srelgot, 3))
+ if (htab == NULL)
return FALSE;
+
+ htab->elf.sgot = bfd_get_linker_section (dynobj, ".got");
+ htab->elf.sgotplt = bfd_get_linker_section (dynobj, ".got.plt");
+ htab->elf.srelgot = bfd_get_linker_section (dynobj, ".rela.got");
+ if (!htab->elf.sgot || !htab->elf.sgotplt || !htab->elf.srelgot)
+ abort ();
return TRUE;
}
hash table. */
static bfd_boolean
-elf_s390_create_dynamic_sections (dynobj, info)
- bfd *dynobj;
- struct bfd_link_info *info;
+elf_s390_create_dynamic_sections (bfd *dynobj,
+ struct bfd_link_info *info)
{
struct elf_s390_link_hash_table *htab;
htab = elf_s390_hash_table (info);
- if (!htab->sgot && !create_got_section (dynobj, info))
+ if (htab == NULL)
+ return FALSE;
+
+ if (!htab->elf.sgot && !create_got_section (dynobj, info))
return FALSE;
if (!_bfd_elf_create_dynamic_sections (dynobj, info))
return FALSE;
- htab->splt = bfd_get_section_by_name (dynobj, ".plt");
- htab->srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
- htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss");
+ htab->elf.splt = bfd_get_linker_section (dynobj, ".plt");
+ htab->elf.srelplt = bfd_get_linker_section (dynobj, ".rela.plt");
+ htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
if (!info->shared)
- htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss");
+ htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
- if (!htab->splt || !htab->srelplt || !htab->sdynbss
+ if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
|| (!info->shared && !htab->srelbss))
abort ();
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
-elf_s390_copy_indirect_symbol (bed, dir, ind)
- struct elf_backend_data *bed;
- struct elf_link_hash_entry *dir, *ind;
+elf_s390_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
{
struct elf_s390_link_hash_entry *edir, *eind;
{
if (edir->dyn_relocs != NULL)
{
- struct elf_s390_dyn_relocs **pp;
- struct elf_s390_dyn_relocs *p;
-
- if (ind->root.type == bfd_link_hash_indirect)
- abort ();
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
- /* Add reloc counts against the weak sym to the strong sym
+ /* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
{
- struct elf_s390_dyn_relocs *q;
+ struct elf_dyn_relocs *q;
for (q = edir->dyn_relocs; q != NULL; q = q->next)
if (q->sec == p->sec)
eind->dyn_relocs = NULL;
}
- _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+ if (ind->root.type == bfd_link_hash_indirect
+ && dir->got.refcount <= 0)
+ {
+ edir->tls_type = eind->tls_type;
+ eind->tls_type = GOT_UNKNOWN;
+ }
+
+ if (ELIMINATE_COPY_RELOCS
+ && ind->root.type != bfd_link_hash_indirect
+ && dir->dynamic_adjusted)
+ {
+ /* If called to transfer flags for a weakdef during processing
+ of elf_adjust_dynamic_symbol, don't copy non_got_ref.
+ We clear it ourselves for ELIMINATE_COPY_RELOCS. */
+ dir->ref_dynamic |= ind->ref_dynamic;
+ dir->ref_regular |= ind->ref_regular;
+ dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+ dir->needs_plt |= ind->needs_plt;
+ }
+ else
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
+static int
+elf_s390_tls_transition (struct bfd_link_info *info,
+ int r_type,
+ int is_local)
+{
+ if (info->shared)
+ return r_type;
+
+ switch (r_type)
+ {
+ case R_390_TLS_GD64:
+ case R_390_TLS_IE64:
+ if (is_local)
+ return R_390_TLS_LE64;
+ return R_390_TLS_IE64;
+ case R_390_TLS_GOTIE64:
+ if (is_local)
+ return R_390_TLS_LE64;
+ return R_390_TLS_GOTIE64;
+ case R_390_TLS_LDM64:
+ return R_390_TLS_LE64;
+ }
+
+ return r_type;
}
/* Look through the relocs for a section during the first phase, and
table. */
static bfd_boolean
-elf_s390_check_relocs (abfd, info, sec, relocs)
- bfd *abfd;
- struct bfd_link_info *info;
- asection *sec;
- const Elf_Internal_Rela *relocs;
+elf_s390_check_relocs (bfd *abfd,
+ struct bfd_link_info *info,
+ asection *sec,
+ const Elf_Internal_Rela *relocs)
{
struct elf_s390_link_hash_table *htab;
Elf_Internal_Shdr *symtab_hdr;
const Elf_Internal_Rela *rel;
const Elf_Internal_Rela *rel_end;
asection *sreloc;
+ bfd_signed_vma *local_got_refcounts;
+ int tls_type, old_tls_type;
- if (info->relocateable)
+ if (info->relocatable)
return TRUE;
+ BFD_ASSERT (is_s390_elf (abfd));
+
htab = elf_s390_hash_table (info);
- symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ if (htab == NULL)
+ return FALSE;
+
+ symtab_hdr = &elf_symtab_hdr (abfd);
sym_hashes = elf_sym_hashes (abfd);
+ local_got_refcounts = elf_local_got_refcounts (abfd);
sreloc = NULL;
rel_end = relocs + sec->reloc_count;
for (rel = relocs; rel < rel_end; rel++)
{
+ unsigned int r_type;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
r_symndx = ELF64_R_SYM (rel->r_info);
if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
{
- (*_bfd_error_handler) (_("%s: bad symbol index: %d"),
- bfd_archive_filename (abfd),
+ (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
+ abfd,
r_symndx);
return FALSE;
}
if (r_symndx < symtab_hdr->sh_info)
- h = NULL;
+ {
+ /* A local symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry *plt;
+
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+
+ if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+ return FALSE;
+
+ if (local_got_refcounts == NULL)
+ {
+ if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
+ return FALSE;
+ local_got_refcounts = elf_local_got_refcounts (abfd);
+ }
+ plt = elf_s390_local_plt (abfd);
+ plt[r_symndx].plt.refcount++;
+ }
+ h = NULL;
+ }
else
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
- switch (ELF64_R_TYPE (rel->r_info))
+ /* Create got section and local_got_refcounts array if they
+ are needed. */
+ r_type = elf_s390_tls_transition (info,
+ ELF64_R_TYPE (rel->r_info),
+ h == NULL);
+ switch (r_type)
{
case R_390_GOT12:
case R_390_GOT16:
+ case R_390_GOT20:
case R_390_GOT32:
case R_390_GOT64:
case R_390_GOTENT:
- /* This symbol requires a global offset table entry. */
- if (h != NULL)
- {
- h->got.refcount += 1;
- }
- else
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ case R_390_TLS_GD64:
+ case R_390_TLS_GOTIE12:
+ case R_390_TLS_GOTIE20:
+ case R_390_TLS_GOTIE64:
+ case R_390_TLS_IEENT:
+ case R_390_TLS_IE64:
+ case R_390_TLS_LDM64:
+ if (h == NULL
+ && local_got_refcounts == NULL)
{
- bfd_signed_vma *local_got_refcounts;
-
- /* This is a global offset table entry for a local symbol. */
+ if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
+ return FALSE;
local_got_refcounts = elf_local_got_refcounts (abfd);
- if (local_got_refcounts == NULL)
- {
- bfd_size_type size;
-
- size = symtab_hdr->sh_info;
- size *= sizeof (bfd_signed_vma);
- local_got_refcounts = ((bfd_signed_vma *)
- bfd_zalloc (abfd, size));
- if (local_got_refcounts == NULL)
- return FALSE;
- elf_local_got_refcounts (abfd) = local_got_refcounts;
- }
- local_got_refcounts[r_symndx] += 1;
}
- /* Fall through */
- case R_390_GOTOFF:
+ /* Fall through. */
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ case R_390_GOTOFF64:
case R_390_GOTPC:
case R_390_GOTPCDBL:
- if (htab->sgot == NULL)
+ if (htab->elf.sgot == NULL)
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
if (!create_got_section (htab->elf.dynobj, info))
return FALSE;
}
+ }
+
+ if (h != NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+ return FALSE;
+
+ /* Make sure an IFUNC symbol defined in a non-shared object
+ always gets a PLT slot. */
+ if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+ {
+ /* The symbol is called by the dynamic loader in order
+ to resolve the relocation. So it is in fact also
+ referenced. */
+ h->ref_regular = 1;
+ h->needs_plt = 1;
+ }
+ }
+
+ switch (r_type)
+ {
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ case R_390_GOTOFF64:
+ case R_390_GOTPC:
+ case R_390_GOTPCDBL:
+ /* These relocs do not need a GOT slot. They just load the
+ GOT pointer itself or address something else relative to
+ the GOT. Since the GOT pointer has been set up above we
+ are done. */
break;
case R_390_PLT16DBL:
case R_390_PLT32:
case R_390_PLT32DBL:
case R_390_PLT64:
+ case R_390_PLTOFF16:
+ case R_390_PLTOFF32:
+ case R_390_PLTOFF64:
/* This symbol requires a procedure linkage table entry. We
actually build the entry in adjust_dynamic_symbol,
because this might be a case of linking PIC code which is
/* If this is a local symbol, we resolve it directly without
creating a procedure linkage table entry. */
- if (h == NULL)
- continue;
+ if (h != NULL)
+ {
+ h->needs_plt = 1;
+ h->plt.refcount += 1;
+ }
+ break;
- h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
- h->plt.refcount += 1;
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ /* This symbol requires either a procedure linkage table entry
+ or an entry in the local got. We actually build the entry
+ in adjust_dynamic_symbol because whether this is really a
+ global reference can change and with it the fact if we have
+ to create a plt entry or a local got entry. To be able to
+ make a once global symbol a local one we have to keep track
+ of the number of gotplt references that exist for this
+ symbol. */
+ if (h != NULL)
+ {
+ ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount++;
+ h->needs_plt = 1;
+ h->plt.refcount += 1;
+ }
+ else
+ local_got_refcounts[r_symndx] += 1;
+ break;
+
+ case R_390_TLS_LDM64:
+ htab->tls_ldm_got.refcount += 1;
break;
+ case R_390_TLS_IE64:
+ case R_390_TLS_GOTIE12:
+ case R_390_TLS_GOTIE20:
+ case R_390_TLS_GOTIE64:
+ case R_390_TLS_IEENT:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+ /* Fall through */
+
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT20:
+ case R_390_GOT32:
+ case R_390_GOT64:
+ case R_390_GOTENT:
+ case R_390_TLS_GD64:
+ /* This symbol requires a global offset table entry. */
+ switch (r_type)
+ {
+ default:
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT20:
+ case R_390_GOT32:
+ case R_390_GOTENT:
+ tls_type = GOT_NORMAL;
+ break;
+ case R_390_TLS_GD64:
+ tls_type = GOT_TLS_GD;
+ break;
+ case R_390_TLS_IE64:
+ case R_390_TLS_GOTIE64:
+ tls_type = GOT_TLS_IE;
+ break;
+ case R_390_TLS_GOTIE12:
+ case R_390_TLS_GOTIE20:
+ case R_390_TLS_IEENT:
+ tls_type = GOT_TLS_IE_NLT;
+ break;
+ }
+
+ if (h != NULL)
+ {
+ h->got.refcount += 1;
+ old_tls_type = elf_s390_hash_entry(h)->tls_type;
+ }
+ else
+ {
+ local_got_refcounts[r_symndx] += 1;
+ old_tls_type = elf_s390_local_got_tls_type (abfd) [r_symndx];
+ }
+ /* If a TLS symbol is accessed using IE at least once,
+ there is no point to use dynamic model for it. */
+ if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN)
+ {
+ if (old_tls_type == GOT_NORMAL || tls_type == GOT_NORMAL)
+ {
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as normal and thread local symbol"),
+ abfd, h->root.root.string);
+ return FALSE;
+ }
+ if (old_tls_type > tls_type)
+ tls_type = old_tls_type;
+ }
+
+ if (old_tls_type != tls_type)
+ {
+ if (h != NULL)
+ elf_s390_hash_entry (h)->tls_type = tls_type;
+ else
+ elf_s390_local_got_tls_type (abfd) [r_symndx] = tls_type;
+ }
+
+ if (r_type != R_390_TLS_IE64)
+ break;
+ /* Fall through */
+
+ case R_390_TLS_LE64:
+ if (!info->shared)
+ break;
+ info->flags |= DF_STATIC_TLS;
+ /* Fall through */
+
case R_390_8:
case R_390_16:
case R_390_32:
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
- if (h != NULL && !info->shared)
+ if (h != NULL)
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
sections have not yet been mapped to output sections.
Tentatively set the flag for now, and correct in
adjust_dynamic_symbol. */
- h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF;
+ h->non_got_ref = 1;
- /* We may need a .plt entry if the function this reloc
- refers to is in a shared lib. */
- h->plt.refcount += 1;
+ if (!info->shared)
+ {
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a shared lib. */
+ h->plt.refcount += 1;
+ }
}
/* If we are creating a shared library, and this is a reloc
&& ELF64_R_TYPE (rel->r_info) != R_390_PC32DBL
&& ELF64_R_TYPE (rel->r_info) != R_390_PC64)
|| (h != NULL
- && (! info->symbolic
+ && (! SYMBOLIC_BIND (info, h)
|| h->root.type == bfd_link_hash_defweak
- || (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0))))
- || (!info->shared
+ || !h->def_regular))))
+ || (ELIMINATE_COPY_RELOCS
+ && !info->shared
&& (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
- || (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0)))
+ || !h->def_regular)))
{
- struct elf_s390_dyn_relocs *p;
- struct elf_s390_dyn_relocs **head;
+ struct elf_dyn_relocs *p;
+ struct elf_dyn_relocs **head;
/* We must copy these reloc types into the output file.
Create a reloc section in dynobj and make room for
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)
- (_("%s: bad relocation section name `%s\'"),
- bfd_archive_filename (abfd), name);
- }
-
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
- dynobj = htab->elf.dynobj;
- sreloc = bfd_get_section_by_name (dynobj, name);
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, htab->elf.dynobj, 3, abfd, /*rela?*/ TRUE);
+
if (sreloc == NULL)
- {
- flagword flags;
-
- sreloc = bfd_make_section (dynobj, name);
- flags = (SEC_HAS_CONTENTS | SEC_READONLY
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- if ((sec->flags & SEC_ALLOC) != 0)
- flags |= SEC_ALLOC | SEC_LOAD;
- if (sreloc == NULL
- || ! bfd_set_section_flags (dynobj, sreloc, flags)
- || ! bfd_set_section_alignment (dynobj, sreloc, 3))
- return FALSE;
- }
- elf_section_data (sec)->sreloc = sreloc;
+ return FALSE;
}
/* If this is a global symbol, we count the number of
/* Track dynamic relocs needed for local syms too.
We really need local syms available to do this
easily. Oh well. */
-
asection *s;
- s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
- sec, r_symndx);
- if (s == NULL)
+ void *vpp;
+
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
return FALSE;
- head = ((struct elf_s390_dyn_relocs **)
- &elf_section_data (s)->local_dynrel);
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (s == NULL)
+ s = sec;
+
+ vpp = &elf_section_data (s)->local_dynrel;
+ head = (struct elf_dyn_relocs **) vpp;
}
p = *head;
if (p == NULL || p->sec != sec)
{
bfd_size_type amt = sizeof *p;
- p = ((struct elf_s390_dyn_relocs *)
+ p = ((struct elf_dyn_relocs *)
bfd_alloc (htab->elf.dynobj, amt));
if (p == NULL)
return FALSE;
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_390_GNU_VTINHERIT:
- if (!_bfd_elf64_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+ if (!bfd_elf_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_390_GNU_VTENTRY:
- if (!_bfd_elf64_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;
relocation. */
static asection *
-elf_s390_gc_mark_hook (sec, info, rel, h, sym)
- asection *sec;
- struct bfd_link_info *info ATTRIBUTE_UNUSED;
- Elf_Internal_Rela *rel;
- struct elf_link_hash_entry *h;
- Elf_Internal_Sym *sym;
+elf_s390_gc_mark_hook (asection *sec,
+ 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_390_GNU_VTINHERIT:
- case R_390_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);
+ switch (ELF64_R_TYPE (rel->r_info))
+ {
+ case R_390_GNU_VTINHERIT:
+ case R_390_GNU_VTENTRY:
+ return NULL;
+ }
- 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
-elf_s390_gc_sweep_hook (abfd, info, sec, relocs)
- bfd *abfd;
- struct bfd_link_info *info;
- asection *sec;
- const Elf_Internal_Rela *relocs;
+elf_s390_gc_sweep_hook (bfd *abfd,
+ struct bfd_link_info *info,
+ asection *sec,
+ const Elf_Internal_Rela *relocs)
{
+ struct elf_s390_link_hash_table *htab;
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
- unsigned long r_symndx;
- struct elf_link_hash_entry *h;
+
+ if (info->relocatable)
+ return TRUE;
+
+ htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
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);
relend = relocs + sec->reloc_count;
for (rel = relocs; rel < relend; rel++)
- switch (ELF64_R_TYPE (rel->r_info))
- {
- case R_390_GOT12:
- case R_390_GOT16:
- case R_390_GOT32:
- case R_390_GOT64:
- case R_390_GOTOFF:
- case R_390_GOTPC:
- case R_390_GOTPCDBL:
- case R_390_GOTENT:
- r_symndx = ELF64_R_SYM (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info)
- {
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- if (h->got.refcount > 0)
- h->got.refcount -= 1;
- }
- else if (local_got_refcounts != NULL)
- {
- if (local_got_refcounts[r_symndx] > 0)
- local_got_refcounts[r_symndx] -= 1;
- }
- break;
-
- case R_390_8:
- case R_390_12:
- case R_390_16:
- case R_390_32:
- case R_390_64:
- case R_390_PC16:
- case R_390_PC16DBL:
- case R_390_PC32:
- case R_390_PC32DBL:
- case R_390_PC64:
- r_symndx = ELF64_R_SYM (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info)
- {
- struct elf_s390_link_hash_entry *eh;
- struct elf_s390_dyn_relocs **pp;
- struct elf_s390_dyn_relocs *p;
+ {
+ unsigned long r_symndx;
+ unsigned int r_type;
+ struct elf_link_hash_entry *h = NULL;
+
+ r_symndx = ELF64_R_SYM (rel->r_info);
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ struct elf_s390_link_hash_entry *eh;
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
+
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ eh = (struct elf_s390_link_hash_entry *) h;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+ if (p->sec == sec)
+ {
+ /* Everything must go for SEC. */
+ *pp = p->next;
+ break;
+ }
+ }
+ else
+ {
+ Elf_Internal_Sym *isym;
+
+ /* A local symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry *plt = elf_s390_local_plt (abfd);
+ if (plt[r_symndx].plt.refcount > 0)
+ plt[r_symndx].plt.refcount--;
+ }
+ }
+
+ r_type = ELF64_R_TYPE (rel->r_info);
+ r_type = elf_s390_tls_transition (info, r_type, h != NULL);
+ switch (r_type)
+ {
+ case R_390_TLS_LDM64:
+ if (htab->tls_ldm_got.refcount > 0)
+ htab->tls_ldm_got.refcount -= 1;
+ break;
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ case R_390_TLS_GD64:
+ case R_390_TLS_IE64:
+ case R_390_TLS_GOTIE12:
+ case R_390_TLS_GOTIE20:
+ case R_390_TLS_GOTIE64:
+ case R_390_TLS_IEENT:
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT20:
+ case R_390_GOT32:
+ case R_390_GOT64:
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ case R_390_GOTOFF64:
+ case R_390_GOTPC:
+ case R_390_GOTPCDBL:
+ case R_390_GOTENT:
+ if (h != NULL)
+ {
+ if (h->got.refcount > 0)
+ h->got.refcount -= 1;
+ }
+ else if (local_got_refcounts != NULL)
+ {
+ if (local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx] -= 1;
+ }
+ break;
- if (!info->shared && h->plt.refcount > 0)
- h->plt.refcount -= 1;
+ case R_390_8:
+ case R_390_12:
+ case R_390_16:
+ case R_390_20:
+ case R_390_32:
+ case R_390_64:
+ case R_390_PC16:
+ case R_390_PC16DBL:
+ case R_390_PC32:
+ case R_390_PC32DBL:
+ case R_390_PC64:
+ if (info->shared)
+ break;
+ /* Fall through */
- eh = (struct elf_s390_link_hash_entry *) h;
+ case R_390_PLT16DBL:
+ case R_390_PLT32:
+ case R_390_PLT32DBL:
+ case R_390_PLT64:
+ case R_390_PLTOFF16:
+ case R_390_PLTOFF32:
+ case R_390_PLTOFF64:
+ if (h != NULL)
+ {
+ if (h->plt.refcount > 0)
+ h->plt.refcount -= 1;
+ }
+ break;
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
- if (p->sec == sec)
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ if (h != NULL)
+ {
+ if (h->plt.refcount > 0)
{
- if (ELF64_R_TYPE (rel->r_info) == R_390_PC16
- || ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
- || ELF64_R_TYPE (rel->r_info) == R_390_PC32)
- p->pc_count -= 1;
- p->count -= 1;
- if (p->count == 0)
- *pp = p->next;
- break;
+ ((struct elf_s390_link_hash_entry *) h)->gotplt_refcount--;
+ h->plt.refcount -= 1;
}
- }
- break;
-
- case R_390_PLT16DBL:
- case R_390_PLT32:
- case R_390_PLT32DBL:
- case R_390_PLT64:
- r_symndx = ELF64_R_SYM (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info)
- {
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- if (h->plt.refcount > 0)
- h->plt.refcount -= 1;
- }
- break;
+ }
+ else if (local_got_refcounts != NULL)
+ {
+ if (local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx] -= 1;
+ }
+ break;
- default:
- break;
- }
+ default:
+ break;
+ }
+ }
return TRUE;
}
+/* Make sure we emit a GOT entry if the symbol was supposed to have a PLT
+ entry but we found we will not create any. Called when we find we will
+ not have any PLT for this symbol, by for example
+ elf_s390_adjust_dynamic_symbol when we're doing a proper dynamic link,
+ or elf_s390_size_dynamic_sections if no dynamic sections will be
+ created (we're only linking static objects). */
+
+static void
+elf_s390_adjust_gotplt (struct elf_s390_link_hash_entry *h)
+{
+ if (h->elf.root.type == bfd_link_hash_warning)
+ h = (struct elf_s390_link_hash_entry *) h->elf.root.u.i.link;
+
+ if (h->gotplt_refcount <= 0)
+ return;
+
+ /* We simply add the number of gotplt references to the number
+ * of got references for this symbol. */
+ h->elf.got.refcount += h->gotplt_refcount;
+ h->gotplt_refcount = -1;
+}
+
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
understand. */
static bfd_boolean
-elf_s390_adjust_dynamic_symbol (info, h)
- struct bfd_link_info *info;
- struct elf_link_hash_entry *h;
+elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
{
struct elf_s390_link_hash_table *htab;
- struct elf_s390_link_hash_entry * eh;
- struct elf_s390_dyn_relocs *p;
asection *s;
- unsigned int power_of_two;
+
+ /* STT_GNU_IFUNC symbol must go through PLT. */
+ if (s390_is_ifunc_symbol_p (h))
+ return TRUE;
/* If this is a function, put it in the procedure linkage table. We
will fill in the contents of the procedure linkage table later
(although we could actually do it here). */
if (h->type == STT_FUNC
- || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+ || h->needs_plt)
{
if (h->plt.refcount <= 0
- || (! info->shared
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
- && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0
- && h->root.type != bfd_link_hash_undefweak
- && h->root.type != bfd_link_hash_undefined))
+ || SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))
{
/* This case can occur if we saw a PLT32 reloc in an input
file, but the symbol was never referred to by a dynamic
such a case, we don't actually need to build a procedure
linkage table, and we can just do a PC32 reloc instead. */
h->plt.offset = (bfd_vma) -1;
- h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+ h->needs_plt = 0;
+ elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
}
return TRUE;
/* If this is a weak symbol, and there is a real definition, the
processor independent code will have arranged for us to see the
real definition first, and we can just use the same value. */
- if (h->weakdef != NULL)
+ if (h->u.weakdef != NULL)
{
- BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
- || h->weakdef->root.type == bfd_link_hash_defweak);
- h->root.u.def.section = h->weakdef->root.u.def.section;
- h->root.u.def.value = h->weakdef->root.u.def.value;
+ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+ || h->u.weakdef->root.type == bfd_link_hash_defweak);
+ h->root.u.def.section = h->u.weakdef->root.u.def.section;
+ h->root.u.def.value = h->u.weakdef->root.u.def.value;
+ if (ELIMINATE_COPY_RELOCS || info->nocopyreloc)
+ h->non_got_ref = h->u.weakdef->non_got_ref;
return TRUE;
}
/* If there are no references to this symbol that do not use the
GOT, we don't need to generate a copy reloc. */
- if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0)
+ if (!h->non_got_ref)
return TRUE;
/* If -z nocopyreloc was given, we won't generate them either. */
if (info->nocopyreloc)
{
- h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
+ h->non_got_ref = 0;
return TRUE;
}
- eh = (struct elf_s390_link_hash_entry *) h;
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ if (ELIMINATE_COPY_RELOCS)
{
- s = p->sec->output_section;
- if (s != NULL && (s->flags & SEC_READONLY) != 0)
- break;
- }
+ struct elf_s390_link_hash_entry * eh;
+ struct elf_dyn_relocs *p;
- /* If we didn't find any dynamic relocs in read-only sections, then
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */
- if (p == NULL)
- {
- h->elf_link_hash_flags &= ~ELF_LINK_NON_GOT_REF;
- return TRUE;
+ eh = (struct elf_s390_link_hash_entry *) h;
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ s = p->sec->output_section;
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ break;
+ }
+
+ /* If we didn't find any dynamic relocs in read-only sections, then
+ we'll be keeping the dynamic relocs and avoiding the copy reloc. */
+ if (p == NULL)
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
}
/* We must allocate the symbol in our .dynbss section, which will
same memory location for the variable. */
htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
/* We must generate a R_390_COPY reloc to tell the dynamic linker to
copy the initial value out of the dynamic object and into the
runtime process image. */
- if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
{
- htab->srelbss->_raw_size += sizeof (Elf64_External_Rela);
- h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
+ htab->srelbss->size += sizeof (Elf64_External_Rela);
+ h->needs_copy = 1;
}
- /* We need to figure out the alignment required for this symbol. I
- have no idea how ELF linkers handle this. */
- power_of_two = bfd_log2 (h->size);
- if (power_of_two > 3)
- power_of_two = 3;
-
- /* Apply the required alignment. */
s = htab->sdynbss;
- s->_raw_size = BFD_ALIGN (s->_raw_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->_raw_size;
-
- /* Increment the section size to make room for the symbol. */
- s->_raw_size += h->size;
- return TRUE;
+ return _bfd_elf_adjust_dynamic_copy (h, s);
}
-/* This is the condition under which elf_s390_finish_dynamic_symbol
- will be called from elflink.h. If elflink.h doesn't call our
- finish_dynamic_symbol routine, we'll need to do something about
- initializing any .plt and .got entries in elf_s390_relocate_section. */
-#define WILL_CALL_FINISH_DYNAMIC_SYMBOL(DYN, INFO, H) \
- ((DYN) \
- && ((INFO)->shared \
- || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0) \
- && ((H)->dynindx != -1 \
- || ((H)->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0))
-
/* Allocate space in .plt, .got and associated reloc sections for
dynamic relocs. */
static bfd_boolean
-allocate_dynrelocs (h, inf)
- struct elf_link_hash_entry *h;
- PTR inf;
+allocate_dynrelocs (struct elf_link_hash_entry *h,
+ void * inf)
{
struct bfd_link_info *info;
struct elf_s390_link_hash_table *htab;
- struct elf_s390_link_hash_entry *eh;
- struct elf_s390_dyn_relocs *p;
+ struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry *)h;
+ struct elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
- if (h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
info = (struct bfd_link_info *) inf;
htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
- if (htab->elf.dynamic_sections_created
- && h->plt.refcount > 0)
+ /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
+ here if it is defined and referenced in a non-shared object. */
+ if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+ return s390_elf_allocate_ifunc_dyn_relocs (info, h,
+ &eh->dyn_relocs);
+ else if (htab->elf.dynamic_sections_created
+ && h->plt.refcount > 0)
{
/* 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->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+ && !h->forced_local)
{
- if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
- if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info, h))
+ if (info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
- asection *s = htab->splt;
+ asection *s = htab->elf.splt;
/* If this is the first .plt entry, make room for the special
first entry. */
- if (s->_raw_size == 0)
- s->_raw_size += PLT_FIRST_ENTRY_SIZE;
+ if (s->size == 0)
+ s->size += PLT_FIRST_ENTRY_SIZE;
- h->plt.offset = s->_raw_size;
+ h->plt.offset = s->size;
/* If this symbol is not defined in a regular file, and we are
not generating a shared library, then set the symbol to this
pointers compare as equal between the normal executable and
the shared library. */
if (! info->shared
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ && !h->def_regular)
{
h->root.u.def.section = s;
h->root.u.def.value = h->plt.offset;
}
/* Make room for this entry. */
- s->_raw_size += PLT_ENTRY_SIZE;
+ s->size += PLT_ENTRY_SIZE;
/* We also need to make an entry in the .got.plt section, which
will be placed in the .got section by the linker script. */
- htab->sgotplt->_raw_size += GOT_ENTRY_SIZE;
+ htab->elf.sgotplt->size += GOT_ENTRY_SIZE;
/* We also need to make an entry in the .rela.plt section. */
- htab->srelplt->_raw_size += sizeof (Elf64_External_Rela);
+ htab->elf.srelplt->size += sizeof (Elf64_External_Rela);
}
else
{
h->plt.offset = (bfd_vma) -1;
- h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+ h->needs_plt = 0;
+ elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
}
}
else
{
h->plt.offset = (bfd_vma) -1;
- h->elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+ h->needs_plt = 0;
+ elf_s390_adjust_gotplt((struct elf_s390_link_hash_entry *) h);
}
- if (h->got.refcount > 0)
+ /* If R_390_TLS_{IE64,GOTIE64,GOTIE12,IEENT} symbol is now local to
+ the binary, we can optimize a bit. IE64 and GOTIE64 get converted
+ to R_390_TLS_LE64 requiring no TLS entry. For GOTIE12 and IEENT
+ we can save the dynamic TLS relocation. */
+ if (h->got.refcount > 0
+ && !info->shared
+ && h->dynindx == -1
+ && elf_s390_hash_entry(h)->tls_type >= GOT_TLS_IE)
+ {
+ if (elf_s390_hash_entry(h)->tls_type == GOT_TLS_IE_NLT)
+ /* For the GOTIE access without a literal pool entry the offset has
+ to be stored somewhere. The immediate value in the instruction
+ is not bit enough so the value is stored in the got. */
+ {
+ h->got.offset = htab->elf.sgot->size;
+ htab->elf.sgot->size += GOT_ENTRY_SIZE;
+ }
+ else
+ h->got.offset = (bfd_vma) -1;
+ }
+ else if (h->got.refcount > 0)
{
asection *s;
bfd_boolean dyn;
+ int tls_type = elf_s390_hash_entry(h)->tls_type;
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1
- && (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+ && !h->forced_local)
{
- if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
- s = htab->sgot;
- h->got.offset = s->_raw_size;
- s->_raw_size += GOT_ENTRY_SIZE;
+ s = htab->elf.sgot;
+ h->got.offset = s->size;
+ s->size += GOT_ENTRY_SIZE;
+ /* R_390_TLS_GD64 needs 2 consecutive GOT slots. */
+ if (tls_type == GOT_TLS_GD)
+ s->size += GOT_ENTRY_SIZE;
dyn = htab->elf.dynamic_sections_created;
- if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h))
- htab->srelgot->_raw_size += sizeof (Elf64_External_Rela);
+ /* R_390_TLS_IE64 needs one dynamic relocation,
+ R_390_TLS_GD64 needs one if local symbol and two if global. */
+ if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
+ || tls_type >= GOT_TLS_IE)
+ htab->elf.srelgot->size += sizeof (Elf64_External_Rela);
+ else if (tls_type == GOT_TLS_GD)
+ htab->elf.srelgot->size += 2 * sizeof (Elf64_External_Rela);
+ else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
+ && (info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+ htab->elf.srelgot->size += sizeof (Elf64_External_Rela);
}
else
h->got.offset = (bfd_vma) -1;
- eh = (struct elf_s390_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return TRUE;
if (info->shared)
{
- if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
- && ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0
- || info->symbolic))
+ if (SYMBOL_CALLS_LOCAL (info, h))
{
- struct elf_s390_dyn_relocs **pp;
+ struct elf_dyn_relocs **pp;
for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
{
pp = &p->next;
}
}
+
+ /* Also discard relocs on undefined weak syms with non-default
+ visibility. */
+ if (eh->dyn_relocs != NULL
+ && h->root.type == bfd_link_hash_undefweak)
+ {
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ eh->dyn_relocs = NULL;
+
+ /* 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;
+ }
+ }
}
- else
+ else if (ELIMINATE_COPY_RELOCS)
{
/* For the non-shared case, discard space for relocs against
symbols which turn out to need copy relocs or are not
dynamic. */
- if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
- && (((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ if (!h->non_got_ref
+ && ((h->def_dynamic
+ && !h->def_regular)
|| (htab->elf.dynamic_sections_created
&& (h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_undefined))))
/* 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->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+ && !h->forced_local)
{
- if (! bfd_elf64_link_record_dynamic_symbol (info, h))
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
- sreloc->_raw_size += p->count * sizeof (Elf64_External_Rela);
+ sreloc->size += p->count * sizeof (Elf64_External_Rela);
}
return TRUE;
/* Find any dynamic relocs that apply to read-only sections. */
static bfd_boolean
-readonly_dynrelocs (h, inf)
- struct elf_link_hash_entry *h;
- PTR inf;
+readonly_dynrelocs (struct elf_link_hash_entry *h, void * inf)
{
struct elf_s390_link_hash_entry *eh;
- struct elf_s390_dyn_relocs *p;
-
- if (h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ struct elf_dyn_relocs *p;
eh = (struct elf_s390_link_hash_entry *) h;
for (p = eh->dyn_relocs; p != NULL; p = p->next)
/* Set the sizes of the dynamic sections. */
static bfd_boolean
-elf_s390_size_dynamic_sections (output_bfd, info)
- bfd *output_bfd ATTRIBUTE_UNUSED;
- struct bfd_link_info *info;
+elf_s390_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info)
{
struct elf_s390_link_hash_table *htab;
bfd *dynobj;
bfd *ibfd;
htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
dynobj = htab->elf.dynobj;
if (dynobj == NULL)
abort ();
if (htab->elf.dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
- if (! info->shared)
+ if (info->executable)
{
- s = bfd_get_section_by_name (dynobj, ".interp");
+ s = bfd_get_linker_section (dynobj, ".interp");
if (s == NULL)
abort ();
- s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER;
+ s->size = sizeof ELF_DYNAMIC_INTERPRETER;
s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
}
}
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
+ char *local_tls_type;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srela;
+ struct plt_entry *local_plt;
+ unsigned int i;
- if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+ if (! is_s390_elf (ibfd))
continue;
for (s = ibfd->sections; s != NULL; s = s->next)
{
- struct elf_s390_dyn_relocs *p;
+ struct elf_dyn_relocs *p;
- for (p = *((struct elf_s390_dyn_relocs **)
- &elf_section_data (s)->local_dynrel);
- p != NULL;
- p = p->next)
+ for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
{
if (!bfd_is_abs_section (p->sec)
&& bfd_is_abs_section (p->sec->output_section))
else if (p->count != 0)
{
srela = elf_section_data (p->sec)->sreloc;
- srela->_raw_size += p->count * sizeof (Elf64_External_Rela);
+ srela->size += p->count * sizeof (Elf64_External_Rela);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
}
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;
- s = htab->sgot;
- srela = htab->srelgot;
- for (; local_got < end_local_got; ++local_got)
+ local_tls_type = elf_s390_local_got_tls_type (ibfd);
+ s = htab->elf.sgot;
+ srela = htab->elf.srelgot;
+ for (; local_got < end_local_got; ++local_got, ++local_tls_type)
{
if (*local_got > 0)
{
- *local_got = s->_raw_size;
- s->_raw_size += GOT_ENTRY_SIZE;
+ *local_got = s->size;
+ s->size += GOT_ENTRY_SIZE;
+ if (*local_tls_type == GOT_TLS_GD)
+ s->size += GOT_ENTRY_SIZE;
if (info->shared)
- srela->_raw_size += sizeof (Elf64_External_Rela);
+ srela->size += sizeof (Elf64_External_Rela);
}
else
*local_got = (bfd_vma) -1;
}
+
+ local_plt = elf_s390_local_plt (ibfd);
+ for (i = 0; i < symtab_hdr->sh_info; i++)
+ {
+ if (local_plt[i].plt.refcount > 0)
+ {
+ local_plt[i].plt.offset = htab->elf.iplt->size;
+ htab->elf.iplt->size += PLT_ENTRY_SIZE;
+ htab->elf.igotplt->size += GOT_ENTRY_SIZE;
+ htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+ }
+ else
+ local_plt[i].plt.offset = (bfd_vma) -1;
+ }
}
+ if (htab->tls_ldm_got.refcount > 0)
+ {
+ /* Allocate 2 got entries and 1 dynamic reloc for R_390_TLS_LDM64
+ relocs. */
+ htab->tls_ldm_got.offset = htab->elf.sgot->size;
+ htab->elf.sgot->size += 2 * GOT_ENTRY_SIZE;
+ htab->elf.srelgot->size += sizeof (Elf64_External_Rela);
+ }
+ else
+ htab->tls_ldm_got.offset = -1;
+
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
- elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
- if (s == htab->splt
- || s == htab->sgot
- || s == htab->sgotplt)
+ if (s == htab->elf.splt
+ || s == htab->elf.sgot
+ || s == htab->elf.sgotplt
+ || s == htab->sdynbss
+ || s == htab->elf.iplt
+ || s == htab->elf.igotplt
+ || s == htab->irelifunc)
{
/* 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->_raw_size != 0 && s != htab->srelplt)
+ if (s->size != 0 && s != htab->elf.srelplt)
relocs = TRUE;
/* We use the reloc_count field as a counter if we need
continue;
}
- if (s->_raw_size == 0)
+ if (s->size == 0)
{
/* If we don't need this section, strip it from the
output file. This is to handle .rela.bss and
function which decides whether anything needs to go
into these sections. */
- _bfd_strip_section_from_output (info, s);
+ s->flags |= SEC_EXCLUDE;
continue;
}
+ if ((s->flags & SEC_HAS_CONTENTS) == 0)
+ continue;
+
/* Allocate memory for the section contents. We use bfd_zalloc
here in case unused entries are not reclaimed before the
section's contents are written out. This should not happen,
but this way if it does, we get a R_390_NONE reloc instead
of garbage. */
- s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->_raw_size);
+ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
if (s->contents == NULL)
return FALSE;
}
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))
+ _bfd_elf_add_dynamic_entry (info, TAG, VAL)
- if (! info->shared)
+ if (info->executable)
{
if (!add_dynamic_entry (DT_DEBUG, 0))
return FALSE;
}
- if (htab->splt->_raw_size != 0)
+ if (htab->elf.splt->size != 0)
{
if (!add_dynamic_entry (DT_PLTGOT, 0)
|| !add_dynamic_entry (DT_PLTRELSZ, 0)
then we need a DT_TEXTREL entry. */
if ((info->flags & DF_TEXTREL) == 0)
elf_link_hash_traverse (&htab->elf, readonly_dynrelocs,
- (PTR) info);
+ info);
if ((info->flags & DF_TEXTREL) != 0)
{
return TRUE;
}
+/* Return the base VMA address which should be subtracted from real addresses
+ when resolving @dtpoff relocation.
+ This is PT_TLS segment p_vaddr. */
+
+static bfd_vma
+dtpoff_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* Return the relocation value for @tpoff relocation
+ if STT_TLS virtual address is ADDRESS. */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (htab->tls_sec == NULL)
+ return 0;
+ return htab->tls_size + htab->tls_sec->vma - address;
+}
+
+/* Complain if TLS instruction relocation is against an invalid
+ instruction. */
+
+static void
+invalid_tls_insn (bfd *input_bfd,
+ asection *input_section,
+ Elf_Internal_Rela *rel)
+{
+ reloc_howto_type *howto;
+
+ howto = elf_howto_table + ELF64_R_TYPE (rel->r_info);
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): invalid instruction for TLS relocation %s"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ howto->name);
+ bfd_set_error (bfd_error_bad_value);
+}
+
/* Relocate a 390 ELF section. */
static bfd_boolean
-elf_s390_relocate_section (output_bfd, info, input_bfd, input_section,
- contents, relocs, local_syms, local_sections)
- bfd *output_bfd;
- struct bfd_link_info *info;
- bfd *input_bfd;
- asection *input_section;
- bfd_byte *contents;
- Elf_Internal_Rela *relocs;
- Elf_Internal_Sym *local_syms;
- asection **local_sections;
+elf_s390_relocate_section (bfd *output_bfd,
+ struct bfd_link_info *info,
+ bfd *input_bfd,
+ asection *input_section,
+ bfd_byte *contents,
+ Elf_Internal_Rela *relocs,
+ Elf_Internal_Sym *local_syms,
+ asection **local_sections)
{
struct elf_s390_link_hash_table *htab;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
- if (info->relocateable)
- return TRUE;
+ BFD_ASSERT (is_s390_elf (input_bfd));
htab = elf_s390_hash_table (info);
- symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ if (htab == NULL)
+ return FALSE;
+
+ symtab_hdr = &elf_symtab_hdr (input_bfd);
sym_hashes = elf_sym_hashes (input_bfd);
local_got_offsets = elf_local_got_offsets (input_bfd);
relend = relocs + input_section->reloc_count;
for (; rel < relend; rel++)
{
- int r_type;
+ unsigned int r_type;
reloc_howto_type *howto;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
bfd_vma relocation;
bfd_boolean unresolved_reloc;
bfd_reloc_status_type r;
+ int tls_type;
+ asection *base_got = htab->elf.sgot;
r_type = ELF64_R_TYPE (rel->r_info);
if (r_type == (int) R_390_GNU_VTINHERIT
|| r_type == (int) R_390_GNU_VTENTRY)
continue;
- if (r_type < 0 || r_type >= (int) R_390_max)
+ if (r_type >= (int) R_390_max)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
howto = elf_howto_table + r_type;
r_symndx = ELF64_R_SYM (rel->r_info);
+
h = NULL;
sym = NULL;
sec = NULL;
{
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
- relocation = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
- }
- else
- {
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
- if (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
{
- sec = h->root.u.def.section;
- if (sec->output_section == NULL)
+ struct plt_entry *local_plt = elf_s390_local_plt (input_bfd);
+ if (local_plt == NULL)
+ return FALSE;
+
+ /* Address of the PLT slot. */
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + local_plt[r_symndx].plt.offset);
+
+ switch (r_type)
{
- /* Set a flag that will be cleared later if we find a
- relocation value for this symbol. output_section
- is typically NULL for symbols satisfied by a shared
- library. */
- unresolved_reloc = TRUE;
- relocation = 0;
+ case R_390_PLTOFF16:
+ case R_390_PLTOFF32:
+ case R_390_PLTOFF64:
+ relocation -= htab->elf.sgot->output_section->vma;
+ break;
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT20:
+ case R_390_GOT32:
+ case R_390_GOT64:
+ case R_390_GOTENT:
+ {
+ /* Write the PLT slot address into the GOT slot. */
+ bfd_put_64 (output_bfd, relocation,
+ htab->elf.sgot->contents +
+ local_got_offsets[r_symndx]);
+ relocation = (local_got_offsets[r_symndx] +
+ htab->elf.sgot->output_offset);
+
+ if (r_type == R_390_GOTENT || r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ break;
+ }
+ default:
+ break;
}
- else
- relocation = (h->root.u.def.value
- + sec->output_section->vma
- + sec->output_offset);
+ /* The output section is needed later in
+ finish_dynamic_section when creating the dynamic
+ relocation. */
+ local_plt[r_symndx].sec = sec;
+ goto do_relocation;
}
- else if (h->root.type == bfd_link_hash_undefweak)
- relocation = 0;
- else if (info->shared
- && (!info->symbolic || info->allow_shlib_undefined)
- && !info->no_undefined
- && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
- relocation = 0;
else
- {
- if (! ((*info->callbacks->undefined_symbol)
- (info, h->root.root.string, input_bfd,
- input_section, rel->r_offset,
- (!info->shared || info->no_undefined
- || ELF_ST_VISIBILITY (h->other)))))
- return FALSE;
- relocation = 0;
- }
+ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
}
+ else
+ {
+ bfd_boolean warned ATTRIBUTE_UNUSED;
+
+ RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+ r_symndx, symtab_hdr, sym_hashes,
+ h, sec, relocation,
+ unresolved_reloc, warned);
+ }
+
+ if (sec != NULL && discarded_section (sec))
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+ rel, 1, relend, howto, 0, contents);
+
+ if (info->relocatable)
+ continue;
switch (r_type)
{
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ /* There are three cases for a GOTPLT relocation. 1) The
+ relocation is against the jump slot entry of a plt that
+ will get emitted to the output file. 2) The relocation
+ is against the jump slot of a plt entry that has been
+ removed. elf_s390_adjust_gotplt has created a GOT entry
+ as replacement. 3) The relocation is against a local symbol.
+ Cases 2) and 3) are the same as the GOT relocation code
+ so we just have to test for case 1 and fall through for
+ the other two. */
+ if (h != NULL && h->plt.offset != (bfd_vma) -1)
+ {
+ bfd_vma plt_index;
+
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+ relocation = (plt_index * GOT_ENTRY_SIZE +
+ htab->elf.igotplt->output_offset);
+ if (r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.igotplt->output_section->vma;
+ }
+ else
+ {
+ /* Calc. index no.
+ Current offset - size first entry / entry size. */
+ plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
+ PLT_ENTRY_SIZE;
+
+ /* Offset in GOT is PLT index plus GOT headers(3)
+ times 4, addr & GOT addr. */
+ relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+ if (r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ }
+ unresolved_reloc = FALSE;
+ break;
+ }
+ /* Fall through. */
+
case R_390_GOT12:
case R_390_GOT16:
+ case R_390_GOT20:
case R_390_GOT32:
case R_390_GOT64:
case R_390_GOTENT:
/* Relocation is to the entry for this symbol in the global
offset table. */
- if (htab->sgot == NULL)
+ if (base_got == NULL)
abort ();
if (h != NULL)
off = h->got.offset;
dyn = htab->elf.dynamic_sections_created;
- if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info, h)
- || (info->shared
- && (info->symbolic
- || h->dynindx == -1
- || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL))
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
+ if (off == (bfd_vma)-1)
+ {
+ /* No explicit GOT usage so redirect to the
+ got.iplt slot. */
+ base_got = htab->elf.igotplt;
+ off = h->plt.offset / PLT_ENTRY_SIZE * GOT_ENTRY_SIZE;
+ }
+ else
+ {
+ /* Explicit GOT slots must contain the address
+ of the PLT slot. This will be handled in
+ finish_dynamic_symbol. */
+ }
+ }
+ else if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ || (info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ || (ELF_ST_VISIBILITY (h->other)
+ && h->root.type == bfd_link_hash_undefweak))
{
/* This is actually a static link, or it is a
-Bsymbolic link and the symbol is defined
else
{
bfd_put_64 (output_bfd, relocation,
- htab->sgot->contents + off);
+ base_got->contents + off);
h->got.offset |= 1;
}
}
else
{
bfd_put_64 (output_bfd, relocation,
- htab->sgot->contents + off);
+ htab->elf.sgot->contents + off);
if (info->shared)
{
Elf_Internal_Rela outrel;
bfd_byte *loc;
- s = htab->srelgot;
+ s = htab->elf.srelgot;
if (s == NULL)
abort ();
- outrel.r_offset = (htab->sgot->output_section->vma
- + htab->sgot->output_offset
+ outrel.r_offset = (htab->elf.sgot->output_section->vma
+ + htab->elf.sgot->output_offset
+ off);
outrel.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
outrel.r_addend = relocation;
if (off >= (bfd_vma) -2)
abort ();
- relocation = htab->sgot->output_offset + off;
+ relocation = base_got->output_offset + off;
- /*
- * For @GOTENT the relocation is against the offset between
- * the instruction and the symbols entry in the GOT and not
- * between the start of the GOT and the symbols entry. We
- * add the vma of the GOT to get the correct value.
- */
- if (r_type == R_390_GOTENT)
- relocation += htab->sgot->output_section->vma;
+ /* For @GOTENT the relocation is against the offset between
+ the instruction and the symbols entry in the GOT and not
+ between the start of the GOT and the symbols entry. We
+ add the vma of the GOT to get the correct value. */
+ if ( r_type == R_390_GOTENT
+ || r_type == R_390_GOTPLTENT)
+ relocation += base_got->output_section->vma;
break;
- case R_390_GOTOFF:
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ case R_390_GOTOFF64:
/* Relocation is relative to the start of the global offset
table. */
defined _GLOBAL_OFFSET_TABLE in a different way, as is
permitted by the ABI, we might have to change this
calculation. */
- relocation -= htab->sgot->output_section->vma;
-
+ relocation -= htab->elf.sgot->output_section->vma;
break;
case R_390_GOTPC:
case R_390_GOTPCDBL:
/* Use global offset table as symbol value. */
- relocation = htab->sgot->output_section->vma;
+ relocation = htab->elf.sgot->output_section->vma;
unresolved_reloc = FALSE;
break;
break;
if (h->plt.offset == (bfd_vma) -1
- || htab->splt == NULL)
+ || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
{
/* We didn't make a PLT entry for this symbol. This
happens when statically linking PIC code, or when
using -Bsymbolic. */
break;
}
+ if (s390_is_ifunc_symbol_p (h))
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset);
+ else
+ relocation = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset);
+ unresolved_reloc = FALSE;
+ break;
+
+ case R_390_PLTOFF16:
+ case R_390_PLTOFF32:
+ case R_390_PLTOFF64:
+ /* Relocation is to the entry for this symbol in the
+ procedure linkage table relative to the start of the GOT. */
+
+ /* For local symbols or if we didn't make a PLT entry for
+ this symbol resolve the symbol directly. */
+ if (h == NULL
+ || h->plt.offset == (bfd_vma) -1
+ || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
+ {
+ relocation -= htab->elf.sgot->output_section->vma;
+ break;
+ }
- relocation = (htab->splt->output_section->vma
- + htab->splt->output_offset
- + h->plt.offset);
+ if (s390_is_ifunc_symbol_p (h))
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset
+ - htab->elf.sgot->output_section->vma);
+ else
+ relocation = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset
+ - htab->elf.sgot->output_section->vma);
unresolved_reloc = FALSE;
break;
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
- /* 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 (h != NULL
+ && s390_is_ifunc_symbol_p (h)
+ && h->def_regular)
+ {
+ if (!info->shared || !h->non_got_ref)
+ {
+ /* For a non-shared object STT_GNU_IFUNC symbol must
+ go through PLT. */
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h ->plt.offset);
+ goto do_relocation;
+ }
+ else
+ {
+ /* For shared objects a runtime relocation is needed. */
+
+ Elf_Internal_Rela outrel;
+ asection *sreloc;
+
+ /* Need a dynamic relocation to get the real function
+ address. */
+ outrel.r_offset = _bfd_elf_section_offset (output_bfd,
+ info,
+ input_section,
+ rel->r_offset);
+ if (outrel.r_offset == (bfd_vma) -1
+ || outrel.r_offset == (bfd_vma) -2)
+ abort ();
+
+ outrel.r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->executable)
+ {
+ /* This symbol is resolved locally. */
+ outrel.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+ outrel.r_addend = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
+ outrel.r_addend = 0;
+ }
+
+ sreloc = htab->elf.irelifunc;
+ elf_append_rela (output_bfd, sreloc, &outrel);
+
+ /* If this reloc is against an external symbol, we
+ do not want to fiddle with the addend. Otherwise,
+ we need to include the symbol value so that it
+ becomes an addend for the dynamic reloc. For an
+ internal symbol, we have updated addend. */
+ continue;
+ }
+ }
+
+ 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_390_PC16
&& r_type != R_390_PC16DBL
&& r_type != R_390_PC32
&& r_type != R_390_PC32DBL
&& r_type != R_390_PC64)
- || (h != NULL
- && h->dynindx != -1
- && (! info->symbolic
- || (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0))))
- || (!info->shared
+ || !SYMBOL_CALLS_LOCAL (info, h)))
+ || (ELIMINATE_COPY_RELOCS
+ && !info->shared
&& h != NULL
&& h->dynindx != -1
- && (h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
- && (((h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_DYNAMIC) != 0
- && (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0)
+ && !h->non_got_ref
+ && ((h->def_dynamic
+ && !h->def_regular)
|| h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_undefined)))
{
/* When generating a shared object, these relocations
are copied into the output file to be resolved at run
time. */
-
skip = FALSE;
relocate = FALSE;
|| r_type == R_390_PC32DBL
|| r_type == R_390_PC64
|| !info->shared
- || !info->symbolic
- || (h->elf_link_hash_flags
- & ELF_LINK_HASH_DEF_REGULAR) == 0))
+ || !SYMBOLIC_BIND (info, h)
+ || !h->def_regular))
{
outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
else
{
/* This symbol is local, or marked to become local. */
- relocate = TRUE;
- outrel.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
outrel.r_addend = relocation + rel->r_addend;
+ if (r_type == R_390_64)
+ {
+ relocate = TRUE;
+ outrel.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
+ }
+ else
+ {
+ long sindx;
+
+ if (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;
+
+ if (sindx == 0)
+ {
+ osec = htab->elf.text_index_section;
+ sindx = elf_section_data (osec)->dynindx;
+ }
+ BFD_ASSERT (sindx != 0);
+
+ /* We are turning this relocation into one
+ against a section symbol, so subtract out
+ the output section's address but not the
+ offset of the input section in the output
+ section. */
+ outrel.r_addend -= osec->vma;
+ }
+ outrel.r_info = ELF64_R_INFO (sindx, r_type);
+ }
}
sreloc = elf_section_data (input_section)->sreloc;
break;
+ /* Relocations for tls literal pool entries. */
+ case R_390_TLS_IE64:
+ if (info->shared)
+ {
+ Elf_Internal_Rela outrel;
+ asection *sreloc;
+ bfd_byte *loc;
+
+ outrel.r_offset = rel->r_offset
+ + input_section->output_section->vma
+ + input_section->output_offset;
+ outrel.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
+ sreloc = elf_section_data (input_section)->sreloc;
+ if (sreloc == NULL)
+ abort ();
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloc_out (output_bfd, &outrel, loc);
+ }
+ /* Fall through. */
+
+ case R_390_TLS_GD64:
+ case R_390_TLS_GOTIE64:
+ r_type = elf_s390_tls_transition (info, r_type, h == NULL);
+ tls_type = GOT_UNKNOWN;
+ if (h == NULL && local_got_offsets)
+ tls_type = elf_s390_local_got_tls_type (input_bfd) [r_symndx];
+ else if (h != NULL)
+ {
+ tls_type = elf_s390_hash_entry(h)->tls_type;
+ if (!info->shared && h->dynindx == -1 && tls_type >= GOT_TLS_IE)
+ r_type = R_390_TLS_LE64;
+ }
+ if (r_type == R_390_TLS_GD64 && tls_type >= GOT_TLS_IE)
+ r_type = R_390_TLS_IE64;
+
+ if (r_type == R_390_TLS_LE64)
+ {
+ /* This relocation gets optimized away by the local exec
+ access optimization. */
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_64 (output_bfd, -tpoff (info, relocation),
+ contents + rel->r_offset);
+ continue;
+ }
+
+ if (htab->elf.sgot == NULL)
+ abort ();
+
+ if (h != NULL)
+ off = h->got.offset;
+ else
+ {
+ if (local_got_offsets == NULL)
+ abort ();
+
+ off = local_got_offsets[r_symndx];
+ }
+
+ emit_tls_relocs:
+
+ if ((off & 1) != 0)
+ off &= ~1;
+ else
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+ int dr_type, indx;
+
+ if (htab->elf.srelgot == NULL)
+ abort ();
+
+ outrel.r_offset = (htab->elf.sgot->output_section->vma
+ + htab->elf.sgot->output_offset + off);
+
+ indx = h && h->dynindx != -1 ? h->dynindx : 0;
+ if (r_type == R_390_TLS_GD64)
+ dr_type = R_390_TLS_DTPMOD;
+ else
+ dr_type = R_390_TLS_TPOFF;
+ if (dr_type == R_390_TLS_TPOFF && indx == 0)
+ outrel.r_addend = relocation - dtpoff_base (info);
+ else
+ outrel.r_addend = 0;
+ outrel.r_info = ELF64_R_INFO (indx, dr_type);
+ loc = htab->elf.srelgot->contents;
+ loc += htab->elf.srelgot->reloc_count++
+ * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+
+ if (r_type == R_390_TLS_GD64)
+ {
+ if (indx == 0)
+ {
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_64 (output_bfd,
+ relocation - dtpoff_base (info),
+ htab->elf.sgot->contents + off + GOT_ENTRY_SIZE);
+ }
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (indx, R_390_TLS_DTPOFF);
+ outrel.r_offset += GOT_ENTRY_SIZE;
+ outrel.r_addend = 0;
+ htab->elf.srelgot->reloc_count++;
+ loc += sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ }
+ }
+
+ if (h != NULL)
+ h->got.offset |= 1;
+ else
+ local_got_offsets[r_symndx] |= 1;
+ }
+
+ if (off >= (bfd_vma) -2)
+ abort ();
+ if (r_type == ELF64_R_TYPE (rel->r_info))
+ {
+ relocation = htab->elf.sgot->output_offset + off;
+ if (r_type == R_390_TLS_IE64 || r_type == R_390_TLS_IEENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ unresolved_reloc = FALSE;
+ }
+ else
+ {
+ bfd_put_64 (output_bfd, htab->elf.sgot->output_offset + off,
+ contents + rel->r_offset);
+ continue;
+ }
+ break;
+
+ case R_390_TLS_GOTIE12:
+ case R_390_TLS_GOTIE20:
+ case R_390_TLS_IEENT:
+ if (h == NULL)
+ {
+ if (local_got_offsets == NULL)
+ abort();
+ off = local_got_offsets[r_symndx];
+ if (info->shared)
+ goto emit_tls_relocs;
+ }
+ else
+ {
+ off = h->got.offset;
+ tls_type = elf_s390_hash_entry(h)->tls_type;
+ if (info->shared || h->dynindx != -1 || tls_type < GOT_TLS_IE)
+ goto emit_tls_relocs;
+ }
+
+ if (htab->elf.sgot == NULL)
+ abort ();
+
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_64 (output_bfd, -tpoff (info, relocation),
+ htab->elf.sgot->contents + off);
+ relocation = htab->elf.sgot->output_offset + off;
+ if (r_type == R_390_TLS_IEENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ unresolved_reloc = FALSE;
+ break;
+
+ case R_390_TLS_LDM64:
+ if (! info->shared)
+ /* The literal pool entry this relocation refers to gets ignored
+ by the optimized code of the local exec model. Do nothing
+ and the value will turn out zero. */
+ continue;
+
+ if (htab->elf.sgot == NULL)
+ abort ();
+
+ off = htab->tls_ldm_got.offset;
+ if (off & 1)
+ off &= ~1;
+ else
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+
+ if (htab->elf.srelgot == NULL)
+ abort ();
+
+ outrel.r_offset = (htab->elf.sgot->output_section->vma
+ + htab->elf.sgot->output_offset + off);
+
+ bfd_put_64 (output_bfd, 0,
+ htab->elf.sgot->contents + off + GOT_ENTRY_SIZE);
+ outrel.r_info = ELF64_R_INFO (0, R_390_TLS_DTPMOD);
+ outrel.r_addend = 0;
+ loc = htab->elf.srelgot->contents;
+ loc += htab->elf.srelgot->reloc_count++
+ * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ htab->tls_ldm_got.offset |= 1;
+ }
+ relocation = htab->elf.sgot->output_offset + off;
+ unresolved_reloc = FALSE;
+ break;
+
+ case R_390_TLS_LE64:
+ if (info->shared)
+ {
+ /* Linking a shared library with non-fpic code requires
+ a R_390_TLS_TPOFF relocation. */
+ Elf_Internal_Rela outrel;
+ asection *sreloc;
+ bfd_byte *loc;
+ int indx;
+
+ outrel.r_offset = rel->r_offset
+ + input_section->output_section->vma
+ + input_section->output_offset;
+ if (h != NULL && h->dynindx != -1)
+ indx = h->dynindx;
+ else
+ indx = 0;
+ outrel.r_info = ELF64_R_INFO (indx, R_390_TLS_TPOFF);
+ if (indx == 0)
+ outrel.r_addend = relocation - dtpoff_base (info);
+ else
+ outrel.r_addend = 0;
+ sreloc = elf_section_data (input_section)->sreloc;
+ if (sreloc == NULL)
+ abort ();
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+ }
+ else
+ {
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_64 (output_bfd, -tpoff (info, relocation),
+ contents + rel->r_offset);
+ }
+ continue;
+
+ case R_390_TLS_LDO64:
+ if (info->shared || (input_section->flags & SEC_DEBUGGING))
+ relocation -= dtpoff_base (info);
+ else
+ /* When converting LDO to LE, we must negate. */
+ relocation = -tpoff (info, relocation);
+ break;
+
+ /* Relocations for tls instructions. */
+ case R_390_TLS_LOAD:
+ case R_390_TLS_GDCALL:
+ case R_390_TLS_LDCALL:
+ tls_type = GOT_UNKNOWN;
+ if (h == NULL && local_got_offsets)
+ tls_type = elf_s390_local_got_tls_type (input_bfd) [r_symndx];
+ else if (h != NULL)
+ tls_type = elf_s390_hash_entry(h)->tls_type;
+
+ if (tls_type == GOT_TLS_GD)
+ continue;
+
+ if (r_type == R_390_TLS_LOAD)
+ {
+ if (!info->shared && (h == NULL || h->dynindx == -1))
+ {
+ /* IE->LE transition. Four valid cases:
+ lg %rx,(0,%ry) -> sllg %rx,%ry,0
+ lg %rx,(%ry,0) -> sllg %rx,%ry,0
+ lg %rx,(%ry,%r12) -> sllg %rx,%ry,0
+ lg %rx,(%r12,%ry) -> sllg %rx,%ry,0 */
+ unsigned int insn0, insn1, ry;
+
+ insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
+ if (insn1 != 0x0004)
+ invalid_tls_insn (input_bfd, input_section, rel);
+ ry = 0;
+ if ((insn0 & 0xff00f000) == 0xe3000000)
+ /* lg %rx,0(%ry,0) -> sllg %rx,%ry,0 */
+ ry = (insn0 & 0x000f0000);
+ else if ((insn0 & 0xff0f0000) == 0xe3000000)
+ /* lg %rx,0(0,%ry) -> sllg %rx,%ry,0 */
+ ry = (insn0 & 0x0000f000) << 4;
+ else if ((insn0 & 0xff00f000) == 0xe300c000)
+ /* lg %rx,0(%ry,%r12) -> sllg %rx,%ry,0 */
+ ry = (insn0 & 0x000f0000);
+ else if ((insn0 & 0xff0f0000) == 0xe30c0000)
+ /* lg %rx,0(%r12,%ry) -> sllg %rx,%ry,0 */
+ ry = (insn0 & 0x0000f000) << 4;
+ else
+ invalid_tls_insn (input_bfd, input_section, rel);
+ insn0 = 0xeb000000 | (insn0 & 0x00f00000) | ry;
+ insn1 = 0x000d;
+ bfd_put_32 (output_bfd, insn0, contents + rel->r_offset);
+ bfd_put_16 (output_bfd, insn1, contents + rel->r_offset + 4);
+ }
+ }
+ else if (r_type == R_390_TLS_GDCALL)
+ {
+ unsigned int insn0, insn1;
+
+ insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn0 & 0xffff0000) != 0xc0e50000)
+ invalid_tls_insn (input_bfd, input_section, rel);
+ if (!info->shared && (h == NULL || h->dynindx == -1))
+ {
+ /* GD->LE transition.
+ brasl %r14,__tls_get_addr@plt -> brcl 0,. */
+ insn0 = 0xc0040000;
+ insn1 = 0x0000;
+ }
+ else
+ {
+ /* GD->IE transition.
+ brasl %r14,__tls_get_addr@plt -> lg %r2,0(%r2,%r12) */
+ insn0 = 0xe322c000;
+ insn1 = 0x0004;
+ }
+ bfd_put_32 (output_bfd, insn0, contents + rel->r_offset);
+ bfd_put_16 (output_bfd, insn1, contents + rel->r_offset + 4);
+ }
+ else if (r_type == R_390_TLS_LDCALL)
+ {
+ if (!info->shared)
+ {
+ unsigned int insn0, insn1;
+
+ insn0 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn1 = bfd_get_16 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn0 & 0xffff0000) != 0xc0e50000)
+ invalid_tls_insn (input_bfd, input_section, rel);
+ /* LD->LE transition.
+ brasl %r14,__tls_get_addr@plt -> brcl 0,. */
+ insn0 = 0xc0040000;
+ insn1 = 0x0000;
+ bfd_put_32 (output_bfd, insn0, contents + rel->r_offset);
+ bfd_put_16 (output_bfd, insn1, contents + rel->r_offset + 4);
+ }
+ }
+ continue;
+
default:
break;
}
not process them. */
if (unresolved_reloc
&& !((input_section->flags & SEC_DEBUGGING) != 0
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0))
+ && h->def_dynamic)
+ && _bfd_elf_section_offset (output_bfd, info, input_section,
+ rel->r_offset) != (bfd_vma) -1)
(*_bfd_error_handler)
- (_("%s(%s+0x%lx): unresolvable relocation against symbol `%s'"),
- bfd_archive_filename (input_bfd),
- bfd_get_section_name (input_bfd, input_section),
+ (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+ input_bfd,
+ input_section,
(long) rel->r_offset,
+ howto->name,
h->root.root.string);
- r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ do_relocation:
+
+ if (r_type == R_390_20
+ || r_type == R_390_GOT20
+ || r_type == R_390_GOTPLT20
+ || r_type == R_390_TLS_GOTIE20)
+ {
+ relocation += rel->r_addend;
+ relocation = (relocation&0xfff) << 8 | (relocation&0xff000) >> 12;
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset,
+ relocation, 0);
+ }
+ else
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
contents, rel->r_offset,
relocation, rel->r_addend);
{
if (! ((*info->callbacks->reloc_overflow)
- (info, name, howto->name, (bfd_vma) 0,
- input_bfd, input_section, rel->r_offset)))
+ (info, (h ? &h->root : NULL), name, howto->name,
+ (bfd_vma) 0, input_bfd, input_section,
+ rel->r_offset)))
return FALSE;
}
else
{
(*_bfd_error_handler)
- (_("%s(%s+0x%lx): reloc against `%s': error %d"),
- bfd_archive_filename (input_bfd),
- bfd_get_section_name (input_bfd, input_section),
+ (_("%B(%A+0x%lx): reloc against `%s': error %d"),
+ input_bfd, input_section,
(long) rel->r_offset, name, (int) r);
return FALSE;
}
return TRUE;
}
+/* Generate the PLT slots together with the dynamic relocations needed
+ for IFUNC symbols. */
+
+static void
+elf_s390_finish_ifunc_symbol (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ struct elf_s390_link_hash_table *htab,
+ bfd_vma plt_offset,
+ bfd_vma resolver_address)
+{
+ bfd_vma plt_index;
+ bfd_vma got_offset;
+ Elf_Internal_Rela rela;
+ bfd_byte *loc;
+ asection *plt, *gotplt, *relplt;
+
+ if (htab->elf.iplt == NULL
+ || htab->elf.igotplt == NULL
+ || htab->elf.irelplt == NULL)
+ abort ();
+
+ /* Index of the PLT slot within iplt section. */
+ plt_index = plt_offset / PLT_ENTRY_SIZE;
+ plt = htab->elf.iplt;
+ /* Offset into the igot.plt section. */
+ got_offset = plt_index * GOT_ENTRY_SIZE;
+ gotplt = htab->elf.igotplt;
+ relplt = htab->elf.irelplt;
+
+ /* Fill in the blueprint of a PLT. */
+ memcpy (plt->contents + plt_offset, elf_s390x_plt_entry,
+ PLT_ENTRY_SIZE);
+
+ /* Fixup the relative address to the GOT entry */
+ bfd_put_32 (output_bfd,
+ (gotplt->output_section->vma +
+ gotplt->output_offset + got_offset
+ - (plt->output_section->vma +
+ plt->output_offset +
+ plt_offset))/2,
+ plt->contents + plt_offset + 2);
+ /* Fixup the relative branch to PLT 0 */
+ bfd_put_32 (output_bfd, - (plt->output_offset +
+ (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+ plt->contents + plt_offset + 24);
+ /* Fixup offset into .rela.plt section. */
+ bfd_put_32 (output_bfd, relplt->output_offset +
+ plt_index * sizeof (Elf64_External_Rela),
+ plt->contents + plt_offset + 28);
+
+ /* Fill in the entry in the global offset table.
+ Points to instruction after GOT offset. */
+ bfd_put_64 (output_bfd,
+ (plt->output_section->vma
+ + plt->output_offset
+ + plt_offset
+ + 14),
+ gotplt->contents + got_offset);
+
+ /* Fill in the entry in the .rela.plt section. */
+ rela.r_offset = (gotplt->output_section->vma
+ + gotplt->output_offset
+ + got_offset);
+
+ if (!h
+ || h->dynindx == -1
+ || ((info->executable
+ || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ && h->def_regular))
+ {
+ /* The symbol can be locally resolved. */
+ rela.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+ rela.r_addend = resolver_address;
+ }
+ else
+ {
+ rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+ rela.r_addend = 0;
+ }
+
+ loc = relplt->contents + plt_index * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+}
+
+
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */
static bfd_boolean
-elf_s390_finish_dynamic_symbol (output_bfd, info, h, sym)
- bfd *output_bfd;
- struct bfd_link_info *info;
- struct elf_link_hash_entry *h;
- Elf_Internal_Sym *sym;
+elf_s390_finish_dynamic_symbol (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
{
struct elf_s390_link_hash_table *htab;
+ struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
if (h->plt.offset != (bfd_vma) -1)
{
/* This symbol has an entry in the procedure linkage table. Set
it up. */
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ /* If we can resolve the IFUNC symbol locally we generate an
+ IRELATIVE reloc. */
+ elf_s390_finish_ifunc_symbol (output_bfd, info, h, htab, h->plt.offset,
+ eh->ifunc_resolver_address +
+ eh->ifunc_resolver_section->output_offset +
+ eh->ifunc_resolver_section->output_section->vma);
+ ;
+ /* Fallthrough. Handling of explicit GOT slots of IFUNC
+ symbols is below. */
+ }
+ else
+ {
+ if (h->dynindx == -1
+ || htab->elf.splt == NULL
+ || htab->elf.sgotplt == NULL
+ || htab->elf.srelplt == NULL)
+ abort ();
- if (h->dynindx == -1
- || htab->splt == NULL
- || htab->sgotplt == NULL
- || htab->srelplt == NULL)
- abort ();
+ /* Calc. index no.
+ Current offset - size first entry / entry size. */
+ plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
- /* Calc. index no.
- Current offset - size first entry / entry size. */
- plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
-
- /* Offset in GOT is PLT index plus GOT headers(3) times 8,
- addr & GOT addr. */
- got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
-
- /* Fill in the blueprint of a PLT. */
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD0,
- htab->splt->contents + h->plt.offset);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD1,
- htab->splt->contents + h->plt.offset + 4);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD2,
- htab->splt->contents + h->plt.offset + 8);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD3,
- htab->splt->contents + h->plt.offset + 12);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD4,
- htab->splt->contents + h->plt.offset + 16);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD5,
- htab->splt->contents + h->plt.offset + 20);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD6,
- htab->splt->contents + h->plt.offset + 24);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_ENTRY_WORD7,
- htab->splt->contents + h->plt.offset + 28);
- /* Fixup the relative address to the GOT entry */
- bfd_put_32 (output_bfd,
- (htab->sgotplt->output_section->vma +
- htab->sgotplt->output_offset + got_offset
- - (htab->splt->output_section->vma + h->plt.offset))/2,
- htab->splt->contents + h->plt.offset + 2);
- /* Fixup the relative branch to PLT 0 */
- bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
- (PLT_ENTRY_SIZE * plt_index) + 22)/2,
- htab->splt->contents + h->plt.offset + 24);
- /* Fixup offset into symbol table */
- bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
- htab->splt->contents + h->plt.offset + 28);
-
- /* Fill in the entry in the global offset table.
- Points to instruction after GOT offset. */
- bfd_put_64 (output_bfd,
- (htab->splt->output_section->vma
- + htab->splt->output_offset
- + h->plt.offset
- + 14),
- htab->sgotplt->contents + got_offset);
-
- /* Fill in the entry in the .rela.plt section. */
- rela.r_offset = (htab->sgotplt->output_section->vma
- + htab->sgotplt->output_offset
- + got_offset);
- rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
- rela.r_addend = 0;
- loc = htab->srelplt->contents + plt_index * sizeof (Elf64_External_Rela);
- bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+ /* Offset in GOT is PLT index plus GOT headers(3) times 8,
+ addr & GOT addr. */
+ got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
- if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
- {
- /* Mark the symbol as undefined, rather than as defined in
- the .plt section. Leave the value alone. This is a clue
- for the dynamic linker, to make function pointer
- comparisons work between an application and shared
- library. */
- sym->st_shndx = SHN_UNDEF;
+ /* Fill in the blueprint of a PLT. */
+ memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
+ PLT_ENTRY_SIZE);
+
+ /* Fixup the relative address to the GOT entry */
+ bfd_put_32 (output_bfd,
+ (htab->elf.sgotplt->output_section->vma +
+ htab->elf.sgotplt->output_offset + got_offset
+ - (htab->elf.splt->output_section->vma +
+ htab->elf.splt->output_offset +
+ h->plt.offset))/2,
+ htab->elf.splt->contents + h->plt.offset + 2);
+ /* Fixup the relative branch to PLT 0 */
+ bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
+ (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+ htab->elf.splt->contents + h->plt.offset + 24);
+ /* Fixup offset into .rela.plt section. */
+ bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
+ htab->elf.splt->contents + h->plt.offset + 28);
+
+ /* Fill in the entry in the global offset table.
+ Points to instruction after GOT offset. */
+ bfd_put_64 (output_bfd,
+ (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset
+ + 14),
+ htab->elf.sgotplt->contents + got_offset);
+
+ /* Fill in the entry in the .rela.plt section. */
+ rela.r_offset = (htab->elf.sgotplt->output_section->vma
+ + htab->elf.sgotplt->output_offset
+ + got_offset);
+ rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+ rela.r_addend = 0;
+ loc = htab->elf.srelplt->contents + plt_index *
+ sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+
+ if (!h->def_regular)
+ {
+ /* Mark the symbol as undefined, rather than as defined in
+ the .plt section. Leave the value alone. This is a clue
+ for the dynamic linker, to make function pointer
+ comparisons work between an application and shared
+ library. */
+ sym->st_shndx = SHN_UNDEF;
+ }
}
}
- if (h->got.offset != (bfd_vma) -1)
+ if (h->got.offset != (bfd_vma) -1
+ && elf_s390_hash_entry(h)->tls_type != GOT_TLS_GD
+ && elf_s390_hash_entry(h)->tls_type != GOT_TLS_IE
+ && elf_s390_hash_entry(h)->tls_type != GOT_TLS_IE_NLT)
{
Elf_Internal_Rela rela;
bfd_byte *loc;
/* This symbol has an entry in the global offset table. Set it
up. */
-
- if (htab->sgot == NULL || htab->srelgot == NULL)
+ if (htab->elf.sgot == NULL || htab->elf.srelgot == NULL)
abort ();
- rela.r_offset = (htab->sgot->output_section->vma
- + htab->sgot->output_offset
+ rela.r_offset = (htab->elf.sgot->output_section->vma
+ + htab->elf.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 (info->shared
- && (info->symbolic
- || h->dynindx == -1
- || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL))
- && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
+ if (h->def_regular && s390_is_ifunc_symbol_p (h))
+ {
+ if (info->shared)
+ {
+ /* An explicit GOT slot usage needs GLOB_DAT. If the
+ symbol references local the implicit got.iplt slot
+ will be used and the IRELATIVE reloc has been created
+ above. */
+ goto do_glob_dat;
+ }
+ else
+ {
+ /* For non-shared objects explicit GOT slots must be
+ filled with the PLT slot address for pointer
+ equality reasons. */
+ bfd_put_64 (output_bfd, (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset),
+ htab->elf.sgot->contents + h->got.offset);
+ return TRUE;
+ }
+ }
+ else if (info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
{
+ /* 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 (!h->def_regular)
+ return FALSE;
BFD_ASSERT((h->got.offset & 1) != 0);
rela.r_info = ELF64_R_INFO (0, R_390_RELATIVE);
rela.r_addend = (h->root.u.def.value
else
{
BFD_ASSERT((h->got.offset & 1) == 0);
- bfd_put_64 (output_bfd, (bfd_vma) 0, htab->sgot->contents + h->got.offset);
+do_glob_dat:
+ bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgot->contents + h->got.offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_390_GLOB_DAT);
rela.r_addend = 0;
}
- loc = htab->srelgot->contents;
- loc += htab->srelgot->reloc_count++ * sizeof (Elf64_External_Rela);
+ loc = htab->elf.srelgot->contents;
+ loc += htab->elf.srelgot->reloc_count++ * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
}
- if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
+ if (h->needs_copy)
{
Elf_Internal_Rela rela;
bfd_byte *loc;
}
/* Mark some specially defined symbols as absolute. */
- if (strcmp (h->root.root.string, "_DYNAMIC") == 0
- || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0
- || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0)
+ if (h == htab->elf.hdynamic
+ || h == htab->elf.hgot
+ || h == htab->elf.hplt)
sym->st_shndx = SHN_ABS;
return TRUE;
dynamic linker, before writing them out. */
static enum elf_reloc_type_class
-elf_s390_reloc_type_class (rela)
- const Elf_Internal_Rela *rela;
+elf_s390_reloc_type_class (const Elf_Internal_Rela *rela)
{
switch ((int) ELF64_R_TYPE (rela->r_info))
{
/* Finish up the dynamic sections. */
static bfd_boolean
-elf_s390_finish_dynamic_sections (output_bfd, info)
- bfd *output_bfd;
- struct bfd_link_info *info;
+elf_s390_finish_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
{
struct elf_s390_link_hash_table *htab;
bfd *dynobj;
asection *sdyn;
+ bfd *ibfd;
+ unsigned int i;
htab = elf_s390_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
dynobj = htab->elf.dynobj;
- sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+ sdyn = bfd_get_linker_section (dynobj, ".dynamic");
if (htab->elf.dynamic_sections_created)
{
Elf64_External_Dyn *dyncon, *dynconend;
- if (sdyn == NULL || htab->sgot == NULL)
+ if (sdyn == NULL || htab->elf.sgot == NULL)
abort ();
dyncon = (Elf64_External_Dyn *) sdyn->contents;
- dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
+ dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
continue;
case DT_PLTGOT:
- dyn.d_un.d_ptr = htab->sgot->output_section->vma;
+ dyn.d_un.d_ptr = htab->elf.sgot->output_section->vma;
break;
case DT_JMPREL:
- dyn.d_un.d_ptr = htab->srelplt->output_section->vma;
+ dyn.d_un.d_ptr = htab->elf.srelplt->output_section->vma;
break;
case DT_PLTRELSZ:
- s = htab->srelplt->output_section;
- if (s->_cooked_size != 0)
- dyn.d_un.d_val = s->_cooked_size;
- else
- dyn.d_un.d_val = s->_raw_size;
+ s = htab->elf.srelplt->output_section;
+ dyn.d_un.d_val = s->size;
break;
case DT_RELASZ:
linker script arranges for .rela.plt to follow all
other relocation sections, we don't have to worry
about changing the DT_RELA entry. */
- s = htab->srelplt->output_section;
- if (s->_cooked_size != 0)
- dyn.d_un.d_val -= s->_cooked_size;
- else
- dyn.d_un.d_val -= s->_raw_size;
+ s = htab->elf.srelplt->output_section;
+ dyn.d_un.d_val -= s->size;
break;
}
}
/* Fill in the special first entry in the procedure linkage table. */
- if (htab->splt && htab->splt->_raw_size > 0)
+ if (htab->elf.splt && htab->elf.splt->size > 0)
{
/* fill in blueprint for plt 0 entry */
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD0,
- htab->splt->contents );
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD1,
- htab->splt->contents +4 );
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD3,
- htab->splt->contents +12 );
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD4,
- htab->splt->contents +16 );
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD5,
- htab->splt->contents +20 );
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD6,
- htab->splt->contents + 24);
- bfd_put_32 (output_bfd, (bfd_vma) PLT_FIRST_ENTRY_WORD7,
- htab->splt->contents + 28 );
+ memcpy (htab->elf.splt->contents, elf_s390x_first_plt_entry,
+ PLT_FIRST_ENTRY_SIZE);
/* Fixup relative address to start of GOT */
bfd_put_32 (output_bfd,
- (htab->sgotplt->output_section->vma +
- htab->sgotplt->output_offset
- - htab->splt->output_section->vma - 6)/2,
- htab->splt->contents + 8);
+ (htab->elf.sgotplt->output_section->vma +
+ htab->elf.sgotplt->output_offset
+ - htab->elf.splt->output_section->vma - 6)/2,
+ htab->elf.splt->contents + 8);
}
- elf_section_data (htab->splt->output_section)
+ elf_section_data (htab->elf.splt->output_section)
->this_hdr.sh_entsize = PLT_ENTRY_SIZE;
}
- if (htab->sgotplt)
+ if (htab->elf.sgotplt)
{
/* Fill in the first three entries in the global offset table. */
- if (htab->sgotplt->_raw_size > 0)
+ if (htab->elf.sgotplt->size > 0)
{
bfd_put_64 (output_bfd,
(sdyn == NULL ? (bfd_vma) 0
: sdyn->output_section->vma + sdyn->output_offset),
- htab->sgotplt->contents);
+ htab->elf.sgotplt->contents);
/* One entry for shared object struct ptr. */
- bfd_put_64 (output_bfd, (bfd_vma) 0, htab->sgotplt->contents + 8);
+ bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 8);
/* One entry for _dl_runtime_resolve. */
- bfd_put_64 (output_bfd, (bfd_vma) 0, htab->sgotplt->contents + 12);
+ bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgotplt->contents + 12);
}
- elf_section_data (htab->sgot->output_section)
+ elf_section_data (htab->elf.sgot->output_section)
->this_hdr.sh_entsize = 8;
}
+
+ /* Finish dynamic symbol for local IFUNC symbols. */
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ {
+ struct plt_entry *local_plt;
+ Elf_Internal_Sym *isym;
+ Elf_Internal_Shdr *symtab_hdr;
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+
+ local_plt = elf_s390_local_plt (ibfd);
+ if (local_plt != NULL)
+ for (i = 0; i < symtab_hdr->sh_info; i++)
+ {
+ if (local_plt[i].plt.offset != (bfd_vma) -1)
+ {
+ asection *sec = local_plt[i].sec;
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, i);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ elf_s390_finish_ifunc_symbol (output_bfd, info, NULL, htab,
+ local_plt[i].plt.offset,
+ isym->st_value
+ + sec->output_section->vma
+ + sec->output_offset);
+
+ }
+ }
+ }
+
return TRUE;
}
-static bfd_boolean
-elf_s390_object_p (abfd)
- bfd *abfd;
+/* Return address for Ith PLT stub in section PLT, for relocation REL
+ or (bfd_vma) -1 if it should not be included. */
+
+static bfd_vma
+elf_s390_plt_sym_val (bfd_vma i, const asection *plt,
+ const arelent *rel ATTRIBUTE_UNUSED)
{
- return bfd_default_set_arch_mach (abfd, bfd_arch_s390, bfd_mach_s390_64);
+ return plt->vma + PLT_FIRST_ENTRY_SIZE + i * PLT_ENTRY_SIZE;
}
-/*
- * 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 s390_elf64_size_info structure.
- */
+/* 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 s390_elf64_size_info structure. */
const struct elf_size_info s390_elf64_size_info =
{
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 */
+ 8, /* hash-table entry size. */
+ 1, /* internal relocations per external relocations. */
+ 64, /* arch_size. */
+ 3, /* log_file_align. */
ELFCLASS64, EV_CURRENT,
bfd_elf64_write_out_phdrs,
bfd_elf64_write_shdrs_and_ehdr,
+ bfd_elf64_checksum_contents,
bfd_elf64_write_relocs,
bfd_elf64_swap_symbol_in,
bfd_elf64_swap_symbol_out,
#define TARGET_BIG_SYM bfd_elf64_s390_vec
#define TARGET_BIG_NAME "elf64-s390"
#define ELF_ARCH bfd_arch_s390
+#define ELF_TARGET_ID S390_ELF_DATA
#define ELF_MACHINE_CODE EM_S390
#define ELF_MACHINE_ALT1 EM_S390_OLD
#define ELF_MAXPAGESIZE 0x1000
#define elf_backend_plt_readonly 1
#define elf_backend_want_plt_sym 0
#define elf_backend_got_header_size 24
-#define elf_backend_plt_header_size PLT_ENTRY_SIZE
#define elf_backend_rela_normal 1
#define elf_info_to_howto elf_s390_info_to_howto
#define bfd_elf64_bfd_is_local_label_name elf_s390_is_local_label_name
#define bfd_elf64_bfd_link_hash_table_create elf_s390_link_hash_table_create
#define bfd_elf64_bfd_reloc_type_lookup elf_s390_reloc_type_lookup
+#define bfd_elf64_bfd_reloc_name_lookup elf_s390_reloc_name_lookup
#define elf_backend_adjust_dynamic_symbol elf_s390_adjust_dynamic_symbol
#define elf_backend_check_relocs elf_s390_check_relocs
#define elf_backend_reloc_type_class elf_s390_reloc_type_class
#define elf_backend_relocate_section elf_s390_relocate_section
#define elf_backend_size_dynamic_sections elf_s390_size_dynamic_sections
+#define elf_backend_init_index_section _bfd_elf_init_1_index_section
#define elf_backend_reloc_type_class elf_s390_reloc_type_class
-
-#define elf_backend_object_p elf_s390_object_p
+#define elf_backend_plt_sym_val elf_s390_plt_sym_val
+#define elf_backend_add_symbol_hook elf_s390_add_symbol_hook
+
+#define bfd_elf64_mkobject elf_s390_mkobject
+#define elf_backend_object_p elf_s390_object_p
+
+/* Enable ELF64 archive functions. */
+#define bfd_elf64_archive_functions
+extern bfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
+extern bfd_boolean bfd_elf64_archive_write_armap (bfd *, unsigned int, struct orl *, unsigned int, int);
+
+#define bfd_elf64_archive_slurp_extended_name_table _bfd_archive_coff_slurp_extended_name_table
+#define bfd_elf64_archive_construct_extended_name_table _bfd_archive_coff_construct_extended_name_table
+#define bfd_elf64_archive_truncate_arname _bfd_archive_coff_truncate_arname
+#define bfd_elf64_archive_read_ar_hdr _bfd_archive_coff_read_ar_hdr
+#define bfd_elf64_archive_write_ar_hdr _bfd_archive_coff_write_ar_hdr
+#define bfd_elf64_archive_openr_next_archived_file _bfd_archive_coff_openr_next_archived_file
+#define bfd_elf64_archive_get_elt_at_index _bfd_archive_coff_get_elt_at_index
+#define bfd_elf64_archive_generic_stat_arch_elt _bfd_archive_coff_generic_stat_arch_elt
+#define bfd_elf64_archive_update_armap_timestamp _bfd_archive_coff_update_armap_timestamp
#include "elf64-target.h"