2001-03-22 Philip Blundell <philb@gnu.org>
[binutils.git] / bfd / coff-a29k.c
blob343b81ebfcaa282df77ce26e0db9fadbd84097d6
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)
102 /* Partial linking - do nothing */
103 reloc_entry->address += input_section->output_offset;
104 return bfd_reloc_ok;
108 if (symbol_in != NULL
109 && bfd_is_und_section (symbol_in->section))
111 /* Keep the state machine happy in case we're called again */
112 if (r_type == R_IHIHALF)
114 part1_consth_active = true;
115 part1_consth_value = 0;
117 return(bfd_reloc_undefined);
120 if ((part1_consth_active) && (r_type != R_IHCONST))
122 part1_consth_active = false;
123 *error_message = (char *) _("Missing IHCONST");
124 return(bfd_reloc_dangerous);
127 sym_value = get_symbol_value(symbol_in);
129 switch (r_type)
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;
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;
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 */
148 else
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);
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)
183 *error_message = (char *) _("Missing IHIHALF");
184 return(bfd_reloc_dangerous);
186 /* sym_ptr_ptr = r_symndx, in coff_slurp_reloc_table() */
187 unsigned_value = 0; /*EXTRACT_HWORD(insn) << 16;*/
188 unsigned_value += reloc_entry->addend; /* r_symndx */
189 unsigned_value += part1_consth_value;
190 unsigned_value = unsigned_value >> 16;
191 insn = INSERT_HWORD(insn, unsigned_value);
192 part1_consth_active = false;
193 bfd_put_32 (abfd, insn, hit_data);
194 break;
195 case R_BYTE:
196 insn = bfd_get_8 (abfd, hit_data);
197 unsigned_value = insn + sym_value + reloc_entry->addend;
198 if (unsigned_value & 0xffffff00)
199 return(bfd_reloc_overflow);
200 bfd_put_8 (abfd, unsigned_value, hit_data);
201 break;
202 case R_HWORD:
203 insn = bfd_get_16 (abfd, hit_data);
204 unsigned_value = insn + sym_value + reloc_entry->addend;
205 if (unsigned_value & 0xffff0000)
206 return(bfd_reloc_overflow);
207 bfd_put_16 (abfd, insn, hit_data);
208 break;
209 case R_WORD:
210 insn = bfd_get_32 (abfd, hit_data);
211 insn += sym_value + reloc_entry->addend;
212 bfd_put_32 (abfd, insn, hit_data);
213 break;
214 default:
215 *error_message = _("Unrecognized reloc");
216 return (bfd_reloc_dangerous);
219 return(bfd_reloc_ok);
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
235 /*FIXME: I'm not real sure about this table */
236 static reloc_howto_type howto_table[] =
238 {R_ABS, 0, 3, 32, false, 0, complain_overflow_bitfield,a29k_reloc,"ABS", true, 0xffffffff,0xffffffff, false},
239 EMPTY_HOWTO (1),
240 EMPTY_HOWTO (2),
241 EMPTY_HOWTO (3),
242 EMPTY_HOWTO (4),
243 EMPTY_HOWTO (5),
244 EMPTY_HOWTO (6),
245 EMPTY_HOWTO (7),
246 EMPTY_HOWTO (8),
247 EMPTY_HOWTO (9),
248 EMPTY_HOWTO (10),
249 EMPTY_HOWTO (11),
250 EMPTY_HOWTO (12),
251 EMPTY_HOWTO (13),
252 EMPTY_HOWTO (14),
253 EMPTY_HOWTO (15),
254 EMPTY_HOWTO (16),
255 EMPTY_HOWTO (17),
256 EMPTY_HOWTO (18),
257 EMPTY_HOWTO (19),
258 EMPTY_HOWTO (20),
259 EMPTY_HOWTO (21),
260 EMPTY_HOWTO (22),
261 EMPTY_HOWTO (23),
262 {R_IREL, 0, 3, 32, true, 0, complain_overflow_signed,a29k_reloc,"IREL", true, 0xffffffff,0xffffffff, false},
263 {R_IABS, 0, 3, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"IABS", true, 0xffffffff,0xffffffff, false},
264 {R_ILOHALF, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"ILOHALF", true, 0x0000ffff,0x0000ffff, false},
265 {R_IHIHALF, 0, 3, 16, true, 16, complain_overflow_signed, a29k_reloc,"IHIHALF", true, 0xffff0000,0xffff0000, false},
266 {R_IHCONST, 0, 3, 16, true, 0, complain_overflow_signed, a29k_reloc,"IHCONST", true, 0xffff0000,0xffff0000, false},
267 {R_BYTE, 0, 0, 8, false, 0, complain_overflow_bitfield, a29k_reloc,"BYTE", true, 0x000000ff,0x000000ff, false},
268 {R_HWORD, 0, 1, 16, false, 0, complain_overflow_bitfield, a29k_reloc,"HWORD", true, 0x0000ffff,0x0000ffff, false},
269 {R_WORD, 0, 2, 32, false, 0, complain_overflow_bitfield, a29k_reloc,"WORD", true, 0xffffffff,0xffffffff, false},
272 #define BADMAG(x) A29KBADMAG(x)
274 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
275 reloc_processing(relent, reloc, symbols, abfd, section)
277 static void
278 reloc_processing (relent,reloc, symbols, abfd, section)
279 arelent *relent;
280 struct internal_reloc *reloc;
281 asymbol **symbols;
282 bfd *abfd;
283 asection *section;
285 static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
287 relent->address = reloc->r_vaddr;
288 relent->howto = howto_table + reloc->r_type;
289 if (reloc->r_type == R_IHCONST)
291 /* The address of an R_IHCONST should always be the address of
292 the immediately preceding R_IHIHALF. relocs generated by gas
293 are correct, but relocs generated by High C are different (I
294 can't figure out what the address means for High C). We can
295 handle both gas and High C by ignoring the address here, and
296 simply reusing the address saved for R_IHIHALF. */
297 if (ihihalf_vaddr == (bfd_vma) -1)
298 abort ();
299 relent->address = ihihalf_vaddr;
300 ihihalf_vaddr = (bfd_vma) -1;
301 relent->addend = reloc->r_symndx;
302 relent->sym_ptr_ptr= bfd_abs_section_ptr->symbol_ptr_ptr;
304 else
306 asymbol *ptr;
307 relent->sym_ptr_ptr = symbols + obj_convert(abfd)[reloc->r_symndx];
309 ptr = *(relent->sym_ptr_ptr);
311 if (ptr
312 && bfd_asymbol_bfd(ptr) == abfd
314 && ((ptr->flags & BSF_OLD_COMMON)== 0))
316 relent->addend = 0;
318 else
320 relent->addend = 0;
322 relent->address-= section->vma;
323 if (reloc->r_type == R_IHIHALF)
324 ihihalf_vaddr = relent->address;
325 else if (ihihalf_vaddr != (bfd_vma) -1)
326 abort ();
330 /* The reloc processing routine for the optimized COFF linker. */
332 static boolean
333 coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section,
334 contents, relocs, syms, sections)
335 bfd *output_bfd ATTRIBUTE_UNUSED;
336 struct bfd_link_info *info;
337 bfd *input_bfd;
338 asection *input_section;
339 bfd_byte *contents;
340 struct internal_reloc *relocs;
341 struct internal_syment *syms;
342 asection **sections;
344 struct internal_reloc *rel;
345 struct internal_reloc *relend;
346 boolean hihalf;
347 bfd_vma hihalf_val;
349 /* If we are performing a relocateable link, we don't need to do a
350 thing. The caller will take care of adjusting the reloc
351 addresses and symbol indices. */
352 if (info->relocateable)
353 return true;
355 hihalf = false;
356 hihalf_val = 0;
358 rel = relocs;
359 relend = rel + input_section->reloc_count;
360 for (; rel < relend; rel++)
362 long symndx;
363 bfd_byte *loc;
364 struct coff_link_hash_entry *h;
365 struct internal_syment *sym;
366 asection *sec;
367 bfd_vma val;
368 boolean overflow;
369 unsigned long insn;
370 long signed_value;
371 unsigned long unsigned_value;
372 bfd_reloc_status_type rstat;
374 symndx = rel->r_symndx;
375 loc = contents + rel->r_vaddr - input_section->vma;
377 if (symndx == -1 || rel->r_type == R_IHCONST)
378 h = NULL;
379 else
380 h = obj_coff_sym_hashes (input_bfd)[symndx];
382 sym = NULL;
383 sec = NULL;
384 val = 0;
386 /* An R_IHCONST reloc does not have a symbol. Instead, the
387 symbol index is an addend. R_IHCONST is always used in
388 conjunction with R_IHHALF. */
389 if (rel->r_type != R_IHCONST)
391 if (h == NULL)
393 if (symndx == -1)
394 sec = bfd_abs_section_ptr;
395 else
397 sym = syms + symndx;
398 sec = sections[symndx];
399 val = (sec->output_section->vma
400 + sec->output_offset
401 + sym->n_value
402 - sec->vma);
405 else
407 if (h->root.type == bfd_link_hash_defined
408 || h->root.type == bfd_link_hash_defweak)
410 sec = h->root.u.def.section;
411 val = (h->root.u.def.value
412 + sec->output_section->vma
413 + sec->output_offset);
415 else
417 if (! ((*info->callbacks->undefined_symbol)
418 (info, h->root.root.string, input_bfd, input_section,
419 rel->r_vaddr - input_section->vma, true)))
420 return false;
424 if (hihalf)
426 if (! ((*info->callbacks->reloc_dangerous)
427 (info, _("missing IHCONST reloc"), input_bfd,
428 input_section, rel->r_vaddr - input_section->vma)))
429 return false;
430 hihalf = false;
434 overflow = false;
436 switch (rel->r_type)
438 default:
439 bfd_set_error (bfd_error_bad_value);
440 return false;
442 case R_IREL:
443 insn = bfd_get_32 (input_bfd, loc);
445 /* Extract the addend. */
446 signed_value = EXTRACT_HWORD (insn);
447 signed_value = SIGN_EXTEND_HWORD (signed_value);
448 signed_value <<= 2;
450 /* Unfortunately, there are two different versions of COFF
451 a29k. In the original AMD version, the value stored in
452 the field for the R_IREL reloc is a simple addend. In
453 the GNU version, the value is the negative of the address
454 of the reloc within section. We try to cope here by
455 assuming the AMD version, unless the addend is exactly
456 the negative of the address; in the latter case we assume
457 the GNU version. This means that something like
458 .text
460 jmp i-4
461 will fail, because the addend of -4 will happen to equal
462 the negative of the address within the section. The
463 compiler will never generate code like this.
465 At some point in the future we may want to take out this
466 check. */
468 if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
469 signed_value = 0;
471 /* Determine the destination of the jump. */
472 signed_value += val;
474 if ((signed_value & ~0x3ffff) == 0)
476 /* We can use an absolute jump. */
477 insn |= (1 << 24);
479 else
481 /* Make the destination PC relative. */
482 signed_value -= (input_section->output_section->vma
483 + input_section->output_offset
484 + (rel->r_vaddr - input_section->vma));
485 if (signed_value > 0x1ffff || signed_value < - 0x20000)
487 overflow = true;
488 signed_value = 0;
492 /* Put the adjusted value back into the instruction. */
493 signed_value >>= 2;
494 insn = INSERT_HWORD (insn, signed_value);
496 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
498 break;
500 case R_ILOHALF:
501 insn = bfd_get_32 (input_bfd, loc);
502 unsigned_value = EXTRACT_HWORD (insn);
503 unsigned_value += val;
504 insn = INSERT_HWORD (insn, unsigned_value);
505 bfd_put_32 (input_bfd, insn, loc);
506 break;
508 case R_IHIHALF:
509 /* Save the value for the R_IHCONST reloc. */
510 hihalf = true;
511 hihalf_val = val;
512 break;
514 case R_IHCONST:
515 if (! hihalf)
517 if (! ((*info->callbacks->reloc_dangerous)
518 (info, _("missing IHIHALF reloc"), input_bfd,
519 input_section, rel->r_vaddr - input_section->vma)))
520 return false;
521 hihalf_val = 0;
524 insn = bfd_get_32 (input_bfd, loc);
525 unsigned_value = rel->r_symndx + hihalf_val;
526 unsigned_value >>= 16;
527 insn = INSERT_HWORD (insn, unsigned_value);
528 bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
530 hihalf = false;
532 break;
534 case R_BYTE:
535 case R_HWORD:
536 case R_WORD:
537 rstat = _bfd_relocate_contents (howto_table + rel->r_type,
538 input_bfd, val, loc);
539 if (rstat == bfd_reloc_overflow)
540 overflow = true;
541 else if (rstat != bfd_reloc_ok)
542 abort ();
543 break;
546 if (overflow)
548 const char *name;
549 char buf[SYMNMLEN + 1];
551 if (symndx == -1)
552 name = "*ABS*";
553 else if (h != NULL)
554 name = h->root.root.string;
555 else if (sym == NULL)
556 name = "*unknown*";
557 else if (sym->_n._n_n._n_zeroes == 0
558 && sym->_n._n_n._n_offset != 0)
559 name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
560 else
562 strncpy (buf, sym->_n._n_name, SYMNMLEN);
563 buf[SYMNMLEN] = '\0';
564 name = buf;
567 if (! ((*info->callbacks->reloc_overflow)
568 (info, name, howto_table[rel->r_type].name, (bfd_vma) 0,
569 input_bfd, input_section,
570 rel->r_vaddr - input_section->vma)))
571 return false;
575 return true;
578 #define coff_relocate_section coff_a29k_relocate_section
580 /* We don't want to change the symndx of a R_IHCONST reloc, since it
581 is actually an addend, not a symbol index at all. */
583 static boolean
584 coff_a29k_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
585 bfd *obfd ATTRIBUTE_UNUSED;
586 struct bfd_link_info *info ATTRIBUTE_UNUSED;
587 bfd *ibfd ATTRIBUTE_UNUSED;
588 asection *sec ATTRIBUTE_UNUSED;
589 struct internal_reloc *irel;
590 boolean *adjustedp;
592 if (irel->r_type == R_IHCONST)
593 *adjustedp = true;
594 else
595 *adjustedp = false;
596 return true;
599 #define coff_adjust_symndx coff_a29k_adjust_symndx
601 #include "coffcode.h"
603 CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec, "coff-a29k-big", 0, SEC_READONLY, '_', NULL)