1/* OR32-specific support for 32-bit ELF
2   Copyright 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
3   Contributed by Ivan Guzvinec  <ivang@opencores.org>
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 3 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., 51 Franklin Street - Fifth Floor, Boston,
20   MA 02110-1301, USA.  */
21
22#include "sysdep.h"
23#include "bfd.h"
24#include "libbfd.h"
25#include "elf-bfd.h"
26#include "elf/or32.h"
27#include "libiberty.h"
28
29/* Try to minimize the amount of space occupied by relocation tables
30   on the ROM (not that the ROM won't be swamped by other ELF overhead).  */
31#define USE_REL	1
32
33/* Set the right machine number for an OR32 ELF file.  */
34
35static bfd_boolean
36or32_elf_object_p (bfd *abfd)
37{
38  (void) bfd_default_set_arch_mach (abfd, bfd_arch_or32, 0);
39  return TRUE;
40}
41
42/* The final processing done just before writing out an OR32 ELF object file.
43   This gets the OR32 architecture right based on the machine number.  */
44
45static void
46or32_elf_final_write_processing (bfd *abfd,
47				 bfd_boolean linker ATTRIBUTE_UNUSED)
48{
49  elf_elfheader (abfd)->e_flags &=~ EF_OR32_MACH;
50}
51
52static bfd_reloc_status_type
53or32_elf_32_reloc (bfd *abfd,
54		   arelent *reloc_entry,
55		   asymbol *symbol,
56		   void * data,
57		   asection *input_section,
58		   bfd *output_bfd,
59		   char **error_message ATTRIBUTE_UNUSED)
60{
61  if (output_bfd != NULL)
62    {
63      unsigned long insn;
64      bfd_size_type addr = reloc_entry->address;
65
66      reloc_entry->address += input_section->output_offset;
67
68      insn = bfd_get_32 (abfd, (bfd_byte *) data + addr);
69      insn += symbol->section->output_section->vma;
70      insn += symbol->section->output_offset;
71      insn += symbol->value;
72      bfd_put_32 (abfd, insn, (bfd_byte *) data + addr);
73
74      return bfd_reloc_ok;
75    }
76
77  return bfd_reloc_continue;
78}
79
80static bfd_reloc_status_type
81or32_elf_16_reloc (bfd *abfd,
82		   arelent *reloc_entry,
83		   asymbol *symbol,
84		   void * data,
85		   asection *input_section,
86		   bfd *output_bfd,
87		   char **error_message ATTRIBUTE_UNUSED)
88{
89  if (output_bfd != NULL)
90    {
91      unsigned short insn;
92      bfd_size_type addr = reloc_entry->address;
93
94      reloc_entry->address += input_section->output_offset;
95
96      insn = bfd_get_16 (abfd, (bfd_byte *) data + addr);
97      insn += symbol->section->output_section->vma;
98      insn += symbol->section->output_offset;
99      insn += symbol->value;
100      bfd_put_16 (abfd, insn, (bfd_byte *) data + addr);
101
102      return bfd_reloc_ok;
103    }
104
105  return bfd_reloc_continue;
106}
107
108static bfd_reloc_status_type
109or32_elf_8_reloc (bfd *abfd ATTRIBUTE_UNUSED,
110		  arelent *reloc_entry,
111		  asymbol *symbol,
112		  void * data,
113		  asection *input_section,
114		  bfd *output_bfd,
115		  char **error_message ATTRIBUTE_UNUSED)
116{
117  if (output_bfd != NULL)
118    {
119      unsigned char insn;
120      bfd_size_type addr = reloc_entry->address;
121
122      reloc_entry->address += input_section->output_offset;
123
124      insn = bfd_get_8 (abfd, (bfd_byte *) data + addr);
125      insn += symbol->section->output_section->vma;
126      insn += symbol->section->output_offset;
127      insn += symbol->value;
128      bfd_put_8 (abfd, insn, (bfd_byte *) data + addr);
129
130      return bfd_reloc_ok;
131    }
132
133  return bfd_reloc_continue;
134}
135
136/* Do a R_OR32_CONSTH relocation.  This has to be done in combination
137   with a R_OR32_CONST reloc, because there is a carry from the LO16 to
138   the HI16.  Here we just save the information we need; we do the
139   actual relocation when we see the LO16.  OR32 ELF requires that the
140   LO16 immediately follow the HI16.  As a GNU extension, we permit an
141   arbitrary number of HI16 relocs to be associated with a single LO16
142   reloc.  This extension permits gcc to output the HI and LO relocs
143   itself. This code is copied from the elf32-mips.c.  */
144
145struct or32_consth
146{
147  struct or32_consth *next;
148  bfd_byte *addr;
149  bfd_vma addend;
150};
151
152/* FIXME: This should not be a static variable.  */
153
154static struct or32_consth *or32_consth_list;
155
156static bfd_reloc_status_type
157or32_elf_consth_reloc (bfd *abfd ATTRIBUTE_UNUSED,
158		       arelent *reloc_entry,
159		       asymbol *symbol,
160		       void * data,
161		       asection *input_section,
162		       bfd *output_bfd,
163		       char **error_message ATTRIBUTE_UNUSED)
164{
165  bfd_reloc_status_type ret;
166  bfd_vma relocation;
167  struct or32_consth *n;
168
169  ret = bfd_reloc_ok;
170
171  if (bfd_is_und_section (symbol->section)
172      && output_bfd == NULL)
173    ret = bfd_reloc_undefined;
174
175  if (bfd_is_com_section (symbol->section))
176    relocation = 0;
177  else
178    relocation = symbol->value;
179
180  relocation += symbol->section->output_section->vma;
181  relocation += symbol->section->output_offset;
182  relocation += reloc_entry->addend;
183
184  if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
185    return bfd_reloc_outofrange;
186
187  /* Save the information, and let LO16 do the actual relocation.  */
188  n = bfd_malloc (sizeof *n);
189  if (n == NULL)
190    return bfd_reloc_outofrange;
191  n->addr = (bfd_byte *) data + reloc_entry->address;
192  n->addend = relocation;
193  n->next = or32_consth_list;
194  or32_consth_list = n;
195
196  if (output_bfd != NULL)
197    reloc_entry->address += input_section->output_offset;
198
199  return ret;
200}
201
202/* Do a R_OR32_CONST relocation.  This is a straightforward 16 bit
203   inplace relocation; this function exists in order to do the
204   R_OR32_CONSTH relocation described above.  */
205
206static bfd_reloc_status_type
207or32_elf_const_reloc (bfd *abfd,
208		      arelent *reloc_entry,
209		      asymbol *symbol,
210		      void * data,
211		      asection *input_section,
212		      bfd *output_bfd,
213		      char **error_message)
214{
215  if (or32_consth_list != NULL)
216    {
217      struct or32_consth *l;
218
219      l = or32_consth_list;
220      while (l != NULL)
221	{
222	  unsigned long insn;
223	  unsigned long val;
224          unsigned long vallo;
225	  struct or32_consth *next;
226
227	  /* Do the HI16 relocation.  Note that we actually don't need
228	     to know anything about the LO16 itself, except where to
229	     find the low 16 bits of the addend needed by the LO16.  */
230	  insn = bfd_get_32 (abfd, l->addr);
231	  vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
232		   & 0xffff);
233	  val = ((insn & 0xffff) << 16) + vallo;
234	  val += l->addend;
235
236	  insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
237	  bfd_put_32 (abfd, insn, l->addr);
238
239	  next = l->next;
240	  free (l);
241	  l = next;
242	}
243
244      or32_consth_list = NULL;
245    }
246
247  if (output_bfd != NULL)
248    {
249      unsigned long insn, tmp;
250      bfd_size_type addr = reloc_entry->address;
251
252      reloc_entry->address += input_section->output_offset;
253
254      insn = bfd_get_32 (abfd, (bfd_byte *) data + addr);
255      tmp = insn & 0x0000ffff;
256      tmp += symbol->section->output_section->vma;
257      tmp += symbol->section->output_offset;
258      tmp += symbol->value;
259      insn = (insn & 0xffff0000) | (tmp & 0x0000ffff);
260      bfd_put_32 (abfd, insn, (bfd_byte *) data + addr);
261
262      return bfd_reloc_ok;
263    }
264
265  /* Now do the LO16 reloc in the usual way.  */
266  return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
267				input_section, output_bfd, error_message);
268}
269
270static bfd_reloc_status_type
271or32_elf_jumptarg_reloc (bfd *abfd,
272			 arelent *reloc_entry,
273			 asymbol *symbol ATTRIBUTE_UNUSED,
274			 void * data,
275			 asection *input_section,
276			 bfd *output_bfd,
277			 char **error_message ATTRIBUTE_UNUSED)
278{
279  if (output_bfd != NULL)
280    {
281      unsigned long insn, tmp;
282      bfd_size_type addr = reloc_entry->address;
283
284      reloc_entry->address += input_section->output_offset;
285
286      insn = bfd_get_32 (abfd, (bfd_byte *) data + addr);
287      tmp = insn | 0xfc000000;
288      tmp -= (input_section->output_offset >> 2);
289      insn = (insn & 0xfc000000) | (tmp & 0x03ffffff);
290      bfd_put_32 (abfd, insn, (bfd_byte *) data + addr);
291
292      return bfd_reloc_ok;
293    }
294
295  return bfd_reloc_continue;
296}
297
298static reloc_howto_type elf_or32_howto_table[] =
299{
300  /* This reloc does nothing.  */
301  HOWTO (R_OR32_NONE,		/* type */
302	 0,			/* rightshift */
303	 2,			/* size (0 = byte, 1 = short, 2 = long) */
304	 32,			/* bitsize */
305	 FALSE,			/* pc_relative */
306	 0,			/* bitpos */
307	 complain_overflow_bitfield, /* complain_on_overflow */
308	 bfd_elf_generic_reloc,	/* special_function */
309	 "R_OR32_NONE",		/* name */
310	 FALSE,			/* partial_inplace */
311	 0,			/* src_mask */
312	 0,			/* dst_mask */
313	 FALSE),		/* pcrel_offset */
314
315  /* A standard 32 bit relocation.  */
316  HOWTO (R_OR32_32,		/* type */
317	 0,	                /* rightshift */
318	 2,	                /* size (0 = byte, 1 = short, 2 = long) */
319	 32,	                /* bitsize */
320	 FALSE,	                /* pc_relative */
321	 0,	                /* bitpos */
322	 complain_overflow_bitfield, /* complain_on_overflow */
323	 or32_elf_32_reloc, 	/* special_function */
324	 "R_OR32_32",		/* name */
325	 FALSE,	                /* partial_inplace */
326	 0xffffffff,	        /* src_mask */
327	 0xffffffff,   		/* dst_mask */
328	 FALSE),                /* pcrel_offset */
329
330  /* A standard 16 bit relocation.  */
331  HOWTO (R_OR32_16,		/* type */
332	 0,	                /* rightshift */
333	 1,	                /* size (0 = byte, 1 = short, 2 = long) */
334	 16,	                /* bitsize */
335	 FALSE,	                /* pc_relative */
336	 0,	                /* bitpos */
337	 complain_overflow_bitfield, /* complain_on_overflow */
338	 or32_elf_16_reloc, 	/* special_function */
339	 "R_OR32_16",		/* name */
340	 FALSE,	                /* partial_inplace */
341	 0x0000ffff,	        /* src_mask */
342	 0x0000ffff,   		/* dst_mask */
343	 FALSE),                /* pcrel_offset */
344
345  /* A standard 8 bit relocation.  */
346  HOWTO (R_OR32_8,		/* type */
347	 0,	                /* rightshift */
348	 0,	                /* size (0 = byte, 1 = short, 2 = long) */
349	 8,	                /* bitsize */
350	 FALSE,	                /* pc_relative */
351	 0,	                /* bitpos */
352	 complain_overflow_bitfield, /* complain_on_overflow */
353	 or32_elf_8_reloc, 	/* special_function */
354	 "R_OR32_8",		/* name */
355	 FALSE,	                /* partial_inplace */
356	 0x000000ff,	        /* src_mask */
357	 0x000000ff,   		/* dst_mask */
358	 FALSE),                /* pcrel_offset */
359
360  /* A standard low 16 bit relocation.  */
361  HOWTO (R_OR32_CONST,		/* type */
362	 0,			/* rightshift */
363	 2,			/* size (0 = byte, 1 = short, 2 = long) */
364	 16,			/* bitsize */
365	 FALSE,			/* pc_relative */
366	 0,			/* bitpos */
367	 complain_overflow_dont, /* complain_on_overflow */
368	 or32_elf_const_reloc,	/* special_function */
369	 "R_OR32_CONST",	/* name */
370	 FALSE,			/* partial_inplace */
371	 0x0000ffff,		/* src_mask */
372	 0x0000ffff,		/* dst_mask */
373	 FALSE),		/* pcrel_offset */
374
375  /* A standard high 16 bit relocation.  */
376  HOWTO (R_OR32_CONSTH,		/* type */
377	 16,			/* rightshift */
378	 2,			/* size (0 = byte, 1 = short, 2 = long) */
379	 16,			/* bitsize */
380	 TRUE,			/* pc_relative */
381	 0,			/* bitpos */
382	 complain_overflow_dont, /* complain_on_overflow */
383	 or32_elf_consth_reloc,	/* special_function */
384	 "R_OR32_CONSTH",	/* name */
385	 FALSE,			/* partial_inplace */
386	 0xffff0000,		/* src_mask */
387	 0x0000ffff,		/* dst_mask */
388	 FALSE),		/* pcrel_offset */
389
390  /* A standard branch relocation.  */
391  HOWTO (R_OR32_JUMPTARG,	/* type */
392	 2,			/* rightshift */
393	 2,			/* size (0 = byte, 1 = short, 2 = long) */
394	 28,			/* bitsize */
395	 TRUE,			/* pc_relative */
396	 0,			/* bitpos */
397	 complain_overflow_signed, /* complain_on_overflow */
398	 or32_elf_jumptarg_reloc,/* special_function */
399	 "R_OR32_JUMPTARG",	/* name */
400	 FALSE,			/* partial_inplace */
401	 0,			/* src_mask */
402	 0x03ffffff,		/* dst_mask */
403	 TRUE), 		/* pcrel_offset */
404
405  /* GNU extension to record C++ vtable hierarchy.  */
406  HOWTO (R_OR32_GNU_VTINHERIT, /* type */
407         0,                     /* rightshift */
408         2,                     /* size (0 = byte, 1 = short, 2 = long) */
409         0,                     /* bitsize */
410         FALSE,                 /* pc_relative */
411         0,                     /* bitpos */
412         complain_overflow_dont, /* complain_on_overflow */
413         NULL,                  /* special_function */
414         "R_OR32_GNU_VTINHERIT", /* name */
415         FALSE,                 /* partial_inplace */
416         0,                     /* src_mask */
417         0,                     /* dst_mask */
418         FALSE),                /* pcrel_offset */
419
420  /* GNU extension to record C++ vtable member usage.  */
421  HOWTO (R_OR32_GNU_VTENTRY,     /* type */
422         0,                     /* rightshift */
423         2,                     /* size (0 = byte, 1 = short, 2 = long) */
424         0,                     /* bitsize */
425         FALSE,                 /* pc_relative */
426         0,                     /* bitpos */
427         complain_overflow_dont, /* complain_on_overflow */
428         _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
429         "R_OR32_GNU_VTENTRY",   /* name */
430         FALSE,                 /* partial_inplace */
431         0,                     /* src_mask */
432         0,                     /* dst_mask */
433         FALSE),                /* pcrel_offset */
434};
435
436/* Map BFD reloc types to OR32 ELF reloc types.  */
437
438struct or32_reloc_map
439{
440  bfd_reloc_code_real_type  bfd_reloc_val;
441  unsigned char             elf_reloc_val;
442};
443
444static const struct or32_reloc_map or32_reloc_map[] =
445{
446  { BFD_RELOC_NONE, R_OR32_NONE },
447  { BFD_RELOC_32, R_OR32_32 },
448  { BFD_RELOC_16, R_OR32_16 },
449  { BFD_RELOC_8, R_OR32_8 },
450  { BFD_RELOC_LO16, R_OR32_CONST },
451  { BFD_RELOC_HI16, R_OR32_CONSTH },
452  { BFD_RELOC_32_GOT_PCREL, R_OR32_JUMPTARG },
453  { BFD_RELOC_VTABLE_INHERIT, R_OR32_GNU_VTINHERIT },
454  { BFD_RELOC_VTABLE_ENTRY, R_OR32_GNU_VTENTRY },
455};
456
457static reloc_howto_type *
458bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
459				 bfd_reloc_code_real_type code)
460{
461  unsigned int i;
462
463  for (i = ARRAY_SIZE (or32_reloc_map); i--;)
464    if (or32_reloc_map[i].bfd_reloc_val == code)
465      return &elf_or32_howto_table[or32_reloc_map[i].elf_reloc_val];
466
467  return NULL;
468}
469
470static reloc_howto_type *
471bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
472				 const char *r_name)
473{
474  unsigned int i;
475
476  for (i = 0;
477       i < sizeof (elf_or32_howto_table) / sizeof (elf_or32_howto_table[0]);
478       i++)
479    if (elf_or32_howto_table[i].name != NULL
480	&& strcasecmp (elf_or32_howto_table[i].name, r_name) == 0)
481      return &elf_or32_howto_table[i];
482
483  return NULL;
484}
485
486/* Set the howto pointer for an OR32 ELF reloc.  */
487
488static void
489or32_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
490			arelent *cache_ptr,
491			Elf_Internal_Rela *dst)
492{
493  unsigned int r_type;
494
495  r_type = ELF32_R_TYPE (dst->r_info);
496  BFD_ASSERT (r_type < (unsigned int) R_OR32_max);
497  cache_ptr->howto = &elf_or32_howto_table[r_type];
498}
499
500#define TARGET_LITTLE_SYM	bfd_elf32_or32_little_vec
501#define TARGET_LITTLE_NAME	"elf32-littleor32"
502#define TARGET_BIG_SYM		bfd_elf32_or32_big_vec
503#define TARGET_BIG_NAME		"elf32-or32"
504#define ELF_ARCH		bfd_arch_or32
505#define ELF_MACHINE_CODE	EM_OR32
506#define ELF_MAXPAGESIZE		0x1000
507
508#define elf_info_to_howto	0
509#define elf_info_to_howto_rel	or32_info_to_howto_rel
510#define elf_backend_object_p	or32_elf_object_p
511#define elf_backend_final_write_processing \
512				or32_elf_final_write_processing
513
514#include "elf32-target.h"
515