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