1/* Infineon XC16X-specific support for 16-bit ELF.
2   Copyright 2006  Free Software Foundation, Inc.
3   Contributed by KPIT Cummins Infosystems
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 2 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
20
21#include "bfd.h"
22#include "sysdep.h"
23#include "libbfd.h"
24#include "elf-bfd.h"
25#include "elf/xc16x.h"
26#include "elf/dwarf2.h"
27#include "libiberty.h"
28
29static reloc_howto_type xc16x_elf_howto_table [] =
30{
31  /* This reloc does nothing.  */
32  HOWTO (R_XC16X_NONE,		/* type */
33	 0,			/* rightshift */
34	 1,			/* size (0 = byte, 1 = short, 2 = long) */
35	 16,			/* bitsize */
36	 FALSE,			/* pc_relative */
37	 0,			/* bitpos */
38	 complain_overflow_bitfield, /* complain_on_overflow */
39	 bfd_elf_generic_reloc,	/* special_function */
40	 "R_XC16X_NONE",	/* name */
41	 FALSE,			/* partial_inplace */
42	 0,			/* src_mask */
43	 0,			/* dst_mask */
44	 FALSE),		/* pcrel_offset */
45
46  /* An 8 bit absolute relocation.  */
47  HOWTO (R_XC16X_ABS_8,		/* type */
48	 0,			/* rightshift */
49	 0,			/* size (0 = byte, 1 = short, 2 = long) */
50	 8,			/* bitsize */
51	 FALSE,			/* pc_relative */
52	 8,			/* bitpos */
53	 complain_overflow_bitfield, /* complain_on_overflow */
54	 bfd_elf_generic_reloc,	/* special_function */
55	 "R_XC16X_ABS_8",	/* name */
56	 TRUE,			/* partial_inplace */
57	 0x0000,		/* src_mask */
58	 0x00ff,		/* dst_mask */
59	 FALSE),		/* pcrel_offset */
60
61  /* A 16 bit absolute relocation.  */
62  HOWTO (R_XC16X_ABS_16,	/* type */
63	 0,			/* rightshift */
64	 1,			/* size (0 = byte, 1 = short, 2 = long) */
65	 16,			/* bitsize */
66	 FALSE,			/* pc_relative */
67	 0,			/* bitpos */
68	 complain_overflow_dont, /* complain_on_overflow */
69	 bfd_elf_generic_reloc,	/* special_function */
70	 "R_XC16X_ABS_16",	/* name */
71	 TRUE,			/* partial_inplace */
72	 0x00000000,		/* src_mask */
73	 0x0000ffff,		/* dst_mask */
74	 FALSE),		/* pcrel_offset */
75
76  HOWTO (R_XC16X_ABS_32,	/* type */
77  	 0,			/* rightshift */
78  	 2,			/* size (0 = byte, 1 = short, 2 = long) */
79  	 32,			/* bitsize */
80  	 FALSE,			/* pc_relative */
81  	 0,			/* bitpos */
82  	 complain_overflow_bitfield, /* complain_on_overflow */
83  	 bfd_elf_generic_reloc,	/* special_function */
84  	 "R_XC16X_ABS_32",	/* name */
85  	 TRUE,			/* partial_inplace */
86  	 0x00000000,		/* src_mask */
87  	 0xffffffff,		/* dst_mask */
88  	 FALSE),		/* pcrel_offset */
89
90
91  /* A PC relative 8 bit relocation.  */
92  HOWTO (R_XC16X_8_PCREL,	/* type */
93	 0,			/* rightshift */
94	 0,			/* size (0 = byte, 1 = short, 2 = long) */
95	 8,			/* bitsize */
96	 TRUE,			/* pc_relative */
97	 8,			/* bitpos */
98	 complain_overflow_signed, /* complain_on_overflow */
99	 bfd_elf_generic_reloc, /* special_function */
100	 "R_XC16X_8_PCREL",	/* name */
101	 FALSE,			/* partial_inplace */
102	 0x0000,		/* src_mask */
103	 0x00ff,		/* dst_mask */
104	 TRUE),		/* pcrel_offset */
105
106  /* Relocation regarding page number.  */
107    HOWTO (R_XC16X_PAG,	/* type */
108  	 0,			/* rightshift */
109  	 1,			/* size (0 = byte, 1 = short, 2 = long) */
110  	 16,			/* bitsize */
111  	 FALSE,			/* pc_relative */
112  	 0,			/* bitpos */
113  	 complain_overflow_signed, /* complain_on_overflow */
114  	 bfd_elf_generic_reloc, /* special_function */
115  	 "R_XC16X_PAG",	/* name */
116  	 TRUE,			/* partial_inplace */
117  	 0x00000000,		/* src_mask */
118  	 0x0000ffff,		/* dst_mask */
119  	 FALSE),		/* pcrel_offset */
120
121
122  /* Relocation regarding page number.  */
123      HOWTO (R_XC16X_POF,	/* type */
124    	 0,			/* rightshift */
125    	 1,			/* size (0 = byte, 1 = short, 2 = long) */
126    	 16,			/* bitsize */
127    	 FALSE,			/* pc_relative */
128    	 0,			/* bitpos  */
129    	 complain_overflow_signed, /* complain_on_overflow  */
130    	 bfd_elf_generic_reloc, /* special_function  */
131    	 "R_XC16X_POF",	/* name  */
132    	 TRUE,			/* partial_inplace  */
133    	 0x00000000,		/* src_mask  */
134    	 0x0000ffff,		/* dst_mask  */
135    	 FALSE),		/* pcrel_offset  */
136
137
138  /* Relocation regarding segment number.   */
139      HOWTO (R_XC16X_SEG,	/* type  */
140    	 0,			/* rightshift  */
141    	 1,			/* size (0 = byte, 1 = short, 2 = long)  */
142    	 16,			/* bitsize  */
143    	 FALSE,			/* pc_relative  */
144    	 0,			/* bitpos  */
145    	 complain_overflow_signed, /* complain_on_overflow  */
146    	 bfd_elf_generic_reloc, /* special_function  */
147    	 "R_XC16X_SEG",	/* name  */
148    	 TRUE,			/* partial_inplace  */
149    	 0x00000000,		/* src_mask  */
150    	 0x0000ffff,		/* dst_mask  */
151    	 FALSE),		/* pcrel_offset  */
152
153  /* Relocation regarding segment offset.  */
154      HOWTO (R_XC16X_SOF,	/* type  */
155    	 0,			/* rightshift  */
156    	 1,			/* size (0 = byte, 1 = short, 2 = long)  */
157    	 16,			/* bitsize  */
158    	 FALSE,			/* pc_relative  */
159    	 0,			/* bitpos  */
160    	 complain_overflow_signed, /* complain_on_overflow  */
161    	 bfd_elf_generic_reloc, /* special_function  */
162    	 "R_XC16X_SOF",	/* name */
163    	 TRUE,			/* partial_inplace  */
164    	 0x00000000,		/* src_mask  */
165    	 0x0000ffff,		/* dst_mask  */
166    	 FALSE)			/* pcrel_offset  */
167};
168
169
170/* Map BFD reloc types to XC16X ELF reloc types.  */
171
172struct xc16x_reloc_map
173{
174  bfd_reloc_code_real_type bfd_reloc_val;
175  unsigned int xc16x_reloc_val;
176};
177
178static const struct xc16x_reloc_map xc16x_reloc_map [] =
179{
180  { BFD_RELOC_NONE,           R_XC16X_NONE },
181  { BFD_RELOC_8,              R_XC16X_ABS_8 },
182  { BFD_RELOC_16,             R_XC16X_ABS_16 },
183  { BFD_RELOC_32,             R_XC16X_ABS_32 },
184  { BFD_RELOC_8_PCREL,        R_XC16X_8_PCREL },
185  { BFD_RELOC_XC16X_PAG,      R_XC16X_PAG},
186  { BFD_RELOC_XC16X_POF,      R_XC16X_POF},
187  { BFD_RELOC_XC16X_SEG,      R_XC16X_SEG},
188  { BFD_RELOC_XC16X_SOF,      R_XC16X_SOF},
189};
190
191
192/* This function is used to search for correct relocation type from
193   howto structure.  */
194
195static reloc_howto_type *
196xc16x_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
197			 bfd_reloc_code_real_type code)
198{
199  unsigned int i;
200
201  for (i = ARRAY_SIZE (xc16x_reloc_map); --i;)
202    if (xc16x_reloc_map [i].bfd_reloc_val == code)
203      return & xc16x_elf_howto_table [xc16x_reloc_map[i].xc16x_reloc_val];
204
205  return NULL;
206}
207
208/* For a particular operand this function is
209   called to finalise the type of relocation.  */
210
211static void
212elf32_xc16x_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *bfd_reloc,
213			   Elf_Internal_Rela *elf_reloc)
214{
215  unsigned int r;
216  unsigned int i;
217
218  r = ELF32_R_TYPE (elf_reloc->r_info);
219  for (i = 0; i < ARRAY_SIZE (xc16x_elf_howto_table); i++)
220    if (xc16x_elf_howto_table[i].type == r)
221      {
222	bfd_reloc->howto = &xc16x_elf_howto_table[i];
223	return;
224      }
225  abort ();
226}
227
228static bfd_reloc_status_type
229elf32_xc16x_final_link_relocate (unsigned long r_type,
230				 bfd *input_bfd,
231				 bfd *output_bfd ATTRIBUTE_UNUSED,
232				 asection *input_section ATTRIBUTE_UNUSED,
233				 bfd_byte *contents,
234				 bfd_vma offset,
235				 bfd_vma value,
236				 bfd_vma addend,
237				 struct bfd_link_info *info ATTRIBUTE_UNUSED,
238				 asection *sym_sec ATTRIBUTE_UNUSED,
239				 int is_local ATTRIBUTE_UNUSED)
240{
241  bfd_byte *hit_data = contents + offset;
242  bfd_vma val1;
243
244  switch (r_type)
245    {
246    case R_XC16X_NONE:
247      return bfd_reloc_ok;
248
249    case R_XC16X_ABS_16:
250      value += addend;
251      bfd_put_16 (input_bfd, value, hit_data);
252      return bfd_reloc_ok;
253
254    case R_XC16X_8_PCREL:
255      bfd_put_8 (input_bfd, value, hit_data);
256      return bfd_reloc_ok;
257
258      /* Following case is to find page number from actual
259	 address for this divide value by 16k i.e. page size.  */
260
261    case R_XC16X_PAG:
262      value += addend;
263      value /= 0x4000;
264      bfd_put_16 (input_bfd, value, hit_data);
265      return bfd_reloc_ok;
266
267      /* Following case is to find page offset from actual address
268	 for this take modulo of value by 16k i.e. page size.  */
269
270    case R_XC16X_POF:
271      value += addend;
272      value %= 0x4000;
273      bfd_put_16 (input_bfd, value, hit_data);
274      return bfd_reloc_ok;
275
276      /* Following case is to find segment number from actual
277	 address for this divide value by 64k i.e. segment size.  */
278
279    case R_XC16X_SEG:
280      value += addend;
281      value /= 0x10000;
282      bfd_put_16 (input_bfd, value, hit_data);
283      return bfd_reloc_ok;
284
285      /* Following case is to find segment offset from actual address
286	 for this take modulo of value by 64k i.e. segment size.  */
287
288    case R_XC16X_SOF:
289      value += addend;
290      value %= 0x10000;
291      bfd_put_16 (input_bfd, value, hit_data);
292      return bfd_reloc_ok;
293
294    case R_XC16X_ABS_32:
295      if (!strstr (input_section->name,".debug"))
296	{
297	  value += addend;
298	  val1 = value;
299	  value %= 0x4000;
300	  val1 /= 0x4000;
301	  val1 = val1 << 16;
302	  value += val1;
303	  bfd_put_32 (input_bfd, value, hit_data);
304	}
305      else
306	{
307	  value += addend;
308	  bfd_put_32 (input_bfd, value, hit_data);
309	}
310      return bfd_reloc_ok;
311
312    default:
313      return bfd_reloc_notsupported;
314    }
315}
316
317static bfd_boolean
318elf32_xc16x_relocate_section (bfd *output_bfd,
319			      struct bfd_link_info *info,
320			      bfd *input_bfd,
321			      asection *input_section,
322			      bfd_byte *contents,
323			      Elf_Internal_Rela *relocs,
324			      Elf_Internal_Sym *local_syms,
325			      asection **local_sections)
326{
327  Elf_Internal_Shdr *symtab_hdr;
328  struct elf_link_hash_entry **sym_hashes;
329  Elf_Internal_Rela *rel, *relend;
330
331  if (info->relocatable)
332    return TRUE;
333
334  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
335  sym_hashes = elf_sym_hashes (input_bfd);
336
337  rel = relocs;
338  relend = relocs + input_section->reloc_count;
339  for (; rel < relend; rel++)
340    {
341      unsigned int r_type;
342      unsigned long r_symndx;
343      Elf_Internal_Sym *sym;
344      asection *sec;
345      struct elf_link_hash_entry *h;
346      bfd_vma relocation;
347      bfd_reloc_status_type r;
348
349      /* This is a final link.  */
350      r_symndx = ELF32_R_SYM (rel->r_info);
351      r_type = ELF32_R_TYPE (rel->r_info);
352      h = NULL;
353      sym = NULL;
354      sec = NULL;
355      if (r_symndx < symtab_hdr->sh_info)
356	{
357	  sym = local_syms + r_symndx;
358	  sec = local_sections[r_symndx];
359	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
360	}
361      else
362	{
363	  bfd_boolean unresolved_reloc, warned;
364
365	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
366				   r_symndx, symtab_hdr, sym_hashes,
367				   h, sec, relocation,
368				   unresolved_reloc, warned);
369	}
370
371      r = elf32_xc16x_final_link_relocate (r_type, input_bfd, output_bfd,
372					input_section,
373					contents, rel->r_offset,
374					relocation, rel->r_addend,
375					info, sec, h == NULL);
376    }
377
378  return TRUE;
379}
380
381
382static void
383elf32_xc16x_final_write_processing (bfd *abfd,
384				    bfd_boolean linker ATTRIBUTE_UNUSED)
385{
386  unsigned long val;
387
388  switch (bfd_get_mach (abfd))
389    {
390    default:
391    case bfd_mach_xc16x:
392      val = 0x1000;
393      break;
394
395    case bfd_mach_xc16xl:
396      val = 0x1001;
397      break;
398
399    case bfd_mach_xc16xs:
400      val = 0x1002;
401      break;
402    }
403
404  elf_elfheader (abfd)->e_flags |= val;
405}
406
407static unsigned long
408elf32_xc16x_mach (flagword flags)
409{
410  switch (flags)
411    {
412    case 0x1000:
413    default:
414      return bfd_mach_xc16x;
415
416    case 0x1001:
417      return bfd_mach_xc16xl;
418
419    case 0x1002:
420      return bfd_mach_xc16xs;
421    }
422}
423
424
425static bfd_boolean
426elf32_xc16x_object_p (bfd *abfd)
427{
428  bfd_default_set_arch_mach (abfd, bfd_arch_xc16x,
429			     elf32_xc16x_mach (elf_elfheader (abfd)->e_flags));
430  return TRUE;
431}
432
433
434#define ELF_ARCH		bfd_arch_xc16x
435#define ELF_MACHINE_CODE	EM_XC16X
436#define ELF_MAXPAGESIZE		0x100
437
438#define TARGET_LITTLE_SYM       bfd_elf32_xc16x_vec
439#define TARGET_LITTLE_NAME	"elf32-xc16x"
440#define elf_backend_final_write_processing	elf32_xc16x_final_write_processing
441#define elf_backend_object_p   		elf32_xc16x_object_p
442#define elf_backend_can_gc_sections	1
443#define bfd_elf32_bfd_reloc_type_lookup	xc16x_reloc_type_lookup
444#define elf_info_to_howto		elf32_xc16x_info_to_howto
445#define elf_info_to_howto_rel		elf32_xc16x_info_to_howto
446#define elf_backend_relocate_section  	elf32_xc16x_relocate_section
447#define elf_backend_rela_normal		1
448
449#include "elf32-target.h"
450