OSDN Git Service

2000-11-08 Kazu Hirata <kazu@hxi.com>
[pf3gnuchains/pf3gnuchains3x.git] / bfd / coff-a29k.c
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.
5
6 This file is part of BFD, the Binary File Descriptor library.
7
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.
12
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.
17
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.  */
21
22 #define A29K 1
23
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"
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) - 0x8000)
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   sym_value = get_symbol_value(symbol_in);
127
128   switch (r_type)
129   {
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;
136
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;
140
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 */
146     }
147     else
148     {
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);
156     }
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);
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, 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);
215   }
216
217   return(bfd_reloc_ok);
218 }
219
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
231 */
232
233 /*FIXME: I'm not real sure about this table */
234 static reloc_howto_type howto_table[] =
235 {
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},
268 };
269
270 #define BADMAG(x) A29KBADMAG(x)
271
272 #define RELOC_PROCESSING(relent, reloc, symbols, abfd, section) \
273  reloc_processing(relent, reloc, symbols, abfd, section)
274
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;
282 {
283     static bfd_vma ihihalf_vaddr = (bfd_vma) -1;
284
285     relent->address = reloc->r_vaddr;
286     relent->howto = howto_table + reloc->r_type;
287     if (reloc->r_type == R_IHCONST)
288     {
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;
301     }
302     else
303     {
304       asymbol *ptr;
305       relent->sym_ptr_ptr = symbols + obj_convert(abfd)[reloc->r_symndx];
306
307       ptr = *(relent->sym_ptr_ptr);
308
309       if (ptr
310           && bfd_asymbol_bfd(ptr) == abfd
311
312           && ((ptr->flags & BSF_OLD_COMMON)== 0))
313       {
314           relent->addend = 0;
315       }
316       else
317       {
318           relent->addend = 0;
319       }
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 ();
325   }
326 }
327
328 /* The reloc processing routine for the optimized COFF linker.  */
329
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;
341 {
342   struct internal_reloc *rel;
343   struct internal_reloc *relend;
344   boolean hihalf;
345   bfd_vma hihalf_val;
346
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;
352
353   hihalf = false;
354   hihalf_val = 0;
355
356   rel = relocs;
357   relend = rel + input_section->reloc_count;
358   for (; rel < relend; rel++)
359     {
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;
371
372       symndx = rel->r_symndx;
373       loc = contents + rel->r_vaddr - input_section->vma;
374
375       if (symndx == -1 || rel->r_type == R_IHCONST)
376         h = NULL;
377       else
378         h = obj_coff_sym_hashes (input_bfd)[symndx];
379
380       sym = NULL;
381       sec = NULL;
382       val = 0;
383
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)
388         {
389           if (h == NULL)
390             {
391               if (symndx == -1)
392                 sec = bfd_abs_section_ptr;
393               else
394                 {
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);
401                 }
402             }
403           else
404             {
405               if (h->root.type == bfd_link_hash_defined
406                   || h->root.type == bfd_link_hash_defweak)
407                 {
408                   sec = h->root.u.def.section;
409                   val = (h->root.u.def.value
410                          + sec->output_section->vma
411                          + sec->output_offset);
412                 }
413               else
414                 {
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;
419                 }
420             }
421
422           if (hihalf)
423             {
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;
429             }
430         }
431
432       overflow = false;
433
434       switch (rel->r_type)
435         {
436         default:
437           bfd_set_error (bfd_error_bad_value);
438           return false;
439
440         case R_IREL:
441           insn = bfd_get_32 (input_bfd, loc);
442
443           /* Extract the addend.  */
444           signed_value = EXTRACT_HWORD (insn);
445           signed_value = SIGN_EXTEND_HWORD (signed_value);
446           signed_value <<= 2;
447
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
457                  nop
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.
462
463              At some point in the future we may want to take out this
464              check.  */
465
466           if (signed_value == - (long) (rel->r_vaddr - input_section->vma))
467             signed_value = 0;
468
469           /* Determine the destination of the jump.  */
470           signed_value += val;
471
472           if ((signed_value & ~0x3ffff) == 0)
473             {
474               /* We can use an absolute jump.  */
475               insn |= (1 << 24);
476             }
477           else
478             {
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)
484                 {
485                   overflow = true;
486                   signed_value = 0;
487                 }
488             }
489
490           /* Put the adjusted value back into the instruction.  */
491           signed_value >>= 2;
492           insn = INSERT_HWORD (insn, signed_value);
493
494           bfd_put_32 (input_bfd, (bfd_vma) insn, loc);
495
496           break;
497
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;
505
506         case R_IHIHALF:
507           /* Save the value for the R_IHCONST reloc.  */
508           hihalf = true;
509           hihalf_val = val;
510           break;
511
512         case R_IHCONST:
513           if (! hihalf)
514             {
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;
520             }
521
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);
527
528           hihalf = false;
529
530           break;
531
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;
542         }
543
544       if (overflow)
545         {
546           const char *name;
547           char buf[SYMNMLEN + 1];
548
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
559             {
560               strncpy (buf, sym->_n._n_name, SYMNMLEN);
561               buf[SYMNMLEN] = '\0';
562               name = buf;
563             }
564
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;
570         }
571     }
572
573   return true;
574 }
575
576 #define coff_relocate_section coff_a29k_relocate_section
577
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.  */
580
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;
589 {
590   if (irel->r_type == R_IHCONST)
591     *adjustedp = true;
592   else
593     *adjustedp = false;
594   return true;
595 }
596
597 #define coff_adjust_symndx coff_a29k_adjust_symndx
598
599 #include "coffcode.h"
600
601 CREATE_BIG_COFF_TARGET_VEC (a29kcoff_big_vec, "coff-a29k-big", 0, SEC_READONLY, '_', NULL)