Get rid of the -1 dummy valued enum in START_RELOC_NUMBERS.
[external/binutils.git] / bfd / elf32-d30v.c
1 /* D30V-specific support for 32-bit ELF
2    Copyright (C) 1997, 98, 99, 2000 Free Software Foundation, Inc.
3    Contributed by Martin Hunt (hunt@cygnus.com).
4
5 This file is part of BFD, the Binary File Descriptor library.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #include "bfd.h"
22 #include "sysdep.h"
23 #include "libbfd.h"
24 #include "elf-bfd.h"
25 #include "elf/d30v.h"
26
27 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
28   PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
29 static void d30v_info_to_howto_rel
30   PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *));
31 static void d30v_info_to_howto_rela
32   PARAMS ((bfd *, arelent *, Elf32_Internal_Rela *));
33 static bfd_reloc_status_type bfd_elf_d30v_reloc PARAMS ((
34      bfd *abfd,
35      arelent *reloc_entry,
36      asymbol *symbol,
37      PTR data,
38      asection *input_section,
39      bfd *output_bfd,
40      char **error_message));
41 static bfd_reloc_status_type bfd_elf_d30v_reloc_21 PARAMS ((
42      bfd *abfd,
43      arelent *reloc_entry,
44      asymbol *symbol,
45      PTR data,
46      asection *input_section,
47      bfd *output_bfd,
48      char **error_message));
49
50 static reloc_howto_type elf_d30v_howto_table[] =
51 {
52   /* This reloc does nothing.  */
53   HOWTO (R_D30V_NONE,           /* type */
54          0,                     /* rightshift */
55          2,                     /* size (0 = byte, 1 = short, 2 = long) */
56          32,                    /* bitsize */
57          false,                 /* pc_relative */
58          0,                     /* bitpos */
59          complain_overflow_bitfield, /* complain_on_overflow */
60          bfd_elf_generic_reloc, /* special_function */
61          "R_D30V_NONE",         /* name */
62          false,                 /* partial_inplace */
63          0,                     /* src_mask */
64          0,                     /* dst_mask */
65          false),                /* pcrel_offset */
66
67   /* A 6 bit absolute relocation */
68   HOWTO (R_D30V_6,              /* type */
69          0,                     /* rightshift */
70          2,                     /* size (0 = byte, 1 = short, 2 = long) */
71          6,                     /* bitsize */
72          false,                 /* pc_relative */
73          0,                     /* bitpos */
74          complain_overflow_bitfield, /* complain_on_overflow */
75          bfd_elf_generic_reloc, /* special_function */
76          "R_D30V_6",            /* name */
77          false,                 /* partial_inplace */
78          0x3f,                  /* src_mask */
79          0x3f,                  /* dst_mask */
80          false),                /* pcrel_offset */
81
82   /* A relative 9 bit relocation, right shifted by 3 */
83   HOWTO (R_D30V_9_PCREL,        /* type */
84          3,                     /* rightshift */
85          2,                     /* size (0 = byte, 1 = short, 2 = long) */
86          6,                     /* bitsize */
87          true,                  /* pc_relative */
88          0,                     /* bitpos */
89          complain_overflow_signed, /* complain_on_overflow */
90          bfd_elf_d30v_reloc_21, /* special_function */
91          "R_D30V_9_PCREL",      /* name */
92          false,                 /* partial_inplace */
93          0x3f,                  /* src_mask */
94          0x3f,                  /* dst_mask */
95          true),                 /* pcrel_offset */
96
97   /* A relative 9 bit relocation, right shifted by 3 */
98   HOWTO (R_D30V_9_PCREL_R,      /* type */
99          3,                     /* rightshift */
100          2,                     /* size (0 = byte, 1 = short, 2 = long) */
101          6,                     /* bitsize */
102          true,                  /* pc_relative */
103          0,                     /* bitpos */
104          complain_overflow_signed, /* complain_on_overflow */
105          bfd_elf_d30v_reloc_21, /* special_function */
106          "R_D30V_9_PCREL_R",    /* name */
107          false,                 /* partial_inplace */
108          0x3f,                  /* src_mask */
109          0x3f,                  /* dst_mask */
110          true),                 /* pcrel_offset */
111
112   /* An absolute 15 bit relocation, right shifted by 3 */
113   HOWTO (R_D30V_15,             /* type */
114          3,                     /* rightshift */
115          2,                     /* size (0 = byte, 1 = short, 2 = long) */
116          12,                    /* bitsize */
117          false,                 /* pc_relative */
118          0,                     /* bitpos */
119          complain_overflow_signed, /* complain_on_overflow */
120          bfd_elf_generic_reloc, /* special_function */
121          "R_D30V_15",           /* name */
122          false,                 /* partial_inplace */
123          0xfff,                 /* src_mask */
124          0xfff,                 /* dst_mask */
125          false),                /* pcrel_offset */
126
127   /* A relative 15 bit relocation, right shifted by 3 */
128   HOWTO (R_D30V_15_PCREL,       /* type */
129          3,                     /* rightshift */
130          2,                     /* size (0 = byte, 1 = short, 2 = long) */
131          12,                    /* bitsize */
132          true,                  /* pc_relative */
133          0,                     /* bitpos */
134          complain_overflow_signed, /* complain_on_overflow */
135          bfd_elf_d30v_reloc_21, /* special_function */
136          "R_D30V_15_PCREL",     /* name */
137          false,                 /* partial_inplace */
138          0xfff,                 /* src_mask */
139          0xfff,                 /* dst_mask */
140          true),                 /* pcrel_offset */
141
142   /* A relative 15 bit relocation, right shifted by 3 */
143   HOWTO (R_D30V_15_PCREL_R,     /* type */
144          3,                     /* rightshift */
145          2,                     /* size (0 = byte, 1 = short, 2 = long) */
146          12,                    /* bitsize */
147          true,                  /* pc_relative */
148          0,                     /* bitpos */
149          complain_overflow_signed, /* complain_on_overflow */
150          bfd_elf_d30v_reloc_21, /* special_function */
151          "R_D30V_15_PCREL_R",   /* name */
152          false,                 /* partial_inplace */
153          0xfff,                 /* src_mask */
154          0xfff,                 /* dst_mask */
155          true),                 /* pcrel_offset */
156
157   /* An absolute 21 bit relocation, right shifted by 3 */
158   HOWTO (R_D30V_21,             /* type */
159          3,                     /* rightshift */
160          2,                     /* size (0 = byte, 1 = short, 2 = long) */
161          18,                    /* bitsize */
162          false,                 /* pc_relative */
163          0,                     /* bitpos */
164          complain_overflow_signed, /* complain_on_overflow */
165          bfd_elf_generic_reloc, /* special_function */
166          "R_D30V_21",           /* name */
167          false,                 /* partial_inplace */
168          0x3ffff,               /* src_mask */
169          0x3ffff,               /* dst_mask */
170          false),                /* pcrel_offset */
171
172   /* A relative 21 bit relocation, right shifted by 3 */
173   HOWTO (R_D30V_21_PCREL,       /* type */
174          3,                     /* rightshift */
175          2,                     /* size (0 = byte, 1 = short, 2 = long) */
176          18,                    /* bitsize */
177          true,                  /* pc_relative */
178          0,                     /* bitpos */
179          complain_overflow_signed, /* complain_on_overflow */
180          bfd_elf_d30v_reloc_21, /* special_function */
181          "R_D30V_21_PCREL",     /* name */
182          false,                 /* partial_inplace */
183          0x3ffff,               /* src_mask */
184          0x3ffff,               /* dst_mask */
185          true),                 /* pcrel_offset */
186
187   /* A relative 21 bit relocation, right shifted by 3, in the Right container */
188   HOWTO (R_D30V_21_PCREL_R,     /* type */
189          3,                     /* rightshift */
190          2,                     /* size (0 = byte, 1 = short, 2 = long) */
191          18,                    /* bitsize */
192          true,                  /* pc_relative */
193          0,                     /* bitpos */
194          complain_overflow_signed, /* complain_on_overflow */
195          bfd_elf_d30v_reloc_21, /* special_function */
196          "R_D30V_21_PCREL_R",   /* name */
197          false,                 /* partial_inplace */
198          0x3ffff,               /* src_mask */
199          0x3ffff,               /* dst_mask */
200          true),                 /* pcrel_offset */
201
202   /* A D30V 32 bit absolute relocation */
203   HOWTO (R_D30V_32,             /* type */
204          0,                     /* rightshift */
205          4,                     /* size (0 = byte, 1 = short, 2 = long) */
206          32,                    /* bitsize */
207          false,                 /* pc_relative */
208          0,                     /* bitpos */
209          complain_overflow_bitfield, /* complain_on_overflow */
210          bfd_elf_d30v_reloc,    /* special_function */
211          "R_D30V_32",           /* name */
212          false,                 /* partial_inplace */
213          0xffffffff,            /* src_mask */
214          0xffffffff,            /* dst_mask */
215          false),                /* pcrel_offset */
216
217   /* A relative 32 bit relocation */
218   HOWTO (R_D30V_32_PCREL,       /* type */
219          0,                     /* rightshift */
220          4,                     /* size (0 = byte, 1 = short, 2 = long) */
221          32,                    /* bitsize */
222          true,                  /* pc_relative */
223          0,                     /* bitpos */
224          complain_overflow_signed, /* complain_on_overflow */
225          bfd_elf_d30v_reloc,    /* special_function */
226          "R_D30V_32_PCREL",     /* name */
227          false,                 /* partial_inplace */
228          0xffffffff,            /* src_mask */
229          0xffffffff,            /* dst_mask */
230          true),                 /* pcrel_offset */
231
232   /* A regular 32 bit absolute relocation */
233   HOWTO (R_D30V_32_NORMAL,              /* type */
234          0,                     /* rightshift */
235          2,                     /* size (0 = byte, 1 = short, 2 = long) */
236          32,                    /* bitsize */
237          false,                 /* pc_relative */
238          0,                     /* bitpos */
239          complain_overflow_bitfield, /* complain_on_overflow */
240          bfd_elf_generic_reloc, /* special_function */
241          "R_D30V_32_NORMAL",            /* name */
242          false,                 /* partial_inplace */
243          0xffffffff,            /* src_mask */
244          0xffffffff,            /* dst_mask */
245          false),                /* pcrel_offset */
246
247 };
248
249 #define MAX32 ((bfd_signed_vma) 0x7fffffff)
250 #define MIN32 (- MAX32 - 1)
251
252 static bfd_reloc_status_type
253 bfd_elf_d30v_reloc (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message)
254      bfd *abfd;
255      arelent *reloc_entry;
256      asymbol *symbol;
257      PTR data;
258      asection *input_section;
259      bfd *output_bfd;
260      char **error_message;
261 {
262   bfd_signed_vma relocation;
263   bfd_vma in1, in2, num;
264   bfd_vma tmp_addr = 0;
265   bfd_reloc_status_type r;
266   asection *reloc_target_output_section;
267   bfd_size_type addr = reloc_entry->address;
268   bfd_reloc_status_type flag = bfd_reloc_ok;
269   bfd_vma output_base = 0;
270   reloc_howto_type *howto = reloc_entry->howto;
271   int make_absolute = 0;
272
273   if (output_bfd != (bfd *) NULL)
274     {
275       /* Partial linking -- do nothing.  */
276       reloc_entry->address += input_section->output_offset;
277       return bfd_reloc_ok;
278     }
279
280   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
281                              input_section, output_bfd, error_message);
282   if (r != bfd_reloc_continue)
283     return r; 
284
285   /* a hacked-up version of bfd_perform_reloc() follows */
286  if (bfd_is_und_section (symbol->section)
287       && (symbol->flags & BSF_WEAK) == 0
288       && output_bfd == (bfd *) NULL)
289     flag = bfd_reloc_undefined;
290
291   /* Is the address of the relocation really within the section?  */
292   if (reloc_entry->address > input_section->_cooked_size)
293     return bfd_reloc_outofrange;
294
295   /* Work out which section the relocation is targetted at and the
296      initial relocation command value.  */
297
298   /* Get symbol value.  (Common symbols are special.)  */
299   if (bfd_is_com_section (symbol->section))
300     relocation = 0;
301   else
302     relocation = symbol->value;
303
304   reloc_target_output_section = symbol->section->output_section;
305
306   /* Convert input-section-relative symbol value to absolute.  */
307   output_base = reloc_target_output_section->vma;
308   relocation += output_base + symbol->section->output_offset;
309
310   /* Add in supplied addend.  */
311   relocation += reloc_entry->addend;
312
313   /* Here the variable relocation holds the final address of the
314      symbol we are relocating against, plus any addend.  */
315
316   if (howto->pc_relative == true)
317     {
318       tmp_addr = input_section->output_section->vma + input_section->output_offset 
319         + reloc_entry->address;
320       relocation -= tmp_addr;
321     }
322   
323   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
324   in2 = bfd_get_32 (abfd, (bfd_byte *) data + addr + 4);
325
326   /* extract the addend */
327   num = ((in2 & 0x3FFFF)
328          | ((in2 & 0xFF00000) >> 2)
329          | ((in1 & 0x3F) << 26));
330   in1 &= 0xFFFFFFC0;
331   in2 = 0x80000000;
332   
333   relocation += num;
334
335   if (howto->pc_relative == true && howto->bitsize == 32)
336     {
337       /* The D30V has a PC that doesn't wrap and PC-relative jumps are
338          signed, so a PC-relative jump can't be more than +/- 2^31 bytes.
339          If one exceeds this, change it to an absolute jump.  */
340       if (relocation > MAX32 || relocation < MIN32)
341         {
342           relocation = (relocation + tmp_addr) & 0xffffffff;
343           make_absolute = 1;
344         }
345     }
346   
347   in1 |= (relocation >> 26) & 0x3F;     /* top 6 bits */
348   in2 |= ((relocation & 0x03FC0000) << 2);  /* next 8 bits */ 
349   in2 |= relocation & 0x0003FFFF;               /* bottom 18 bits */
350   
351   /* change a PC-relative instruction to its absolute equivalent */
352   /* with this simple hack */
353   if (make_absolute)
354     in1 |= 0x00100000;
355
356   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
357   bfd_put_32 (abfd, in2, (bfd_byte *) data + addr + 4);
358   
359   return flag;
360 }   
361
362
363 static bfd_reloc_status_type
364 bfd_elf_d30v_reloc_21 (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message)
365      bfd *abfd;
366      arelent *reloc_entry;
367      asymbol *symbol;
368      PTR data;
369      asection *input_section;
370      bfd *output_bfd;
371      char **error_message;
372 {
373   bfd_vma relocation;
374   bfd_vma in1, num;
375   bfd_reloc_status_type r;
376   asection *reloc_target_output_section;
377   bfd_size_type addr = reloc_entry->address;
378   bfd_reloc_status_type flag = bfd_reloc_ok;
379   bfd_vma output_base = 0;
380   reloc_howto_type *howto = reloc_entry->howto;
381   int mask, max;
382
383   if (output_bfd != (bfd *) NULL)
384     {
385       /* Partial linking -- do nothing.  */
386       reloc_entry->address += input_section->output_offset;
387       return bfd_reloc_ok;
388     }
389   
390   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
391                              input_section, output_bfd, error_message);
392   if (r != bfd_reloc_continue)
393     return r; 
394
395   /* a hacked-up version of bfd_perform_reloc() follows */
396  if (bfd_is_und_section (symbol->section)
397       && (symbol->flags & BSF_WEAK) == 0
398       && output_bfd == (bfd *) NULL)
399     flag = bfd_reloc_undefined;
400
401   /* Is the address of the relocation really within the section?  */
402   if (reloc_entry->address > input_section->_cooked_size)
403     return bfd_reloc_outofrange;
404
405   /* Work out which section the relocation is targetted at and the
406      initial relocation command value.  */
407
408   /* Get symbol value.  (Common symbols are special.)  */
409   if (bfd_is_com_section (symbol->section))
410     relocation = 0;
411   else
412     relocation = symbol->value;
413
414   reloc_target_output_section = symbol->section->output_section;
415
416   /* Convert input-section-relative symbol value to absolute.  */
417   output_base = reloc_target_output_section->vma;
418   relocation += output_base + symbol->section->output_offset;
419
420   /* Add in supplied addend.  */
421   relocation += reloc_entry->addend;
422
423   /* Here the variable relocation holds the final address of the
424      symbol we are relocating against, plus any addend.  */
425
426   if (howto->pc_relative == true)
427     {
428       relocation -= (input_section->output_section->vma
429                      + input_section->output_offset);
430       if (howto->pcrel_offset == true)
431         relocation -= reloc_entry->address;
432     }
433
434   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
435
436   mask =  (1 << howto->bitsize) - 1;
437   if (howto->bitsize == 6)
438     mask <<= 12;
439   max = (1 << (howto->bitsize + 2)) - 1;
440
441   /* extract the addend */
442   num = in1 & mask;  /* 18 bits */
443   if (howto->bitsize == 6)
444     num >>= 12;
445   num <<= 3; /* shift left 3 */
446   in1 &= ~mask;  /* mask out addend */
447
448   relocation += num;
449   if (howto->type == R_D30V_21_PCREL_R || howto->type == R_D30V_15_PCREL_R ||
450       howto->type == R_D30V_9_PCREL_R )
451     {
452       relocation += 4;
453     }
454
455   if ((int)relocation < 0 )
456     {
457       if (~(int)relocation > max)
458         flag = bfd_reloc_overflow;
459     }
460   else
461     {
462       if ((int)relocation > max)
463         flag = bfd_reloc_overflow;
464     }
465   relocation >>= 3; 
466   if (howto->bitsize == 6)
467     in1 |= ((relocation & (mask >> 12)) << 12);
468   else
469     in1 |= relocation & mask;
470
471   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
472   
473   return flag;
474 }   
475
476 /* Map BFD reloc types to D30V ELF reloc types.  */
477
478 struct d30v_reloc_map
479 {
480   bfd_reloc_code_real_type bfd_reloc_val;
481   unsigned char elf_reloc_val;
482 };
483
484
485 static const struct d30v_reloc_map d30v_reloc_map[] =
486 {
487   { BFD_RELOC_NONE, R_D30V_NONE, },
488   { BFD_RELOC_D30V_6, R_D30V_6 },
489   { BFD_RELOC_D30V_9_PCREL, R_D30V_9_PCREL },
490   { BFD_RELOC_D30V_9_PCREL_R, R_D30V_9_PCREL_R },
491   { BFD_RELOC_D30V_15, R_D30V_15 },
492   { BFD_RELOC_D30V_15_PCREL, R_D30V_15_PCREL },
493   { BFD_RELOC_D30V_15_PCREL_R, R_D30V_15_PCREL_R },
494   { BFD_RELOC_D30V_21, R_D30V_21 },
495   { BFD_RELOC_D30V_21_PCREL, R_D30V_21_PCREL },
496   { BFD_RELOC_D30V_21_PCREL_R, R_D30V_21_PCREL_R },
497   { BFD_RELOC_D30V_32, R_D30V_32 },
498   { BFD_RELOC_D30V_32_PCREL, R_D30V_32_PCREL },
499   { BFD_RELOC_32, R_D30V_32_NORMAL },
500 };
501
502 static reloc_howto_type *
503 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
504      bfd *abfd ATTRIBUTE_UNUSED;
505      bfd_reloc_code_real_type code;
506 {
507   unsigned int i;
508
509   for (i = 0;
510        i < sizeof (d30v_reloc_map) / sizeof (struct d30v_reloc_map);
511        i++)
512     {
513       if (d30v_reloc_map[i].bfd_reloc_val == code)
514         return &elf_d30v_howto_table[d30v_reloc_map[i].elf_reloc_val];
515     }
516
517   return NULL;
518 }
519
520 /* Set the howto pointer for an D30V ELF reloc (type REL).  */
521
522 static void
523 d30v_info_to_howto_rel (abfd, cache_ptr, dst)
524      bfd *abfd ATTRIBUTE_UNUSED;
525      arelent *cache_ptr;
526      Elf32_Internal_Rel *dst;
527 {
528   unsigned int r_type;
529
530   r_type = ELF32_R_TYPE (dst->r_info);
531   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
532   cache_ptr->howto = &elf_d30v_howto_table[r_type];
533 }
534
535 /* Set the howto pointer for an D30V ELF reloc (type RELA).  */
536
537 static void
538 d30v_info_to_howto_rela (abfd, cache_ptr, dst)
539      bfd *abfd ATTRIBUTE_UNUSED;
540      arelent *cache_ptr;
541      Elf32_Internal_Rela *dst;
542 {
543   unsigned int r_type;
544
545   r_type = ELF32_R_TYPE (dst->r_info);
546   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
547   cache_ptr->howto = &elf_d30v_howto_table[r_type];
548 }
549
550 #define ELF_ARCH                bfd_arch_d30v
551 #define ELF_MACHINE_CODE        EM_CYGNUS_D30V
552 #define ELF_MAXPAGESIZE         0x1000
553
554 #define TARGET_BIG_SYM          bfd_elf32_d30v_vec
555 #define TARGET_BIG_NAME         "elf32-d30v"
556
557 #define elf_info_to_howto       d30v_info_to_howto_rela
558 #define elf_info_to_howto_rel   d30v_info_to_howto_rel
559 #define elf_backend_object_p    0
560 #define elf_backend_final_write_processing      0
561
562 #include "elf32-target.h"