1/* IBM S/390-specific support for ELF 32 and 64 bit functions
2   Copyright (C) 2000-2020 Free Software Foundation, Inc.
3   Contributed by Andreas Krebbel.
4
5   This file is part of BFD, the Binary File Descriptor library.
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
20   02110-1301, USA.  */
21
22
23/* Return TRUE if H is an IFUNC symbol.  Simply checking for the
24   symbol type might not be enough since it might get changed to
25   STT_FUNC for pointer equality reasons.  */
26static inline bfd_boolean
27s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
28{
29  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
30  return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
31}
32
33/* Return true if .got.plt is supposed to be emitted after .got.  */
34
35static inline bfd_boolean
36s390_gotplt_after_got_p (struct bfd_link_info *info)
37{
38  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
39
40  if (!htab->elf.sgot || !htab->elf.sgotplt)
41    return TRUE;
42
43  if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section)
44    {
45      if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset)
46	return TRUE;
47    }
48  else
49    {
50      if (htab->elf.sgot->output_section->vma
51	  <= htab->elf.sgotplt->output_section->vma)
52	return TRUE;
53    }
54  return FALSE;
55}
56
57/* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol.  */
58
59static inline bfd_vma
60s390_got_pointer (struct bfd_link_info *info)
61{
62  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
63  bfd_vma got_pointer;
64
65  BFD_ASSERT (htab && htab->elf.hgot);
66
67  got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma
68		 + htab->elf.hgot->root.u.def.section->output_offset);
69  /* Our ABI requires the GOT pointer to point at the very beginning
70     of the global offset table.  */
71  BFD_ASSERT (got_pointer
72	      <= (htab->elf.sgot->output_section->vma
73		  + htab->elf.sgot->output_offset));
74  BFD_ASSERT (got_pointer
75	      <= (htab->elf.sgotplt->output_section->vma
76		  + htab->elf.sgotplt->output_offset));
77
78  return got_pointer;
79}
80
81
82/* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_.  */
83
84static inline bfd_vma
85s390_got_offset (struct bfd_link_info *info)
86{
87  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
88
89  /* The absolute address of the .got in the target image.  */
90  bfd_vma got_address = (htab->elf.sgot->output_section->vma
91			 + htab->elf.sgot->output_offset);
92
93  /* GOT offset must not be negative.  */
94  BFD_ASSERT (s390_got_pointer (info) <= got_address);
95  return got_address - s390_got_pointer (info);
96}
97
98/* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_.  */
99
100static inline bfd_vma
101s390_gotplt_offset (struct bfd_link_info *info)
102{
103  struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
104
105  /* The absolute address of the .got.plt in the target image.  */
106  bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma
107			    + htab->elf.sgotplt->output_offset);
108
109  /* GOT offset must not be negative.  */
110  BFD_ASSERT (s390_got_pointer (info) <= gotplt_address);
111  return gotplt_address - s390_got_pointer (info);
112}
113
114/* Create sections needed by STT_GNU_IFUNC symbol.  */
115
116static bfd_boolean
117s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
118{
119  flagword flags;
120  asection *s;
121  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
122  struct elf_link_hash_table *htab = elf_hash_table (info);
123
124  if (htab->iplt != NULL)
125    return TRUE;
126
127  flags = bed->dynamic_sec_flags;
128
129  if (bfd_link_pic (info))
130    {
131      s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
132				       flags | SEC_READONLY);
133      if (s == NULL
134	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
135	return FALSE;
136      htab->irelifunc = s;
137    }
138
139  /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
140  s = bfd_make_section_with_flags (abfd, ".iplt",
141				   flags | SEC_CODE | SEC_READONLY);
142  if (s == NULL
143      || !bfd_set_section_alignment (s, bed->plt_alignment))
144    return FALSE;
145  htab->iplt = s;
146
147  s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
148  if (s == NULL
149      || !bfd_set_section_alignment (s, bed->s->log_file_align))
150    return FALSE;
151  htab->irelplt = s;
152
153  s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
154  if (s == NULL
155      || !bfd_set_section_alignment (s, bed->s->log_file_align))
156    return FALSE;
157  htab->igotplt = s;
158
159  return TRUE;
160}
161
162
163/* Allocate space in .plt, .got and associated reloc sections for
164   dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
165
166static bfd_boolean
167s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
168				    struct elf_link_hash_entry *h)
169{
170  struct elf_dyn_relocs *p;
171  struct elf_link_hash_table *htab;
172  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
173  struct elf_dyn_relocs **head = &eh->dyn_relocs;
174
175  htab = elf_hash_table (info);
176  eh->ifunc_resolver_address = h->root.u.def.value;
177  eh->ifunc_resolver_section = h->root.u.def.section;
178
179  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
180  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
181    {
182      /* When building shared library, we need to handle the case
183	 where it is marked with regular reference, but not non-GOT
184	 reference.  It may happen if we didn't see STT_GNU_IFUNC
185	 symbol at the time when checking relocations.  */
186      if (bfd_link_pic (info)
187	  && !h->non_got_ref
188	  && h->ref_regular)
189	for (p = *head; p != NULL; p = p->next)
190	  if (p->count)
191	    {
192	      h->non_got_ref = 1;
193	      goto keep;
194	    }
195
196      h->got = htab->init_got_offset;
197      h->plt = htab->init_plt_offset;
198      *head = NULL;
199      return TRUE;
200    }
201
202  /* Return and discard space for dynamic relocations against it if
203     it is never referenced in a non-shared object.  */
204  if (!h->ref_regular)
205    {
206      if (h->plt.refcount > 0
207	  || h->got.refcount > 0)
208	abort ();
209      h->got = htab->init_got_offset;
210      h->plt = htab->init_plt_offset;
211      *head = NULL;
212      return TRUE;
213    }
214
215keep:
216  /* Without checking h->plt.refcount here we allocate a PLT slot.
217     When setting plt.refcount in check_relocs it might not have been
218     known that this will be an IFUNC symol.  */
219  h->plt.offset = htab->iplt->size;
220  h->needs_plt = 1;
221  htab->iplt->size += PLT_ENTRY_SIZE;
222  htab->igotplt->size += GOT_ENTRY_SIZE;
223  htab->irelplt->size += RELA_ENTRY_SIZE;
224  htab->irelplt->reloc_count++;
225
226  /* In order to make pointer equality work with IFUNC symbols defined
227     in a non-PIE executable and referenced in a shared lib, we turn
228     the symbol into a STT_FUNC symbol and make the symbol value to
229     point to the IPLT slot.  That way the referencing shared lib will
230     always get the PLT slot address when resolving the respective
231     R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
232  if (bfd_link_pde (info)
233      && h->def_regular
234      && h->ref_dynamic)
235    {
236      h->root.u.def.section = htab->iplt;
237      h->root.u.def.value = h->plt.offset;
238      h->size = PLT_ENTRY_SIZE;
239      h->type = STT_FUNC;
240    }
241
242  if (!bfd_link_pic (info))
243    *head = NULL;
244
245  /* Finally, allocate space.  */
246  p = *head;
247  if (p != NULL)
248    {
249      bfd_size_type count = 0;
250      do
251	{
252	  count += p->count;
253	  p = p->next;
254	}
255      while (p != NULL);
256      htab->irelifunc->size += count * RELA_ENTRY_SIZE;
257    }
258
259  /* Decide whether the got.iplt slot can be used.  This has to be
260     avoided if the values in the GOT slots could differ for pointer
261     equality reasons.  */
262  if (h->got.refcount <= 0
263      || (bfd_link_pic (info)
264	  && (h->dynindx == -1 || h->forced_local))
265      || bfd_link_pie (info)
266      || htab->sgot == NULL)
267    {
268      /* Use .got.iplt.  */
269      h->got.offset = (bfd_vma) -1;
270    }
271  else
272    {
273      h->got.offset = htab->sgot->size;
274      htab->sgot->size += GOT_ENTRY_SIZE;
275      if (bfd_link_pic (info))
276	htab->srelgot->size += RELA_ENTRY_SIZE;
277    }
278
279  return TRUE;
280}
281
282static bfd_boolean
283elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
284{
285  bfd_size_type size;
286
287  size = symtab_hdr->sh_info;
288  size *= (sizeof (bfd_signed_vma)	 /* local got */
289	   + sizeof (struct plt_entry)	 /* local plt */
290	   + sizeof(char));		 /* local tls type */
291  elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
292				    bfd_zalloc (abfd, size));
293  if (elf_local_got_refcounts (abfd) == NULL)
294    return FALSE;
295  elf_s390_local_plt (abfd)
296    = (struct plt_entry*)(elf_local_got_refcounts (abfd)
297			  + symtab_hdr->sh_info);
298  elf_s390_local_got_tls_type (abfd)
299    = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info);
300
301  return TRUE;
302}
303
304/* Whether to sort relocs output by ld -r or ld --emit-relocs, by
305   r_offset.  Don't do so for code sections.  We want to keep ordering
306   of GDCALL / PLT32DBL for TLS optimizations as is.  On the other
307   hand, elf-eh-frame.c processing requires .eh_frame relocs to be
308   sorted.  */
309
310static bfd_boolean
311elf_s390_elf_sort_relocs_p (asection *sec)
312{
313  return (sec->flags & SEC_CODE) == 0;
314}
315
316/* Merge object attributes from IBFD into OBFD.  Raise an error if
317   there are conflicting attributes.  */
318static bfd_boolean
319elf_s390_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
320{
321  bfd *obfd = info->output_bfd;
322  obj_attribute *in_attr, *in_attrs;
323  obj_attribute *out_attr, *out_attrs;
324
325  if (!elf_known_obj_attributes_proc (obfd)[0].i)
326    {
327      /* This is the first object.  Copy the attributes.  */
328      _bfd_elf_copy_obj_attributes (ibfd, obfd);
329
330      /* Use the Tag_null value to indicate the attributes have been
331	 initialized.  */
332      elf_known_obj_attributes_proc (obfd)[0].i = 1;
333
334      return TRUE;
335    }
336
337  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
338  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
339
340  /* Check for conflicting Tag_GNU_S390_ABI_Vector attributes and
341     merge non-conflicting ones.  */
342  in_attr = &in_attrs[Tag_GNU_S390_ABI_Vector];
343  out_attr = &out_attrs[Tag_GNU_S390_ABI_Vector];
344
345  if (in_attr->i > 2)
346    _bfd_error_handler
347      /* xgettext:c-format */
348      (_("warning: %pB uses unknown vector ABI %d"), ibfd,
349       in_attr->i);
350  else if (out_attr->i > 2)
351    _bfd_error_handler
352      /* xgettext:c-format */
353      (_("warning: %pB uses unknown vector ABI %d"), obfd,
354       out_attr->i);
355  else if (in_attr->i != out_attr->i)
356    {
357      out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
358
359      if (in_attr->i && out_attr->i)
360	{
361	  const char abi_str[3][9] = { "none", "software", "hardware" };
362
363	  _bfd_error_handler
364	    /* xgettext:c-format */
365	    (_("warning: %pB uses vector %s ABI, %pB uses %s ABI"),
366	     ibfd, abi_str[in_attr->i], obfd, abi_str[out_attr->i]);
367	}
368      if (in_attr->i > out_attr->i)
369	out_attr->i = in_attr->i;
370    }
371
372  /* Merge Tag_compatibility attributes and any common GNU ones.  */
373  _bfd_elf_merge_object_attributes (ibfd, info);
374
375  return TRUE;
376}
377