]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - bfd/coff-a29k.c
* coff-a29k.c (a29k_reloc): Change handling of R_IREL reloc to be
[thirdparty/binutils-gdb.git] / bfd / coff-a29k.c
1 /* BFD back-end for AMD 29000 COFF binaries.
2 Copyright 1990, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
3 Contributed by David Wood at New York University 7/8/91.
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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 #define A29K 1
22
23 #include "bfd.h"
24 #include "sysdep.h"
25 #include "libbfd.h"
26 #include "obstack.h"
27 #include "coff/a29k.h"
28 #include "coff/internal.h"
29 #include "libcoff.h"
30
31 static long get_symbol_value PARAMS ((asymbol *));
32 static bfd_reloc_status_type a29k_reloc
33 PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
34 static boolean coff_a29k_relocate_section
35 PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
36 struct internal_reloc *, struct internal_syment *, asection **));
37 static boolean coff_a29k_adjust_symndx
38 PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *,
39 struct internal_reloc *, boolean *));
40
41 #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
42
43 #define INSERT_HWORD(WORD,HWORD) \
44 (((WORD) & 0xff00ff00) | (((HWORD) & 0xff00) << 8) | ((HWORD)& 0xff))
45 #define EXTRACT_HWORD(WORD) \
46 ((((WORD) & 0x00ff0000) >> 8) | ((WORD)& 0xff))
47 #define SIGN_EXTEND_HWORD(HWORD) \
48 ((HWORD) & 0x8000 ? (HWORD)|(~0xffffL) : (HWORD))
49
50 /* Provided the symbol, returns the value reffed */
51 static long
52 get_symbol_value (symbol)
53 asymbol *symbol;
54 {
55 long relocation = 0;
56
57 if (bfd_is_com_section (symbol->section))
58 {
59 relocation = 0;
60 }
61 else
62 {
63 relocation = symbol->value +
64 symbol->section->output_section->vma +
65 symbol->section->output_offset;
66 }
67
68 return(relocation);
69 }
70
71 /* this function is in charge of performing all the 29k relocations */
72
73 static bfd_reloc_status_type
74 a29k_reloc (abfd, reloc_entry, symbol_in, data, input_section, output_bfd,
75 error_message)
76 bfd *abfd;
77 arelent *reloc_entry;
78 asymbol *symbol_in;
79 PTR data;
80 asection *input_section;
81 bfd *output_bfd;
82 char **error_message;
83 {
84 /* the consth relocation comes in two parts, we have to remember
85 the state between calls, in these variables */
86 static boolean part1_consth_active = false;
87 static unsigned long part1_consth_value;
88
89 unsigned long insn;
90 unsigned long sym_value;
91 unsigned long unsigned_value;
92 unsigned short r_type;
93 long signed_value;
94
95 unsigned long addr = reloc_entry->address ; /*+ input_section->vma*/
96 bfd_byte *hit_data =addr + (bfd_byte *)(data);
97
98 r_type = reloc_entry->howto->type;
99
100 if (output_bfd) {
101 /* Partial linking - do nothing */
102 reloc_entry->address += input_section->output_offset;
103 return bfd_reloc_ok;
104
105 }
106
107 if (symbol_in != NULL
108 && bfd_is_und_section (symbol_in->section))
109 {
110 /* Keep the state machine happy in case we're called again */
111 if (r_type == R_IHIHALF)
112 {
113 part1_consth_active = true;
114 part1_consth_value = 0;
115 }
116 return(bfd_reloc_undefined);
117 }
118
119 if ((part1_consth_active) && (r_type != R_IHCONST))
120 {
121 part1_consth_active = false;
122 *error_message = (char *) "Missing IHCONST";
123 return(bfd_reloc_dangerous);
124 }
125
126
127 sym_value = get_symbol_value(symbol_in);
128
129 switch (r_type)
130 {
131 case R_IREL:
132 insn = bfd_get_32(abfd, hit_data);
133 /* Take the value in the field and sign extend it */
134 signed_value = EXTRACT_HWORD(insn);
135 signed_value = SIGN_EXTEND_HWORD(signed_value);
136 signed_value <<= 2;
137
138 /* See the note on the R_IREL reloc in coff_a29k_relocate_section. */
139 if (signed_value == - (long) reloc_entry->address)
140 signed_value = 0;
141
142 signed_value += sym_value + reloc_entry->addend;
143 if ((signed_value & ~0x3ffff) == 0)
144 { /* Absolute jmp/call */
145 insn |= (1<<24); /* Make it absolute */
146 /* FIXME: Should we change r_type to R_IABS */
147 }
148 else
149 {
150 /* Relative jmp/call, so subtract from the value the
151 address of the place we're coming from */
152 signed_value -= (reloc_entry->address
153 + input_section->output_section->vma
154 + input_section->output_offset);
155 if (signed_value>0x1ffff || signed_value<-0x20000)
156 return(bfd_reloc_overflow);
157 }
158 signed_value >>= 2;
159 insn = INSERT_HWORD(insn, signed_value);
160 bfd_put_32(abfd, insn ,hit_data);
161 break;
162 case R_ILOHALF:
163 insn = bfd_get_32(abfd, hit_data);
164 unsigned_value = EXTRACT_HWORD(insn);
165 unsigned_value += sym_value + reloc_entry->addend;
166 insn = INSERT_HWORD(insn, unsigned_value);
167 bfd_put_32(abfd, insn, hit_data);
168 break;
169 case R_IHIHALF:
170 insn = bfd_get_32(abfd, hit_data);
171 /* consth, part 1
172 Just get the symbol value that is referenced */
173 part1_consth_active = true;
174 part1_consth_value = sym_value + reloc_entry->addend;
175 /* Don't modify insn until R_IHCONST */
176 break;
177 case R_IHCONST:
178 insn = bfd_get_32(abfd, hit_data);
179 /* consth, part 2
180 Now relocate the reference */
181 if (part1_consth_active == false) {
182 *error_message = (char *) "Missing IHIHALF";
183 return(bfd_reloc_dangerous);
184 }
185 /* sym_ptr_ptr = r_symndx, in coff_slurp_reloc_table() */
186 unsigned_value = 0; /*EXTRACT_HWORD(insn) << 16;*/
187 unsigned_value += reloc_entry->addend; /* r_symndx */
188 unsigned_value += part1_consth_value;
189 unsigned_value = unsigned_value >> 16;
190 insn = INSERT_HWORD(insn, unsigned_value);
191 part1_consth_active = false;
192 bfd_put_32(abfd, insn, hit_data);
193 break;
194 case R_BYTE:
195 insn = bfd_get_8(abfd, hit_data);
196 unsigned_value = insn + sym_value + reloc_entry->addend;
197 if (unsigned_value & 0xffffff00)
198 return(bfd_reloc_overflow);
199 bfd_put_8(abfd, unsigned_value, hit_data);
200 break;
201 case R_HWORD:
202 insn = bfd_get_16(abfd, hit_data);
203 unsigned_value = insn + sym_value + reloc_entry->addend;
204 if (unsigned_value & 0xffff0000)
205 return(bfd_reloc_overflow);
206 bfd_put_16(abfd, insn, hit_data);
207 break;
208 case R_WORD:
209 insn = bfd_get_32(abfd, hit_data);
210 insn += sym_value + reloc_entry->addend;
211 bfd_put_32(abfd, insn, hit_data);
212 break;
213 default:
214 *error_message = "Unrecognized reloc";
215 return (bfd_reloc_dangerous);
216 }
217
218
219 return(bfd_reloc_ok);
220 }
221
222 /* type rightshift
223 size
224 bitsize
225 pc-relative
226 bitpos
227 absolute
228 complain_on_overflow
229 special_function
230 relocation name
231 partial_inplace
232 src_mask
233 */
234
235 /*FIXME: I'm not real sure about this table */
236 static reloc_howto_type howto_table[] =
237 {
238 {R_ABS, 0, 3, 32, false, 0, complain_overflow_bitfield,a29k_reloc,"ABS", true, 0xffffffff,0xffffffff, false},
239 {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10},
240 {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19}, {20},
241 {21}, {22}, {23},
242 {R_IREL, 0, 3, 32, true, 0, complain_overflow_signed,a29k_reloc,"IREL", true, 0xffffffff,0xffffffff, false},
243 {R_IABS, 0, 3, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"IABS", true, 0xffffffff,0xffffffff, false},
244 {R_ILOHALF, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"ILOHALF", true, 0x0000ffff,0x0000ffff, false},
245 {R_IHIHALF, 0, 3, 16, true, 16, complain_overflow_signed, a29k_reloc,"IHIHALF", true, 0xffff0000,0xffff0000, false},
246 {R_IHCONST, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"IHCONST", true, 0xffff0000,0xffff0000, false},
247 {R_BYTE, 0, 0, 8, false, 0, complain_overflow_bitfield, a29k_reloc,"BYTE", true, 0x000000ff,0x000000ff, false},
248 {R_HWORD, 0, 1, 16, false, 0, complain_overflow_bitfield, a29k_reloc,"HWORD", true, 0x0000ffff,0x0000ffff, false},
249 {R_WORD, 0, 2, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"WORD", true, 0xffffffff,0xffffffff, false},
250 };
251
252 #define BADMAG(x) A29KBADMAG(x)
253
254 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
255 reloc_processing(relent, reloc, symbols, abfd, section)
256
257 static void
258 reloc_processing (relent,reloc, symbols, abfd, section)
259 arelent *relent;
260 struct internal_reloc *reloc;
261 asymbol **symbols;
262 bfd *abfd;
263 asection *section;
264 {
265 static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
266
267 relent->address = reloc->r_vaddr;
268 relent->howto = howto_table + reloc->r_type;
269 if (reloc->r_type == R_IHCONST)
270 {
271 /* The address of an R_IHCONST should always be the address of
272 the immediately preceding R_IHIHALF. relocs generated by gas
273 are correct, but relocs generated by High C are different (I
274 can't figure out what the address means for High C). We can
275 handle both gas and High C by ignoring the address here, and
276 simply reusing the address saved for R_IHIHALF. */
277 if (ihihalf_vaddr == (bfd_vma) -1)
278 abort ();
279 relent->address = ihihalf_vaddr;
280 ihihalf_vaddr = (bfd_vma) -1;
281 relent->addend = reloc->r_symndx;
282 relent->sym_ptr_ptr= bfd_abs_section_ptr->symbol_ptr_ptr;
283 }
284 else
285 {
286 asymbol *ptr;
287 relent->sym_ptr_ptr = symbols + obj_convert(abfd)[reloc->r_symndx];
288
289 ptr = *(relent->sym_ptr_ptr);
290
291 if (ptr
292 && bfd_asymbol_bfd(ptr) == abfd
293
294 && ((ptr->flags & BSF_OLD_COMMON)== 0))
295 {
296 relent->addend = 0;
297 }
298 else
299 {
300 relent->addend = 0;
301 }
302 relent->address-= section->vma;
303 if (reloc->r_type == R_IHIHALF)
304 ihihalf_vaddr = relent->address;
305 else if (ihihalf_vaddr != (bfd_vma) -1)
306 abort ();
307 }
308 }
309
310 /* The reloc processing routine for the optimized COFF linker. */
311
312 static boolean
313 coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section,
314 contents, relocs, syms, sections)
315 bfd *output_bfd;
316 struct bfd_link_info *info;
317 bfd *input_bfd;
318 asection *input_section;
319 bfd_byte *contents;
320 struct internal_reloc *relocs;
321 struct internal_syment *syms;
322 asection **sections;
323 {
324 struct internal_reloc *rel;
325 struct internal_reloc *relend;
326 boolean hihalf;
327 bfd_vma hihalf_val;
328
329 /* If we are performing a relocateable link, we don't need to do a
330 thing. The caller will take care of adjusting the reloc
331 addresses and symbol indices. */
332 if (info->relocateable)
333 return true;
334
335 hihalf = false;
336 hihalf_val = 0;
337
338 rel = relocs;
339 relend = rel + input_section->reloc_count;
340 for (; rel < relend; rel++)
341 {
342 long symndx;
343 bfd_byte *loc;
344 struct coff_link_hash_entry *h;
345 struct internal_syment *sym;
346 asection *sec;
347 bfd_vma val;
348 boolean overflow;
349 unsigned long insn;
350 long signed_value;
351 unsigned long unsigned_value;
352 bfd_reloc_status_type rstat;
353
354 symndx = rel->r_symndx;
355 loc = contents + rel->r_vaddr - input_section->vma;
356
357 if (symndx == -1)
358 h = NULL;
359 else
360 h = obj_coff_sym_hashes (input_bfd)[symndx];
361
362 sym = NULL;
363 sec = NULL;
364 val = 0;
365
366 /* An R_IHCONST reloc does not have a symbol. Instead, the
367 symbol index is an addend. R_IHCONST is always used in
368 conjunction with R_IHHALF. */
369 if (rel->r_type != R_IHCONST)
370 {
371 if (h == NULL)
372 {
373 if (symndx == -1)
374 sec = bfd_abs_section_ptr;
375 else
376 {
377 sym = syms + symndx;
378 sec = sections[symndx];
379 val = (sec->output_section->vma
380 + sec->output_offset
381 + sym->n_value
382 - sec->vma);
383 }
384 }
385 else
386 {
387 if (h->root.type == bfd_link_hash_defined
388 || h->root.type == bfd_link_hash_defweak)
389 {
390 sec = h->root.u.def.section;
391 val = (h->root.u.def.value
392 + sec->output_section->vma
393 + sec->output_offset);
394 }
395 else
396 {
397 if (! ((*info->callbacks->undefined_symbol)
398 (info, h->root.root.string, input_bfd, input_section,
399 rel->r_vaddr - input_section->vma)))
400 return false;
401 }
402 }
403
404 if (hihalf)
405 {
406 if (! ((*info->callbacks->reloc_dangerous)
407 (info, "missing IHCONST reloc", input_bfd,
408 input_section, rel->r_vaddr - input_section->vma)))
409 return false;
410 hihalf = false;
411 }
412 }
413
414 overflow = false;
415
416 switch (rel->r_type)
417 {
418 default:
419 bfd_set_error (bfd_error_bad_value);
420 return false;
421
422 case R_IREL:
423 insn = bfd_get_32 (input_bfd, loc);
424
425 /* Extract the addend. */
426 signed_value = EXTRACT_HWORD (insn);
427 signed_value = SIGN_EXTEND_HWORD (signed_value);
428 signed_value <<= 2;
429
430 /* Unfortunately, there are two different versions of COFF
431 a29k. In the original AMD version, the value stored in
432 the field for the R_IREL reloc is a simple addend. In
433 the GNU version, the value is the negative of the address
434 of the reloc within section. We try to cope here by
435 assuming the AMD version, unless the addend is exactly
436 the negative of the address; in the latter case we assume
437 the GNU version. This means that something like
438 .text
439 nop
440 jmp i-4
441 will fail, because the addend of -4 will happen to equal
442 the negative of the address within the section. The
443 compiler will never generate code like this.
444
445 At some point in the future we may want to take out this
446 check. */
447
448 if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
449 signed_value = 0;
450
451 /* Determine the destination of the jump. */
452 signed_value += val;
453
454 if ((signed_value & ~0x3ffff) == 0)
455 {
456 /* We can use an absolute jump. */
457 insn |= (1 << 24);
458 }
459 else
460 {
461 /* Make the destination PC relative. */
462 signed_value -= (input_section->output_section->vma
463 + input_section->output_offset
464 + (rel->r_vaddr - input_section->vma));
465 if (signed_value > 0x1ffff || signed_value < - 0x20000)
466 {
467 overflow = true;
468 signed_value = 0;
469 }
470 }
471
472 /* Put the adjusted value back into the instruction. */
473 signed_value >>= 2;
474 insn = INSERT_HWORD (insn, signed_value);
475
476 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
477
478 break;
479
480 case R_ILOHALF:
481 insn = bfd_get_32 (input_bfd, loc);
482 unsigned_value = EXTRACT_HWORD (insn);
483 unsigned_value += val;
484 insn = INSERT_HWORD (insn, unsigned_value);
485 bfd_put_32 (input_bfd, insn, loc);
486 break;
487
488 case R_IHIHALF:
489 /* Save the value for the R_IHCONST reloc. */
490 hihalf = true;
491 hihalf_val = val;
492 break;
493
494 case R_IHCONST:
495 if (! hihalf)
496 {
497 if (! ((*info->callbacks->reloc_dangerous)
498 (info, "missing IHIHALF reloc", input_bfd,
499 input_section, rel->r_vaddr - input_section->vma)))
500 return false;
501 hihalf_val = 0;
502 }
503
504 insn = bfd_get_32 (input_bfd, loc);
505 unsigned_value = rel->r_symndx + hihalf_val;
506 unsigned_value >>= 16;
507 insn = INSERT_HWORD (insn, unsigned_value);
508 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
509
510 hihalf = false;
511
512 break;
513
514 case R_BYTE:
515 case R_HWORD:
516 case R_WORD:
517 rstat = _bfd_relocate_contents (howto_table + rel->r_type,
518 input_bfd, val, loc);
519 if (rstat == bfd_reloc_overflow)
520 overflow = true;
521 else if (rstat != bfd_reloc_ok)
522 abort ();
523 break;
524 }
525
526 if (overflow)
527 {
528 const char *name;
529 char buf[SYMNMLEN + 1];
530
531 if (symndx == -1)
532 name = "*ABS*";
533 else if (h != NULL)
534 name = h->root.root.string;
535 else if (sym == NULL)
536 name = "*unknown*";
537 else if (sym->_n._n_n._n_zeroes == 0
538 && sym->_n._n_n._n_offset != 0)
539 name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
540 else
541 {
542 strncpy (buf, sym->_n._n_name, SYMNMLEN);
543 buf[SYMNMLEN] = '\0';
544 name = buf;
545 }
546
547 if (! ((*info->callbacks->reloc_overflow)
548 (info, name, howto_table[rel->r_type].name, (bfd_vma) 0,
549 input_bfd, input_section,
550 rel->r_vaddr - input_section->vma)))
551 return false;
552 }
553 }
554
555 return true;
556 }
557
558 #define coff_relocate_section coff_a29k_relocate_section
559
560 /* We don't want to change the symndx of a R_IHCONST reloc, since it
561 is actually an addend, not a symbol index at all. */
562
563 /*ARGSUSED*/
564 static boolean
565 coff_a29k_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
566 bfd *obfd;
567 struct bfd_link_info *info;
568 bfd *ibfd;
569 asection *sec;
570 struct internal_reloc *irel;
571 boolean *adjustedp;
572 {
573 if (irel->r_type == R_IHCONST)
574 *adjustedp = true;
575 else
576 *adjustedp = false;
577 return true;
578 }
579
580 #define coff_adjust_symndx coff_a29k_adjust_symndx
581
582 #include "coffcode.h"
583
584 const bfd_target a29kcoff_big_vec =
585 {
586 "coff-a29k-big", /* name */
587 bfd_target_coff_flavour,
588 true, /* data byte order is big */
589 true, /* header byte order is big */
590
591 (HAS_RELOC | EXEC_P | /* object flags */
592 HAS_LINENO | HAS_DEBUG |
593 HAS_SYMS | HAS_LOCALS | WP_TEXT),
594
595 (SEC_HAS_CONTENTS | SEC_ALLOC /* section flags */
596 | SEC_LOAD | SEC_RELOC
597 | SEC_READONLY ),
598 '_', /* leading underscore */
599 '/', /* ar_pad_char */
600 15, /* ar_max_namelen */
601 /* data */
602 bfd_getb64, bfd_getb_signed_64, bfd_putb64,
603 bfd_getb32, bfd_getb_signed_32, bfd_putb32,
604 bfd_getb16, bfd_getb_signed_16, bfd_putb16,
605 /* hdrs */
606 bfd_getb64, bfd_getb_signed_64, bfd_putb64,
607 bfd_getb32, bfd_getb_signed_32, bfd_putb32,
608 bfd_getb16, bfd_getb_signed_16, bfd_putb16,
609
610 {
611
612 _bfd_dummy_target,
613 coff_object_p,
614 bfd_generic_archive_p,
615 _bfd_dummy_target
616 },
617 {
618 bfd_false,
619 coff_mkobject,
620 _bfd_generic_mkarchive,
621 bfd_false
622 },
623 {
624 bfd_false,
625 coff_write_object_contents,
626 _bfd_write_archive_contents,
627 bfd_false
628 },
629
630 BFD_JUMP_TABLE_GENERIC (coff),
631 BFD_JUMP_TABLE_COPY (coff),
632 BFD_JUMP_TABLE_CORE (_bfd_nocore),
633 BFD_JUMP_TABLE_ARCHIVE (_bfd_archive_coff),
634 BFD_JUMP_TABLE_SYMBOLS (coff),
635 BFD_JUMP_TABLE_RELOCS (coff),
636 BFD_JUMP_TABLE_WRITE (coff),
637 BFD_JUMP_TABLE_LINK (coff),
638 BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
639
640 COFF_SWAP_TABLE
641 };