2001-06-02 H.J. Lu <hjl@gnu.org>
[platform/upstream/binutils.git] / bfd / elf32-d30v.c
1 /* D30V-specific support for 32-bit ELF
2    Copyright 1997, 1998, 1999, 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 static bfd_reloc_status_type
363 bfd_elf_d30v_reloc_21 (abfd, reloc_entry, symbol, data, input_section, output_bfd, error_message)
364      bfd *abfd;
365      arelent *reloc_entry;
366      asymbol *symbol;
367      PTR data;
368      asection *input_section;
369      bfd *output_bfd;
370      char **error_message;
371 {
372   bfd_vma relocation;
373   bfd_vma in1, num;
374   bfd_reloc_status_type r;
375   asection *reloc_target_output_section;
376   bfd_size_type addr = reloc_entry->address;
377   bfd_reloc_status_type flag = bfd_reloc_ok;
378   bfd_vma output_base = 0;
379   reloc_howto_type *howto = reloc_entry->howto;
380   int mask, max;
381
382   if (output_bfd != (bfd *) NULL)
383     {
384       /* Partial linking -- do nothing.  */
385       reloc_entry->address += input_section->output_offset;
386       return bfd_reloc_ok;
387     }
388
389   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
390                              input_section, output_bfd, error_message);
391   if (r != bfd_reloc_continue)
392     return r;
393
394   /* a hacked-up version of bfd_perform_reloc() follows */
395  if (bfd_is_und_section (symbol->section)
396       && (symbol->flags & BSF_WEAK) == 0
397       && output_bfd == (bfd *) NULL)
398     flag = bfd_reloc_undefined;
399
400   /* Is the address of the relocation really within the section?  */
401   if (reloc_entry->address > input_section->_cooked_size)
402     return bfd_reloc_outofrange;
403
404   /* Work out which section the relocation is targetted at and the
405      initial relocation command value.  */
406
407   /* Get symbol value.  (Common symbols are special.)  */
408   if (bfd_is_com_section (symbol->section))
409     relocation = 0;
410   else
411     relocation = symbol->value;
412
413   reloc_target_output_section = symbol->section->output_section;
414
415   /* Convert input-section-relative symbol value to absolute.  */
416   output_base = reloc_target_output_section->vma;
417   relocation += output_base + symbol->section->output_offset;
418
419   /* Add in supplied addend.  */
420   relocation += reloc_entry->addend;
421
422   /* Here the variable relocation holds the final address of the
423      symbol we are relocating against, plus any addend.  */
424
425   if (howto->pc_relative == true)
426     {
427       relocation -= (input_section->output_section->vma
428                      + input_section->output_offset);
429       if (howto->pcrel_offset == true)
430         relocation -= reloc_entry->address;
431     }
432
433   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
434
435   mask =  (1 << howto->bitsize) - 1;
436   if (howto->bitsize == 6)
437     mask <<= 12;
438   max = (1 << (howto->bitsize + 2)) - 1;
439
440   /* extract the addend */
441   num = in1 & mask;  /* 18 bits */
442   if (howto->bitsize == 6)
443     num >>= 12;
444   num <<= 3; /* shift left 3 */
445   in1 &= ~mask;  /* mask out addend */
446
447   relocation += num;
448   if (howto->type == R_D30V_21_PCREL_R || howto->type == R_D30V_15_PCREL_R ||
449       howto->type == R_D30V_9_PCREL_R )
450     {
451       relocation += 4;
452     }
453
454   if ((int)relocation < 0 )
455     {
456       if (~(int)relocation > max)
457         flag = bfd_reloc_overflow;
458     }
459   else
460     {
461       if ((int)relocation > max)
462         flag = bfd_reloc_overflow;
463     }
464   relocation >>= 3;
465   if (howto->bitsize == 6)
466     in1 |= ((relocation & (mask >> 12)) << 12);
467   else
468     in1 |= relocation & mask;
469
470   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
471
472   return flag;
473 }
474
475 /* Map BFD reloc types to D30V ELF reloc types.  */
476
477 struct d30v_reloc_map
478 {
479   bfd_reloc_code_real_type bfd_reloc_val;
480   unsigned char elf_reloc_val;
481 };
482
483 static const struct d30v_reloc_map d30v_reloc_map[] =
484 {
485   { BFD_RELOC_NONE, R_D30V_NONE, },
486   { BFD_RELOC_D30V_6, R_D30V_6 },
487   { BFD_RELOC_D30V_9_PCREL, R_D30V_9_PCREL },
488   { BFD_RELOC_D30V_9_PCREL_R, R_D30V_9_PCREL_R },
489   { BFD_RELOC_D30V_15, R_D30V_15 },
490   { BFD_RELOC_D30V_15_PCREL, R_D30V_15_PCREL },
491   { BFD_RELOC_D30V_15_PCREL_R, R_D30V_15_PCREL_R },
492   { BFD_RELOC_D30V_21, R_D30V_21 },
493   { BFD_RELOC_D30V_21_PCREL, R_D30V_21_PCREL },
494   { BFD_RELOC_D30V_21_PCREL_R, R_D30V_21_PCREL_R },
495   { BFD_RELOC_D30V_32, R_D30V_32 },
496   { BFD_RELOC_D30V_32_PCREL, R_D30V_32_PCREL },
497   { BFD_RELOC_32, R_D30V_32_NORMAL },
498 };
499
500 static reloc_howto_type *
501 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
502      bfd *abfd ATTRIBUTE_UNUSED;
503      bfd_reloc_code_real_type code;
504 {
505   unsigned int i;
506
507   for (i = 0;
508        i < sizeof (d30v_reloc_map) / sizeof (struct d30v_reloc_map);
509        i++)
510     {
511       if (d30v_reloc_map[i].bfd_reloc_val == code)
512         return &elf_d30v_howto_table[d30v_reloc_map[i].elf_reloc_val];
513     }
514
515   return NULL;
516 }
517
518 /* Set the howto pointer for an D30V ELF reloc (type REL).  */
519
520 static void
521 d30v_info_to_howto_rel (abfd, cache_ptr, dst)
522      bfd *abfd ATTRIBUTE_UNUSED;
523      arelent *cache_ptr;
524      Elf32_Internal_Rel *dst;
525 {
526   unsigned int r_type;
527
528   r_type = ELF32_R_TYPE (dst->r_info);
529   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
530   cache_ptr->howto = &elf_d30v_howto_table[r_type];
531 }
532
533 /* Set the howto pointer for an D30V ELF reloc (type RELA).  */
534
535 static void
536 d30v_info_to_howto_rela (abfd, cache_ptr, dst)
537      bfd *abfd ATTRIBUTE_UNUSED;
538      arelent *cache_ptr;
539      Elf32_Internal_Rela *dst;
540 {
541   unsigned int r_type;
542
543   r_type = ELF32_R_TYPE (dst->r_info);
544   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
545   cache_ptr->howto = &elf_d30v_howto_table[r_type];
546 }
547
548 #define ELF_ARCH                bfd_arch_d30v
549 #define ELF_MACHINE_CODE        EM_CYGNUS_D30V
550 #define ELF_MAXPAGESIZE         0x1000
551
552 #define TARGET_BIG_SYM          bfd_elf32_d30v_vec
553 #define TARGET_BIG_NAME         "elf32-d30v"
554
555 #define elf_info_to_howto       d30v_info_to_howto_rela
556 #define elf_info_to_howto_rel   d30v_info_to_howto_rel
557 #define elf_backend_object_p    0
558 #define elf_backend_final_write_processing      0
559
560 #include "elf32-target.h"