Automatic date update in version.in
[deliverable/binutils-gdb.git] / bfd / elf-s390-common.c
index dd35c40bc99b4b449fa98eff5538f7b71fae1780..d7cd7e60e9e376599268daf1f1c0ff1dc53ac160 100644 (file)
@@ -1,5 +1,5 @@
 /* IBM S/390-specific support for ELF 32 and 64 bit functions
 /* IBM S/390-specific support for ELF 32 and 64 bit functions
-   Copyright (C) 2000-2014 Free Software Foundation, Inc.
+   Copyright (C) 2000-2019 Free Software Foundation, Inc.
    Contributed by Andreas Krebbel.
 
    This file is part of BFD, the Binary File Descriptor library.
    Contributed by Andreas Krebbel.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -30,6 +30,87 @@ s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
   return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
 }
 
   return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
 }
 
+/* Return true if .got.plt is supposed to be emitted after .got.  */
+
+static inline bfd_boolean
+s390_gotplt_after_got_p (struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+  if (!htab->elf.sgot || !htab->elf.sgotplt)
+    return TRUE;
+
+  if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section)
+    {
+      if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset)
+       return TRUE;
+    }
+  else
+    {
+      if (htab->elf.sgot->output_section->vma
+         <= htab->elf.sgotplt->output_section->vma)
+       return TRUE;
+    }
+  return FALSE;
+}
+
+/* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol.  */
+
+static inline bfd_vma
+s390_got_pointer (struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+  bfd_vma got_pointer;
+
+  BFD_ASSERT (htab && htab->elf.hgot);
+
+  got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma
+                + htab->elf.hgot->root.u.def.section->output_offset);
+  /* Our ABI requires the GOT pointer to point at the very beginning
+     of the global offset table.  */
+  BFD_ASSERT (got_pointer
+             <= (htab->elf.sgot->output_section->vma
+                 + htab->elf.sgot->output_offset));
+  BFD_ASSERT (got_pointer
+             <= (htab->elf.sgotplt->output_section->vma
+                 + htab->elf.sgotplt->output_offset));
+
+  return got_pointer;
+}
+
+
+/* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_.  */
+
+static inline bfd_vma
+s390_got_offset (struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+  /* The absolute address of the .got in the target image.  */
+  bfd_vma got_address = (htab->elf.sgot->output_section->vma
+                        + htab->elf.sgot->output_offset);
+
+  /* GOT offset must not be negative.  */
+  BFD_ASSERT (s390_got_pointer (info) <= got_address);
+  return got_address - s390_got_pointer (info);
+}
+
+/* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_.  */
+
+static inline bfd_vma
+s390_gotplt_offset (struct bfd_link_info *info)
+{
+  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+
+  /* The absolute address of the .got.plt in the target image.  */
+  bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma
+                           + htab->elf.sgotplt->output_offset);
+
+  /* GOT offset must not be negative.  */
+  BFD_ASSERT (s390_got_pointer (info) <= gotplt_address);
+  return gotplt_address - s390_got_pointer (info);
+}
+
 /* Create sections needed by STT_GNU_IFUNC symbol.  */
 
 static bfd_boolean
 /* Create sections needed by STT_GNU_IFUNC symbol.  */
 
 static bfd_boolean
@@ -45,13 +126,12 @@ s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
 
   flags = bed->dynamic_sec_flags;
 
 
   flags = bed->dynamic_sec_flags;
 
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
                                       flags | SEC_READONLY);
       if (s == NULL
     {
       s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
                                       flags | SEC_READONLY);
       if (s == NULL
-         || ! bfd_set_section_alignment (abfd, s,
-                                         bed->s->log_file_align))
+         || !bfd_set_section_alignment (s, bed->s->log_file_align))
        return FALSE;
       htab->irelifunc = s;
     }
        return FALSE;
       htab->irelifunc = s;
     }
