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