1 /* ARC-specific support for 32-bit ELF 2 Copyright (C) 1994-2024 Free Software Foundation, Inc. 3 Contributed by Cupertino Miranda (cmiranda@synopsys.com). 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, 20 MA 02110-1301, USA. */ 21 22 #ifndef ARC_GOT_H 23 #define ARC_GOT_H 24 25 #define TCB_SIZE (8) 26 27 #define align_power(addr, align) \ 28 (((addr) + ((bfd_vma) 1 << (align)) - 1) & (-((bfd_vma) 1 << (align)))) 29 30 enum tls_type_e 31 { 32 GOT_UNKNOWN = 0, 33 GOT_NORMAL, 34 GOT_TLS_GD, 35 GOT_TLS_IE, 36 GOT_TLS_LE 37 }; 38 39 enum tls_got_entries 40 { 41 TLS_GOT_NONE = 0, 42 TLS_GOT_MOD, 43 TLS_GOT_OFF, 44 TLS_GOT_MOD_AND_OFF 45 }; 46 47 struct got_entry 48 { 49 struct got_entry *next; 50 enum tls_type_e type; 51 bfd_vma offset; 52 bool processed; 53 bool created_dyn_relocation; 54 enum tls_got_entries existing_entries; 55 }; 56 57 /* Return the local got list, if not defined, create an empty one. */ 58 59 static struct got_entry ** 60 arc_get_local_got_ents (bfd * abfd) 61 { 62 if (elf_local_got_ents (abfd) == NULL) 63 { 64 bfd_size_type amt = (elf_tdata (abfd)->symtab_hdr.sh_info 65 * sizeof (*elf_local_got_ents (abfd))); 66 elf_local_got_ents (abfd) = bfd_zmalloc (amt); 67 if (elf_local_got_ents (abfd) == NULL) 68 { 69 _bfd_error_handler (_("%pB: cannot allocate memory for local " 70 "GOT entries"), abfd); 71 bfd_set_error (bfd_error_bad_value); 72 return NULL; 73 } 74 } 75 76 return elf_local_got_ents (abfd); 77 } 78 79 static struct got_entry * 80 got_entry_for_type (struct got_entry **list, 81 enum tls_type_e type) 82 { 83 struct got_entry **p = list; 84 85 while (*p != NULL) 86 { 87 if ((*p)->type == type) 88 return *p; 89 p = &((*p)->next); 90 } 91 return NULL; 92 } 93 94 static void 95 new_got_entry_to_list (struct got_entry **list, 96 enum tls_type_e type, 97 bfd_vma offset, 98 enum tls_got_entries existing_entries) 99 { 100 /* Find list end. Avoid having multiple entries of the same 101 type. */ 102 struct got_entry **p = list; 103 struct got_entry *entry; 104 105 while (*p != NULL) 106 { 107 if ((*p)->type == type) 108 return; 109 p = &((*p)->next); 110 } 111 112 entry = (struct got_entry *) xmalloc (sizeof (struct got_entry)); 113 114 entry->type = type; 115 entry->offset = offset; 116 entry->next = NULL; 117 entry->processed = false; 118 entry->created_dyn_relocation = false; 119 entry->existing_entries = existing_entries; 120 121 ARC_DEBUG ("New GOT got entry added to list: " 122 "type: %d, offset: %ld, existing_entries: %d\n", 123 type, (long) offset, existing_entries); 124 125 /* Add the entry to the end of the list. */ 126 *p = entry; 127 } 128 129 static enum tls_type_e 130 tls_type_for_reloc (reloc_howto_type *howto) 131 { 132 enum tls_type_e ret = GOT_UNKNOWN; 133 134 if (is_reloc_for_GOT (howto)) 135 return GOT_NORMAL; 136 137 switch (howto->type) 138 { 139 case R_ARC_TLS_GD_GOT: 140 ret = GOT_TLS_GD; 141 break; 142 case R_ARC_TLS_IE_GOT: 143 ret = GOT_TLS_IE; 144 break; 145 case R_ARC_TLS_LE_32: 146 ret = GOT_TLS_LE; 147 break; 148 default: 149 ret = GOT_UNKNOWN; 150 break; 151 } 152 153 return ret; 154 }; 155 156 static struct got_entry ** 157 get_got_entry_list_for_symbol (bfd *abfd, 158 unsigned long r_symndx, 159 struct elf_link_hash_entry *h) 160 { 161 struct elf_arc_link_hash_entry *h1 = 162 ((struct elf_arc_link_hash_entry *) h); 163 if (h1 != NULL) 164 { 165 return &h1->got_ents; 166 } 167 else 168 { 169 return arc_get_local_got_ents (abfd) + r_symndx; 170 } 171 } 172 173 174 static enum tls_type_e 175 arc_got_entry_type_for_reloc (reloc_howto_type *howto) 176 { 177 enum tls_type_e type = GOT_UNKNOWN; 178 179 if (is_reloc_for_GOT (howto)) 180 return GOT_NORMAL; 181 182 if (is_reloc_for_TLS (howto)) 183 { 184 switch (howto->type) 185 { 186 case R_ARC_TLS_GD_GOT: 187 type = GOT_TLS_GD; 188 break; 189 case R_ARC_TLS_IE_GOT: 190 type = GOT_TLS_IE; 191 break; 192 default: 193 break; 194 } 195 } 196 return type; 197 } 198 199 #define ADD_SYMBOL_REF_SEC_AND_RELOC(SECNAME, COND_FOR_RELOC, H) \ 200 htab->s##SECNAME->size; \ 201 { \ 202 if (COND_FOR_RELOC) \ 203 { \ 204 htab->srel##SECNAME->size += sizeof (Elf32_External_Rela); \ 205 ARC_DEBUG ("arc_info: Added reloc space in " \ 206 #SECNAME " section at " __FILE__ \ 207 ":%d for symbol %s\n", \ 208 __LINE__, name_for_global_symbol (H)); \ 209 } \ 210 if (H) \ 211 if (H->dynindx == -1 && !H->forced_local) \ 212 if (! bfd_elf_link_record_dynamic_symbol (info, H)) \ 213 return false; \ 214 htab->s##SECNAME->size += 4; \ 215 } \ 216 217 static bool 218 arc_fill_got_info_for_reloc (enum tls_type_e type, 219 struct got_entry **list, 220 struct bfd_link_info * info, 221 struct elf_link_hash_entry *h) 222 { 223 struct elf_link_hash_table *htab = elf_hash_table (info); 224 225 if (got_entry_for_type (list, type) != NULL) 226 return true; 227 228 switch (type) 229 { 230 case GOT_NORMAL: 231 { 232 bfd_vma offset 233 = ADD_SYMBOL_REF_SEC_AND_RELOC (got, bfd_link_pic (info) 234 || h != NULL, h); 235 new_got_entry_to_list (list, type, offset, TLS_GOT_NONE); 236 } 237 break; 238 239 240 case GOT_TLS_GD: 241 { 242 bfd_vma offset 243 = ADD_SYMBOL_REF_SEC_AND_RELOC (got, true, h); 244 bfd_vma ATTRIBUTE_UNUSED notneeded 245 = ADD_SYMBOL_REF_SEC_AND_RELOC (got, true, h); 246 new_got_entry_to_list (list, type, offset, TLS_GOT_MOD_AND_OFF); 247 } 248 break; 249 case GOT_TLS_IE: 250 case GOT_TLS_LE: 251 { 252 bfd_vma offset 253 = ADD_SYMBOL_REF_SEC_AND_RELOC (got, true, h); 254 new_got_entry_to_list (list, type, offset, TLS_GOT_OFF); 255 } 256 break; 257 258 default: 259 return false; 260 break; 261 } 262 return true; 263 } 264 265 struct arc_static_sym_data { 266 bfd_vma sym_value; 267 const char *symbol_name; 268 }; 269 270 static struct arc_static_sym_data 271 get_static_sym_data (unsigned long r_symndx, 272 Elf_Internal_Sym *local_syms, 273 asection **local_sections, 274 struct elf_link_hash_entry *h, 275 struct arc_relocation_data *reloc_data) 276 { 277 static const char local_name[] = "(local)"; 278 struct arc_static_sym_data ret = { 0, NULL }; 279 280 if (h != NULL) 281 { 282 BFD_ASSERT (h->root.type != bfd_link_hash_undefweak 283 && h->root.type != bfd_link_hash_undefined); 284 /* TODO: This should not be here. */ 285 reloc_data->sym_value = h->root.u.def.value; 286 reloc_data->sym_section = h->root.u.def.section; 287 288 ret.sym_value = h->root.u.def.value 289 + h->root.u.def.section->output_section->vma 290 + h->root.u.def.section->output_offset; 291 292 ret.symbol_name = h->root.root.string; 293 } 294 else 295 { 296 Elf_Internal_Sym *sym = local_syms + r_symndx; 297 asection *sec = local_sections[r_symndx]; 298 299 ret.sym_value = sym->st_value 300 + sec->output_section->vma 301 + sec->output_offset; 302 303 ret.symbol_name = local_name; 304 } 305 return ret; 306 } 307 308 static bfd_vma 309 relocate_fix_got_relocs_for_got_info (struct got_entry ** list_p, 310 enum tls_type_e type, 311 struct bfd_link_info * info, 312 bfd * output_bfd, 313 unsigned long r_symndx, 314 Elf_Internal_Sym * local_syms, 315 asection ** local_sections, 316 struct elf_link_hash_entry * h, 317 struct arc_relocation_data * reloc_data) 318 { 319 struct elf_link_hash_table *htab = elf_hash_table (info); 320 struct got_entry *entry = NULL; 321 322 if (list_p == NULL || type == GOT_UNKNOWN || type == GOT_TLS_LE) 323 return 0; 324 325 entry = got_entry_for_type (list_p, type); 326 BFD_ASSERT (entry); 327 328 if (h == NULL 329 || h->forced_local == true 330 || (! elf_hash_table (info)->dynamic_sections_created 331 || (bfd_link_pic (info) 332 && SYMBOL_REFERENCES_LOCAL (info, h)))) 333 { 334 const char ATTRIBUTE_UNUSED *symbol_name; 335 asection *tls_sec = elf_hash_table (info)->tls_sec; 336 337 if (entry && !entry->processed) 338 { 339 switch (entry->type) 340 { 341 case GOT_TLS_GD: 342 { 343 BFD_ASSERT (tls_sec && tls_sec->output_section); 344 bfd_vma sec_vma = tls_sec->output_section->vma; 345 346 if (h == NULL || h->forced_local 347 || !elf_hash_table (info)->dynamic_sections_created) 348 { 349 struct arc_static_sym_data tmp = 350 get_static_sym_data (r_symndx, local_syms, local_sections, 351 h, reloc_data); 352 353 bfd_put_32 (output_bfd, 354 tmp.sym_value - sec_vma 355 + (elf_hash_table (info)->dynamic_sections_created 356 ? 0 357 : (align_power (0, 358 tls_sec->alignment_power))), 359 htab->sgot->contents + entry->offset 360 + (entry->existing_entries == TLS_GOT_MOD_AND_OFF 361 ? 4 : 0)); 362 363 ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx " 364 "@ %lx, for symbol %s\n", 365 (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" : 366 "GOT_TLS_IE"), 367 (long) (sym_value - sec_vma), 368 (long) (htab->sgot->output_section->vma 369 + htab->sgot->output_offset 370 + entry->offset 371 + (entry->existing_entries == TLS_GOT_MOD_AND_OFF 372 ? 4 : 0)), 373 tmp.symbol_name); 374 } 375 } 376 break; 377 378 case GOT_TLS_IE: 379 { 380 BFD_ASSERT (tls_sec && tls_sec->output_section); 381 bfd_vma ATTRIBUTE_UNUSED sec_vma 382 = tls_sec->output_section->vma; 383 384 struct arc_static_sym_data tmp = 385 get_static_sym_data (r_symndx, local_syms, local_sections, 386 h, reloc_data); 387 388 bfd_put_32 (output_bfd, 389 tmp.sym_value - sec_vma 390 + (elf_hash_table (info)->dynamic_sections_created 391 ? 0 392 : (align_power (TCB_SIZE, 393 tls_sec->alignment_power))), 394 htab->sgot->contents + entry->offset 395 + (entry->existing_entries == TLS_GOT_MOD_AND_OFF 396 ? 4 : 0)); 397 398 ARC_DEBUG ("arc_info: FIXED -> %s value = %#lx " 399 "@ %p, for symbol %s\n", 400 (entry->type == GOT_TLS_GD ? "GOT_TLS_GD" : 401 "GOT_TLS_IE"), 402 (long) (sym_value - sec_vma), 403 (long) (htab->sgot->output_section->vma 404 + htab->sgot->output_offset 405 + entry->offset 406 + (entry->existing_entries == TLS_GOT_MOD_AND_OFF 407 ? 4 : 0)), 408 tmp.symbol_name); 409 } 410 break; 411 412 case GOT_NORMAL: 413 { 414 bfd_vma sec_vma 415 = reloc_data->sym_section->output_section->vma 416 + reloc_data->sym_section->output_offset; 417 418 if (h != NULL 419 && h->root.type == bfd_link_hash_undefweak) 420 ARC_DEBUG ("arc_info: PATCHED: NOT_PATCHED " 421 "@ %#08lx for sym %s in got offset %#lx " 422 "(is undefweak)\n", 423 (long) (htab->sgot->output_section->vma 424 + htab->sgot->output_offset 425 + entry->offset), 426 symbol_name, 427 (long) entry->offset); 428 else 429 { 430 bfd_put_32 (output_bfd, 431 reloc_data->sym_value + sec_vma, 432 htab->sgot->contents + entry->offset); 433 ARC_DEBUG ("arc_info: PATCHED: %#08lx " 434 "@ %#08lx for sym %s in got offset %#lx\n", 435 (long) (reloc_data->sym_value + sec_vma), 436 (long) (htab->sgot->output_section->vma 437 + htab->sgot->output_offset 438 + entry->offset), 439 symbol_name, 440 (long) entry->offset); 441 } 442 } 443 break; 444 default: 445 BFD_ASSERT (0); 446 break; 447 } 448 entry->processed = true; 449 } 450 } 451 452 return entry->offset; 453 } 454 455 static void 456 create_got_dynrelocs_for_single_entry (struct got_entry *list, 457 bfd *output_bfd, 458 struct bfd_link_info * info, 459 struct elf_link_hash_entry *h) 460 { 461 if (list == NULL) 462 return; 463 464 bfd_vma got_offset = list->offset; 465 466 if (list->type == GOT_NORMAL 467 && !list->created_dyn_relocation) 468 { 469 if (bfd_link_pic (info) 470 && h != NULL 471 && (info->symbolic || h->dynindx == -1) 472 && h->def_regular) 473 { 474 ADD_RELA (output_bfd, got, got_offset, 0, R_ARC_RELATIVE, 0); 475 } 476 /* Do not fully understand the side effects of this condition. 477 The relocation space might still being reserved. Perhaps 478 I should clear its value. */ 479 else if (h != NULL && h->dynindx != -1) 480 { 481 ADD_RELA (output_bfd, got, got_offset, h->dynindx, R_ARC_GLOB_DAT, 0); 482 } 483 list->created_dyn_relocation = true; 484 } 485 else if (list->existing_entries != TLS_GOT_NONE 486 && !list->created_dyn_relocation) 487 { 488 /* TODO TLS: This is not called for local symbols. 489 In order to correctly implement TLS, this should also 490 be called for all local symbols with tls got entries. 491 Should be moved to relocate_section in order to make it 492 work for local symbols. */ 493 struct elf_link_hash_table *htab = elf_hash_table (info); 494 enum tls_got_entries e = list->existing_entries; 495 496 BFD_ASSERT (list->type != GOT_TLS_GD 497 || list->existing_entries == TLS_GOT_MOD_AND_OFF); 498 499 bfd_vma dynindx = (h == NULL || h->dynindx == -1) ? 0 : h->dynindx; 500 501 if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_MOD) 502 { 503 ADD_RELA (output_bfd, got, got_offset, dynindx, 504 R_ARC_TLS_DTPMOD, 0); 505 ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \ 506 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = 0x0\n", 507 list->type, 508 (long) got_offset, 509 (long) (htab->sgot->output_section->vma 510 + htab->sgot->output_offset + got_offset), 511 (long) dynindx); 512 } 513 514 if (e == TLS_GOT_MOD_AND_OFF || e == TLS_GOT_OFF) 515 { 516 bfd_vma addend = 0; 517 if (list->type == GOT_TLS_IE) 518 { 519 addend = bfd_get_32 (output_bfd, 520 htab->sgot->contents + got_offset); 521 } 522 523 ADD_RELA (output_bfd, got, 524 got_offset + (e == TLS_GOT_MOD_AND_OFF ? 4 : 0), 525 dynindx, 526 (list->type == GOT_TLS_IE ? R_ARC_TLS_TPOFF 527 : R_ARC_TLS_DTPOFF), 528 addend); 529 530 ARC_DEBUG ("arc_info: TLS_DYNRELOC: type = %d, \ 531 GOT_OFFSET = %#lx, GOT_VMA = %#lx, INDEX = %ld, ADDEND = %#lx\n", 532 list->type, 533 (long) got_offset, 534 (long) (htab->sgot->output_section->vma 535 + htab->sgot->output_offset + got_offset), 536 (long) dynindx, (long) addend); 537 } 538 list->created_dyn_relocation = true; 539 } 540 } 541 542 static void 543 create_got_dynrelocs_for_got_info (struct got_entry **list_p, 544 bfd *output_bfd, 545 struct bfd_link_info * info, 546 struct elf_link_hash_entry *h) 547 { 548 if (list_p == NULL) 549 return; 550 551 struct got_entry *list = *list_p; 552 /* Traverse the list of got entries for this symbol. */ 553 while (list) 554 { 555 create_got_dynrelocs_for_single_entry (list, output_bfd, info, h); 556 list = list->next; 557 } 558 } 559 560 #undef ADD_SYMBOL_REF_SEC_AND_RELOC 561 562 #endif /* ARC_GOT_H */ 563