1 /* D30V-specific support for 32-bit ELF
2    Copyright 1997, 1998, 1999, 2000, 2001, 2002
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
bfd_elf_d30v_reloc(abfd,reloc_entry,symbol,data,input_section,output_bfd,error_message)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_reloc_status_type flag = bfd_reloc_ok;
270   bfd_vma output_base = 0;
271   reloc_howto_type *howto = reloc_entry->howto;
272   int make_absolute = 0;
273 
274   if (output_bfd != (bfd *) NULL)
275     {
276       /* Partial linking -- do nothing.  */
277       reloc_entry->address += input_section->output_offset;
278       return bfd_reloc_ok;
279     }
280 
281   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
282                              input_section, output_bfd, error_message);
283   if (r != bfd_reloc_continue)
284     return r;
285 
286   /* a hacked-up version of bfd_perform_reloc() follows */
287  if (bfd_is_und_section (symbol->section)
288       && (symbol->flags & BSF_WEAK) == 0
289       && output_bfd == (bfd *) NULL)
290     flag = bfd_reloc_undefined;
291 
292   /* Is the address of the relocation really within the section?  */
293   if (reloc_entry->address > input_section->_cooked_size)
294     return bfd_reloc_outofrange;
295 
296   /* Work out which section the relocation is targeted at and the
297      initial relocation command value.  */
298 
299   /* Get symbol value.  (Common symbols are special.)  */
300   if (bfd_is_com_section (symbol->section))
301     relocation = 0;
302   else
303     relocation = symbol->value;
304 
305   reloc_target_output_section = symbol->section->output_section;
306 
307   /* Convert input-section-relative symbol value to absolute.  */
308   output_base = reloc_target_output_section->vma;
309   relocation += output_base + symbol->section->output_offset;
310 
311   /* Add in supplied addend.  */
312   relocation += reloc_entry->addend;
313 
314   /* Here the variable relocation holds the final address of the
315      symbol we are relocating against, plus any addend.  */
316 
317   if (howto->pc_relative)
318     {
319       tmp_addr = input_section->output_section->vma + input_section->output_offset
320 	+ reloc_entry->address;
321       relocation -= tmp_addr;
322     }
323 
324   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
325   in2 = bfd_get_32 (abfd, (bfd_byte *) data + addr + 4);
326 
327   /* extract the addend */
328   num = ((in2 & 0x3FFFF)
329 	 | ((in2 & 0xFF00000) >> 2)
330 	 | ((in1 & 0x3F) << 26));
331   in1 &= 0xFFFFFFC0;
332   in2 = 0x80000000;
333 
334   relocation += num;
335 
336   if (howto->pc_relative && howto->bitsize == 32)
337     {
338       /* The D30V has a PC that doesn't wrap and PC-relative jumps are
339 	 signed, so a PC-relative jump can't be more than +/- 2^31 bytes.
340 	 If one exceeds this, change it to an absolute jump.  */
341       if (relocation > MAX32 || relocation < MIN32)
342 	{
343 	  relocation = (relocation + tmp_addr) & 0xffffffff;
344 	  make_absolute = 1;
345 	}
346     }
347 
348   in1 |= (relocation >> 26) & 0x3F;	/* top 6 bits */
349   in2 |= ((relocation & 0x03FC0000) << 2);  /* next 8 bits */
350   in2 |= relocation & 0x0003FFFF;		/* bottom 18 bits */
351 
352   /* change a PC-relative instruction to its absolute equivalent */
353   /* with this simple hack */
354   if (make_absolute)
355     in1 |= 0x00100000;
356 
357   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
358   bfd_put_32 (abfd, in2, (bfd_byte *) data + addr + 4);
359 
360   return flag;
361 }
362 
363 static bfd_reloc_status_type
bfd_elf_d30v_reloc_21(abfd,reloc_entry,symbol,data,input_section,output_bfd,error_message)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 targeted 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)
427     {
428       relocation -= (input_section->output_section->vma
429 		     + input_section->output_offset);
430       if (howto->pcrel_offset)
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 static const struct d30v_reloc_map d30v_reloc_map[] =
485 {
486   { BFD_RELOC_NONE, R_D30V_NONE, },
487   { BFD_RELOC_D30V_6, R_D30V_6 },
488   { BFD_RELOC_D30V_9_PCREL, R_D30V_9_PCREL },
489   { BFD_RELOC_D30V_9_PCREL_R, R_D30V_9_PCREL_R },
490   { BFD_RELOC_D30V_15, R_D30V_15 },
491   { BFD_RELOC_D30V_15_PCREL, R_D30V_15_PCREL },
492   { BFD_RELOC_D30V_15_PCREL_R, R_D30V_15_PCREL_R },
493   { BFD_RELOC_D30V_21, R_D30V_21 },
494   { BFD_RELOC_D30V_21_PCREL, R_D30V_21_PCREL },
495   { BFD_RELOC_D30V_21_PCREL_R, R_D30V_21_PCREL_R },
496   { BFD_RELOC_D30V_32, R_D30V_32 },
497   { BFD_RELOC_D30V_32_PCREL, R_D30V_32_PCREL },
498   { BFD_RELOC_32, R_D30V_32_NORMAL },
499 };
500 
501 static reloc_howto_type *
bfd_elf32_bfd_reloc_type_lookup(abfd,code)502 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
503      bfd *abfd ATTRIBUTE_UNUSED;
504      bfd_reloc_code_real_type code;
505 {
506   unsigned int i;
507 
508   for (i = 0;
509        i < sizeof (d30v_reloc_map) / sizeof (struct d30v_reloc_map);
510        i++)
511     {
512       if (d30v_reloc_map[i].bfd_reloc_val == code)
513 	return &elf_d30v_howto_table[d30v_reloc_map[i].elf_reloc_val];
514     }
515 
516   return NULL;
517 }
518 
519 /* Set the howto pointer for an D30V ELF reloc (type REL).  */
520 
521 static void
d30v_info_to_howto_rel(abfd,cache_ptr,dst)522 d30v_info_to_howto_rel (abfd, cache_ptr, dst)
523      bfd *abfd ATTRIBUTE_UNUSED;
524      arelent *cache_ptr;
525      Elf_Internal_Rela *dst;
526 {
527   unsigned int r_type;
528 
529   r_type = ELF32_R_TYPE (dst->r_info);
530   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
531   cache_ptr->howto = &elf_d30v_howto_table[r_type];
532 }
533 
534 /* Set the howto pointer for an D30V ELF reloc (type RELA).  */
535 
536 static void
d30v_info_to_howto_rela(abfd,cache_ptr,dst)537 d30v_info_to_howto_rela (abfd, cache_ptr, dst)
538      bfd *abfd ATTRIBUTE_UNUSED;
539      arelent *cache_ptr;
540      Elf_Internal_Rela *dst;
541 {
542   unsigned int r_type;
543 
544   r_type = ELF32_R_TYPE (dst->r_info);
545   BFD_ASSERT (r_type < (unsigned int) R_D30V_max);
546   cache_ptr->howto = &elf_d30v_howto_table[r_type];
547 }
548 
549 #define ELF_ARCH		bfd_arch_d30v
550 #define ELF_MACHINE_CODE	EM_D30V
551 #define ELF_MACHINE_ALT1	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"
563