Added support for FR30
[external/binutils.git] / bfd / elf32-fr30.c
1 /* FR30-specific support for 32-bit ELF.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3
4 This file is part of BFD, the Binary File Descriptor library.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "bfd.h"
21 #include "sysdep.h"
22 #include "libbfd.h"
23 #include "elf-bfd.h"
24 #include "elf/fr30.h"
25
26 /* Forward declarations.  */
27 static bfd_reloc_status_type fr30_elf_i20_reloc
28   PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
29
30 static reloc_howto_type fr30_elf_howto_table [] =
31 {
32   /* This reloc does nothing.  */
33   HOWTO (R_FR30_NONE,           /* type */
34          0,                     /* rightshift */
35          2,                     /* size (0 = byte, 1 = short, 2 = long) */
36          32,                    /* bitsize */
37          false,                 /* pc_relative */
38          0,                     /* bitpos */
39          complain_overflow_bitfield, /* complain_on_overflow */
40          bfd_elf_generic_reloc, /* special_function */
41          "R_FR30_NONE",         /* name */
42          false,                 /* partial_inplace */
43          0,                     /* src_mask */
44          0,                     /* dst_mask */
45          false),                /* pcrel_offset */
46
47   /* An 8 bit absolute relocation.  */
48   HOWTO (R_FR30_8,              /* type */
49          0,                     /* rightshift */
50          1,                     /* size (0 = byte, 1 = short, 2 = long) */
51          8,                     /* bitsize */
52          false,                 /* pc_relative */
53          4,                     /* bitpos */
54          complain_overflow_bitfield, /* complain_on_overflow */
55          bfd_elf_generic_reloc, /* special_function */
56          "R_FR30_8",            /* name */
57          true,                  /* partial_inplace */
58          0x0ff0,                /* src_mask */
59          0x0ff0,                /* dst_mask */
60          false),                /* pcrel_offset */
61
62   /* A 20 bit absolute relocation.  */
63   HOWTO (R_FR30_20,             /* type */
64          0,                     /* rightshift */
65          2,                     /* size (0 = byte, 1 = short, 2 = long) */
66          20,                    /* bitsize */
67          false,                 /* pc_relative */
68          0,                     /* bitpos */
69          complain_overflow_bitfield, /* complain_on_overflow */
70          fr30_elf_i20_reloc,    /* special_function */
71          "R_FR30_20",           /* name */
72          true,                  /* partial_inplace */
73          0x00f0ffff,            /* src_mask */
74          0x00f0ffff,            /* dst_mask */
75          false),                /* pcrel_offset */
76
77   /* A 32 bit absolute relocation.  */
78   HOWTO (R_FR30_32,             /* type */
79          0,                     /* rightshift */
80          2,                     /* size (0 = byte, 1 = short, 2 = long) */
81          32,                    /* bitsize */
82          false,                 /* pc_relative */
83          0,                     /* bitpos */
84          complain_overflow_bitfield, /* complain_on_overflow */
85          bfd_elf_generic_reloc, /* special_function */
86          "R_FR30_32",           /* name */
87          true,                  /* partial_inplace */
88          0xffffffff,            /* src_mask */
89          0xffffffff,            /* dst_mask */
90          false),                /* pcrel_offset */
91
92   /* A 6 bit absolute relocation.  */
93   HOWTO (R_FR30_6_IN_4,         /* type */
94          2,                     /* rightshift */
95          1,                     /* size (0 = byte, 1 = short, 2 = long) */
96          6,                     /* bitsize */
97          false,                 /* pc_relative */
98          4,                     /* bitpos */
99          complain_overflow_unsigned, /* complain_on_overflow */
100          bfd_elf_generic_reloc, /* special_function */
101          "R_FR30_6_IN_4",       /* name */
102          true,                  /* partial_inplace */
103          0x00f0,                /* src_mask */
104          0x00f0,                /* dst_mask */
105          false),                /* pcrel_offset */
106   
107   /* An 8 bit absolute relocation.  */
108   HOWTO (R_FR30_8_IN_8,         /* type */
109          0,                     /* rightshift */
110          1,                     /* size (0 = byte, 1 = short, 2 = long) */
111          8,                     /* bitsize */
112          false,                 /* pc_relative */
113          4,                     /* bitpos */
114          complain_overflow_signed, /* complain_on_overflow */
115          bfd_elf_generic_reloc,/* special_function */
116          "R_FR30_8_IN_8",       /* name */
117          true,                  /* partial_inplace */
118          0x0ff0,                /* src_mask */
119          0x0ff0,                /* dst_mask */
120          false),                /* pcrel_offset */
121   
122   /* A 9 bit absolute relocation.  */
123   HOWTO (R_FR30_9_IN_8,         /* type */
124          1,                     /* rightshift */
125          1,                     /* size (0 = byte, 1 = short, 2 = long) */
126          9,                     /* bitsize */
127          false,                 /* pc_relative */
128          4,                     /* bitpos */
129          complain_overflow_signed, /* complain_on_overflow */
130          bfd_elf_generic_reloc,/* special_function */
131          "R_FR30_9_IN_8",       /* name */
132          true,                  /* partial_inplace */
133          0x0ff0,                /* src_mask */
134          0x0ff0,                /* dst_mask */
135          false),                /* pcrel_offset */
136   
137   /* A 10 bit absolute relocation.  */
138   HOWTO (R_FR30_10_IN_8,        /* type */
139          2,                     /* rightshift */
140          1,                     /* size (0 = byte, 1 = short, 2 = long) */
141          10,                    /* bitsize */
142          false,                 /* pc_relative */
143          4,                     /* bitpos */
144          complain_overflow_signed, /* complain_on_overflow */
145          bfd_elf_generic_reloc,/* special_function */
146          "R_FR30_10_IN_8",      /* name */
147          true,                  /* partial_inplace */
148          0x0ff0,                /* src_mask */
149          0x0ff0,                /* dst_mask */
150          false),                /* pcrel_offset */
151
152   /* A PC relative 9 bit relocation, right shifted by 1.  */
153   HOWTO (R_FR30_9_PCREL,        /* type */
154          1,                     /* rightshift */
155          1,                     /* size (0 = byte, 1 = short, 2 = long) */
156          9,                     /* bitsize */
157          true,                  /* pc_relative */
158          0,                     /* bitpos */
159          complain_overflow_signed, /* complain_on_overflow */
160          bfd_elf_generic_reloc, /* special_function */
161          "R_FR30_9_PCREL",      /* name */
162          false,                 /* partial_inplace */
163          0x00ff,                /* src_mask */
164          0x00ff,                /* dst_mask */
165          true),                 /* pcrel_offset */
166
167   /* A PC relative 12 bit relocation, right shifted by 1.  */
168   HOWTO (R_FR30_12_PCREL,       /* type */
169          1,                     /* rightshift */
170          1,                     /* size (0 = byte, 1 = short, 2 = long) */
171          12,                    /* bitsize */
172          true,                  /* pc_relative */
173          0,                     /* bitpos */
174          complain_overflow_signed, /* complain_on_overflow */
175          bfd_elf_generic_reloc, /* special_function */
176          "R_FR30_12_PCREL",     /* name */
177          false,                 /* partial_inplace */
178          0x07ff,                /* src_mask */
179          0x07ff,                /* dst_mask */
180          true),                 /* pcrel_offset */
181 };
182 \f
183 /* Utility to actually perform an R_FR30_20 reloc.  */
184
185 static bfd_reloc_status_type
186 fr30_elf_i20_reloc (abfd, reloc_entry, symbol, data,
187                     input_section, output_bfd, error_message)
188      bfd *      abfd;
189      arelent *  reloc_entry;
190      asymbol *  symbol;
191      PTR        data;
192      asection * input_section;
193      bfd *      output_bfd;
194      char **    error_message;
195 {
196   bfd_vma       relocation;
197   unsigned long x;
198   
199   /* This part is from bfd_elf_generic_reloc.  */
200   if (output_bfd != (bfd *) NULL
201       && (symbol->flags & BSF_SECTION_SYM) == 0
202       && (! reloc_entry->howto->partial_inplace
203           || reloc_entry->addend == 0))
204     {
205       reloc_entry->address += input_section->output_offset;
206       return bfd_reloc_ok;
207     }
208
209   if (output_bfd != NULL)
210     /* FIXME: See bfd_perform_relocation.  Is this right?  */
211     return bfd_reloc_continue;
212
213   relocation =
214     symbol->value
215     + symbol->section->output_section->vma
216     + symbol->section->output_offset
217     + reloc_entry->addend;
218
219   if (relocation > ((1U << 20) - 1))
220     return bfd_reloc_overflow;
221
222   x = bfd_get_32 (abfd, data + reloc_entry->address);
223   x = (x & 0xff0f0000) | (relocation & 0x0000ffff) | ((relocation & 0x000f0000) << 4);
224   bfd_put_32 (abfd, x, data + reloc_entry->address);
225
226   return bfd_reloc_ok;
227 }
228
229 \f
230 /* Map BFD reloc types to FR30 ELF reloc types.  */
231
232 struct fr30_reloc_map
233 {
234   unsigned char bfd_reloc_val;
235   unsigned char elf_reloc_val;
236 };
237
238 static const struct fr30_reloc_map fr30_reloc_map [] =
239 {
240   { BFD_RELOC_NONE,           R_FR30_NONE },
241   { BFD_RELOC_8,              R_FR30_8 },
242   { BFD_RELOC_FR30_20,        R_FR30_20 },
243   { BFD_RELOC_32,             R_FR30_32 },
244   { BFD_RELOC_FR30_6_IN_4,    R_FR30_6_IN_4 },
245   { BFD_RELOC_FR30_8_IN_8,    R_FR30_8_IN_8 },
246   { BFD_RELOC_FR30_9_IN_8,    R_FR30_9_IN_8 },
247   { BFD_RELOC_FR30_10_IN_8,   R_FR30_10_IN_8 },
248   { BFD_RELOC_FR30_9_PCREL,   R_FR30_9_PCREL },
249   { BFD_RELOC_FR30_12_PCREL,  R_FR30_12_PCREL },
250 };
251
252 static reloc_howto_type *
253 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
254      bfd * abfd;
255      bfd_reloc_code_real_type code;
256 {
257   unsigned int i;
258
259   for (i = sizeof (fr30_reloc_map) / sizeof (fr30_reloc_map[0]);
260        --i;)
261     {
262       if (fr30_reloc_map [i].bfd_reloc_val == code)
263         return & fr30_elf_howto_table [fr30_reloc_map[i].elf_reloc_val];
264     }
265
266   return NULL;
267 }
268
269 /* Set the howto pointer for an FR30 ELF reloc.  */
270
271 static void
272 fr30_info_to_howto_rel (abfd, cache_ptr, dst)
273      bfd * abfd;
274      arelent * cache_ptr;
275      Elf32_Internal_Rela * dst;
276 {
277   unsigned int r_type;
278
279   r_type = ELF32_R_TYPE (dst->r_info);
280   BFD_ASSERT (r_type < (unsigned int) R_FR30_max);
281   cache_ptr->howto = & fr30_elf_howto_table [r_type];
282 }
283 \f
284 /* Relocate an FR30 ELF section.
285    There is some attempt to make this function usable for many architectures,
286    both USE_REL and USE_RELA ['twould be nice if such a critter existed],
287    if only to serve as a learning tool.
288
289    The RELOCATE_SECTION function is called by the new ELF backend linker
290    to handle the relocations for a section.
291
292    The relocs are always passed as Rela structures; if the section
293    actually uses Rel structures, the r_addend field will always be
294    zero.
295
296    This function is responsible for adjusting the section contents as
297    necessary, and (if using Rela relocs and generating a relocateable
298    output file) adjusting the reloc addend as necessary.
299
300    This function does not have to worry about setting the reloc
301    address or the reloc symbol index.
302
303    LOCAL_SYMS is a pointer to the swapped in local symbols.
304
305    LOCAL_SECTIONS is an array giving the section in the input file
306    corresponding to the st_shndx field of each local symbol.
307
308    The global hash table entry for the global symbols can be found
309    via elf_sym_hashes (input_bfd).
310
311    When generating relocateable output, this function must handle
312    STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
313    going to be the section symbol corresponding to the output
314    section, which means that the addend must be adjusted
315    accordingly.  */
316
317 static boolean
318 fr30_elf_relocate_section (output_bfd, info, input_bfd, input_section,
319                            contents, relocs, local_syms, local_sections)
320      bfd *                   output_bfd;
321      struct bfd_link_info *  info;
322      bfd *                   input_bfd;
323      asection *              input_section;
324      bfd_byte *              contents;
325      Elf_Internal_Rela *     relocs;
326      Elf_Internal_Sym *      local_syms;
327      asection **             local_sections;
328 {
329   Elf_Internal_Shdr *           symtab_hdr;
330   struct elf_link_hash_entry ** sym_hashes;
331   Elf_Internal_Rela *           rel;
332   Elf_Internal_Rela *           relend;
333
334   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
335   sym_hashes = elf_sym_hashes (input_bfd);
336   relend     = relocs + input_section->reloc_count;
337   
338   for (rel = relocs; rel < relend; rel ++)
339     {
340       reloc_howto_type *           howto;
341       unsigned long                r_symndx;
342       Elf_Internal_Sym *           sym;
343       asection *                   sec;
344       struct elf_link_hash_entry * h;
345       bfd_vma                      relocation;
346       bfd_reloc_status_type        r;
347       const char *                 name;
348
349       r_symndx = ELF32_R_SYM (rel->r_info);
350
351       if (info->relocateable)
352         {
353           /* This is a relocateable link.  We don't have to change
354              anything, unless the reloc is against a section symbol,
355              in which case we have to adjust according to where the
356              section symbol winds up in the output section.  */
357           if (r_symndx < symtab_hdr->sh_info)
358             {
359               sym = local_syms + r_symndx;
360               
361               if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
362                 {
363                   sec = local_sections [r_symndx];
364                   rel->r_addend += sec->output_offset + sym->st_value;
365                 }
366             }
367
368           continue;
369         }
370
371       /* This is a final link.  */
372       howto  = fr30_elf_howto_table + ELF32_R_TYPE (rel->r_info);
373       h      = NULL;
374       sym    = NULL;
375       sec    = NULL;
376       
377       if (r_symndx < symtab_hdr->sh_info)
378         {
379           sym = local_syms + r_symndx;
380           sec = local_sections [r_symndx];
381           relocation = (sec->output_section->vma
382                         + sec->output_offset
383                         + sym->st_value);
384           
385           name = bfd_elf_string_from_elf_section
386             (input_bfd, symtab_hdr->sh_link, sym->st_name);
387           name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
388 #if 0
389           fprintf (stderr, "local: sec: %s, sym: %s (%d), value: %x + %x + %x addend %x\n",
390                    sec->name, name, sym->st_name,
391                    sec->output_section->vma, sec->output_offset,
392                    sym->st_value, rel->r_addend);
393 #endif
394         }
395       else
396         {
397           h = sym_hashes [r_symndx - symtab_hdr->sh_info];
398           
399           while (h->root.type == bfd_link_hash_indirect
400                  || h->root.type == bfd_link_hash_warning)
401             h = (struct elf_link_hash_entry *) h->root.u.i.link;
402
403           name = h->root.root.string;
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               relocation = (h->root.u.def.value
410                             + sec->output_section->vma
411                             + sec->output_offset);
412 #if 0
413               fprintf (stderr,
414                        "defined: sec: %s, name: %s, value: %x + %x + %x gives: %x\n",
415                        sec->name, name, h->root.u.def.value,
416                        sec->output_section->vma, sec->output_offset, relocation);
417 #endif
418             }
419           else if (h->root.type == bfd_link_hash_undefweak)
420             {
421 #if 0
422               fprintf (stderr, "undefined: sec: %s, name: %s\n",
423                        sec->name, name);
424 #endif
425               relocation = 0;
426             }
427           else
428             {
429               if (! ((*info->callbacks->undefined_symbol)
430                      (info, h->root.root.string, input_bfd,
431                       input_section, rel->r_offset)))
432                 return false;
433 #if 0
434               fprintf (stderr, "unknown: name: %s\n", name);
435 #endif
436               relocation = 0;
437             }
438         }
439
440       r = _bfd_final_link_relocate (howto, input_bfd, input_section,
441                                     contents, rel->r_offset,
442                                     relocation, rel->r_addend);
443
444       if (r != bfd_reloc_ok)
445         {
446           const char * msg = (const char *)0;
447
448           switch (r)
449             {
450             case bfd_reloc_overflow:
451               if (! ((*info->callbacks->reloc_overflow)
452                      (info, name, howto->name, (bfd_vma) 0,
453                       input_bfd, input_section, rel->r_offset)))
454                 return false;
455               break;
456
457             case bfd_reloc_undefined:
458               if (! ((*info->callbacks->undefined_symbol)
459                      (info, name, input_bfd, input_section,
460                       rel->r_offset)))
461                 return false;
462               break;
463
464             case bfd_reloc_outofrange:
465               msg = _("internal error: out of range error");
466               goto common_error;
467
468             case bfd_reloc_notsupported:
469               msg = _("internal error: unsupported relocation error");
470               goto common_error;
471
472             case bfd_reloc_dangerous:
473               msg = _("internal error: dangerous relocation");
474               goto common_error;
475
476             case bfd_reloc_other:
477               msg = _("could not locate special linker symbol __gp");
478               goto common_error;
479
480             case bfd_reloc_continue:
481               msg = _("could not locate special linker symbol __ep");
482               goto common_error;
483
484             case (bfd_reloc_dangerous + 1):
485               msg = _("could not locate special linker symbol __ctbp");
486               goto common_error;
487               
488             default:
489               msg = _("internal error: unknown error");
490               /* fall through */
491
492             common_error:
493               if (!((*info->callbacks->warning)
494                     (info, msg, name, input_bfd, input_section,
495                      rel->r_offset)))
496                 return false;
497               break;
498             }
499         }
500     }
501
502   return true;
503 }
504 \f
505 #define ELF_ARCH                bfd_arch_fr30
506 #define ELF_MACHINE_CODE        EM_CYGNUS_FR30
507 #define ELF_MAXPAGESIZE         0x1000
508
509 #define TARGET_BIG_SYM          bfd_elf32_fr30_vec
510 #define TARGET_BIG_NAME         "elf32-fr30"
511
512 #define elf_info_to_howto                       0
513 #define elf_info_to_howto_rel                   fr30_info_to_howto_rel
514 #define elf_backend_relocate_section            fr30_elf_relocate_section
515                                         
516 #include "elf32-target.h"