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
6This file is part of BFD, the Binary File Descriptor library.
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, 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
28static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
29  PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
30static void d30v_info_to_howto_rel
31  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
32static void d30v_info_to_howto_rela
33  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
34static 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));
42static 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
51static 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
253static bfd_reloc_status_type
254bfd_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 > bfd_get_section_limit (abfd, input_section))
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
363static bfd_reloc_status_type
364bfd_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 > bfd_get_section_limit (abfd, input_section))
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
478struct d30v_reloc_map
479{
480  bfd_reloc_code_real_type bfd_reloc_val;
481  unsigned char elf_reloc_val;
482};
483
484static 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
501static reloc_howto_type *
502bfd_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
521static void
522d30v_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
536static void
537d30v_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