2001-01-03 Philip Blundell <pb@futuretv.com>
[binutils.git] / bfd / coff-a29k.c
blob55b244cfdbb07e17feac30e7256d4c38f6bd7159
1 /* BFD back-end for AMD 29000 COFF binaries.
2 Copyright 1990, 91, 92, 93, 94, 95, 97, 98, 99, 2000
3 Free Software Foundation, Inc.
4 Contributed by David Wood at New York University 7/8/91.
6 This file is part of BFD, the Binary File Descriptor library.
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.
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.
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. */
22 #define A29K 1
24 #include "bfd.h"
25 #include "sysdep.h"
26 #include "libbfd.h"
27 #include "coff/a29k.h"
28 #include "coff/internal.h"
29 #include "libcoff.h"
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 *));
41 #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
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) - 0x8000)
50 /* Provided the symbol, returns the value reffed */
51 static long
52 get_symbol_value (symbol)
53 asymbol *symbol;
55 long relocation = 0;
57 if (bfd_is_com_section (symbol->section))
59 relocation = 0;
61 else
63 relocation = symbol->value +
64 symbol->section->output_section->vma +
65 symbol->section->output_offset;
68 return(relocation);
71 /* this function is in charge of performing all the 29k relocations */
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;
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;
89 unsigned long insn;
90 unsigned long sym_value;
91 unsigned long unsigned_value;
92 unsigned short r_type;
93 long signed_value;
95 unsigned long addr = reloc_entry->address ; /*+ input_section->vma*/
96 bfd_byte *hit_data =addr + (bfd_byte *) (data);
98 r_type = reloc_entry->howto->type;
100 if (output_bfd) {
101 /* Partial linking - do nothing */
102 reloc_entry->address += input_section->output_offset;
103 return bfd_reloc_ok;
107 if (symbol_in != NULL
108 && bfd_is_und_section (symbol_in->section))
110 /* Keep the state machine happy in case we're called again */
111 if (r_type == R_IHIHALF)
113 part1_consth_active = true;
114 part1_consth_value = 0;
116 return(bfd_reloc_undefined);
119 if ((part1_consth_active) && (r_type != R_IHCONST))
121 part1_consth_active = false;
122 *error_message = (char *) _("Missing IHCONST");
123 return(bfd_reloc_dangerous);
126 sym_value = get_symbol_value(symbol_in);
128 switch (r_type)
130 case R_IREL:
131 insn = bfd_get_32 (abfd, hit_data);
132 /* Take the value in the field and sign extend it */
133 signed_value = EXTRACT_HWORD(insn);
134 signed_value = SIGN_EXTEND_HWORD(signed_value);
135 signed_value <<= 2;
137 /* See the note on the R_IREL reloc in coff_a29k_relocate_section. */
138 if (signed_value == - (long) reloc_entry->address)
139 signed_value = 0;
141 signed_value += sym_value + reloc_entry->addend;
142 if ((signed_value & ~0x3ffff) == 0)
143 { /* Absolute jmp/call */
144 insn |= (1<<24); /* Make it absolute */
145 /* FIXME: Should we change r_type to R_IABS */
147 else
149 /* Relative jmp/call, so subtract from the value the
150 address of the place we're coming from */
151 signed_value -= (reloc_entry->address
152 + input_section->output_section->vma
153 + input_section->output_offset);
154 if (signed_value>0x1ffff || signed_value<-0x20000)
155 return(bfd_reloc_overflow);
157 signed_value >>= 2;
158 insn = INSERT_HWORD(insn, signed_value);
159 bfd_put_32 (abfd, insn ,hit_data);
160 break;
161 case R_ILOHALF:
162 insn = bfd_get_32 (abfd, hit_data);
163 unsigned_value = EXTRACT_HWORD(insn);
164 unsigned_value += sym_value + reloc_entry->addend;
165 insn = INSERT_HWORD(insn, unsigned_value);
166 bfd_put_32 (abfd, insn, hit_data);
167 break;
168 case R_IHIHALF:
169 insn = bfd_get_32 (abfd, hit_data);
170 /* consth, part 1
171 Just get the symbol value that is referenced */
172 part1_consth_active = true;
173 part1_consth_value = sym_value + reloc_entry->addend;
174 /* Don't modify insn until R_IHCONST */
175 break;
176 case R_IHCONST:
177 insn = bfd_get_32 (abfd, hit_data);
178 /* consth, part 2
179 Now relocate the reference */
180 if (part1_consth_active == false) {
181 *error_message = (char *) _("Missing IHIHALF");
182 return(bfd_reloc_dangerous);
184 /* sym_ptr_ptr = r_symndx, in coff_slurp_reloc_table() */
185 unsigned_value = 0; /*EXTRACT_HWORD(insn) << 16;*/
186 unsigned_value += reloc_entry->addend; /* r_symndx */
187 unsigned_value += part1_consth_value;
188 unsigned_value = unsigned_value >> 16;
189 insn = INSERT_HWORD(insn, unsigned_value);
190 part1_consth_active = false;
191 bfd_put_32 (abfd, insn, hit_data);
192 break;
193 case R_BYTE:
194 insn = bfd_get_8 (abfd, hit_data);
195 unsigned_value = insn + sym_value + reloc_entry->addend;
196 if (unsigned_value & 0xffffff00)
197 return(bfd_reloc_overflow);
198 bfd_put_8 (abfd, unsigned_value, hit_data);
199 break;
200 case R_HWORD:
201 insn = bfd_get_16 (abfd, hit_data);
202 unsigned_value = insn + sym_value + reloc_entry->addend;
203 if (unsigned_value & 0xffff0000)
204 return(bfd_reloc_overflow);
205 bfd_put_16 (abfd, insn, hit_data);
206 break;
207 case R_WORD:
208 insn = bfd_get_32 (abfd, hit_data);
209 insn += sym_value + reloc_entry->addend;
210 bfd_put_32 (abfd, insn, hit_data);
211 break;
212 default:
213 *error_message = _("Unrecognized reloc");
214 return (bfd_reloc_dangerous);
217 return(bfd_reloc_ok);
220 /* type rightshift
221 size
222 bitsize
223 pc-relative
224 bitpos
225 absolute
226 complain_on_overflow
227 special_function
228 relocation name
229 partial_inplace
230 src_mask
233 /*FIXME: I'm not real sure about this table */
234 static reloc_howto_type howto_table[] =
236 {R_ABS, 0, 3, 32, false, 0, complain_overflow_bitfield,a29k_reloc,"ABS", true, 0xffffffff,0xffffffff, false},
237 EMPTY_HOWTO (1),
238 EMPTY_HOWTO (2),
239 EMPTY_HOWTO (3),
240 EMPTY_HOWTO (4),
241 EMPTY_HOWTO (5),
242 EMPTY_HOWTO (6),
243 EMPTY_HOWTO (7),
244 EMPTY_HOWTO (8),
245 EMPTY_HOWTO (9),
246 EMPTY_HOWTO (10),
247 EMPTY_HOWTO (11),
248 EMPTY_HOWTO (12),
249 EMPTY_HOWTO (13),
250 EMPTY_HOWTO (14),
251 EMPTY_HOWTO (15),
252 EMPTY_HOWTO (16),
253 EMPTY_HOWTO (17),
254 EMPTY_HOWTO (18),
255 EMPTY_HOWTO (19),
256 EMPTY_HOWTO (20),
257 EMPTY_HOWTO (21),
258 EMPTY_HOWTO (22),
259 EMPTY_HOWTO (23),
260 {R_IREL, 0, 3, 32, true, 0, complain_overflow_signed,a29k_reloc,"IREL", true, 0xffffffff,0xffffffff, false},
261 {R_IABS, 0, 3, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"IABS", true, 0xffffffff,0xffffffff, false},
262 {R_ILOHALF, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"ILOHALF", true, 0x0000ffff,0x0000ffff, false},
263 {R_IHIHALF, 0, 3, 16, true, 16, complain_overflow_signed, a29k_reloc,"IHIHALF", true, 0xffff0000,0xffff0000, false},
264 {R_IHCONST, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"IHCONST", true, 0xffff0000,0xffff0000, false},
265 {R_BYTE, 0, 0, 8, false, 0, complain_overflow_bitfield, a29k_reloc,"BYTE", true, 0x000000ff,0x000000ff, false},
266 {R_HWORD, 0, 1, 16, false, 0, complain_overflow_bitfield, a29k_reloc,"HWORD", true, 0x0000ffff,0x0000ffff, false},
267 {R_WORD, 0, 2, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"WORD", true, 0xffffffff,0xffffffff, false},
270 #define BADMAG(x) A29KBADMAG(x)
272 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
273 reloc_processing(relent, reloc, symbols, abfd, section)
275 static void
276 reloc_processing (relent,reloc, symbols, abfd, section)
277 arelent *relent;
278 struct internal_reloc *reloc;
279 asymbol **symbols;
280 bfd *abfd;
281 asection *section;
283 static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
285 relent->address = reloc->r_vaddr;
286 relent->howto = howto_table + reloc->r_type;
287 if (reloc->r_type == R_IHCONST)
289 /* The address of an R_IHCONST should always be the address of
290 the immediately preceding R_IHIHALF. relocs generated by gas
291 are correct, but relocs generated by High C are different (I
292 can't figure out what the address means for High C). We can
293 handle both gas and High C by ignoring the address here, and
294 simply reusing the address saved for R_IHIHALF. */
295 if (ihihalf_vaddr == (bfd_vma) -1)
296 abort ();
297 relent->address = ihihalf_vaddr;
298 ihihalf_vaddr = (bfd_vma) -1;
299 relent->addend = reloc->r_symndx;
300 relent->sym_ptr_ptr= bfd_abs_section_ptr->symbol_ptr_ptr;
302 else
304 asymbol *ptr;
305 relent->sym_ptr_ptr = symbols + obj_convert(abfd)[reloc->r_symndx];
307 ptr = *(relent->sym_ptr_ptr);
309 if (ptr
310 && bfd_asymbol_bfd(ptr) == abfd
312 && ((ptr->flags & BSF_OLD_COMMON)== 0))
314 relent->addend = 0;
316 else
318 relent->addend = 0;
320 relent->address-= section->vma;
321 if (reloc->r_type == R_IHIHALF)
322 ihihalf_vaddr = relent->address;
323 else if (ihihalf_vaddr != (bfd_vma) -1)
324 abort ();
328 /* The reloc processing routine for the optimized COFF linker. */
330 static boolean
331 coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section,
332 contents, relocs, syms, sections)
333 bfd *output_bfd ATTRIBUTE_UNUSED;
334 struct bfd_link_info *info;
335 bfd *input_bfd;
336 asection *input_section;
337 bfd_byte *contents;
338 struct internal_reloc *relocs;
339 struct internal_syment *syms;
340 asection **sections;
342 struct internal_reloc *rel;
343 struct internal_reloc *relend;
344 boolean hihalf;
345 bfd_vma hihalf_val;
347 /* If we are performing a relocateable link, we don't need to do a
348 thing. The caller will take care of adjusting the reloc
349 addresses and symbol indices. */
350 if (info->relocateable)
351 return true;
353 hihalf = false;
354 hihalf_val = 0;
356 rel = relocs;
357 relend = rel + input_section->reloc_count;
358 for (; rel < relend; rel++)
360 long symndx;
361 bfd_byte *loc;
362 struct coff_link_hash_entry *h;
363 struct internal_syment *sym;
364 asection *sec;
365 bfd_vma val;
366 boolean overflow;
367 unsigned long insn;
368 long signed_value;
369 unsigned long unsigned_value;
370 bfd_reloc_status_type rstat;
372 symndx = rel->r_symndx;
373 loc = contents + rel->r_vaddr - input_section->vma;
375 if (symndx == -1 || rel->r_type == R_IHCONST)
376 h = NULL;
377 else
378 h = obj_coff_sym_hashes (input_bfd)[symndx];
380 sym = NULL;
381 sec = NULL;
382 val = 0;
384 /* An R_IHCONST reloc does not have a symbol. Instead, the
385 symbol index is an addend. R_IHCONST is always used in
386 conjunction with R_IHHALF. */
387 if (rel->r_type != R_IHCONST)
389 if (h == NULL)
391 if (symndx == -1)
392 sec = bfd_abs_section_ptr;
393 else
395 sym = syms + symndx;
396 sec = sections[symndx];
397 val = (sec->output_section->vma
398 + sec->output_offset
399 + sym->n_value
400 - sec->vma);
403 else
405 if (h->root.type == bfd_link_hash_defined
406 || h->root.type == bfd_link_hash_defweak)
408 sec = h->root.u.def.section;
409 val = (h->root.u.def.value
410 + sec->output_section->vma
411 + sec->output_offset);
413 else
415 if (! ((*info->callbacks->undefined_symbol)
416 (info, h->root.root.string, input_bfd, input_section,
417 rel->r_vaddr - input_section->vma, true)))
418 return false;
422 if (hihalf)
424 if (! ((*info->callbacks->reloc_dangerous)
425 (info, _("missing IHCONST reloc"), input_bfd,
426 input_section, rel->r_vaddr - input_section->vma)))
427 return false;
428 hihalf = false;
432 overflow = false;
434 switch (rel->r_type)
436 default:
437 bfd_set_error (bfd_error_bad_value);
438 return false;
440 case R_IREL:
441 insn = bfd_get_32 (input_bfd, loc);
443 /* Extract the addend. */
444 signed_value = EXTRACT_HWORD (insn);
445 signed_value = SIGN_EXTEND_HWORD (signed_value);
446 signed_value <<= 2;
448 /* Unfortunately, there are two different versions of COFF
449 a29k. In the original AMD version, the value stored in
450 the field for the R_IREL reloc is a simple addend. In
451 the GNU version, the value is the negative of the address
452 of the reloc within section. We try to cope here by
453 assuming the AMD version, unless the addend is exactly
454 the negative of the address; in the latter case we assume
455 the GNU version. This means that something like
456 .text
458 jmp i-4
459 will fail, because the addend of -4 will happen to equal
460 the negative of the address within the section. The
461 compiler will never generate code like this.
463 At some point in the future we may want to take out this
464 check. */
466 if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
467 signed_value = 0;
469 /* Determine the destination of the jump. */
470 signed_value += val;
472 if ((signed_value & ~0x3ffff) == 0)
474 /* We can use an absolute jump. */
475 insn |= (1 << 24);
477 else
479 /* Make the destination PC relative. */
480 signed_value -= (input_section->output_section->vma
481 + input_section->output_offset
482 + (rel->r_vaddr - input_section->vma));
483 if (signed_value > 0x1ffff || signed_value < - 0x20000)
485 overflow = true;
486 signed_value = 0;
490 /* Put the adjusted value back into the instruction. */
491 signed_value >>= 2;
492 insn = INSERT_HWORD (insn, signed_value);
494 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
496 break;
498 case R_ILOHALF:
499 insn = bfd_get_32 (input_bfd, loc);
500 unsigned_value = EXTRACT_HWORD (insn);
501 unsigned_value += val;
502 insn = INSERT_HWORD (insn, unsigned_value);
503 bfd_put_32 (input_bfd, insn, loc);
504 break;
506 case R_IHIHALF:
507 /* Save the value for the R_IHCONST reloc. */
508 hihalf = true;
509 hihalf_val = val;
510 break;
512 case R_IHCONST:
513 if (! hihalf)
515 if (! ((*info->callbacks->reloc_dangerous)
516 (info, _("missing IHIHALF reloc"), input_bfd,
517 input_section, rel->r_vaddr - input_section->vma)))
518 return false;
519 hihalf_val = 0;
522 insn = bfd_get_32 (input_bfd, loc);
523 unsigned_value = rel->r_symndx + hihalf_val;
524 unsigned_value >>= 16;
525 insn = INSERT_HWORD (insn, unsigned_value);
526 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
528 hihalf = false;
530 break;
532 case R_BYTE:
533 case R_HWORD:
534 case R_WORD:
535 rstat = _bfd_relocate_contents (howto_table + rel->r_type,
536 input_bfd, val, loc);
537 if (rstat == bfd_reloc_overflow)
538 overflow = true;
539 else if (rstat != bfd_reloc_ok)
540 abort ();
541 break;
544 if (overflow)
546 const char *name;
547 char buf[SYMNMLEN + 1];
549 if (symndx == -1)
550 name = "*ABS*";
551 else if (h != NULL)
552 name = h->root.root.string;
553 else if (sym == NULL)
554 name = "*unknown*";
555 else if (sym->_n._n_n._n_zeroes == 0
556 && sym->_n._n_n._n_offset != 0)
557 name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
558 else
560 strncpy (buf, sym->_n._n_name, SYMNMLEN);
561 buf[SYMNMLEN] = '\0';
562 name = buf;
565 if (! ((*info->callbacks->reloc_overflow)
566 (info, name, howto_table[rel->r_type].name, (bfd_vma) 0,
567 input_bfd, input_section,
568 rel->r_vaddr - input_section->vma)))
569 return false;
573 return true;
576 #define coff_relocate_section coff_a29k_relocate_section
578 /* We don't want to change the symndx of a R_IHCONST reloc, since it
579 is actually an addend, not a symbol index at all. */
581 static boolean
582 coff_a29k_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
583 bfd *obfd ATTRIBUTE_UNUSED;
584 struct bfd_link_info *info ATTRIBUTE_UNUSED;
585 bfd *ibfd ATTRIBUTE_UNUSED;
586 asection *sec ATTRIBUTE_UNUSED;
587 struct internal_reloc *irel;
588 boolean *adjustedp;
590 if (irel->r_type == R_IHCONST)
591 *adjustedp = true;
592 else
593 *adjustedp = false;
594 return true;
597 #define coff_adjust_symndx coff_a29k_adjust_symndx
599 #include "coffcode.h"
601 CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec, "coff-a29k-big", 0, SEC_READONLY, '_', NULL)