1 /* BFD back-end for raw ARM a.out binaries. 2 Copyright (C) 1994-2014 Free Software Foundation, Inc. 3 Contributed by Richard Earnshaw (rwe (at) pegasus.esprit.ec.org) 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, MA 02110-1301, USA. */ 20 21 #include "sysdep.h" 22 #include "bfd.h" 23 24 /* Avoid multiple definitions from aoutx if supporting standard a.out 25 as well as our own. */ 26 /* Do not "beautify" the CONCAT* macro args. Traditional C will not 27 remove whitespace added here, and thus will fail to concatenate 28 the tokens. */ 29 #define NAME(x,y) CONCAT3 (aoutarm,_32_,y) 30 31 #define N_TXTADDR(x) \ 32 ((N_MAGIC (x) == NMAGIC) \ 33 ? (bfd_vma) 0x8000 \ 34 : ((N_MAGIC (x) != ZMAGIC) \ 35 ? (bfd_vma) 0 \ 36 : ((N_SHARED_LIB (x)) \ 37 ? ((x).a_entry & ~(bfd_vma) (TARGET_PAGE_SIZE - 1)) \ 38 : (bfd_vma) TEXT_START_ADDR))) 39 40 #define TEXT_START_ADDR 0x8000 41 #define TARGET_PAGE_SIZE 0x8000 42 #define SEGMENT_SIZE TARGET_PAGE_SIZE 43 #define DEFAULT_ARCH bfd_arch_arm 44 45 #define MY(OP) CONCAT2 (arm_aout_,OP) 46 #define N_BADMAG(x) ((((x).a_info & ~007200) != ZMAGIC) && \ 47 (((x).a_info & ~006000) != OMAGIC) && \ 48 ((x).a_info != NMAGIC)) 49 #define N_MAGIC(x) ((x).a_info & ~07200) 50 51 #define MY_bfd_reloc_type_lookup arm_aout_bfd_reloc_type_lookup 52 #define MY_bfd_reloc_name_lookup arm_aout_bfd_reloc_name_lookup 53 54 #include "libaout.h" 55 #include "aout/aout64.h" 56 57 58 static bfd_reloc_status_type 60 MY (fix_pcrel_26) (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); 61 static bfd_reloc_status_type 62 MY (fix_pcrel_26_done) (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); 63 64 reloc_howto_type MY (howto_table)[] = 65 { 66 /* Type rs size bsz pcrel bitpos ovrf sf name part_inpl 67 readmask setmask pcdone. */ 68 HOWTO (0, 0, 0, 8, FALSE, 0, complain_overflow_bitfield, 0, "8", TRUE, 69 0x000000ff, 0x000000ff, FALSE), 70 HOWTO (1, 0, 1, 16, FALSE, 0, complain_overflow_bitfield, 0, "16", TRUE, 71 0x0000ffff, 0x0000ffff, FALSE), 72 HOWTO (2, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, 0, "32", TRUE, 73 0xffffffff, 0xffffffff, FALSE), 74 HOWTO (3, 2, 2, 26, TRUE, 0, complain_overflow_signed, MY (fix_pcrel_26), 75 "ARM26", TRUE, 0x00ffffff, 0x00ffffff, TRUE), 76 HOWTO (4, 0, 0, 8, TRUE, 0, complain_overflow_signed, 0, "DISP8", TRUE, 77 0x000000ff, 0x000000ff, TRUE), 78 HOWTO (5, 0, 1, 16, TRUE, 0, complain_overflow_signed, 0, "DISP16", TRUE, 79 0x0000ffff, 0x0000ffff, TRUE), 80 HOWTO (6, 0, 2, 32, TRUE, 0, complain_overflow_signed, 0, "DISP32", TRUE, 81 0xffffffff, 0xffffffff, TRUE), 82 HOWTO (7, 2, 2, 26, FALSE, 0, complain_overflow_signed, 83 MY (fix_pcrel_26_done), "ARM26D", TRUE, 0x0, 0x0, 84 FALSE), 85 EMPTY_HOWTO (-1), 86 HOWTO (9, 0, -1, 16, FALSE, 0, complain_overflow_bitfield, 0, "NEG16", TRUE, 87 0x0000ffff, 0x0000ffff, FALSE), 88 HOWTO (10, 0, -2, 32, FALSE, 0, complain_overflow_bitfield, 0, "NEG32", TRUE, 89 0xffffffff, 0xffffffff, FALSE) 90 }; 91 92 #define RELOC_ARM_BITS_NEG_BIG ((unsigned int) 0x08) 93 #define RELOC_ARM_BITS_NEG_LITTLE ((unsigned int) 0x10) 94 95 static reloc_howto_type * 96 MY (reloc_howto) (bfd *abfd, 97 struct reloc_std_external *rel, 98 int *r_index, 99 int *r_extern, 100 int *r_pcrel) 101 { 102 unsigned int r_length; 103 unsigned int r_pcrel_done; 104 unsigned int r_neg; 105 int howto_index; 106 107 *r_pcrel = 0; 108 if (bfd_header_big_endian (abfd)) 109 { 110 *r_index = ((rel->r_index[0] << 16) 111 | (rel->r_index[1] << 8) 112 | rel->r_index[2]); 113 *r_extern = (0 != (rel->r_type[0] & RELOC_STD_BITS_EXTERN_BIG)); 114 r_pcrel_done = (0 != (rel->r_type[0] & RELOC_STD_BITS_PCREL_BIG)); 115 r_neg = (0 != (rel->r_type[0] & RELOC_ARM_BITS_NEG_BIG)); 116 r_length = ((rel->r_type[0] & RELOC_STD_BITS_LENGTH_BIG) 117 >> RELOC_STD_BITS_LENGTH_SH_BIG); 118 } 119 else 120 { 121 *r_index = ((rel->r_index[2] << 16) 122 | (rel->r_index[1] << 8) 123 | rel->r_index[0]); 124 *r_extern = (0 != (rel->r_type[0] & RELOC_STD_BITS_EXTERN_LITTLE)); 125 r_pcrel_done = (0 != (rel->r_type[0] & RELOC_STD_BITS_PCREL_LITTLE)); 126 r_neg = (0 != (rel->r_type[0] & RELOC_ARM_BITS_NEG_LITTLE)); 127 r_length = ((rel->r_type[0] & RELOC_STD_BITS_LENGTH_LITTLE) 128 >> RELOC_STD_BITS_LENGTH_SH_LITTLE); 129 } 130 howto_index = r_length + 4 * r_pcrel_done + 8 * r_neg; 131 if (howto_index == 3) 132 *r_pcrel = 1; 133 134 return MY (howto_table) + howto_index; 135 } 136 137 #define MY_reloc_howto(BFD, REL, IN, EX, PC) \ 138 MY (reloc_howto) (BFD, REL, &IN, &EX, &PC) 139 140 static void 141 MY (put_reloc) (bfd *abfd, 142 int r_extern, 143 int r_index, 144 bfd_vma value, 145 reloc_howto_type *howto, 146 struct reloc_std_external *reloc) 147 { 148 unsigned int r_length; 149 int r_pcrel; 150 int r_neg; 151 152 PUT_WORD (abfd, value, reloc->r_address); 153 /* Size as a power of two. */ 154 r_length = howto->size; 155 156 /* Special case for branch relocations. */ 157 if (howto->type == 3 || howto->type == 7) 158 r_length = 3; 159 160 r_pcrel = howto->type & 4; /* PC Relative done? */ 161 r_neg = howto->type & 8; /* Negative relocation. */ 162 163 if (bfd_header_big_endian (abfd)) 164 { 165 reloc->r_index[0] = r_index >> 16; 166 reloc->r_index[1] = r_index >> 8; 167 reloc->r_index[2] = r_index; 168 reloc->r_type[0] = 169 ((r_extern ? RELOC_STD_BITS_EXTERN_BIG : 0) 170 | (r_pcrel ? RELOC_STD_BITS_PCREL_BIG : 0) 171 | (r_neg ? RELOC_ARM_BITS_NEG_BIG : 0) 172 | (r_length << RELOC_STD_BITS_LENGTH_SH_BIG)); 173 } 174 else 175 { 176 reloc->r_index[2] = r_index >> 16; 177 reloc->r_index[1] = r_index >> 8; 178 reloc->r_index[0] = r_index; 179 reloc->r_type[0] = 180 ((r_extern ? RELOC_STD_BITS_EXTERN_LITTLE : 0) 181 | (r_pcrel ? RELOC_STD_BITS_PCREL_LITTLE : 0) 182 | (r_neg ? RELOC_ARM_BITS_NEG_LITTLE : 0) 183 | (r_length << RELOC_STD_BITS_LENGTH_SH_LITTLE)); 184 } 185 } 186 187 #define MY_put_reloc(BFD, EXT, IDX, VAL, HOWTO, RELOC) \ 188 MY (put_reloc) (BFD, EXT, IDX, VAL, HOWTO, RELOC) 189 190 static void 191 MY (relocatable_reloc) (reloc_howto_type *howto, 192 bfd *abfd, 193 struct reloc_std_external *reloc, 194 bfd_vma *amount, 195 bfd_vma r_addr) 196 { 197 if (howto->type == 3) 198 { 199 if (reloc->r_type[0] 200 & (bfd_header_big_endian (abfd) 201 ? RELOC_STD_BITS_EXTERN_BIG : RELOC_STD_BITS_EXTERN_LITTLE)) 202 /* The reloc is still external, so don't modify anything. */ 203 *amount = 0; 204 else 205 { 206 *amount -= r_addr; 207 /* Change the r_pcrel value -- on the ARM, this bit is set once the 208 relocation is done. */ 209 if (bfd_header_big_endian (abfd)) 210 reloc->r_type[0] |= RELOC_STD_BITS_PCREL_BIG; 211 else 212 reloc->r_type[0] |= RELOC_STD_BITS_PCREL_LITTLE; 213 } 214 } 215 else if (howto->type == 7) 216 *amount = 0; 217 } 218 219 #define MY_relocatable_reloc(HOW, BFD, REL, AMOUNT, ADDR) \ 220 MY (relocatable_reloc) (HOW, BFD, REL, &(AMOUNT), ADDR) 221 222 static bfd_reloc_status_type 223 MY (fix_pcrel_26_done) (bfd *abfd ATTRIBUTE_UNUSED, 224 arelent *reloc_entry ATTRIBUTE_UNUSED, 225 asymbol *symbol ATTRIBUTE_UNUSED, 226 void * data ATTRIBUTE_UNUSED, 227 asection *input_section ATTRIBUTE_UNUSED, 228 bfd *output_bfd ATTRIBUTE_UNUSED, 229 char **error_message ATTRIBUTE_UNUSED) 230 { 231 /* This is dead simple at present. */ 232 return bfd_reloc_ok; 233 } 234 235 static bfd_reloc_status_type 236 MY (fix_pcrel_26) (bfd *abfd, 237 arelent *reloc_entry, 238 asymbol *symbol, 239 void * data, 240 asection *input_section, 241 bfd *output_bfd, 242 char **error_message ATTRIBUTE_UNUSED) 243 { 244 bfd_vma relocation; 245 bfd_size_type addr = reloc_entry->address; 246 bfd_vma target = bfd_get_32 (abfd, (bfd_byte *) data + addr); 247 bfd_reloc_status_type flag = bfd_reloc_ok; 248 249 /* If this is an undefined symbol, return error. */ 250 if (bfd_is_und_section (symbol->section) 251 && (symbol->flags & BSF_WEAK) == 0) 252 return output_bfd ? bfd_reloc_ok : bfd_reloc_undefined; 253 254 /* If the sections are different, and we are doing a partial relocation, 255 just ignore it for now. */ 256 if (symbol->section->name != input_section->name 257 && output_bfd != NULL) 258 return bfd_reloc_ok; 259 260 relocation = (target & 0x00ffffff) << 2; 261 relocation = (relocation ^ 0x02000000) - 0x02000000; /* Sign extend. */ 262 relocation += symbol->value; 263 relocation += symbol->section->output_section->vma; 264 relocation += symbol->section->output_offset; 265 relocation += reloc_entry->addend; 266 relocation -= input_section->output_section->vma; 267 relocation -= input_section->output_offset; 268 relocation -= addr; 269 if (relocation & 3) 270 return bfd_reloc_overflow; 271 272 /* Check for overflow. */ 273 if (relocation & 0x02000000) 274 { 275 if ((relocation & ~ (bfd_vma) 0x03ffffff) != ~ (bfd_vma) 0x03ffffff) 276 flag = bfd_reloc_overflow; 277 } 278 else if (relocation & ~ (bfd_vma) 0x03ffffff) 279 flag = bfd_reloc_overflow; 280 281 target &= ~ (bfd_vma) 0x00ffffff; 282 target |= (relocation >> 2) & 0x00ffffff; 283 bfd_put_32 (abfd, target, (bfd_byte *) data + addr); 284 285 /* Now the ARM magic... Change the reloc type so that it is marked as done. 286 Strictly this is only necessary if we are doing a partial relocation. */ 287 reloc_entry->howto = &MY (howto_table)[7]; 288 289 return flag; 290 } 291 292 static reloc_howto_type * 293 MY (bfd_reloc_type_lookup) (bfd *abfd, 294 bfd_reloc_code_real_type code) 295 { 296 #define ASTD(i,j) case i: return & MY (howto_table)[j] 297 298 if (code == BFD_RELOC_CTOR) 299 switch (bfd_arch_bits_per_address (abfd)) 300 { 301 case 32: 302 code = BFD_RELOC_32; 303 break; 304 default: 305 return NULL; 306 } 307 308 switch (code) 309 { 310 ASTD (BFD_RELOC_16, 1); 311 ASTD (BFD_RELOC_32, 2); 312 ASTD (BFD_RELOC_ARM_PCREL_BRANCH, 3); 313 ASTD (BFD_RELOC_8_PCREL, 4); 314 ASTD (BFD_RELOC_16_PCREL, 5); 315 ASTD (BFD_RELOC_32_PCREL, 6); 316 default: 317 return NULL; 318 } 319 } 320 321 static reloc_howto_type * 322 MY (bfd_reloc_name_lookup) (bfd *abfd ATTRIBUTE_UNUSED, 323 const char *r_name) 324 { 325 unsigned int i; 326 327 for (i = 0; 328 i < sizeof (MY (howto_table)) / sizeof (MY (howto_table)[0]); 329 i++) 330 if (MY (howto_table)[i].name != NULL 331 && strcasecmp (MY (howto_table)[i].name, r_name) == 0) 332 return &MY (howto_table)[i]; 333 334 return NULL; 335 } 336 337 #define MY_swap_std_reloc_in MY (swap_std_reloc_in) 338 #define MY_swap_std_reloc_out MY (swap_std_reloc_out) 339 #define MY_get_section_contents _bfd_generic_get_section_contents 340 341 void MY_swap_std_reloc_in (bfd *, struct reloc_std_external *, arelent *, asymbol **, bfd_size_type); 342 void MY_swap_std_reloc_out (bfd *, arelent *, struct reloc_std_external *); 343 344 #include "aoutx.h" 345 346 void 347 MY_swap_std_reloc_in (bfd *abfd, 348 struct reloc_std_external *bytes, 349 arelent *cache_ptr, 350 asymbol **symbols, 351 bfd_size_type symcount ATTRIBUTE_UNUSED) 352 { 353 int r_index; 354 int r_extern; 355 int r_pcrel; 356 struct aoutdata *su = &(abfd->tdata.aout_data->a); 357 358 cache_ptr->address = H_GET_32 (abfd, bytes->r_address); 359 360 cache_ptr->howto = MY_reloc_howto (abfd, bytes, r_index, r_extern, r_pcrel); 361 362 MOVE_ADDRESS (0); 363 } 364 365 void 366 MY_swap_std_reloc_out (bfd *abfd, 367 arelent *g, 368 struct reloc_std_external *natptr) 369 { 370 int r_index; 371 asymbol *sym = *(g->sym_ptr_ptr); 372 int r_extern; 373 int r_length; 374 int r_pcrel; 375 int r_neg = 0; /* Negative relocs use the BASEREL bit. */ 376 asection *output_section = sym->section->output_section; 377 378 PUT_WORD (abfd, g->address, natptr->r_address); 379 380 r_length = g->howto->size ; /* Size as a power of two. */ 381 if (r_length < 0) 382 { 383 r_length = -r_length; 384 r_neg = 1; 385 } 386 387 r_pcrel = (int) g->howto->pc_relative; /* Relative to PC? */ 388 389 /* For RISC iX, in pc-relative relocs the r_pcrel bit means that the 390 relocation has been done already (Only for the 26-bit one I think). */ 391 if (g->howto->type == 3) 392 { 393 r_length = 3; 394 r_pcrel = 0; 395 } 396 else if (g->howto->type == 7) 397 { 398 r_length = 3; 399 r_pcrel = 1; 400 } 401 402 /* Name was clobbered by aout_write_syms to be symbol index. */ 403 404 /* If this relocation is relative to a symbol then set the 405 r_index to the symbols index, and the r_extern bit. 406 407 Absolute symbols can come in in two ways, either as an offset 408 from the abs section, or as a symbol which has an abs value. 409 check for that here. */ 410 411 if (bfd_is_com_section (output_section) 412 || bfd_is_abs_section (output_section) 413 || bfd_is_und_section (output_section)) 414 { 415 if (bfd_abs_section_ptr->symbol == sym) 416 { 417 /* Whoops, looked like an abs symbol, but is really an offset 418 from the abs section. */ 419 r_index = 0; 420 r_extern = 0; 421 } 422 else 423 { 424 /* Fill in symbol. */ 425 r_extern = 1; 426 r_index = (*(g->sym_ptr_ptr))->KEEPIT; 427 } 428 } 429 else 430 { 431 /* Just an ordinary section. */ 432 r_extern = 0; 433 r_index = output_section->target_index; 434 } 435 436 /* Now the fun stuff. */ 437 if (bfd_header_big_endian (abfd)) 438 { 439 natptr->r_index[0] = r_index >> 16; 440 natptr->r_index[1] = r_index >> 8; 441 natptr->r_index[2] = r_index; 442 natptr->r_type[0] = 443 ( (r_extern ? RELOC_STD_BITS_EXTERN_BIG: 0) 444 | (r_pcrel ? RELOC_STD_BITS_PCREL_BIG: 0) 445 | (r_neg ? RELOC_ARM_BITS_NEG_BIG: 0) 446 | (r_length << RELOC_STD_BITS_LENGTH_SH_BIG)); 447 } 448 else 449 { 450 natptr->r_index[2] = r_index >> 16; 451 natptr->r_index[1] = r_index >> 8; 452 natptr->r_index[0] = r_index; 453 natptr->r_type[0] = 454 ( (r_extern ? RELOC_STD_BITS_EXTERN_LITTLE: 0) 455 | (r_pcrel ? RELOC_STD_BITS_PCREL_LITTLE: 0) 456 | (r_neg ? RELOC_ARM_BITS_NEG_LITTLE: 0) 457 | (r_length << RELOC_STD_BITS_LENGTH_SH_LITTLE)); 458 } 459 } 460 461 #define MY_BFD_TARGET 462 463 #include "aout-target.h" 464 465 extern const bfd_target arm_aout_be_vec; 466 467 const bfd_target arm_aout_le_vec = 468 { 469 "a.out-arm-little", /* Name. */ 470 bfd_target_aout_flavour, 471 BFD_ENDIAN_LITTLE, /* Target byte order (little). */ 472 BFD_ENDIAN_LITTLE, /* Target headers byte order (little). */ 473 (HAS_RELOC | EXEC_P | /* Object flags. */ 474 HAS_LINENO | HAS_DEBUG | 475 HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), 476 (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA), 477 MY_symbol_leading_char, 478 AR_PAD_CHAR, /* AR_pad_char. */ 479 15, /* AR_max_namelen. */ 480 0, /* match priority. */ 481 bfd_getl64, bfd_getl_signed_64, bfd_putl64, 482 bfd_getl32, bfd_getl_signed_32, bfd_putl32, 483 bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Data. */ 484 bfd_getl64, bfd_getl_signed_64, bfd_putl64, 485 bfd_getl32, bfd_getl_signed_32, bfd_putl32, 486 bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* Headers. */ 487 {_bfd_dummy_target, MY_object_p, /* bfd_check_format. */ 488 bfd_generic_archive_p, MY_core_file_p}, 489 {bfd_false, MY_mkobject, /* bfd_set_format. */ 490 _bfd_generic_mkarchive, bfd_false}, 491 {bfd_false, MY_write_object_contents, /* bfd_write_contents. */ 492 _bfd_write_archive_contents, bfd_false}, 493 494 BFD_JUMP_TABLE_GENERIC (MY), 495 BFD_JUMP_TABLE_COPY (MY), 496 BFD_JUMP_TABLE_CORE (MY), 497 BFD_JUMP_TABLE_ARCHIVE (MY), 498 BFD_JUMP_TABLE_SYMBOLS (MY), 499 BFD_JUMP_TABLE_RELOCS (MY), 500 BFD_JUMP_TABLE_WRITE (MY), 501 BFD_JUMP_TABLE_LINK (MY), 502 BFD_JUMP_TABLE_DYNAMIC (MY), 503 504 & arm_aout_be_vec, 505 506 (void *) MY_backend_data, 507 }; 508 509 const bfd_target arm_aout_be_vec = 510 { 511 "a.out-arm-big", /* Name. */ 512 bfd_target_aout_flavour, 513 BFD_ENDIAN_BIG, /* Target byte order (big). */ 514 BFD_ENDIAN_BIG, /* Target headers byte order (big). */ 515 (HAS_RELOC | EXEC_P | /* Object flags. */ 516 HAS_LINENO | HAS_DEBUG | 517 HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT | D_PAGED), 518 (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA), 519 MY_symbol_leading_char, 520 AR_PAD_CHAR, /* AR_pad_char. */ 521 15, /* AR_max_namelen. */ 522 0, /* match priority. */ 523 bfd_getb64, bfd_getb_signed_64, bfd_putb64, 524 bfd_getb32, bfd_getb_signed_32, bfd_putb32, 525 bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Data. */ 526 bfd_getb64, bfd_getb_signed_64, bfd_putb64, 527 bfd_getb32, bfd_getb_signed_32, bfd_putb32, 528 bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* Headers. */ 529 {_bfd_dummy_target, MY_object_p, /* bfd_check_format. */ 530 bfd_generic_archive_p, MY_core_file_p}, 531 {bfd_false, MY_mkobject, /* bfd_set_format. */ 532 _bfd_generic_mkarchive, bfd_false}, 533 {bfd_false, MY_write_object_contents, /* bfd_write_contents. */ 534 _bfd_write_archive_contents, bfd_false}, 535 536 BFD_JUMP_TABLE_GENERIC (MY), 537 BFD_JUMP_TABLE_COPY (MY), 538 BFD_JUMP_TABLE_CORE (MY), 539 BFD_JUMP_TABLE_ARCHIVE (MY), 540 BFD_JUMP_TABLE_SYMBOLS (MY), 541 BFD_JUMP_TABLE_RELOCS (MY), 542 BFD_JUMP_TABLE_WRITE (MY), 543 BFD_JUMP_TABLE_LINK (MY), 544 BFD_JUMP_TABLE_DYNAMIC (MY), 545 546 & arm_aout_le_vec, 547 548 (void *) MY_backend_data, 549 }; 550