1 /* BFD back-end for Zilog Z80 COFF binaries. 2 Copyright (C) 2005-2022 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 void 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 unsigned int *src_ptr, 340 unsigned int *dst_ptr) 341 { 342 asection * input_section = link_order->u.indirect.section; 343 int val = bfd_coff_reloc16_get_value (reloc, link_info, input_section); 344 345 switch (reloc->howto->type) 346 { 347 case R_OFF8: 348 if (reloc->howto->partial_inplace) 349 val += (signed char)(bfd_get_8 ( in_abfd, data+*src_ptr) 350 & reloc->howto->src_mask); 351 if (val>127 || val<-128) /* Test for overflow. */ 352 (*link_info->callbacks->reloc_overflow) 353 (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr), 354 reloc->howto->name, reloc->addend, input_section->owner, 355 input_section, reloc->address); 356 357 bfd_put_8 (in_abfd, val, data + *dst_ptr); 358 (*dst_ptr) += 1; 359 (*src_ptr) += 1; 360 break; 361 362 case R_BYTE3: 363 bfd_put_8 (in_abfd, val >> 24, data + *dst_ptr); 364 (*dst_ptr) += 1; 365 (*src_ptr) += 1; 366 break; 367 368 case R_BYTE2: 369 bfd_put_8 (in_abfd, val >> 16, data + *dst_ptr); 370 (*dst_ptr) += 1; 371 (*src_ptr) += 1; 372 break; 373 374 case R_BYTE1: 375 bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr); 376 (*dst_ptr) += 1; 377 (*src_ptr) += 1; 378 break; 379 380 case R_IMM8: 381 if (reloc->howto->partial_inplace) 382 val += bfd_get_8 ( in_abfd, data+*src_ptr) & reloc->howto->src_mask; 383 /* Fall through. */ 384 case R_BYTE0: 385 bfd_put_8 (in_abfd, val, data + *dst_ptr); 386 (*dst_ptr) += 1; 387 (*src_ptr) += 1; 388 break; 389 390 case R_WORD1: 391 bfd_put_16 (in_abfd, val >> 16, data + *dst_ptr); 392 (*dst_ptr) += 2; 393 (*src_ptr) += 2; 394 break; 395 396 case R_IMM16: 397 if (reloc->howto->partial_inplace) 398 val += bfd_get_16 ( in_abfd, data+*src_ptr) & reloc->howto->src_mask; 399 /* Fall through. */ 400 case R_WORD0: 401 bfd_put_16 (in_abfd, val, data + *dst_ptr); 402 (*dst_ptr) += 2; 403 (*src_ptr) += 2; 404 break; 405 406 case R_IMM24: 407 if (reloc->howto->partial_inplace) 408 val += (bfd_get_24 (in_abfd, data + *src_ptr) 409 & reloc->howto->src_mask); 410 bfd_put_24 (in_abfd, val, data + *dst_ptr); 411 (*dst_ptr) += 3; 412 (*src_ptr) += 3; 413 break; 414 415 case R_IMM32: 416 if (reloc->howto->partial_inplace) 417 val += bfd_get_32 ( in_abfd, data+*src_ptr) & reloc->howto->src_mask; 418 bfd_put_32 (in_abfd, val, data + *dst_ptr); 419 (*dst_ptr) += 4; 420 (*src_ptr) += 4; 421 break; 422 423 case R_JR: 424 { 425 if (reloc->howto->partial_inplace) 426 val += (signed char)(bfd_get_8 ( in_abfd, data+*src_ptr) 427 & reloc->howto->src_mask); 428 bfd_vma dot = (*dst_ptr 429 + input_section->output_offset 430 + input_section->output_section->vma); 431 int gap = val - dot; 432 if (gap >= 128 || gap < -128) 433 (*link_info->callbacks->reloc_overflow) 434 (link_info, NULL, bfd_asymbol_name (*reloc->sym_ptr_ptr), 435 reloc->howto->name, reloc->addend, input_section->owner, 436 input_section, reloc->address); 437 438 bfd_put_8 (in_abfd, gap, data + *dst_ptr); 439 (*dst_ptr)++; 440 (*src_ptr)++; 441 break; 442 } 443 444 case R_IMM16BE: 445 if (reloc->howto->partial_inplace) 446 val += (bfd_get_8 ( in_abfd, data+*src_ptr+0) * 0x100 + 447 bfd_get_8 ( in_abfd, data+*src_ptr+1)) & reloc->howto->src_mask; 448 449 bfd_put_8 (in_abfd, val >> 8, data + *dst_ptr+0); 450 bfd_put_8 (in_abfd, val, data + *dst_ptr+1); 451 (*dst_ptr) += 2; 452 (*src_ptr) += 2; 453 break; 454 455 default: 456 abort (); 457 } 458 } 459 460 static bool 461 z80_is_local_label_name (bfd * abfd ATTRIBUTE_UNUSED, 462 const char * name) 463 { 464 return (name[0] == '.' && name[1] == 'L') || 465 _bfd_coff_is_local_label_name (abfd, name); 466 } 467 468 #define coff_bfd_is_local_label_name z80_is_local_label_name 469 470 #define coff_reloc16_extra_cases extra_case 471 #define coff_bfd_reloc_type_lookup coff_z80_reloc_type_lookup 472 #define coff_bfd_reloc_name_lookup coff_z80_reloc_name_lookup 473 474 #ifndef bfd_pe_print_pdata 475 #define bfd_pe_print_pdata NULL 476 #endif 477 478 #include "coffcode.h" 479 480 #undef coff_bfd_get_relocated_section_contents 481 #define coff_bfd_get_relocated_section_contents \ 482 bfd_coff_reloc16_get_relocated_section_contents 483 484 #undef coff_bfd_relax_section 485 #define coff_bfd_relax_section bfd_coff_reloc16_relax_section 486 487 CREATE_LITTLE_COFF_TARGET_VEC (z80_coff_vec, "coff-z80", 0, 488 SEC_CODE | SEC_DATA, '\0', NULL, 489 COFF_SWAP_TABLE) 490 491