/src/binutils-gdb/bfd/elf-s390-common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* IBM S/390-specific support for ELF 32 and 64 bit functions |
2 | | Copyright (C) 2000-2025 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. */ |
26 | | static inline bool |
27 | | s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h) |
28 | 0 | { |
29 | 0 | struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h; |
30 | 0 | return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0; |
31 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_is_ifunc_symbol_p Unexecuted instantiation: elf32-s390.c:s390_is_ifunc_symbol_p |
32 | | |
33 | | /* Return true if .got.plt is supposed to be emitted after .got. */ |
34 | | |
35 | | static inline bool |
36 | | s390_gotplt_after_got_p (struct bfd_link_info *info) |
37 | 0 | { |
38 | 0 | struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); |
39 | |
|
40 | 0 | if (!htab->elf.sgot || !htab->elf.sgotplt) |
41 | 0 | return true; |
42 | | |
43 | 0 | if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section) |
44 | 0 | { |
45 | 0 | if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset) |
46 | 0 | return true; |
47 | 0 | } |
48 | 0 | else |
49 | 0 | { |
50 | 0 | if (htab->elf.sgot->output_section->vma |
51 | 0 | <= htab->elf.sgotplt->output_section->vma) |
52 | 0 | return true; |
53 | 0 | } |
54 | 0 | return false; |
55 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_gotplt_after_got_p Unexecuted instantiation: elf32-s390.c:s390_gotplt_after_got_p |
56 | | |
57 | | /* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol. */ |
58 | | |
59 | | static inline bfd_vma |
60 | | s390_got_pointer (struct bfd_link_info *info) |
61 | 0 | { |
62 | 0 | struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info); |
63 | 0 | bfd_vma got_pointer; |
64 | |
|
65 | 0 | BFD_ASSERT (htab && htab->elf.hgot); |
66 | |
|
67 | 0 | got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma |
68 | 0 | + 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 | 0 | BFD_ASSERT (got_pointer |
72 | 0 | <= (htab->elf.sgot->output_section->vma |
73 | 0 | + htab->elf.sgot->output_offset)); |
74 | 0 | BFD_ASSERT (got_pointer |
75 | 0 | <= (htab->elf.sgotplt->output_section->vma |
76 | 0 | + htab->elf.sgotplt->output_offset)); |
77 | |
|
78 | 0 | return got_pointer; |
79 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_got_pointer Unexecuted instantiation: elf32-s390.c:s390_got_pointer |
80 | | |
81 | | |
82 | | /* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_. */ |
83 | | |
84 | | static inline bfd_vma |
85 | | s390_got_offset (struct bfd_link_info *info) |
86 | 0 | { |
87 | 0 | 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 | 0 | bfd_vma got_address = (htab->elf.sgot->output_section->vma |
91 | 0 | + htab->elf.sgot->output_offset); |
92 | | |
93 | | /* GOT offset must not be negative. */ |
94 | 0 | BFD_ASSERT (s390_got_pointer (info) <= got_address); |
95 | 0 | return got_address - s390_got_pointer (info); |
96 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_got_offset Unexecuted instantiation: elf32-s390.c:s390_got_offset |
97 | | |
98 | | /* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_. */ |
99 | | |
100 | | static inline bfd_vma |
101 | | s390_gotplt_offset (struct bfd_link_info *info) |
102 | 0 | { |
103 | 0 | 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 | 0 | bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma |
107 | 0 | + htab->elf.sgotplt->output_offset); |
108 | | |
109 | | /* GOT offset must not be negative. */ |
110 | 0 | BFD_ASSERT (s390_got_pointer (info) <= gotplt_address); |
111 | 0 | return gotplt_address - s390_got_pointer (info); |
112 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_gotplt_offset Unexecuted instantiation: elf32-s390.c:s390_gotplt_offset |
113 | | |
114 | | /* Create sections needed by STT_GNU_IFUNC symbol. */ |
115 | | |
116 | | static bool |
117 | | s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info) |
118 | 0 | { |
119 | 0 | flagword flags; |
120 | 0 | asection *s; |
121 | 0 | const struct elf_backend_data *bed = get_elf_backend_data (abfd); |
122 | 0 | struct elf_link_hash_table *htab = elf_hash_table (info); |
123 | |
|
124 | 0 | if (htab->iplt != NULL) |
125 | 0 | return true; |
126 | | |
127 | 0 | flags = bed->dynamic_sec_flags; |
128 | |
|
129 | 0 | if (bfd_link_pic (info)) |
130 | 0 | { |
131 | 0 | s = bfd_make_section_with_flags (abfd, ".rela.ifunc", |
132 | 0 | flags | SEC_READONLY); |
133 | 0 | if (s == NULL |
134 | 0 | || !bfd_set_section_alignment (s, bed->s->log_file_align)) |
135 | 0 | return false; |
136 | 0 | htab->irelifunc = s; |
137 | 0 | } |
138 | | |
139 | | /* Create .iplt, .rel[a].iplt, and .igot.plt. */ |
140 | 0 | s = bfd_make_section_with_flags (abfd, ".iplt", |
141 | 0 | flags | SEC_CODE | SEC_READONLY); |
142 | 0 | if (s == NULL |
143 | 0 | || !bfd_set_section_alignment (s, bed->plt_alignment)) |
144 | 0 | return false; |
145 | 0 | htab->iplt = s; |
146 | |
|
147 | 0 | s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY); |
148 | 0 | if (s == NULL |
149 | 0 | || !bfd_set_section_alignment (s, bed->s->log_file_align)) |
150 | 0 | return false; |
151 | 0 | htab->irelplt = s; |
152 | |
|
153 | 0 | s = bfd_make_section_with_flags (abfd, ".igot.plt", flags); |
154 | 0 | if (s == NULL |
155 | 0 | || !bfd_set_section_alignment (s, bed->s->log_file_align)) |
156 | 0 | return false; |
157 | 0 | htab->igotplt = s; |
158 | |
|
159 | 0 | return true; |
160 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_elf_create_ifunc_sections Unexecuted instantiation: elf32-s390.c:s390_elf_create_ifunc_sections |
161 | | |
162 | | |
163 | | /* Allocate space in .plt, .got and associated reloc sections for |
164 | | dynamic relocs against a STT_GNU_IFUNC symbol definition. */ |
165 | | |
166 | | static bool |
167 | | s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, |
168 | | struct elf_link_hash_entry *h) |
169 | 0 | { |
170 | 0 | struct elf_dyn_relocs *p; |
171 | 0 | struct elf_link_hash_table *htab; |
172 | 0 | struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h; |
173 | 0 | struct elf_dyn_relocs **head = &h->dyn_relocs; |
174 | |
|
175 | 0 | htab = elf_hash_table (info); |
176 | 0 | eh->ifunc_resolver_address = h->root.u.def.value; |
177 | 0 | eh->ifunc_resolver_section = h->root.u.def.section; |
178 | | |
179 | | /* Support garbage collection against STT_GNU_IFUNC symbols. */ |
180 | 0 | if (h->plt.refcount <= 0 && h->got.refcount <= 0) |
181 | 0 | { |
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 | 0 | if (bfd_link_pic (info) |
187 | 0 | && !h->non_got_ref |
188 | 0 | && h->ref_regular) |
189 | 0 | for (p = *head; p != NULL; p = p->next) |
190 | 0 | if (p->count) |
191 | 0 | { |
192 | 0 | h->non_got_ref = 1; |
193 | 0 | goto keep; |
194 | 0 | } |
195 | | |
196 | 0 | h->got = htab->init_got_offset; |
197 | 0 | h->plt = htab->init_plt_offset; |
198 | 0 | *head = NULL; |
199 | 0 | return true; |
200 | 0 | } |
201 | | |
202 | | /* Return and discard space for dynamic relocations against it if |
203 | | it is never referenced in a non-shared object. */ |
204 | 0 | if (!h->ref_regular) |
205 | 0 | { |
206 | 0 | if (h->plt.refcount > 0 |
207 | 0 | || h->got.refcount > 0) |
208 | 0 | abort (); |
209 | 0 | h->got = htab->init_got_offset; |
210 | 0 | h->plt = htab->init_plt_offset; |
211 | 0 | *head = NULL; |
212 | 0 | return true; |
213 | 0 | } |
214 | | |
215 | 0 | keep: |
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 | 0 | h->plt.offset = htab->iplt->size; |
220 | 0 | h->needs_plt = 1; |
221 | 0 | htab->iplt->size += PLT_ENTRY_SIZE; |
222 | 0 | htab->igotplt->size += GOT_ENTRY_SIZE; |
223 | 0 | htab->irelplt->size += RELA_ENTRY_SIZE; |
224 | 0 | 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 | 0 | if (bfd_link_pde (info) |
233 | 0 | && h->def_regular |
234 | 0 | && h->ref_dynamic) |
235 | 0 | { |
236 | 0 | h->root.u.def.section = htab->iplt; |
237 | 0 | h->root.u.def.value = h->plt.offset; |
238 | 0 | h->size = PLT_ENTRY_SIZE; |
239 | 0 | h->type = STT_FUNC; |
240 | 0 | } |
241 | |
|
242 | 0 | if (!bfd_link_pic (info)) |
243 | 0 | *head = NULL; |
244 | | |
245 | | /* Finally, allocate space. */ |
246 | 0 | p = *head; |
247 | 0 | if (p != NULL) |
248 | 0 | { |
249 | 0 | bfd_size_type count = 0; |
250 | 0 | do |
251 | 0 | { |
252 | 0 | count += p->count; |
253 | 0 | p = p->next; |
254 | 0 | } |
255 | 0 | while (p != NULL); |
256 | 0 | htab->irelifunc->size += count * RELA_ENTRY_SIZE; |
257 | 0 | } |
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 | 0 | if (h->got.refcount <= 0 |
263 | 0 | || (bfd_link_pic (info) |
264 | 0 | && (h->dynindx == -1 || h->forced_local)) |
265 | 0 | || bfd_link_pie (info) |
266 | 0 | || htab->sgot == NULL) |
267 | 0 | { |
268 | | /* Use .got.iplt. */ |
269 | 0 | h->got.offset = (bfd_vma) -1; |
270 | 0 | } |
271 | 0 | else |
272 | 0 | { |
273 | 0 | h->got.offset = htab->sgot->size; |
274 | 0 | htab->sgot->size += GOT_ENTRY_SIZE; |
275 | 0 | if (bfd_link_pic (info)) |
276 | 0 | htab->srelgot->size += RELA_ENTRY_SIZE; |
277 | 0 | } |
278 | |
|
279 | 0 | return true; |
280 | 0 | } Unexecuted instantiation: elf64-s390.c:s390_elf_allocate_ifunc_dyn_relocs Unexecuted instantiation: elf32-s390.c:s390_elf_allocate_ifunc_dyn_relocs |
281 | | |
282 | | static bool |
283 | | elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr) |
284 | 0 | { |
285 | 0 | bfd_size_type size; |
286 | |
|
287 | 0 | size = symtab_hdr->sh_info; |
288 | 0 | size *= (sizeof (bfd_signed_vma) /* local got */ |
289 | 0 | + sizeof (struct plt_entry) /* local plt */ |
290 | 0 | + sizeof(char)); /* local tls type */ |
291 | 0 | elf_local_got_refcounts (abfd) = ((bfd_signed_vma *) |
292 | 0 | bfd_zalloc (abfd, size)); |
293 | 0 | if (elf_local_got_refcounts (abfd) == NULL) |
294 | 0 | return false; |
295 | 0 | elf_s390_local_plt (abfd) |
296 | 0 | = (struct plt_entry*)(elf_local_got_refcounts (abfd) |
297 | 0 | + symtab_hdr->sh_info); |
298 | 0 | elf_s390_local_got_tls_type (abfd) |
299 | 0 | = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info); |
300 | |
|
301 | 0 | return true; |
302 | 0 | } Unexecuted instantiation: elf64-s390.c:elf_s390_allocate_local_syminfo Unexecuted instantiation: elf32-s390.c:elf_s390_allocate_local_syminfo |
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 | | |
310 | | static bool |
311 | | elf_s390_elf_sort_relocs_p (asection *sec) |
312 | 0 | { |
313 | 0 | return (sec->flags & SEC_CODE) == 0; |
314 | 0 | } Unexecuted instantiation: elf64-s390.c:elf_s390_elf_sort_relocs_p Unexecuted instantiation: elf32-s390.c:elf_s390_elf_sort_relocs_p |
315 | | |
316 | | /* Merge object attributes from IBFD into OBFD. Raise an error if |
317 | | there are conflicting attributes. */ |
318 | | static bool |
319 | | elf_s390_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info) |
320 | 0 | { |
321 | 0 | bfd *obfd = info->output_bfd; |
322 | 0 | obj_attribute *in_attr, *in_attrs; |
323 | 0 | obj_attribute *out_attr, *out_attrs; |
324 | |
|
325 | 0 | if (!elf_known_obj_attributes_proc (obfd)[0].i) |
326 | 0 | { |
327 | | /* This is the first object. Copy the attributes. */ |
328 | 0 | _bfd_elf_copy_obj_attributes (ibfd, obfd); |
329 | | |
330 | | /* Use the Tag_null value to indicate the attributes have been |
331 | | initialized. */ |
332 | 0 | elf_known_obj_attributes_proc (obfd)[0].i = 1; |
333 | |
|
334 | 0 | return true; |
335 | 0 | } |
336 | | |
337 | 0 | in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU]; |
338 | 0 | 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 | 0 | in_attr = &in_attrs[Tag_GNU_S390_ABI_Vector]; |
343 | 0 | out_attr = &out_attrs[Tag_GNU_S390_ABI_Vector]; |
344 | |
|
345 | 0 | if (in_attr->i > 2) |
346 | 0 | _bfd_error_handler |
347 | | /* xgettext:c-format */ |
348 | 0 | (_("warning: %pB uses unknown vector ABI %d"), ibfd, |
349 | 0 | in_attr->i); |
350 | 0 | else if (out_attr->i > 2) |
351 | 0 | _bfd_error_handler |
352 | | /* xgettext:c-format */ |
353 | 0 | (_("warning: %pB uses unknown vector ABI %d"), obfd, |
354 | 0 | out_attr->i); |
355 | 0 | else if (in_attr->i != out_attr->i) |
356 | 0 | { |
357 | 0 | out_attr->type = ATTR_TYPE_FLAG_INT_VAL; |
358 | |
|
359 | 0 | if (in_attr->i && out_attr->i) |
360 | 0 | { |
361 | 0 | const char abi_str[3][9] = { "none", "software", "hardware" }; |
362 | |
|
363 | 0 | _bfd_error_handler |
364 | | /* xgettext:c-format */ |
365 | 0 | (_("warning: %pB uses vector %s ABI, %pB uses %s ABI"), |
366 | 0 | ibfd, abi_str[in_attr->i], obfd, abi_str[out_attr->i]); |
367 | 0 | } |
368 | 0 | if (in_attr->i > out_attr->i) |
369 | 0 | out_attr->i = in_attr->i; |
370 | 0 | } |
371 | | |
372 | | /* Merge Tag_compatibility attributes and any common GNU ones. */ |
373 | 0 | _bfd_elf_merge_object_attributes (ibfd, info); |
374 | |
|
375 | 0 | return true; |
376 | 0 | } Unexecuted instantiation: elf64-s390.c:elf_s390_merge_obj_attributes Unexecuted instantiation: elf32-s390.c:elf_s390_merge_obj_attributes |