1/* BFD back-end for MAXQ COFF binaries. 2 Copyright 2004, 2007 Free Software Foundation, Inc. 3 4 Contributed by Vineet Sharma (vineets@noida.hcltech.com) Inderpreet S. 5 (inderpreetb@noida.hcltech.com) 6 7 HCL Technologies Ltd. 8 9 This file is part of BFD, the Binary File Descriptor library. 10 11 This program is free software; you can redistribute it and/or modify it 12 under the terms of the GNU General Public License as published by the Free 13 Software Foundation; either version 3 of the License, or (at your option) 14 any later version. 15 16 This program is distributed in the hope that it will be useful, but 17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 19 for more details. 20 21 You should have received a copy of the GNU General Public License along 22 with this program; if not, write to the Free Software Foundation, Inc., 23 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 24 25#include "sysdep.h" 26#include "bfd.h" 27#include "libbfd.h" 28#include "coff/maxq.h" 29#include "coff/internal.h" 30#include "libcoff.h" 31#include "libiberty.h" 32 33#ifndef MAXQ20 34#define MAXQ20 1 35#endif 36 37#define RTYPE2HOWTO(cache_ptr, dst) \ 38 ((cache_ptr)->howto = \ 39 ((dst)->r_type < 48 \ 40 ? howto_table + (((dst)->r_type==47) ? 6: ((dst)->r_type)) \ 41 : NULL)) 42 43#define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2) 44 45/* Code to swap in the reloc offset. */ 46#define SWAP_IN_RELOC_OFFSET H_GET_16 47#define SWAP_OUT_RELOC_OFFSET H_PUT_16 48 49#define SHORT_JUMP BFD_RELOC_16_PCREL_S2 50#define LONG_JUMP BFD_RELOC_14 51#define ABSOLUTE_ADDR_FOR_DATA BFD_RELOC_24 52 53/* checks the range of short jump -127 to 128 */ 54#define IS_SJUMP_RANGE(x) ((x > -128) && (x < 129)) 55#define HIGH_WORD_MASK 0xff00 56#define LOW_WORD_MASK 0x00ff 57 58static long 59get_symbol_value (asymbol *symbol) 60{ 61 long relocation = 0; 62 63 if (bfd_is_com_section (symbol->section)) 64 relocation = 0; 65 else 66 relocation = symbol->value + 67 symbol->section->output_section->vma + symbol->section->output_offset; 68 69 return relocation; 70} 71 72/* This function performs all the maxq relocations. 73 FIXME: The handling of the addend in the 'BFD_*' 74 relocations types. */ 75 76static bfd_reloc_status_type 77coff_maxq20_reloc (bfd * abfd, 78 arelent * reloc_entry, 79 asymbol * symbol_in, 80 void * data, 81 asection * input_section ATTRIBUTE_UNUSED, 82 bfd * output_bfd ATTRIBUTE_UNUSED, 83 char ** error_message ATTRIBUTE_UNUSED) 84{ 85 unsigned char *addr = NULL; 86 unsigned long x = 0; 87 long call_addr = 0; 88 short addend = 0; 89 long diff = 0; 90 91 /* If this is an undefined symbol, return error. */ 92 if (symbol_in->section == &bfd_und_section 93 && (symbol_in->flags & BSF_WEAK) == 0) 94 return bfd_reloc_continue; 95 96 if (data && reloc_entry) 97 { 98 addr = (unsigned char *) data + reloc_entry->address; 99 call_addr = call_addr - call_addr; 100 call_addr = get_symbol_value (symbol_in); 101 102 /* Over here the value val stores the 8 bit/16 bit value. We will put a 103 check if we are moving a 16 bit immediate value into an 8 bit 104 register. In that case we will generate a Upper bytes into PFX[0] 105 and move the lower 8 bits as SRC. */ 106 107 switch (reloc_entry->howto->type) 108 { 109 /* BFD_RELOC_16_PCREL_S2 47 Handles all the relative jumps and 110 calls Note: Every relative jump or call is in words. */ 111 case SHORT_JUMP: 112 /* Handle any addend. */ 113 addend = reloc_entry->addend; 114 115 if (addend > call_addr || addend > 0) 116 call_addr = symbol_in->section->output_section->vma + addend; 117 else if (addend < call_addr && addend > 0) 118 call_addr = call_addr + addend; 119 else if (addend < 0) 120 call_addr = call_addr + addend; 121 122 diff = ((call_addr << 1) - (reloc_entry->address << 1)); 123 124 if (!IS_SJUMP_RANGE (diff)) 125 { 126 bfd_perror (_("Can't Make it a Short Jump")); 127 return bfd_reloc_outofrange; 128 } 129 130 x = bfd_get_16 (abfd, addr); 131 132 x = x & LOW_WORD_MASK; 133 x = x | (diff << 8); 134 bfd_put_16 (abfd, (bfd_vma) x, addr); 135 136 return bfd_reloc_ok; 137 138 case ABSOLUTE_ADDR_FOR_DATA: 139 case LONG_JUMP: 140 /* BFD_RELOC_14 Handles intersegment or long jumps which might be 141 from code to code or code to data segment jumps. Note: When this 142 fucntion is called by gas the section flags somehow do not 143 contain the info about the section type(CODE or DATA). Thus the 144 user needs to evoke the linker after assembling the files 145 because the Code-Code relocs are word aligned but code-data are 146 byte aligned. */ 147 addend = (reloc_entry->addend - reloc_entry->addend); 148 149 /* Handle any addend. */ 150 addend = reloc_entry->addend; 151 152 /* For relocation involving multiple file added becomes zero thus 153 this fails - check for zero added. In another case when we try 154 to add a stub to a file the addend shows the offset from the 155 start od this file. */ 156 addend = 0; 157 158 if (!bfd_is_com_section (symbol_in->section) && 159 ((symbol_in->flags & BSF_OLD_COMMON) == 0)) 160 { 161 if (reloc_entry->addend > symbol_in->value) 162 addend = reloc_entry->addend - symbol_in->value; 163 164 if ((reloc_entry->addend < symbol_in->value) 165 && (reloc_entry->addend != 0)) 166 addend = reloc_entry->addend - symbol_in->value; 167 168 if (reloc_entry->addend == symbol_in->value) 169 addend = 0; 170 } 171 172 if (bfd_is_com_section (symbol_in->section) || 173 ((symbol_in->flags & BSF_OLD_COMMON) != 0)) 174 addend = reloc_entry->addend; 175 176 if (addend < 0 177 && (call_addr < (long) (addend * (-1)))) 178 addend = 0; 179 180 call_addr += addend; 181 182 /* FIXME: This check does not work well with the assembler, 183 linker needs to be run always. */ 184 if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE) 185 { 186 /* Convert it into words. */ 187 call_addr = call_addr >> 1; 188 189 if (call_addr > 0xFFFF) /* Intersegment Jump. */ 190 { 191 bfd_perror (_("Exceeds Long Jump Range")); 192 return bfd_reloc_outofrange; 193 } 194 } 195 else 196 { 197 /* case ABSOLUTE_ADDR_FOR_DATA : Resolves any code-data 198 segemnt relocs. These are NOT word aligned. */ 199 200 if (call_addr > 0xFFFF) /* Intersegment Jump. */ 201 { 202 bfd_perror (_("Absolute address Exceeds 16 bit Range")); 203 return bfd_reloc_outofrange; 204 } 205 } 206 207 x = bfd_get_32 (abfd, addr); 208 209 x = (x & 0xFF00FF00); 210 x = (x | ((call_addr & HIGH_WORD_MASK) >> 8)); 211 x = (x | (call_addr & LOW_WORD_MASK) << 16); 212 213 bfd_put_32 (abfd, (bfd_vma) x, addr); 214 return bfd_reloc_ok; 215 216 case BFD_RELOC_8: 217 addend = (reloc_entry->addend - reloc_entry->addend); 218 219 if (!bfd_is_com_section (symbol_in->section) && 220 ((symbol_in->flags & BSF_OLD_COMMON) == 0)) 221 { 222 if (reloc_entry->addend > symbol_in->value) 223 addend = reloc_entry->addend - symbol_in->value; 224 if (reloc_entry->addend < symbol_in->value) 225 addend = reloc_entry->addend - symbol_in->value; 226 if (reloc_entry->addend == symbol_in->value) 227 addend = 0; 228 } 229 230 if (bfd_is_com_section (symbol_in->section) || 231 ((symbol_in->flags & BSF_OLD_COMMON) != 0)) 232 addend = reloc_entry->addend; 233 234 if (addend < 0 235 && (call_addr < (long) (addend * (-1)))) 236 addend = 0; 237 238 if (call_addr + addend > 0xFF) 239 { 240 bfd_perror (_("Absolute address Exceeds 8 bit Range")); 241 return bfd_reloc_outofrange; 242 } 243 244 x = bfd_get_8 (abfd, addr); 245 x = x & 0x00; 246 x = x | (call_addr + addend); 247 248 bfd_put_8 (abfd, (bfd_vma) x, addr); 249 return bfd_reloc_ok; 250 251 case BFD_RELOC_16: 252 addend = (reloc_entry->addend - reloc_entry->addend); 253 if (!bfd_is_com_section (symbol_in->section) && 254 ((symbol_in->flags & BSF_OLD_COMMON) == 0)) 255 { 256 if (reloc_entry->addend > symbol_in->value) 257 addend = reloc_entry->addend - symbol_in->value; 258 259 if (reloc_entry->addend < symbol_in->value) 260 addend = reloc_entry->addend - symbol_in->value; 261 262 if (reloc_entry->addend == symbol_in->value) 263 addend = 0; 264 } 265 266 if (bfd_is_com_section (symbol_in->section) || 267 ((symbol_in->flags & BSF_OLD_COMMON) != 0)) 268 addend = reloc_entry->addend; 269 270 if (addend < 0 271 && (call_addr < (long) (addend * (-1)))) 272 addend = 0; 273 274 if ((call_addr + addend) > 0xFFFF) 275 { 276 bfd_perror (_("Absolute address Exceeds 16 bit Range")); 277 return bfd_reloc_outofrange; 278 } 279 else 280 { 281 unsigned short val = (call_addr + addend); 282 283 x = bfd_get_16 (abfd, addr); 284 285 /* LE */ 286 x = (x & 0x0000); /* Flush garbage value. */ 287 x = val; 288 if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE) 289 x = x >> 1; /* Convert it into words. */ 290 } 291 292 bfd_put_16 (abfd, (bfd_vma) x, addr); 293 return bfd_reloc_ok; 294 295 case BFD_RELOC_32: 296 addend = (reloc_entry->addend - reloc_entry->addend); 297 298 if (!bfd_is_com_section (symbol_in->section) && 299 ((symbol_in->flags & BSF_OLD_COMMON) == 0)) 300 { 301 if (reloc_entry->addend > symbol_in->value) 302 addend = reloc_entry->addend - symbol_in->value; 303 if (reloc_entry->addend < symbol_in->value) 304 addend = reloc_entry->addend - symbol_in->value; 305 if (reloc_entry->addend == symbol_in->value) 306 addend = 0; 307 } 308 309 if (bfd_is_com_section (symbol_in->section) || 310 ((symbol_in->flags & BSF_OLD_COMMON) != 0)) 311 addend = reloc_entry->addend; 312 313 if (addend < 0 314 && (call_addr < (long) (addend * (-1)))) 315 addend = 0; 316 317 if ((call_addr + addend) < 0) 318 { 319 bfd_perror ("Absolute address Exceeds 32 bit Range"); 320 return bfd_reloc_outofrange; 321 } 322 323 x = bfd_get_32 (abfd, addr); 324 x = (x & 0x0000); /* Flush garbage value. */ 325 x = call_addr + addend; 326 if ((symbol_in->section->flags & SEC_CODE) == SEC_CODE) 327 x = x >> 1; /* Convert it into words. */ 328 329 bfd_put_32 (abfd, (bfd_vma) x, addr); 330 return bfd_reloc_ok; 331 332 default: 333 bfd_perror (_("Unrecognized Reloc Type")); 334 return bfd_reloc_notsupported; 335 } 336 } 337 338 return bfd_reloc_notsupported; 339} 340 341static reloc_howto_type howto_table[] = 342{ 343 EMPTY_HOWTO (0), 344 EMPTY_HOWTO (1), 345 { 346 BFD_RELOC_32, 0, 1, 8, FALSE, 0, complain_overflow_bitfield, 347 coff_maxq20_reloc, "32Bit", TRUE, 0x000000ff, 0x000000ff, TRUE 348 }, 349 { 350 SHORT_JUMP, 0, 1, 8, FALSE, 0, complain_overflow_bitfield, 351 coff_maxq20_reloc, "SHORT_JMP", TRUE, 0x000000ff, 0x000000ff, TRUE 352 }, 353 { 354 ABSOLUTE_ADDR_FOR_DATA, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, 355 coff_maxq20_reloc, "INTERSEGMENT_RELOC", TRUE, 0x00000000, 0x00000000, 356 FALSE 357 }, 358 { 359 BFD_RELOC_16, 0, 1, 8, FALSE, 0, complain_overflow_bitfield, 360 coff_maxq20_reloc, "16Bit", TRUE, 0x000000ff, 0x000000ff, TRUE 361 }, 362 { 363 LONG_JUMP, 0, 2, 32, FALSE, 0, complain_overflow_bitfield, 364 coff_maxq20_reloc, "LONG_JUMP", TRUE, 0x00000000, 0x00000000, FALSE 365 }, 366 { 367 BFD_RELOC_8, 0, 1, 8, FALSE, 0, complain_overflow_bitfield, 368 coff_maxq20_reloc, "8bit", TRUE, 0x000000ff, 0x000000ff, TRUE 369 }, 370 EMPTY_HOWTO (8), 371 EMPTY_HOWTO (9), 372 EMPTY_HOWTO (10), 373}; 374 375static reloc_howto_type * 376maxq_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, 377 bfd_reloc_code_real_type code) 378{ 379 switch (code) 380 { 381 /* SHORT JUMP */ 382 case BFD_RELOC_16_PCREL_S2: 383 return howto_table + 3; 384 385 /* INTERSEGMENT JUMP */ 386 case BFD_RELOC_24: 387 return howto_table + 4; 388 389 /* BYTE RELOC */ 390 case BFD_RELOC_8: 391 return howto_table + 7; 392 393 /* WORD RELOC */ 394 case BFD_RELOC_16: 395 return howto_table + 5; 396 397 /* LONG RELOC */ 398 case BFD_RELOC_32: 399 return howto_table + 2; 400 401 /* LONG JUMP */ 402 case BFD_RELOC_14: 403 return howto_table + 6; 404 405 default: 406 return NULL; 407 } 408} 409 410static reloc_howto_type * 411maxq_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) 412{ 413 unsigned int i; 414 415 for (i = 0; i < sizeof (howto_table) / sizeof (howto_table[0]); i++) 416 if (howto_table[i].name != NULL 417 && strcasecmp (howto_table[i].name, r_name) == 0) 418 return &howto_table[i]; 419 420 return NULL; 421} 422 423#define coff_bfd_reloc_type_lookup maxq_reloc_type_lookup 424#define coff_bfd_reloc_name_lookup maxq_reloc_name_lookup 425 426/* Perform any necessary magic to the addend in a reloc entry. */ 427#define CALC_ADDEND(abfd, symbol, ext_reloc, cache_ptr) \ 428 cache_ptr->addend = ext_reloc.r_offset; 429 430#include "coffcode.h" 431 432#ifndef TARGET_UNDERSCORE 433#define TARGET_UNDERSCORE 1 434#endif 435 436#ifndef EXTRA_S_FLAGS 437#define EXTRA_S_FLAGS 0 438#endif 439 440/* Forward declaration for use initialising alternative_target field. */ 441CREATE_LITTLE_COFF_TARGET_VEC (maxqcoff_vec, "coff-maxq", 0, EXTRA_S_FLAGS, 442 TARGET_UNDERSCORE, NULL, COFF_SWAP_TABLE); 443 444