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