xref: /openbsd/gnu/usr.bin/binutils/bfd/coff-a29k.c (revision d415bd75)
1 /* BFD back-end for AMD 29000 COFF binaries.
2    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1997, 1999, 2000, 2001,
3    2002, 2003
4    Free Software Foundation, Inc.
5    Contributed by David Wood at New York University 7/8/91.
6 
7 This file is part of BFD, the Binary File Descriptor library.
8 
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22 
23 #define A29K 1
24 
25 #include "bfd.h"
26 #include "sysdep.h"
27 #include "libbfd.h"
28 #include "coff/a29k.h"
29 #include "coff/internal.h"
30 #include "libcoff.h"
31 
32 static long get_symbol_value PARAMS ((asymbol *));
33 static bfd_reloc_status_type a29k_reloc
34   PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
35 static bfd_boolean coff_a29k_relocate_section
36   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
37 	   struct internal_reloc *, struct internal_syment *, asection **));
38 static bfd_boolean coff_a29k_adjust_symndx
39   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *,
40 	   struct internal_reloc *, bfd_boolean *));
41 static void reloc_processing
42   PARAMS ((arelent *, struct internal_reloc *, asymbol **, bfd *, asection *));
43 
44 #define COFF_DEFAULT_SECTION_ALIGNMENT_POWER (2)
45 
46 #define INSERT_HWORD(WORD,HWORD)	\
47     (((WORD) & 0xff00ff00) | (((HWORD) & 0xff00) << 8) | ((HWORD)& 0xff))
48 #define EXTRACT_HWORD(WORD) \
49     ((((WORD) & 0x00ff0000) >> 8) | ((WORD) & 0xff))
50 #define SIGN_EXTEND_HWORD(HWORD) \
51     (((HWORD) ^ 0x8000) - 0x8000)
52 
53 /* Provided the symbol, returns the value reffed.  */
54 
55 static long
56 get_symbol_value (symbol)
57      asymbol *symbol;
58 {
59   long relocation = 0;
60 
61   if (bfd_is_com_section (symbol->section))
62     relocation = 0;
63   else
64     relocation = symbol->value +
65       symbol->section->output_section->vma +
66       symbol->section->output_offset;
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 bfd_boolean part1_consth_active = FALSE;
87   static unsigned long part1_consth_value;
88   unsigned long insn;
89   unsigned long sym_value;
90   unsigned long unsigned_value;
91   unsigned short r_type;
92   long signed_value;
93   unsigned long addr = reloc_entry->address ; /*+ input_section->vma*/
94   bfd_byte  *hit_data =addr + (bfd_byte *) (data);
95 
96   r_type = reloc_entry->howto->type;
97 
98   if (output_bfd)
99     {
100       /* Partial linking - do nothing.  */
101       reloc_entry->address += input_section->output_offset;
102       return bfd_reloc_ok;
103     }
104 
105   if (symbol_in != NULL
106       && bfd_is_und_section (symbol_in->section))
107     {
108       /* Keep the state machine happy in case we're called again.  */
109       if (r_type == R_IHIHALF)
110 	{
111 	  part1_consth_active = TRUE;
112 	  part1_consth_value  = 0;
113 	}
114       return bfd_reloc_undefined;
115     }
116 
117   if ((part1_consth_active) && (r_type != R_IHCONST))
118     {
119       part1_consth_active = FALSE;
120       *error_message = (char *) _("Missing IHCONST");
121 
122       return bfd_reloc_dangerous;
123     }
124 
125   sym_value = get_symbol_value(symbol_in);
126 
127   switch (r_type)
128     {
129     case R_IREL:
130       insn = bfd_get_32 (abfd, hit_data);
131       /* Take the value in the field and sign extend it.  */
132       signed_value = EXTRACT_HWORD(insn);
133       signed_value = SIGN_EXTEND_HWORD(signed_value);
134       signed_value <<= 2;
135 
136       /* See the note on the R_IREL reloc in coff_a29k_relocate_section.  */
137       if (signed_value == - (long) reloc_entry->address)
138 	signed_value = 0;
139 
140       signed_value += sym_value + reloc_entry->addend;
141       if ((signed_value & ~0x3ffff) == 0)
142 	{				/* Absolute jmp/call */
143 	  insn |= (1 << 24);		/* Make it absolute */
144 	  /* FIXME: Should we change r_type to R_IABS.  */
145 	}
146       else
147 	{
148 	  /* Relative jmp/call, so subtract from the value the
149 	     address of the place we're coming from.  */
150 	  signed_value -= (reloc_entry->address
151 			   + input_section->output_section->vma
152 			   + input_section->output_offset);
153 	  if (signed_value > 0x1ffff || signed_value < -0x20000)
154 	    return bfd_reloc_overflow;
155 	}
156       signed_value >>= 2;
157       insn = INSERT_HWORD (insn, signed_value);
158       bfd_put_32 (abfd, (bfd_vma) insn ,hit_data);
159       break;
160     case R_ILOHALF:
161       insn = bfd_get_32 (abfd, hit_data);
162       unsigned_value = EXTRACT_HWORD(insn);
163       unsigned_value +=  sym_value + reloc_entry->addend;
164       insn = INSERT_HWORD(insn, unsigned_value);
165       bfd_put_32 (abfd, (bfd_vma) insn, hit_data);
166       break;
167     case R_IHIHALF:
168       insn = bfd_get_32 (abfd, hit_data);
169       /* consth, part 1
170 	 Just get the symbol value that is referenced.  */
171       part1_consth_active = TRUE;
172       part1_consth_value = sym_value + reloc_entry->addend;
173       /* Don't modify insn until R_IHCONST.  */
174       break;
175     case R_IHCONST:
176       insn = bfd_get_32 (abfd, hit_data);
177       /* consth, part 2
178 	 Now relocate the reference.  */
179       if (! part1_consth_active)
180 	{
181 	  *error_message = (char *) _("Missing IHIHALF");
182 	  return bfd_reloc_dangerous;
183 	}
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, (bfd_vma) 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, (bfd_vma) 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, (bfd_vma) insn, hit_data);
211       break;
212     default:
213       *error_message = _("Unrecognized reloc");
214       return bfd_reloc_dangerous;
215     }
216 
217   return(bfd_reloc_ok);
218 }
219 
220 /*FIXME: I'm not real sure about this table.  */
221 static reloc_howto_type howto_table[] =
222   {
223     {R_ABS,     0, 3, 32, FALSE, 0, complain_overflow_bitfield,a29k_reloc,"ABS",     TRUE, 0xffffffff,0xffffffff, FALSE},
224     EMPTY_HOWTO (1),
225     EMPTY_HOWTO (2),
226     EMPTY_HOWTO (3),
227     EMPTY_HOWTO (4),
228     EMPTY_HOWTO (5),
229     EMPTY_HOWTO (6),
230     EMPTY_HOWTO (7),
231     EMPTY_HOWTO (8),
232     EMPTY_HOWTO (9),
233     EMPTY_HOWTO (10),
234     EMPTY_HOWTO (11),
235     EMPTY_HOWTO (12),
236     EMPTY_HOWTO (13),
237     EMPTY_HOWTO (14),
238     EMPTY_HOWTO (15),
239     EMPTY_HOWTO (16),
240     EMPTY_HOWTO (17),
241     EMPTY_HOWTO (18),
242     EMPTY_HOWTO (19),
243     EMPTY_HOWTO (20),
244     EMPTY_HOWTO (21),
245     EMPTY_HOWTO (22),
246     EMPTY_HOWTO (23),
247     {R_IREL,    0, 3, 32, TRUE,  0, complain_overflow_signed,a29k_reloc,"IREL",    TRUE, 0xffffffff,0xffffffff, FALSE},
248     {R_IABS,    0, 3, 32, FALSE, 0, complain_overflow_bitfield, a29k_reloc,"IABS",    TRUE, 0xffffffff,0xffffffff, FALSE},
249     {R_ILOHALF, 0, 3, 16, TRUE,  0, complain_overflow_signed, a29k_reloc,"ILOHALF", TRUE, 0x0000ffff,0x0000ffff, FALSE},
250     {R_IHIHALF, 0, 3, 16, TRUE,  16, complain_overflow_signed, a29k_reloc,"IHIHALF", TRUE, 0xffff0000,0xffff0000, FALSE},
251     {R_IHCONST, 0, 3, 16, TRUE,  0, complain_overflow_signed, a29k_reloc,"IHCONST", TRUE, 0xffff0000,0xffff0000, FALSE},
252     {R_BYTE,    0, 0, 8, FALSE, 0, complain_overflow_bitfield, a29k_reloc,"BYTE",    TRUE, 0x000000ff,0x000000ff, FALSE},
253     {R_HWORD,   0, 1, 16, FALSE, 0, complain_overflow_bitfield, a29k_reloc,"HWORD",   TRUE, 0x0000ffff,0x0000ffff, FALSE},
254     {R_WORD,    0, 2, 32, FALSE, 0, complain_overflow_bitfield, a29k_reloc,"WORD",    TRUE, 0xffffffff,0xffffffff, FALSE},
255   };
256 
257 #define BADMAG(x) A29KBADMAG(x)
258 
259 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
260  reloc_processing(relent, reloc, symbols, abfd, section)
261 
262 static void
263 reloc_processing (relent,reloc, symbols, abfd, section)
264      arelent *relent;
265      struct internal_reloc *reloc;
266      asymbol **symbols;
267      bfd *abfd;
268      asection *section;
269 {
270   static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
271 
272   relent->address = reloc->r_vaddr;
273   relent->howto = howto_table + reloc->r_type;
274   if (reloc->r_type == R_IHCONST)
275     {
276       /* The address of an R_IHCONST should always be the address of
277 	 the immediately preceding R_IHIHALF.  relocs generated by gas
278 	 are correct, but relocs generated by High C are different (I
279 	 can't figure out what the address means for High C).  We can
280 	 handle both gas and High C by ignoring the address here, and
281 	 simply reusing the address saved for R_IHIHALF.  */
282       if (ihihalf_vaddr == (bfd_vma) -1)
283 	abort ();
284       relent->address = ihihalf_vaddr;
285       ihihalf_vaddr = (bfd_vma) -1;
286       relent->addend = reloc->r_symndx;
287       relent->sym_ptr_ptr= bfd_abs_section_ptr->symbol_ptr_ptr;
288     }
289   else
290     {
291       asymbol *ptr;
292 
293       relent->sym_ptr_ptr = symbols + obj_convert (abfd)[reloc->r_symndx];
294 
295       ptr = *(relent->sym_ptr_ptr);
296 
297       if (ptr
298 	  && bfd_asymbol_bfd(ptr) == abfd
299 	  && ((ptr->flags & BSF_OLD_COMMON) == 0))
300 	relent->addend = 0;
301       else
302 	relent->addend = 0;
303 
304       relent->address-= section->vma;
305       if (reloc->r_type == R_IHIHALF)
306 	ihihalf_vaddr = relent->address;
307       else if (ihihalf_vaddr != (bfd_vma) -1)
308 	abort ();
309     }
310 }
311 
312 /* The reloc processing routine for the optimized COFF linker.  */
313 
314 static bfd_boolean
315 coff_a29k_relocate_section (output_bfd, info, input_bfd, input_section,
316 			    contents, relocs, syms, sections)
317      bfd *output_bfd ATTRIBUTE_UNUSED;
318      struct bfd_link_info *info;
319      bfd *input_bfd;
320      asection *input_section;
321      bfd_byte *contents;
322      struct internal_reloc *relocs;
323      struct internal_syment *syms;
324      asection **sections;
325 {
326   struct internal_reloc *rel;
327   struct internal_reloc *relend;
328   bfd_boolean hihalf;
329   bfd_vma hihalf_val;
330 
331   /* If we are performing a relocatable link, we don't need to do a
332      thing.  The caller will take care of adjusting the reloc
333      addresses and symbol indices.  */
334   if (info->relocatable)
335     return TRUE;
336 
337   hihalf = FALSE;
338   hihalf_val = 0;
339 
340   rel = relocs;
341   relend = rel + input_section->reloc_count;
342   for (; rel < relend; rel++)
343     {
344       long symndx;
345       bfd_byte *loc;
346       struct coff_link_hash_entry *h;
347       struct internal_syment *sym;
348       asection *sec;
349       bfd_vma val;
350       bfd_boolean overflow;
351       unsigned long insn;
352       long signed_value;
353       unsigned long unsigned_value;
354       bfd_reloc_status_type rstat;
355 
356       symndx = rel->r_symndx;
357       loc = contents + rel->r_vaddr - input_section->vma;
358 
359       if (symndx == -1 || rel->r_type == R_IHCONST)
360 	h = NULL;
361       else
362 	h = obj_coff_sym_hashes (input_bfd)[symndx];
363 
364       sym = NULL;
365       sec = NULL;
366       val = 0;
367 
368       /* An R_IHCONST reloc does not have a symbol.  Instead, the
369          symbol index is an addend.  R_IHCONST is always used in
370          conjunction with R_IHHALF.  */
371       if (rel->r_type != R_IHCONST)
372 	{
373 	  if (h == NULL)
374 	    {
375 	      if (symndx == -1)
376 		sec = bfd_abs_section_ptr;
377 	      else
378 		{
379 		  sym = syms + symndx;
380 		  sec = sections[symndx];
381 		  val = (sec->output_section->vma
382 			 + sec->output_offset
383 			 + sym->n_value
384 			 - sec->vma);
385 		}
386 	    }
387 	  else
388 	    {
389 	      if (   h->root.type == bfd_link_hash_defined
390 		  || h->root.type == bfd_link_hash_defweak)
391 		{
392 		  sec = h->root.u.def.section;
393 		  val = (h->root.u.def.value
394 			 + sec->output_section->vma
395 			 + sec->output_offset);
396 		}
397 	      else
398 		{
399 		  if (! ((*info->callbacks->undefined_symbol)
400 			 (info, h->root.root.string, input_bfd, input_section,
401 			  rel->r_vaddr - input_section->vma, TRUE)))
402 		    return FALSE;
403 		}
404 	    }
405 
406 	  if (hihalf)
407 	    {
408 	      if (! ((*info->callbacks->reloc_dangerous)
409 		     (info, _("missing IHCONST reloc"), input_bfd,
410 		      input_section, rel->r_vaddr - input_section->vma)))
411 		return FALSE;
412 	      hihalf = FALSE;
413 	    }
414 	}
415 
416       overflow = FALSE;
417 
418       switch (rel->r_type)
419 	{
420 	default:
421 	  bfd_set_error (bfd_error_bad_value);
422 	  return FALSE;
423 
424 	case R_IREL:
425 	  insn = bfd_get_32 (input_bfd, loc);
426 
427 	  /* Extract the addend.  */
428 	  signed_value = EXTRACT_HWORD (insn);
429 	  signed_value = SIGN_EXTEND_HWORD (signed_value);
430 	  signed_value <<= 2;
431 
432 	  /* Unfortunately, there are two different versions of COFF
433 	     a29k.  In the original AMD version, the value stored in
434 	     the field for the R_IREL reloc is a simple addend.  In
435 	     the GNU version, the value is the negative of the address
436 	     of the reloc within section.  We try to cope here by
437 	     assuming the AMD version, unless the addend is exactly
438 	     the negative of the address; in the latter case we assume
439 	     the GNU version.  This means that something like
440 	         .text
441 		 nop
442 		 jmp i-4
443 	     will fail, because the addend of -4 will happen to equal
444 	     the negative of the address within the section.  The
445 	     compiler will never generate code like this.
446 
447 	     At some point in the future we may want to take out this
448 	     check.  */
449 
450 	  if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
451 	    signed_value = 0;
452 
453 	  /* Determine the destination of the jump.  */
454 	  signed_value += val;
455 
456 	  if ((signed_value & ~0x3ffff) == 0)
457 	    {
458 	      /* We can use an absolute jump.  */
459 	      insn |= (1 << 24);
460 	    }
461 	  else
462 	    {
463 	      /* Make the destination PC relative.  */
464 	      signed_value -= (input_section->output_section->vma
465 			       + input_section->output_offset
466 			       + (rel->r_vaddr - input_section->vma));
467 	      if (signed_value > 0x1ffff || signed_value < - 0x20000)
468 		{
469 		  overflow = TRUE;
470 		  signed_value = 0;
471 		}
472 	    }
473 
474 	  /* Put the adjusted value back into the instruction.  */
475 	  signed_value >>= 2;
476 	  insn = INSERT_HWORD (insn, signed_value);
477 
478 	  bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
479 	  break;
480 
481 	case R_ILOHALF:
482 	  insn = bfd_get_32 (input_bfd, loc);
483 	  unsigned_value = EXTRACT_HWORD (insn);
484 	  unsigned_value += val;
485 	  insn = INSERT_HWORD (insn, unsigned_value);
486 	  bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
487 	  break;
488 
489 	case R_IHIHALF:
490 	  /* Save the value for the R_IHCONST reloc.  */
491 	  hihalf = TRUE;
492 	  hihalf_val = val;
493 	  break;
494 
495 	case R_IHCONST:
496 	  if (! hihalf)
497 	    {
498 	      if (! ((*info->callbacks->reloc_dangerous)
499 		     (info, _("missing IHIHALF reloc"), input_bfd,
500 		      input_section, rel->r_vaddr - input_section->vma)))
501 		return FALSE;
502 	      hihalf_val = 0;
503 	    }
504 
505 	  insn = bfd_get_32 (input_bfd, loc);
506 	  unsigned_value = rel->r_symndx + hihalf_val;
507 	  unsigned_value >>= 16;
508 	  insn = INSERT_HWORD (insn, unsigned_value);
509 	  bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
510 
511 	  hihalf = FALSE;
512 
513 	  break;
514 
515 	case R_BYTE:
516 	case R_HWORD:
517 	case R_WORD:
518 	  rstat = _bfd_relocate_contents (howto_table + rel->r_type,
519 					  input_bfd, val, loc);
520 	  if (rstat == bfd_reloc_overflow)
521 	    overflow = TRUE;
522 	  else if (rstat != bfd_reloc_ok)
523 	    abort ();
524 	  break;
525 	}
526 
527       if (overflow)
528 	{
529 	  const char *name;
530 	  char buf[SYMNMLEN + 1];
531 
532 	  if (symndx == -1)
533 	    name = "*ABS*";
534 	  else if (h != NULL)
535 	    name = h->root.root.string;
536 	  else if (sym == NULL)
537 	    name = "*unknown*";
538 	  else if (sym->_n._n_n._n_zeroes == 0
539 		   && sym->_n._n_n._n_offset != 0)
540 	    name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
541 	  else
542 	    {
543 	      strncpy (buf, sym->_n._n_name, SYMNMLEN);
544 	      buf[SYMNMLEN] = '\0';
545 	      name = buf;
546 	    }
547 
548 	  if (! ((*info->callbacks->reloc_overflow)
549 		 (info, name, howto_table[rel->r_type].name, (bfd_vma) 0,
550 		  input_bfd, input_section,
551 		  rel->r_vaddr - input_section->vma)))
552 	    return FALSE;
553 	}
554     }
555 
556   return TRUE;
557 }
558 
559 #define coff_relocate_section coff_a29k_relocate_section
560 
561 /* We don't want to change the symndx of a R_IHCONST reloc, since it
562    is actually an addend, not a symbol index at all.  */
563 
564 static bfd_boolean
565 coff_a29k_adjust_symndx (obfd, info, ibfd, sec, irel, adjustedp)
566      bfd *obfd ATTRIBUTE_UNUSED;
567      struct bfd_link_info *info ATTRIBUTE_UNUSED;
568      bfd *ibfd ATTRIBUTE_UNUSED;
569      asection *sec ATTRIBUTE_UNUSED;
570      struct internal_reloc *irel;
571      bfd_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 CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec, "coff-a29k-big", 0, SEC_READONLY, '_', NULL, COFF_SWAP_TABLE)
585