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