@@ -60,21 +140,19 @@ s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
   s = bfd_make_section_with_flags (abfd, ".iplt",
                                   flags | SEC_CODE | SEC_READONLY);
   if (s == NULL
   s = bfd_make_section_with_flags (abfd, ".iplt",
                                   flags | SEC_CODE | SEC_READONLY);
   if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+      || !bfd_set_section_alignment (s, bed->plt_alignment))
     return FALSE;
   htab->iplt = s;
 
   s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
   if (s == NULL
     return FALSE;
   htab->iplt = s;
 
   s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
   if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s,
-                                     bed->s->log_file_align))
+      || !bfd_set_section_alignment (s, bed->s->log_file_align))
     return FALSE;
   htab->irelplt = s;
 
   s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
   if (s == NULL
     return FALSE;
   htab->irelplt = s;
 
   s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
   if (s == NULL
-      || !bfd_set_section_alignment (abfd, s,
-                                    bed->s->log_file_align))
+      || !bfd_set_section_alignment (s, bed->s->log_file_align))
     return FALSE;
   htab->igotplt = s;
 
     return FALSE;
   htab->igotplt = s;
 
@@ -87,12 +165,12 @@ s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
 
 static bfd_boolean
 s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
 
 static bfd_boolean
 s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
-                                   struct elf_link_hash_entry *h,
-                                   struct elf_dyn_relocs **head)
+                                   struct elf_link_hash_entry *h)
 {
   struct elf_dyn_relocs *p;
   struct elf_link_hash_table *htab;
   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
 {
   struct elf_dyn_relocs *p;
   struct elf_link_hash_table *htab;
   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
+  struct elf_dyn_relocs **head = &eh->dyn_relocs;
 
   htab = elf_hash_table (info);
   eh->ifunc_resolver_address = h->root.u.def.value;
 
   htab = elf_hash_table (info);
   eh->ifunc_resolver_address = h->root.u.def.value;
@@ -102,10 +180,10 @@ s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
   if (h->plt.refcount <= 0 && h->got.refcount <= 0)
     {
       /* When building shared library, we need to handle the case
   if (h->plt.refcount <= 0 && h->got.refcount <= 0)
     {
       /* When building shared library, we need to handle the case
-         where it is marked with regular reference, but not non-GOT
+        where it is marked with regular reference, but not non-GOT
         reference.  It may happen if we didn't see STT_GNU_IFUNC
         symbol at the time when checking relocations.  */
         reference.  It may happen if we didn't see STT_GNU_IFUNC
         symbol at the time when checking relocations.  */
-      if (info->shared
+      if (bfd_link_pic (info)
          && !h->non_got_ref
          && h->ref_regular)
        for (p = *head; p != NULL; p = p->next)
          && !h->non_got_ref
          && h->ref_regular)
        for (p = *head; p != NULL; p = p->next)
@@ -151,7 +229,9 @@ keep:
      point to the IPLT slot.  That way the referencing shared lib will
      always get the PLT slot address when resolving the respective
      R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
      point to the IPLT slot.  That way the referencing shared lib will
      always get the PLT slot address when resolving the respective
      R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
-  if (info->executable && !info->shared && h->def_regular && h->ref_dynamic)
+  if (bfd_link_pde (info)
+      && h->def_regular
+      && h->ref_dynamic)
     {
       h->root.u.def.section = htab->iplt;
       h->root.u.def.value = h->plt.offset;
     {
       h->root.u.def.section = htab->iplt;
       h->root.u.def.value = h->plt.offset;
@@ -159,9 +239,7 @@ keep:
       h->type = STT_FUNC;
     }
 
       h->type = STT_FUNC;
     }
 
-  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
-     there is a non-GOT reference in a shared object.  */
-  if (!info->shared || !h->non_got_ref)
+  if (!bfd_link_pic (info))
     *head = NULL;
 
   /* Finally, allocate space.  */
     *head = NULL;
 
   /* Finally, allocate space.  */
@@ -182,9 +260,9 @@ keep:
      avoided if the values in the GOT slots could differ for pointer
      equality reasons.  */
   if (h->got.refcount <= 0
      avoided if the values in the GOT slots could differ for pointer
      equality reasons.  */
   if (h->got.refcount <= 0
-      || (info->shared
+      || (bfd_link_pic (info)
          && (h->dynindx == -1 || h->forced_local))
          && (h->dynindx == -1 || h->forced_local))
-      || (info->executable && info->shared)
+      || bfd_link_pie (info)
       || htab->sgot == NULL)
     {
       /* Use .got.iplt.  */
       || htab->sgot == NULL)
     {
       /* Use .got.iplt.  */
@@ -194,7 +272,7 @@ keep:
     {
       h->got.offset = htab->sgot->size;
       htab->sgot->size += GOT_ENTRY_SIZE;
     {
       h->got.offset = htab->sgot->size;
       htab->sgot->size += GOT_ENTRY_SIZE;
-      if (info->shared)
+      if (bfd_link_pic (info))
        htab->srelgot->size += RELA_ENTRY_SIZE;
     }
 
        htab->srelgot->size += RELA_ENTRY_SIZE;
     }
 
@@ -207,9 +285,9 @@ elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
   bfd_size_type size;
 
   size = symtab_hdr->sh_info;
   bfd_size_type size;
 
   size = symtab_hdr->sh_info;
-  size *= (sizeof (bfd_signed_vma)       /* local got */
-          + sizeof (struct plt_entry)   /* local plt */
-          + sizeof(char));              /* local tls type */
+  size *= (sizeof (bfd_signed_vma)      /* local got */
+          + sizeof (struct plt_entry)   /* local plt */
+          + sizeof(char));              /* local tls type */
   elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
                                    bfd_zalloc (abfd, size));
   if (elf_local_got_refcounts (abfd) == NULL)
   elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
                                    bfd_zalloc (abfd, size));
   if (elf_local_got_refcounts (abfd) == NULL)
@@ -223,20 +301,76 @@ elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
   return TRUE;
 }
 
   return TRUE;
 }
 
-/* Pick ELFOSABI_GNU if IFUNC symbols are used.  */
+/* Whether to sort relocs output by ld -r or ld --emit-relocs, by
+   r_offset.  Don't do so for code sections.  We want to keep ordering
+   of GDCALL / PLT32DBL for TLS optimizations as is.  On the other
+   hand, elf-eh-frame.c processing requires .eh_frame relocs to be
+   sorted.  */
 
 static bfd_boolean
 
 static bfd_boolean
-elf_s390_add_symbol_hook (bfd *abfd,
-                         struct bfd_link_info *info,
-                         Elf_Internal_Sym *sym,
-                         const char **namep ATTRIBUTE_UNUSED,
-                         flagword *flagsp ATTRIBUTE_UNUSED,
-                         asection **secp ATTRIBUTE_UNUSED,
-                         bfd_vma *valp ATTRIBUTE_UNUSED)
+elf_s390_elf_sort_relocs_p (asection *sec)
 {
 {
-  if ((abfd->flags & DYNAMIC) == 0
-      && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
-    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+  return (sec->flags & SEC_CODE) == 0;
+}
+
+/* Merge object attributes from IBFD into OBFD.  Raise an error if
+   there are conflicting attributes.  */
+static bfd_boolean
+elf_s390_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+  bfd *obfd = info->output_bfd;
+  obj_attribute *in_attr, *in_attrs;
+  obj_attribute *out_attr, *out_attrs;
+
+  if (!elf_known_obj_attributes_proc (obfd)[0].i)
+    {
+      /* This is the first object.  Copy the attributes.  */
+      _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+      /* Use the Tag_null value to indicate the attributes have been
+        initialized.  */
+      elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+      return TRUE;
+    }
+
+  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+
+  /* Check for conflicting Tag_GNU_S390_ABI_Vector attributes and
+     merge non-conflicting ones.  */
+  in_attr = &in_attrs[Tag_GNU_S390_ABI_Vector];
+  out_attr = &out_attrs[Tag_GNU_S390_ABI_Vector];
+
+  if (in_attr->i > 2)
+    _bfd_error_handler
+      /* xgettext:c-format */
+      (_("warning: %pB uses unknown vector ABI %d"), ibfd,
+       in_attr->i);
+  else if (out_attr->i > 2)
+    _bfd_error_handler
+      /* xgettext:c-format */
+      (_("warning: %pB uses unknown vector ABI %d"), obfd,
+       out_attr->i);
+  else if (in_attr->i != out_attr->i)
+    {
+      out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
+
+      if (in_attr->i && out_attr->i)
+       {
+         const char abi_str[3][9] = { "none", "software", "hardware" };
+
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("warning: %pB uses vector %s ABI, %pB uses %s ABI"),
+            ibfd, abi_str[in_attr->i], obfd, abi_str[out_attr->i]);
+       }
+      if (in_attr->i > out_attr->i)
+       out_attr->i = in_attr->i;
+    }
+
+  /* Merge Tag_compatibility attributes and any common GNU ones.  */
+  _bfd_elf_merge_object_attributes (ibfd, info);
 
   return TRUE;
 }
 
   return TRUE;
 }
This page took 0.028533 seconds and 4 git commands to generate.