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