1 /* D30V-specific support for 32-bit ELF 2 Copyright 1997, 1998, 1999, 2000, 2001, 2002 3 Free Software Foundation, Inc. 4 Contributed by Martin Hunt (hunt@cygnus.com). 5 6 This file is part of BFD, the Binary File Descriptor library. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 21 22 #include "bfd.h" 23 #include "sysdep.h" 24 #include "libbfd.h" 25 #include "elf-bfd.h" 26 #include "elf/d30v.h" 27 28 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup 29 PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); 30 static void d30v_info_to_howto_rel 31 PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); 32 static void d30v_info_to_howto_rela 33 PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); 34 static bfd_reloc_status_type bfd_elf_d30v_reloc PARAMS (( 35 bfd *abfd, 36 arelent *reloc_entry, 37 asymbol *symbol, 38 PTR data, 39 asection *input_section, 40 bfd *output_bfd, 41 char **error_message)); 42 static bfd_reloc_status_type bfd_elf_d30v_reloc_21 PARAMS (( 43 bfd *abfd, 44 arelent *reloc_entry, 45 asymbol *symbol, 46 PTR data, 47 asection *input_section, 48 bfd *output_bfd, 49 char **error_message)); 50 51 static reloc_howto_type elf_d30v_howto_table[] = 52 { 53 /* This reloc does nothing. */ 54 HOWTO (R_D30V_NONE, /* type */ 55 0, /* rightshift */ 56 2, /* size (0 = byte, 1 = short, 2 = long) */ 57 32, /* bitsize */ 58 FALSE, /* pc_relative */ 59 0, /* bitpos */ 60 complain_overflow_bitfield, /* complain_on_overflow */ 61 bfd_elf_generic_reloc, /* special_function */ 62 "R_D30V_NONE", /* name */ 63 FALSE, /* partial_inplace */ 64 0, /* src_mask */ 65 0, /* dst_mask */ 66 FALSE), /* pcrel_offset */ 67 68 /* A 6 bit absolute relocation */ 69 HOWTO (R_D30V_6, /* type */ 70 0, /* rightshift */ 71 2, /* size (0 = byte, 1 = short, 2 = long) */ 72 6, /* bitsize */ 73 FALSE, /* pc_relative */ 74 0, /* bitpos */ 75 complain_overflow_bitfield, /* complain_on_overflow */ 76 bfd_elf_generic_reloc, /* special_function */ 77 "R_D30V_6", /* name */ 78 FALSE, /* partial_inplace */ 79 0x3f, /* src_mask */ 80 0x3f, /* dst_mask */ 81 FALSE), /* pcrel_offset */ 82 83 /* A relative 9 bit relocation, right shifted by 3 */ 84 HOWTO (R_D30V_9_PCREL, /* type */ 85 3, /* rightshift */ 86 2, /* size (0 = byte, 1 = short, 2 = long) */ 87 6, /* bitsize */ 88 TRUE, /* pc_relative */ 89 0, /* bitpos */ 90 complain_overflow_signed, /* complain_on_overflow */ 91 bfd_elf_d30v_reloc_21, /* special_function */ 92 "R_D30V_9_PCREL", /* name */ 93 FALSE, /* partial_inplace */ 94 0x3f, /* src_mask */ 95 0x3f, /* dst_mask */ 96 TRUE), /* pcrel_offset */ 97 98 /* A relative 9 bit relocation, right shifted by 3 */ 99 HOWTO (R_D30V_9_PCREL_R, /* type */ 100 3, /* rightshift */ 101 2, /* size (0 = byte, 1 = short, 2 = long) */ 102 6, /* bitsize */ 103 TRUE, /* pc_relative */ 104 0, /* bitpos */ 105 complain_overflow_signed, /* complain_on_overflow */ 106 bfd_elf_d30v_reloc_21, /* special_function */ 107 "R_D30V_9_PCREL_R", /* name */ 108 FALSE, /* partial_inplace */ 109 0x3f, /* src_mask */ 110 0x3f, /* dst_mask */ 111 TRUE), /* pcrel_offset */ 112 113 /* An absolute 15 bit relocation, right shifted by 3 */ 114 HOWTO (R_D30V_15, /* type */ 115 3, /* rightshift */ 116 2, /* size (0 = byte, 1 = short, 2 = long) */ 117 12, /* bitsize */ 118 FALSE, /* pc_relative */ 119 0, /* bitpos */ 120 complain_overflow_signed, /* complain_on_overflow */ 121 bfd_elf_generic_reloc, /* special_function */ 122 "R_D30V_15", /* name */ 123 FALSE, /* partial_inplace */ 124 0xfff, /* src_mask */ 125 0xfff, /* dst_mask */ 126 FALSE), /* pcrel_offset */ 127 128 /* A relative 15 bit relocation, right shifted by 3 */ 129 HOWTO (R_D30V_15_PCREL, /* type */ 130 3, /* rightshift */ 131 2, /* size (0 = byte, 1 = short, 2 = long) */ 132 12, /* bitsize */ 133 TRUE, /* pc_relative */ 134 0, /* bitpos */ 135 complain_overflow_signed, /* complain_on_overflow */ 136 bfd_elf_d30v_reloc_21, /* special_function */ 137 "R_D30V_15_PCREL", /* name */ 138 FALSE, /* partial_inplace */ 139 0xfff, /* src_mask */ 140 0xfff, /* dst_mask */ 141 TRUE), /* pcrel_offset */ 142 143 /* A relative 15 bit relocation, right shifted by 3 */ 144 HOWTO (R_D30V_15_PCREL_R, /* type */ 145 3, /* rightshift */ 146 2, /* size (0 = byte, 1 = short, 2 = long) */ 147 12, /* bitsize */ 148 TRUE, /* pc_relative */ 149 0, /* bitpos */ 150 complain_overflow_signed, /* complain_on_overflow */ 151 bfd_elf_d30v_reloc_21, /* special_function */ 152 "R_D30V_15_PCREL_R", /* name */ 153 FALSE, /* partial_inplace */ 154 0xfff, /* src_mask */ 155 0xfff, /* dst_mask */ 156 TRUE), /* pcrel_offset */ 157 158 /* An absolute 21 bit relocation, right shifted by 3 */ 159 HOWTO (R_D30V_21, /* type */ 160 3, /* rightshift */ 161 2, /* size (0 = byte, 1 = short, 2 = long) */ 162 18, /* bitsize */ 163 FALSE, /* pc_relative */ 164 0, /* bitpos */ 165 complain_overflow_signed, /* complain_on_overflow */ 166 bfd_elf_generic_reloc, /* special_function */ 167 "R_D30V_21", /* name */ 168 FALSE, /* partial_inplace */ 169 0x3ffff, /* src_mask */ 170 0x3ffff, /* dst_mask */ 171 FALSE), /* pcrel_offset */ 172 173 /* A relative 21 bit relocation, right shifted by 3 */ 174 HOWTO (R_D30V_21_PCREL, /* type */ 175 3, /* rightshift */ 176 2, /* size (0 = byte, 1 = short, 2 = long) */ 177 18, /* bitsize */ 178 TRUE, /* pc_relative */ 179 0, /* bitpos */ 180 complain_overflow_signed, /* complain_on_overflow */ 181 bfd_elf_d30v_reloc_21, /* special_function */ 182 "R_D30V_21_PCREL", /* name */ 183 FALSE, /* partial_inplace */ 184 0x3ffff, /* src_mask */ 185 0x3ffff, /* dst_mask */ 186 TRUE), /* pcrel_offset */ 187 188 /* A relative 21 bit relocation, right shifted by 3, in the Right container */ 189 HOWTO (R_D30V_21_PCREL_R, /* type */ 190 3, /* rightshift */ 191 2, /* size (0 = byte, 1 = short, 2 = long) */ 192 18, /* bitsize */ 193 TRUE, /* pc_relative */ 194 0, /* bitpos */ 195 complain_overflow_signed, /* complain_on_overflow */ 196 bfd_elf_d30v_reloc_21, /* special_function */ 197 "R_D30V_21_PCREL_R", /* name */ 198 FALSE, /* partial_inplace */ 199 0x3ffff, /* src_mask */ 200 0x3ffff, /* dst_mask */ 201 TRUE), /* pcrel_offset */ 202 203 /* A D30V 32 bit absolute relocation */ 204 HOWTO (R_D30V_32, /* type */ 205 0, /* rightshift */ 206 4, /* size (0 = byte, 1 = short, 2 = long) */ 207 32, /* bitsize */ 208 FALSE, /* pc_relative */ 209 0, /* bitpos */ 210 complain_overflow_bitfield, /* complain_on_overflow */ 211 bfd_elf_d30v_reloc, /* special_function */ 212 "R_D30V_32", /* name */ 213 FALSE, /* partial_inplace */ 214 0xffffffff, /* src_mask */ 215 0xffffffff, /* dst_mask */ 216 FALSE), /* pcrel_offset */ 217 218 /* A relative 32 bit relocation */ 219 HOWTO (R_D30V_32_PCREL, /* type */ 220 0, /* rightshift */ 221 4, /* size (0 = byte, 1 = short, 2 = long) */ 222 32, /* bitsize */ 223 TRUE, /* pc_relative */ 224 0, /* bitpos */ 225 complain_overflow_signed, /* complain_on_overflow */ 226 bfd_elf_d30v_reloc, /* special_function */ 227 "R_D30V_32_PCREL", /* name */ 228 FALSE, /* partial_inplace */ 229 0xffffffff, /* src_mask */ 230 0xffffffff, /* dst_mask */ 231 TRUE), /* pcrel_offset */ 232 233 /* A regular 32 bit absolute relocation */ 234 HOWTO (R_D30V_32_NORMAL, /* type */ 235 0, /* rightshift */ 236 2, /* size (0 = byte, 1 = short, 2 = long) */ 237 32, /* bitsize */ 238 FALSE, /* pc_relative */ 239 0, /* bitpos */ 240 complain_overflow_bitfield, /* complain_on_overflow */ 241 bfd_elf_generic_reloc, /* special_function */ 242 "R_D30V_32_NORMAL", /* name */ 243 FALSE, /* partial_inplace */ 244 0xffffffff, /* src_mask */ 245 0xffffffff, /* dst_mask */ 246 FALSE), /* pcrel_offset */ 247 248 }; 249 250 #define MAX32 ((bfd_signed_vma) 0x7fffffff) 251 #define MIN32 (- MAX32 - 1) 252 253 static bfd_reloc_status_type 254 bfd_elf_d30v_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) 255 bfd *abfd; 256 arelent *reloc_entry; 257 asymbol *symbol; 258 PTR data; 259 asection *input_section; 260 bfd *output_bfd; 261 char **error_message; 262 { 263 bfd_signed_vma relocation; 264 bfd_vma in1, in2, num; 265 bfd_vma tmp_addr = 0; 266 bfd_reloc_status_type r; 267 asection *reloc_target_output_section; 268 bfd_size_type addr = reloc_entry->address; 269 bfd_reloc_status_type flag = bfd_reloc_ok; 270 bfd_vma output_base = 0; 271 reloc_howto_type *howto = reloc_entry->howto; 272 int make_absolute = 0; 273 274 if (output_bfd != (bfd *) NULL) 275 { 276 /* Partial linking -- do nothing. */ 277 reloc_entry->address += input_section->output_offset; 278 return bfd_reloc_ok; 279 } 280 281 r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, 282 input_section, output_bfd, error_message); 283 if (r != bfd_reloc_continue) 284 return r; 285 286 /* a hacked-up version of bfd_perform_reloc() follows */ 287 if (bfd_is_und_section (symbol->section) 288 && (symbol->flags & BSF_WEAK) == 0 289 && output_bfd == (bfd *) NULL) 290 flag = bfd_reloc_undefined; 291 292 /* Is the address of the relocation really within the section? */ 293 if (reloc_entry->address > input_section->_cooked_size) 294 return bfd_reloc_outofrange; 295 296 /* Work out which section the relocation is targeted at and the 297 initial relocation command value. */ 298 299 /* Get symbol value. (Common symbols are special.) */ 300 if (bfd_is_com_section (symbol->section)) 301 relocation = 0; 302 else 303 relocation = symbol->value; 304 305 reloc_target_output_section = symbol->section->output_section; 306 307 /* Convert input-section-relative symbol value to absolute. */ 308 output_base = reloc_target_output_section->vma; 309 relocation += output_base + symbol->section->output_offset; 310 311 /* Add in supplied addend. */ 312 relocation += reloc_entry->addend; 313 314 /* Here the variable relocation holds the final address of the 315 symbol we are relocating against, plus any addend. */ 316 317 if (howto->pc_relative) 318 { 319 tmp_addr = input_section->output_section->vma + input_section->output_offset 320 + reloc_entry->address; 321 relocation -= tmp_addr; 322 } 323 324 in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr); 325 in2 = bfd_get_32 (abfd, (bfd_byte *) data + addr + 4); 326 327 /* extract the addend */ 328 num = ((in2 & 0x3FFFF) 329 | ((in2 & 0xFF00000) >> 2) 330 | ((in1 & 0x3F) << 26)); 331 in1 &= 0xFFFFFFC0; 332 in2 = 0x80000000; 333 334 relocation += num; 335 336 if (howto->pc_relative && howto->bitsize == 32) 337 { 338 /* The D30V has a PC that doesn't wrap and PC-relative jumps are 339 signed, so a PC-relative jump can't be more than +/- 2^31 bytes. 340 If one exceeds this, change it to an absolute jump. */ 341 if (relocation > MAX32 || relocation < MIN32) 342 { 343 relocation = (relocation + tmp_addr) & 0xffffffff; 344 make_absolute = 1; 345 } 346 } 347 348 in1 |= (relocation >> 26) & 0x3F; /* top 6 bits */ 349 in2 |= ((relocation & 0x03FC0000) << 2); /* next 8 bits */ 350 in2 |= relocation & 0x0003FFFF; /* bottom 18 bits */ 351 352 /* change a PC-relative instruction to its absolute equivalent */ 353 /* with this simple hack */ 354 if (make_absolute) 355 in1 |= 0x00100000; 356 357 bfd_put_32 (abfd, in1, (bfd_byte *) data + addr); 358 bfd_put_32 (abfd, in2, (bfd_byte *) data + addr + 4); 359 360 return flag; 361 } 362 363 static bfd_reloc_status_type 364 bfd_elf_d30v_reloc_21 (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message) 365 bfd *abfd; 366 arelent *reloc_entry; 367 asymbol *symbol; 368 PTR data; 369 asection *input_section; 370 bfd *output_bfd; 371 char **error_message; 372 { 373 bfd_vma relocation; 374 bfd_vma in1, num; 375 bfd_reloc_status_type r; 376 asection *reloc_target_output_section; 377 bfd_size_type addr = reloc_entry->address; 378 bfd_reloc_status_type flag = bfd_reloc_ok; 379 bfd_vma output_base = 0; 380 reloc_howto_type *howto = reloc_entry->howto; 381 int mask, max; 382 383 if (output_bfd != (bfd *) NULL) 384 { 385 /* Partial linking -- do nothing. */ 386 reloc_entry->address += input_section->output_offset; 387 return bfd_reloc_ok; 388 } 389 390 r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, 391 input_section, output_bfd, error_message); 392 if (r != bfd_reloc_continue) 393 return r; 394 395 /* a hacked-up version of bfd_perform_reloc() follows */ 396 if (bfd_is_und_section (symbol->section) 397 && (symbol->flags & BSF_WEAK) == 0 398 && output_bfd == (bfd *) NULL) 399 flag = bfd_reloc_undefined; 400 401 /* Is the address of the relocation really within the section? */ 402 if (reloc_entry->address > input_section->_cooked_size) 403 return bfd_reloc_outofrange; 404 405 /* Work out which section the relocation is targeted at and the 406 initial relocation command value. */ 407 408 /* Get symbol value. (Common symbols are special.) */ 409 if (bfd_is_com_section (symbol->section)) 410 relocation = 0; 411 else 412 relocation = symbol->value; 413 414 reloc_target_output_section = symbol->section->output_section; 415 416 /* Convert input-section-relative symbol value to absolute. */ 417 output_base = reloc_target_output_section->vma; 418 relocation += output_base + symbol->section->output_offset; 419 420 /* Add in supplied addend. */ 421 relocation += reloc_entry->addend; 422 423 /* Here the variable relocation holds the final address of the 424 symbol we are relocating against, plus any addend. */ 425 426 if (howto->pc_relative) 427 { 428 relocation -= (input_section->output_section->vma 429 + input_section->output_offset); 430 if (howto->pcrel_offset) 431 relocation -= reloc_entry->address; 432 } 433 434 in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr); 435 436 mask = (1 << howto->bitsize) - 1; 437 if (howto->bitsize == 6) 438 mask <<= 12; 439 max = (1 << (howto->bitsize + 2)) - 1; 440 441 /* extract the addend */ 442 num = in1 & mask; /* 18 bits */ 443 if (howto->bitsize == 6) 444 num >>= 12; 445 num <<= 3; /* shift left 3 */ 446 in1 &= ~mask; /* mask out addend */ 447 448 relocation += num; 449 if (howto->type == R_D30V_21_PCREL_R || howto->type == R_D30V_15_PCREL_R || 450 howto->type == R_D30V_9_PCREL_R ) 451 { 452 relocation += 4; 453 } 454 455 if ((int)relocation < 0 ) 456 { 457 if (~(int)relocation > max) 458 flag = bfd_reloc_overflow; 459 } 460 else 461 { 462 if ((int)relocation > max) 463 flag = bfd_reloc_overflow; 464 } 465 relocation >>= 3; 466 if (howto->bitsize == 6) 467 in1 |= ((relocation & (mask >> 12)) << 12); 468 else 469 in1 |= relocation & mask; 470 471 bfd_put_32 (abfd, in1, (bfd_byte *) data + addr); 472 473 return flag; 474 } 475 476 /* Map BFD reloc types to D30V ELF reloc types. */ 477 478 struct d30v_reloc_map 479 { 480 bfd_reloc_code_real_type bfd_reloc_val; 481 unsigned char elf_reloc_val; 482 }; 483 484 static const struct d30v_reloc_map d30v_reloc_map[] = 485 { 486 { BFD_RELOC_NONE, R_D30V_NONE, }, 487 { BFD_RELOC_D30V_6, R_D30V_6 }, 488 { BFD_RELOC_D30V_9_PCREL, R_D30V_9_PCREL }, 489 { BFD_RELOC_D30V_9_PCREL_R, R_D30V_9_PCREL_R }, 490 { BFD_RELOC_D30V_15, R_D30V_15 }, 491 { BFD_RELOC_D30V_15_PCREL, R_D30V_15_PCREL }, 492 { BFD_RELOC_D30V_15_PCREL_R, R_D30V_15_PCREL_R }, 493 { BFD_RELOC_D30V_21, R_D30V_21 }, 494 { BFD_RELOC_D30V_21_PCREL, R_D30V_21_PCREL }, 495 { BFD_RELOC_D30V_21_PCREL_R, R_D30V_21_PCREL_R }, 496 { BFD_RELOC_D30V_32, R_D30V_32 }, 497 { BFD_RELOC_D30V_32_PCREL, R_D30V_32_PCREL }, 498 { BFD_RELOC_32, R_D30V_32_NORMAL }, 499 }; 500 501 static reloc_howto_type * 502 bfd_elf32_bfd_reloc_type_lookup (abfd, code) 503 bfd *abfd ATTRIBUTE_UNUSED; 504 bfd_reloc_code_real_type code; 505 { 506 unsigned int i; 507 508 for (i = 0; 509 i < sizeof (d30v_reloc_map) / sizeof (struct d30v_reloc_map); 510 i++) 511 { 512 if (d30v_reloc_map[i].bfd_reloc_val == code) 513 return &elf_d30v_howto_table[d30v_reloc_map[i].elf_reloc_val]; 514 } 515 516 return NULL; 517 } 518 519 /* Set the howto pointer for an D30V ELF reloc (type REL). */ 520 521 static void 522 d30v_info_to_howto_rel (abfd, cache_ptr, dst) 523 bfd *abfd ATTRIBUTE_UNUSED; 524 arelent *cache_ptr; 525 Elf_Internal_Rela *dst; 526 { 527 unsigned int r_type; 528 529 r_type = ELF32_R_TYPE (dst->r_info); 530 BFD_ASSERT (r_type < (unsigned int) R_D30V_max); 531 cache_ptr->howto = &elf_d30v_howto_table[r_type]; 532 } 533 534 /* Set the howto pointer for an D30V ELF reloc (type RELA). */ 535 536 static void 537 d30v_info_to_howto_rela (abfd, cache_ptr, dst) 538 bfd *abfd ATTRIBUTE_UNUSED; 539 arelent *cache_ptr; 540 Elf_Internal_Rela *dst; 541 { 542 unsigned int r_type; 543 544 r_type = ELF32_R_TYPE (dst->r_info); 545 BFD_ASSERT (r_type < (unsigned int) R_D30V_max); 546 cache_ptr->howto = &elf_d30v_howto_table[r_type]; 547 } 548 549 #define ELF_ARCH bfd_arch_d30v 550 #define ELF_MACHINE_CODE EM_D30V 551 #define ELF_MACHINE_ALT1 EM_CYGNUS_D30V 552 #define ELF_MAXPAGESIZE 0x1000 553 554 #define TARGET_BIG_SYM bfd_elf32_d30v_vec 555 #define TARGET_BIG_NAME "elf32-d30v" 556 557 #define elf_info_to_howto d30v_info_to_howto_rela 558 #define elf_info_to_howto_rel d30v_info_to_howto_rel 559 #define elf_backend_object_p 0 560 #define elf_backend_final_write_processing 0 561 562 #include "elf32-target.h" 563