1/* picoJava specific support for 32-bit ELF
2   Copyright 1999, 2000, 2001, 2002, 2005, 2007
3   Free Software Foundation, Inc.
4   Contributed by Steve Chamberlan of Transmeta (sac@pobox.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 "bfdlink.h"
26#include "libbfd.h"
27#include "elf-bfd.h"
28#include "elf/pj.h"
29
30/* This function is used for normal relocs.  This is like the COFF
31   function, and is almost certainly incorrect for other ELF targets.  */
32
33static bfd_reloc_status_type
34pj_elf_reloc (bfd *abfd,
35	      arelent *reloc_entry,
36	      asymbol *symbol_in,
37	      void * data,
38	      asection *input_section,
39	      bfd *output_bfd,
40	      char **error_message ATTRIBUTE_UNUSED)
41{
42  unsigned long insn;
43  bfd_vma sym_value;
44  enum elf_pj_reloc_type r_type;
45  bfd_vma addr = reloc_entry->address;
46  bfd_byte *hit_data = addr + (bfd_byte *) data;
47
48  r_type = (enum elf_pj_reloc_type) reloc_entry->howto->type;
49
50  if (output_bfd != NULL)
51    {
52      /* Partial linking--do nothing.  */
53      reloc_entry->address += input_section->output_offset;
54      return bfd_reloc_ok;
55    }
56
57  if (symbol_in != NULL
58      && bfd_is_und_section (symbol_in->section))
59    return bfd_reloc_undefined;
60
61  if (bfd_is_com_section (symbol_in->section))
62    sym_value = 0;
63  else
64    sym_value = (symbol_in->value +
65		 symbol_in->section->output_section->vma +
66		 symbol_in->section->output_offset);
67
68  switch (r_type)
69    {
70    case R_PJ_DATA_DIR32:
71      insn = bfd_get_32 (abfd, hit_data);
72      insn += sym_value + reloc_entry->addend;
73      bfd_put_32 (abfd, (bfd_vma) insn, hit_data);
74      break;
75
76      /* Relocations in code are always bigendian, no matter what the
77	 data endianness is.  */
78
79    case R_PJ_CODE_DIR32:
80      insn = bfd_getb32 (hit_data);
81      insn += sym_value + reloc_entry->addend;
82      bfd_putb32 ((bfd_vma) insn, hit_data);
83      break;
84
85    case R_PJ_CODE_REL16:
86      insn = bfd_getb16 (hit_data);
87      insn += sym_value + reloc_entry->addend
88        -  (input_section->output_section->vma
89            + input_section->output_offset);
90      bfd_putb16 ((bfd_vma) insn, hit_data);
91      break;
92    case R_PJ_CODE_LO16:
93      insn = bfd_getb16 (hit_data);
94      insn += sym_value + reloc_entry->addend;
95      bfd_putb16 ((bfd_vma) insn, hit_data);
96      break;
97
98    case R_PJ_CODE_HI16:
99      insn = bfd_getb16 (hit_data);
100      insn += (sym_value + reloc_entry->addend) >> 16;
101      bfd_putb16 ((bfd_vma) insn, hit_data);
102      break;
103
104    default:
105      abort ();
106      break;
107    }
108
109  return bfd_reloc_ok;
110}
111
112static reloc_howto_type pj_elf_howto_table[] =
113{
114  /* No relocation.  */
115  HOWTO (R_PJ_NONE,		/* type */
116	 0,			/* rightshift */
117	 0,			/* size (0 = byte, 1 = short, 2 = long) */
118	 0,			/* bitsize */
119	 FALSE,			/* pc_relative */
120	 0,			/* bitpos */
121	 complain_overflow_dont, /* complain_on_overflow */
122	 pj_elf_reloc,		/* special_function */
123	 "R_PJ_NONE",		/* name */
124	 FALSE,			/* partial_inplace */
125	 0,			/* src_mask */
126	 0,			/* dst_mask */
127	 FALSE),		/* pcrel_offset */
128
129  /* 32 bit absolute relocation.  Setting partial_inplace to TRUE and
130     src_mask to a non-zero value is similar to the COFF toolchain.  */
131  HOWTO (R_PJ_DATA_DIR32,	/* type */
132	 0,			/* rightshift */
133	 2,			/* size (0 = byte, 1 = short, 2 = long) */
134	 32,			/* bitsize */
135	 FALSE,			/* pc_relative */
136	 0,			/* bitpos */
137	 complain_overflow_bitfield, /* complain_on_overflow */
138	 pj_elf_reloc,		/* special_function */
139	 "R_PJ_DIR32",		/* name */
140	 TRUE,			/* partial_inplace */
141	 0xffffffff,		/* src_mask */
142	 0xffffffff,		/* dst_mask */
143	 FALSE),		/* pcrel_offset */
144
145  /* 32 bit PC relative relocation.  */
146  HOWTO (R_PJ_CODE_REL32,	/* type */
147	 0,			/* rightshift */
148	 2,			/* size (0 = byte, 1 = short, 2 = long) */
149	 32,			/* bitsize */
150	 TRUE,			/* pc_relative */
151	 0,			/* bitpos */
152	 complain_overflow_signed, /* complain_on_overflow */
153	 pj_elf_reloc,		/* special_function */
154	 "R_PJ_REL32",		/* name */
155	 FALSE,			/* partial_inplace */
156	 0,			/* src_mask */
157	 0xffffffff,		/* dst_mask */
158	 TRUE),			/* pcrel_offset */
159
160/* 16 bit PC relative relocation.  */
161  HOWTO (R_PJ_CODE_REL16,	/* type */
162	 0,			/* rightshift */
163	 1,			/* size (0 = byte, 1 = short, 2 = long) */
164	 16,			/* bitsize */
165	 TRUE,			/* pc_relative */
166	 0,			/* bitpos */
167	 complain_overflow_signed, /* complain_on_overf6w */
168	 pj_elf_reloc,		/* special_function */
169	 "R_PJ_REL16",		/* name */
170	 FALSE,			/* partial_inplace */
171	 0xffff,		/* src_mask */
172	 0xffff,		/* dst_mask */
173	 TRUE),			/* pcrel_offset */
174  EMPTY_HOWTO (4),
175  EMPTY_HOWTO (5),
176  HOWTO (R_PJ_CODE_DIR32,	/* type */
177	 0,			/* rightshift */
178	 2,			/* size (0 = byte, 1 = short, 2 = long) */
179	 32,			/* bitsize */
180	 FALSE,			/* pc_relative */
181	 0,			/* bitpos */
182	 complain_overflow_bitfield, /* complain_on_overflow */
183	 pj_elf_reloc,		/* special_function */
184	 "R_PJ_CODE_DIR32",	/* name */
185	 TRUE,			/* partial_inplace */
186	 0xffffffff,		/* src_mask */
187	 0xffffffff,		/* dst_mask */
188	 FALSE),		/* pcrel_offset */
189
190  EMPTY_HOWTO (7),
191  EMPTY_HOWTO (8),
192  EMPTY_HOWTO (9),
193  EMPTY_HOWTO (10),
194  EMPTY_HOWTO (11),
195  EMPTY_HOWTO (12),
196
197  HOWTO (R_PJ_CODE_LO16,	/* type */
198	 0,			/* rightshift */
199	 1,			/* size (0 = byte, 1 = short, 2 = long) */
200	 16,			/* bitsize */
201	 FALSE,			/* pc_relative */
202	 0,			/* bitpos */
203	 complain_overflow_unsigned, /* complain_on_overflow */
204	 pj_elf_reloc,		/* special_function */
205	 "R_PJ_LO16",		/* name */
206	 FALSE,			/* partial_inplace */
207	 0xffff,		/* src_mask */
208	 0xffff,		/* dst_mask */
209	 TRUE),			/* pcrel_offset */
210
211    HOWTO (R_PJ_CODE_HI16,	/* type */
212	 16,			/* rightshift */
213	 1,			/* size (0 = byte, 1 = short, 2 = long) */
214	 16,			/* bitsize */
215	 FALSE,			/* pc_relative */
216	 0,			/* bitpos */
217	 complain_overflow_unsigned, /* complain_on_overflow */
218	 pj_elf_reloc,		/* special_function */
219	 "R_PJ_HI16",		/* name */
220	 FALSE,			/* partial_inplace */
221	 0xffff,		/* src_mask */
222	 0xffff,		/* dst_mask */
223	 TRUE),			/* pcrel_offset */
224
225  /* GNU extension to record C++ vtable hierarchy.  */
226  HOWTO (R_PJ_GNU_VTINHERIT,    /* type */
227         0,                     /* rightshift */
228         2,                     /* size (0 = byte, 1 = short, 2 = long) */
229         0,                     /* bitsize */
230         FALSE,                 /* pc_relative */
231         0,                     /* bitpos */
232         complain_overflow_dont, /* complain_on_overflow */
233         NULL,                  /* special_function */
234         "R_PJ_GNU_VTINHERIT",  /* name */
235         FALSE,                 /* partial_inplace */
236         0,                     /* src_mask */
237         0,                     /* dst_mask */
238         FALSE),                /* pcrel_offset */
239
240  /* GNU extension to record C++ vtable member usage.  */
241  HOWTO (R_PJ_GNU_VTENTRY,     /* type */
242         0,                     /* rightshift */
243         2,                     /* size (0 = byte, 1 = short, 2 = long) */
244         0,                     /* bitsize */
245         FALSE,                 /* pc_relative */
246         0,                     /* bitpos */
247         complain_overflow_dont, /* complain_on_overflow */
248         _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
249         "R_PJ_GNU_VTENTRY",   /* name */
250         FALSE,                 /* partial_inplace */
251         0,                     /* src_mask */
252         0,                     /* dst_mask */
253         FALSE),                /* pcrel_offset */
254};
255
256/* This structure is used to map BFD reloc codes to PJ ELF relocs.  */
257
258struct elf_reloc_map
259{
260  bfd_reloc_code_real_type bfd_reloc_val;
261  unsigned char elf_reloc_val;
262};
263
264/* An array mapping BFD reloc codes to PJ ELF relocs.  */
265
266static const struct elf_reloc_map pj_reloc_map[] =
267{
268    { BFD_RELOC_NONE, 		R_PJ_NONE          },
269    { BFD_RELOC_32, 		R_PJ_DATA_DIR32    },
270    { BFD_RELOC_PJ_CODE_DIR16, 	R_PJ_CODE_DIR16    },
271    { BFD_RELOC_PJ_CODE_DIR32, 	R_PJ_CODE_DIR32    },
272    { BFD_RELOC_PJ_CODE_LO16, 	R_PJ_CODE_LO16     },
273    { BFD_RELOC_PJ_CODE_HI16, 	R_PJ_CODE_HI16     },
274    { BFD_RELOC_PJ_CODE_REL32,  R_PJ_CODE_REL32    },
275    { BFD_RELOC_PJ_CODE_REL16,  R_PJ_CODE_REL16    },
276    { BFD_RELOC_VTABLE_INHERIT, R_PJ_GNU_VTINHERIT },
277    { BFD_RELOC_VTABLE_ENTRY,   R_PJ_GNU_VTENTRY   },
278};
279
280/* Given a BFD reloc code, return the howto structure for the
281   corresponding PJ ELf reloc.  */
282
283static reloc_howto_type *
284pj_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
285			  bfd_reloc_code_real_type code)
286{
287  unsigned int i;
288
289  for (i = 0; i < sizeof (pj_reloc_map) / sizeof (struct elf_reloc_map); i++)
290    if (pj_reloc_map[i].bfd_reloc_val == code)
291      return & pj_elf_howto_table[(int) pj_reloc_map[i].elf_reloc_val];
292
293  return NULL;
294}
295
296static reloc_howto_type *
297pj_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
298			  const char *r_name)
299{
300  unsigned int i;
301
302  for (i = 0;
303       i < sizeof (pj_elf_howto_table) / sizeof (pj_elf_howto_table[0]);
304       i++)
305    if (pj_elf_howto_table[i].name != NULL
306	&& strcasecmp (pj_elf_howto_table[i].name, r_name) == 0)
307      return &pj_elf_howto_table[i];
308
309  return NULL;
310}
311
312/* Given an ELF reloc, fill in the howto field of a relent.  */
313
314static void
315pj_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
316		      arelent *cache_ptr,
317		      Elf_Internal_Rela *dst)
318{
319  unsigned int r;
320
321  r = ELF32_R_TYPE (dst->r_info);
322
323  BFD_ASSERT (r < (unsigned int) R_PJ_max);
324
325  cache_ptr->howto = &pj_elf_howto_table[r];
326}
327
328/* Take this moment to fill in the special picoJava bits in the
329   e_flags field.  */
330
331static void
332pj_elf_final_write_processing (bfd *abfd,
333			       bfd_boolean linker ATTRIBUTE_UNUSED)
334{
335  elf_elfheader (abfd)->e_flags |= EF_PICOJAVA_ARCH;
336  elf_elfheader (abfd)->e_flags |= EF_PICOJAVA_GNUCALLS;
337}
338
339#define TARGET_BIG_SYM		bfd_elf32_pj_vec
340#define TARGET_BIG_NAME		"elf32-pj"
341#define TARGET_LITTLE_SYM	bfd_elf32_pjl_vec
342#define TARGET_LITTLE_NAME	"elf32-pjl"
343#define ELF_ARCH		bfd_arch_pj
344#define ELF_MACHINE_CODE	EM_PJ
345#define ELF_MACHINE_ALT1	EM_PJ_OLD
346#define ELF_MAXPAGESIZE		0x1000
347#define bfd_elf32_bfd_get_relocated_section_contents \
348  bfd_generic_get_relocated_section_contents
349#define bfd_elf32_bfd_reloc_type_lookup	        pj_elf_reloc_type_lookup
350#define bfd_elf32_bfd_reloc_name_lookup   pj_elf_reloc_name_lookup
351#define elf_backend_final_write_processing      pj_elf_final_write_processing
352#define elf_info_to_howto		        pj_elf_info_to_howto
353#include "elf32-target.h"
354