1 /* BFD back-end for Zilog Z80 COFF binaries. 2 Copyright (C) 2005-2024 Free Software Foundation, Inc. 3 Contributed by Arnold Metselaar <arnold_m@operamail.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 #include "sysdep.h" 23 #include "bfd.h" 24 #include "libbfd.h" 25 #include "bfdlink.h" 26 #include "coff/z80.h" 27 #include "coff/internal.h" 28 #include "libcoff.h" 29 #include "libiberty.h" 30 31 #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER 0 32 33 typedef const struct { 34 bfd_reloc_code_real_type r_type; 35 reloc_howto_type howto; 36 } bfd_howto_type; 37 38 #define BFD_EMPTY_HOWTO(rt,x) {rt, EMPTY_HOWTO(x)} 39 #define BFD_HOWTO(rt,a,b,c,d,e,f,g,h,i,j,k,l,m) {rt, HOWTO(a,b,c,d,e,f,g,h,i,j,k,l,m)} 40 41 static bfd_howto_type howto_table[] = 42 { 43 BFD_EMPTY_HOWTO (BFD_RELOC_NONE, 0), 44 45 BFD_HOWTO (BFD_RELOC_32, 46 R_IMM32, /* type */ 47 0, /* rightshift */ 48 4, /* size */ 49 32, /* bitsize */ 50 false, /* pc_relative */ 51 0, /* bitpos */ 52 complain_overflow_bitfield, /* complain_on_overflow */ 53 0, /* special_function */ 54 "r_imm32", /* name */ 55 false, /* partial_inplace */ 56 0xffffffff, /* src_mask */ 57 0xffffffff, /* dst_mask */ 58 false), /* pcrel_offset */ 59 60 BFD_HOWTO (BFD_RELOC_24, 61 R_IMM24, /* type */ 62 0, /* rightshift */ 63 3, /* size */ 64 24, /* bitsize */ 65 false, /* pc_relative */ 66 0, /* bitpos */ 67 complain_overflow_bitfield, /* complain_on_overflow */ 68 0, /* special_function */ 69 "r_imm24", /* name */ 70 false, /* partial_inplace */ 71 0x00ffffff, /* src_mask */ 72 0x00ffffff, /* dst_mask */ 73 false), /* pcrel_offset */ 74 75 BFD_HOWTO (BFD_RELOC_16, 76 R_IMM16, /* type */ 77 0, /* rightshift */ 78 2, /* size */ 79 16, /* bitsize */ 80 false, /* pc_relative */ 81 0, /* bitpos */ 82 complain_overflow_bitfield, /* complain_on_overflow */ 83 0, /* special_function */ 84 "r_imm16", /* name */ 85 false, /* partial_inplace */ 86 0x0000ffff, /* src_mask */ 87 0x0000ffff, /* dst_mask */ 88 false), /* pcrel_offset */ 89 90 BFD_HOWTO (BFD_RELOC_8, 91 R_IMM8, /* type */ 92 0, /* rightshift */ 93 1, /* size */ 94 8, /* bitsize */ 95 false, /* pc_relative */ 96 0, /* bitpos */ 97 complain_overflow_bitfield, /* complain_on_overflow */ 98 0, /* special_function */ 99 "r_imm8", /* name */ 100 false, /* partial_inplace */ 101 0x000000ff, /* src_mask */ 102 0x000000ff, /* dst_mask */ 103 false), /* pcrel_offset */ 104 105 BFD_HOWTO (BFD_RELOC_8_PCREL, 106 R_JR, /* type */ 107 0, /* rightshift */ 108 1, /* size */ 109 8, /* bitsize */ 110 true, /* pc_relative */ 111 0, /* bitpos */ 112 complain_overflow_signed, /* complain_on_overflow */ 113 0, /* special_function */ 114 "r_jr", /* name */ 115 false, /* partial_inplace */ 116 0, /* src_mask */ 117 0xFF, /* dst_mask */ 118 true), /* pcrel_offset */ 119 120 BFD_HOWTO (BFD_RELOC_Z80_DISP8, 121 R_OFF8, /* type */ 122 0, /* rightshift */ 123 1, /* size */ 124 8, /* bitsize */ 125 false, /* pc_relative */ 126 0, /* bitpos */ 127 complain_overflow_signed, /* complain_on_overflow */ 128 0, /* special_function */ 129 "r_off8", /* name */ 130 false, /* partial_inplace */ 131 0, /* src_mask */ 132 0xff, /* dst_mask */ 133 false), /* pcrel_offset */ 134 135 BFD_HOWTO (BFD_RELOC_Z80_BYTE0, 136 R_BYTE0, /* type */ 137 0, /* rightshift */ 138 1, /* size */ 139 8, /* bitsize */ 140 false, /* pc_relative */ 141 0, /* bitpos */ 142 complain_overflow_dont, /* complain_on_overflow */ 143 0, /* special_function */ 144 "r_byte0", /* name */ 145 false, /* partial_inplace */ 146 0, /* src_mask */ 147 0xff, /* dst_mask */ 148 false), /* pcrel_offset */ 149 150 BFD_HOWTO (BFD_RELOC_Z80_BYTE1, 151 R_BYTE1, /* type */ 152 8, /* rightshift */ 153 1, /* size */ 154 8, /* bitsize */ 155 false, /* pc_relative */ 156 0, /* bitpos */ 157 complain_overflow_dont, /* complain_on_overflow */ 158 0, /* special_function */ 159 "r_byte1", /* name */ 160 false, /* partial_inplace */ 161 0, /* src_mask */ 162 0xff, /* dst_mask */ 163 false), /* pcrel_offset */ 164 165 BFD_HOWTO (BFD_RELOC_Z80_BYTE2, 166 R_BYTE2, /* type */ 167 16, /* rightshift */ 168 1, /* size */ 169 8, /* bitsize */ 170 false, /* pc_relative */ 171 0, /* bitpos */ 172 complain_overflow_dont, /* complain_on_overflow */ 173 0, /* special_function */ 174 "r_byte2", /* name */ 175 false, /* partial_inplace */ 176 0, /* src_mask */ 177 0xff, /* dst_mask */ 178 false), /* pcrel_offset */ 179 180 BFD_HOWTO (BFD_RELOC_Z80_BYTE3, 181 R_BYTE3, /* type */ 182 24, /* rightshift */ 183 1, /* size */ 184 8, /* bitsize */ 185 false, /* pc_relative */ 186 0, /* bitpos */ 187 complain_overflow_dont, /* complain_on_overflow */ 188 0, /* special_function */ 189 "r_byte3", /* name */ 190 false, /* partial_inplace */ 191 0, /* src_mask */ 192 0xff, /* dst_mask */ 193 false), /* pcrel_offset */ 194 195 BFD_HOWTO (BFD_RELOC_Z80_WORD0, 196 R_WORD0, /* type */ 197 0, /* rightshift */ 198 2, /* size */ 199 16, /* bitsize */ 200 false, /* pc_relative */ 201 0, /* bitpos */ 202 complain_overflow_dont, /* complain_on_overflow */ 203 0, /* special_function */ 204 "r_word0", /* name */ 205 false, /* partial_inplace */ 206 0, /* src_mask */ 207 0xffff, /* dst_mask */ 208 false), /* pcrel_offset */ 209 210 BFD_HOWTO (BFD_RELOC_Z80_WORD1, 211 R_WORD1, /* type */ 212 16, /* rightshift */ 213 2, /* size */ 214 16, /* bitsize */ 215 false, /* pc_relative */ 216 0, /* bitpos */ 217 complain_overflow_dont, /* complain_on_overflow */ 218 0, /* special_function */ 219 "r_word1", /* name */ 220 false, /* partial_inplace */ 221 0, /* src_mask */ 222 0xffff, /* dst_mask */ 223 false), /* pcrel_offset */ 224 225 BFD_HOWTO (BFD_RELOC_Z80_16_BE, 226 R_IMM16BE, /* type */ 227 0, /* rightshift */ 228 2, /* size */ 229 16, /* bitsize */ 230 false, /* pc_relative */ 231 0, /* bitpos */ 232 complain_overflow_bitfield, /* complain_on_overflow */ 233 0, /* special_function */ 234 "r_imm16be", /* name */ 235 false, /* partial_inplace */ 236 0x0000ffff, /* src_mask */ 237 0x0000ffff, /* dst_mask */ 238 false), /* pcrel_offset */ 239 }; 240 241 #define NUM_HOWTOS ARRAY_SIZE (howto_table) 242 243 #define BADMAG(x) Z80BADMAG(x) 244 #define Z80 1 /* Customize coffcode.h. */ 245 #define __A_MAGIC_SET__ 246 247 /* Code to swap in the reloc. */ 248 249 #define SWAP_IN_RELOC_OFFSET H_GET_32 250 #define SWAP_OUT_RELOC_OFFSET H_PUT_32 251 252 #define SWAP_OUT_RELOC_EXTRA(abfd, src, dst) \ 253 dst->r_stuff[0] = 'S'; \ 254 dst->r_stuff[1] = 'C'; 255 256 /* Code to turn a r_type into a howto ptr, uses the above howto table. */ 257 static void 258 rtype2howto (arelent *internal, struct internal_reloc *dst) 259 { 260 unsigned i; 261 for (i = 0; i < NUM_HOWTOS; i++) 262 { 263 if (howto_table[i].howto.type == dst->r_type) 264 { 265 internal->howto = &howto_table[i].howto; 266 return; 267 } 268 } 269 internal->howto = NULL; 270 } 271 272 #define RTYPE2HOWTO(internal, relocentry) rtype2howto (internal, relocentry) 273 274 static reloc_howto_type * 275 coff_z80_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, 276 bfd_reloc_code_real_type code) 277 { 278 unsigned i; 279 for (i = 0; i < NUM_HOWTOS; i++) 280 if (howto_table[i].r_type == code) 281 return &howto_table[i].howto; 282 283 BFD_FAIL (); 284 return NULL; 285 } 286 287 static reloc_howto_type * 288 coff_z80_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, 289 const char *r_name) 290 { 291 unsigned i; 292 for (i = 0; i < NUM_HOWTOS; i++) 293 if (strcasecmp(howto_table[i].howto.name, r_name) == 0) 294 return &howto_table[i].howto; 295 296 return NULL; 297 } 298 299 /* Perform any necessary magic to the addend in a reloc entry. */ 300 301 #define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \ 302 cache_ptr->addend = ext_reloc.r_offset; 303 304 #define RELOC_PROCESSING(relent,reloc,symbols,abfd,section) \ 305 reloc_processing(relent, reloc, symbols, abfd, section) 306 307 static void 308 reloc_processing (arelent *relent, 309 struct internal_reloc *reloc, 310 asymbol **symbols, 311 bfd *abfd, 312 asection *section) 313 { 314 relent->address = reloc->r_vaddr; 315 rtype2howto (relent, reloc); 316 317 if (reloc->r_symndx == -1 || symbols == NULL) 318 relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; 319 else if (reloc->r_symndx >= 0 && reloc->r_symndx < obj_conv_table_size (abfd)) 320 relent->sym_ptr_ptr = symbols + obj_convert (abfd)[reloc->r_symndx]; 321 else 322 { 323 _bfd_error_handler 324 /* xgettext:c-format */ 325 (_("%pB: warning: illegal symbol index %ld in relocs"), 326 abfd, reloc->r_symndx); 327 relent->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; 328 } 329 relent->addend = reloc->r_offset; 330 relent->address -= section->vma; 331 } 332 333 static bool 334 extra_case (bfd *in_abfd, 335 struct bfd_link_info *link_info, 336 struct bfd_link_order *link_order, 337 arelent *reloc, 338 bfd_byte *data, 339 size_t *src_ptr, 340 size_t *dst_ptr) 341 { 342 asection * input_section = link_order->u.indirect.section; 343 bfd_size_type end = bfd_get_section_limit_octets (in_abfd, input_section); 344 bfd_size_type reloc_size = bfd_get_reloc_size (reloc->howto); 345 346 if (*src_ptr > end 347 || reloc_size > end - *src_ptr) 348 { 349 link_info->callbacks->einfo 350 /* xgettext:c-format */ 351 (_("%X%P: %pB(%pA): relocation \"%pR\" goes out of range\n"), 352 in_abfd, input_section, reloc); 353 return false; 354 } 355 356 int val = bfd_coff_reloc16_get_value (reloc, link_info, input_section); 357 switch (reloc->howto->type) 358 { 359 case R_OFF8: 360 if (reloc->howto->partial_inplace) 361 val += (signed char) (bfd_get_8 (in_abfd, data + *src_ptr) 362 & reloc->howto->src_mask); 363 if (val > 127 || val < -128) 364 { 365 link_info->callbacks->reloc_overflow 366 (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr), 367 reloc->howto->name, reloc->addend, input_section->owner, 368 input_section, reloc->address); 369 return false; 370 } 371 372 bfd_put_8 (in_abfd, val, data + *dst_ptr); 373 *dst_ptr += 1; 374 *src_ptr += 1; 375 break; 376 377 case R_BYTE3: 378 bfd_put_8 (in_abfd, val >> 24, data + *dst_ptr); 379 *dst_ptr += 1; 380 *src_ptr += 1; 381 break; 382 383 case R_BYTE2: 384 bfd_put_8 (in_abfd, val >> 16, data + *dst_ptr); 385 *dst_ptr += 1; 386 *src_ptr += 1; 387 break; 388 389 case R_BYTE1: 390 bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr); 391 *dst_ptr += 1; 392 *src_ptr += 1; 393 break; 394 395 case R_IMM8: 396 if (reloc->howto->partial_inplace) 397 val += bfd_get_8 (in_abfd, data + *src_ptr) & reloc->howto->src_mask; 398 /* Fall through. */ 399 case R_BYTE0: 400 bfd_put_8 (in_abfd, val, data + *dst_ptr); 401 *dst_ptr += 1; 402 *src_ptr += 1; 403 break; 404 405 case R_WORD1: 406 bfd_put_16 (in_abfd, val >> 16, data + *dst_ptr); 407 *dst_ptr += 2; 408 *src_ptr += 2; 409 break; 410 411 case R_IMM16: 412 if (reloc->howto->partial_inplace) 413 val += bfd_get_16 (in_abfd, data + *src_ptr) & reloc->howto->src_mask; 414 /* Fall through. */ 415 case R_WORD0: 416 bfd_put_16 (in_abfd, val, data + *dst_ptr); 417 *dst_ptr += 2; 418 *src_ptr += 2; 419 break; 420 421 case R_IMM24: 422 if (reloc->howto->partial_inplace) 423 val += (bfd_get_24 (in_abfd, data + *src_ptr) 424 & reloc->howto->src_mask); 425 bfd_put_24 (in_abfd, val, data + *dst_ptr); 426 *dst_ptr += 3; 427 *src_ptr += 3; 428 break; 429 430 case R_IMM32: 431 if (reloc->howto->partial_inplace) 432 val += bfd_get_32 (in_abfd, data + *src_ptr) & reloc->howto->src_mask; 433 bfd_put_32 (in_abfd, val, data + *dst_ptr); 434 *dst_ptr += 4; 435 *src_ptr += 4; 436 break; 437 438 case R_JR: 439 { 440 if (reloc->howto->partial_inplace) 441 val += (signed char) (bfd_get_8 (in_abfd, data + *src_ptr) 442 & reloc->howto->src_mask); 443 bfd_vma dot = (*dst_ptr 444 + input_section->output_offset 445 + input_section->output_section->vma); 446 bfd_signed_vma gap = val - dot; 447 if (gap >= 128 || gap < -128) 448 { 449 link_info->callbacks->reloc_overflow 450 (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr), 451 reloc->howto->name, reloc->addend, input_section->owner, 452 input_section, reloc->address); 453 return false; 454 } 455 456 bfd_put_8 (in_abfd, gap, data + *dst_ptr); 457 *dst_ptr += 1; 458 *src_ptr += 1; 459 break; 460 } 461 462 case R_IMM16BE: 463 if (reloc->howto->partial_inplace) 464 val += ((bfd_get_8 (in_abfd, data + *src_ptr + 0) * 0x100 465 + bfd_get_8 (in_abfd, data + *src_ptr + 1)) 466 & reloc->howto->src_mask); 467 468 bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr + 0); 469 bfd_put_8 (in_abfd, val, data + *dst_ptr + 1); 470 *dst_ptr += 2; 471 *src_ptr += 2; 472 break; 473 474 default: 475 link_info->callbacks->einfo 476 /* xgettext:c-format */ 477 (_("%X%P: %pB(%pA): relocation \"%pR\" is not supported\n"), 478 in_abfd, input_section, reloc); 479 return false; 480 } 481 return true; 482 } 483 484 static bool 485 z80_is_local_label_name (bfd * abfd ATTRIBUTE_UNUSED, 486 const char * name) 487 { 488 return (name[0] == '.' && name[1] == 'L') || 489 _bfd_coff_is_local_label_name (abfd, name); 490 } 491 492 #define coff_bfd_is_local_label_name z80_is_local_label_name 493 494 #define coff_reloc16_extra_cases extra_case 495 #define coff_bfd_reloc_type_lookup coff_z80_reloc_type_lookup 496 #define coff_bfd_reloc_name_lookup coff_z80_reloc_name_lookup 497 498 #ifndef bfd_pe_print_pdata 499 #define bfd_pe_print_pdata NULL 500 #endif 501 502 #include "coffcode.h" 503 504 #undef coff_bfd_get_relocated_section_contents 505 #define coff_bfd_get_relocated_section_contents \ 506 bfd_coff_reloc16_get_relocated_section_contents 507 508 #undef coff_bfd_relax_section 509 #define coff_bfd_relax_section bfd_coff_reloc16_relax_section 510 511 CREATE_LITTLE_COFF_TARGET_VEC (z80_coff_vec, "coff-z80", 0, 512 SEC_CODE | SEC_DATA, '\0', NULL, 513 COFF_SWAP_TABLE) 514 515