xref: /netbsd-src/external/gpl3/binutils/dist/bfd/elf32-xstormy16.c (revision 80d9064ac03cbb6a4174695f0d5b237c8766d3d0)
1 /* Xstormy16-specific support for 32-bit ELF.
2    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2010, 2011, 2012
3    Free Software Foundation, Inc.
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/xstormy16.h"
27 #include "libiberty.h"
28 
29 /* Handle the R_XSTORMY16_24 reloc, which has an odd bit arrangement.  */
30 
31 static bfd_reloc_status_type
32 xstormy16_elf_24_reloc (bfd *abfd,
33 			arelent *reloc_entry,
34 			asymbol *symbol,
35 			void * data,
36 			asection *input_section,
37 			bfd *output_bfd,
38 			char **error_message ATTRIBUTE_UNUSED)
39 {
40   bfd_vma relocation, x;
41 
42   if (output_bfd != NULL)
43     {
44       reloc_entry->address += input_section->output_offset;
45       return bfd_reloc_ok;
46     }
47 
48   if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
49     return bfd_reloc_outofrange;
50 
51   if (bfd_is_com_section (symbol->section))
52     relocation = 0;
53   else
54     relocation = symbol->value;
55 
56   relocation += symbol->section->output_section->vma;
57   relocation += symbol->section->output_offset;
58   relocation += reloc_entry->addend;
59 
60   x = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
61   x &= 0x0000ff00;
62   x |= relocation & 0xff;
63   x |= (relocation << 8) & 0xffff0000;
64   bfd_put_32 (abfd, x, (bfd_byte *) data + reloc_entry->address);
65 
66   if (relocation & ~ (bfd_vma) 0xffffff)
67     return bfd_reloc_overflow;
68 
69   return bfd_reloc_ok;
70 }
71 
72 static reloc_howto_type xstormy16_elf_howto_table [] =
73 {
74   /* This reloc does nothing.  */
75   HOWTO (R_XSTORMY16_NONE,	/* type */
76 	 0,			/* rightshift */
77 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
78 	 32,			/* bitsize */
79 	 FALSE,			/* pc_relative */
80 	 0,			/* bitpos */
81 	 complain_overflow_bitfield, /* complain_on_overflow */
82 	 bfd_elf_generic_reloc,	/* special_function */
83 	 "R_XSTORMY16_NONE",	/* name */
84 	 FALSE,			/* partial_inplace */
85 	 0,			/* src_mask */
86 	 0,			/* dst_mask */
87 	 FALSE),		/* pcrel_offset */
88 
89   /* A 32 bit absolute relocation.  */
90   HOWTO (R_XSTORMY16_32,	/* type */
91 	 0,			/* rightshift */
92 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
93 	 32,			/* bitsize */
94 	 FALSE,			/* pc_relative */
95 	 0,			/* bitpos */
96 	 complain_overflow_dont, /* complain_on_overflow */
97 	 bfd_elf_generic_reloc,	/* special_function */
98 	 "R_XSTORMY16_32",	/* name */
99 	 FALSE,			/* partial_inplace */
100 	 0,			/* src_mask */
101 	 0xffffffff,		/* dst_mask */
102 	 FALSE),		/* pcrel_offset */
103 
104   /* A 16 bit absolute relocation.  */
105   HOWTO (R_XSTORMY16_16,	/* type */
106 	 0,			/* rightshift */
107 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
108 	 16,			/* bitsize */
109 	 FALSE,			/* pc_relative */
110 	 0,			/* bitpos */
111 	 complain_overflow_bitfield, /* complain_on_overflow */
112 	 bfd_elf_generic_reloc,	/* special_function */
113 	 "R_XSTORMY16_16",	/* name */
114 	 FALSE,			/* partial_inplace */
115 	 0,			/* src_mask */
116 	 0xffff,		/* dst_mask */
117 	 FALSE),		/* pcrel_offset */
118 
119   /* An 8 bit absolute relocation.  */
120   HOWTO (R_XSTORMY16_8,		/* type */
121 	 0,			/* rightshift */
122 	 0,			/* size (0 = byte, 1 = short, 2 = long) */
123 	 8,			/* bitsize */
124 	 FALSE,			/* pc_relative */
125 	 0,			/* bitpos */
126 	 complain_overflow_unsigned, /* complain_on_overflow */
127 	 bfd_elf_generic_reloc,	/* special_function */
128 	 "R_XSTORMY16_8",	/* name */
129 	 FALSE,			/* partial_inplace */
130 	 0,			/* src_mask */
131 	 0xff,			/* dst_mask */
132 	 FALSE),		/* pcrel_offset */
133 
134   /* A 32 bit pc-relative relocation.  */
135   HOWTO (R_XSTORMY16_PC32,	/* type */
136 	 0,			/* rightshift */
137 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
138 	 32,			/* bitsize */
139 	 TRUE,			/* pc_relative */
140 	 0,			/* bitpos */
141 	 complain_overflow_dont, /* complain_on_overflow */
142 	 bfd_elf_generic_reloc,	/* special_function */
143 	 "R_XSTORMY16_PC32",	/* name */
144 	 FALSE,			/* partial_inplace */
145 	 0,			/* src_mask */
146 	 0xffffffff,		/* dst_mask */
147 	 TRUE),			/* pcrel_offset */
148 
149   /* A 16 bit pc-relative relocation.  */
150   HOWTO (R_XSTORMY16_PC16,	/* type */
151 	 0,			/* rightshift */
152 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
153 	 16,			/* bitsize */
154 	 TRUE,			/* pc_relative */
155 	 0,			/* bitpos */
156 	 complain_overflow_signed, /* complain_on_overflow */
157 	 bfd_elf_generic_reloc,	/* special_function */
158 	 "R_XSTORMY16_PC16",	/* name */
159 	 FALSE,			/* partial_inplace */
160 	 0,			/* src_mask */
161 	 0xffffffff,		/* dst_mask */
162 	 TRUE),			/* pcrel_offset */
163 
164   /* An 8 bit pc-relative relocation.  */
165   HOWTO (R_XSTORMY16_PC8,	/* type */
166 	 0,			/* rightshift */
167 	 0,			/* size (0 = byte, 1 = short, 2 = long) */
168 	 8,			/* bitsize */
169 	 TRUE,			/* pc_relative */
170 	 0,			/* bitpos */
171 	 complain_overflow_signed, /* complain_on_overflow */
172 	 bfd_elf_generic_reloc,	/* special_function */
173 	 "R_XSTORMY16_PC8",	/* name */
174 	 FALSE,			/* partial_inplace */
175 	 0,			/* src_mask */
176 	 0xffffffff,		/* dst_mask */
177 	 TRUE),			/* pcrel_offset */
178 
179   /* A 12-bit pc-relative relocation suitable for the branch instructions.  */
180   HOWTO (R_XSTORMY16_REL_12,	/* type */
181 	 1,			/* rightshift */
182 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
183 	 11,			/* bitsize */
184 	 TRUE,			/* pc_relative */
185 	 1,			/* bitpos */
186 	 complain_overflow_signed, /* complain_on_overflow */
187 	 bfd_elf_generic_reloc,	/* special_function */
188 	 "R_XSTORMY16_REL_12",	/* name */
189 	 FALSE,			/* partial_inplace */
190 	 0,			/* src_mask */
191 	 0x0ffe,		/* dst_mask */
192 	 TRUE),			/* pcrel_offset */
193 
194   /* A 24-bit absolute relocation suitable for the jump instructions.  */
195   HOWTO (R_XSTORMY16_24,	/* type */
196 	 0,			/* rightshift */
197 	 2,			/* size (0 = byte, 1 = short, 2 = long) */
198 	 24,			/* bitsize */
199 	 FALSE,			/* pc_relative */
200 	 0,			/* bitpos */
201 	 complain_overflow_unsigned, /* complain_on_overflow */
202 	 xstormy16_elf_24_reloc,	/* special_function */
203 	 "R_XSTORMY16_24",	/* name */
204 	 TRUE,			/* partial_inplace */
205 	 0,			/* src_mask */
206 	 0xffff00ff,		/* dst_mask */
207 	 TRUE),			/* pcrel_offset */
208 
209   /* A 16 bit absolute relocation to a function pointer.  */
210   HOWTO (R_XSTORMY16_FPTR16,	/* type */
211 	 0,			/* rightshift */
212 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
213 	 16,			/* bitsize */
214 	 FALSE,			/* pc_relative */
215 	 0,			/* bitpos */
216 	 complain_overflow_bitfield, /* complain_on_overflow */
217 	 bfd_elf_generic_reloc,	/* special_function */
218 	 "R_XSTORMY16_FPTR16",	/* name */
219 	 FALSE,			/* partial_inplace */
220 	 0,			/* src_mask */
221 	 0xffffffff,		/* dst_mask */
222 	 FALSE),		/* pcrel_offset */
223 
224   /* Low order 16 bit value of a high memory address.  */
225   HOWTO (R_XSTORMY16_LO16,	/* type */
226 	 0,			/* rightshift */
227 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
228 	 16,			/* bitsize */
229 	 FALSE,			/* pc_relative */
230 	 0,			/* bitpos */
231 	 complain_overflow_dont, /* complain_on_overflow */
232 	 bfd_elf_generic_reloc,	/* special_function */
233 	 "R_XSTORMY16_LO16",	/* name */
234 	 FALSE,			/* partial_inplace */
235 	 0,			/* src_mask */
236 	 0xffff,		/* dst_mask */
237 	 FALSE),		/* pcrel_offset */
238 
239   /* High order 16 bit value of a high memory address.  */
240   HOWTO (R_XSTORMY16_HI16,	/* type */
241 	 16,			/* rightshift */
242 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
243 	 16,			/* bitsize */
244 	 FALSE,			/* pc_relative */
245 	 0,			/* bitpos */
246 	 complain_overflow_dont, /* complain_on_overflow */
247 	 bfd_elf_generic_reloc,	/* special_function */
248 	 "R_XSTORMY16_HI16",	/* name */
249 	 FALSE,			/* partial_inplace */
250 	 0,			/* src_mask */
251 	 0xffff,		/* dst_mask */
252 	 FALSE),		/* pcrel_offset */
253 
254   /* A 12 bit absolute relocation.  */
255   HOWTO (R_XSTORMY16_12,	/* type */
256 	 0,			/* rightshift */
257 	 1,			/* size (0 = byte, 1 = short, 2 = long) */
258 	 12,			/* bitsize */
259 	 FALSE,			/* pc_relative */
260 	 0,			/* bitpos */
261 	 complain_overflow_signed, /* complain_on_overflow */
262 	 bfd_elf_generic_reloc,	/* special_function */
263 	 "R_XSTORMY16_12",	/* name */
264 	 FALSE,			/* partial_inplace */
265 	 0x0000,		/* src_mask */
266 	 0x0fff,		/* dst_mask */
267 	 FALSE),		/* pcrel_offset */
268 };
269 
270 static reloc_howto_type xstormy16_elf_howto_table2 [] =
271 {
272   /* GNU extension to record C++ vtable hierarchy */
273   HOWTO (R_XSTORMY16_GNU_VTINHERIT, /* type */
274          0,                     /* rightshift */
275          2,                     /* size (0 = byte, 1 = short, 2 = long) */
276          0,                     /* bitsize */
277          FALSE,                 /* pc_relative */
278          0,                     /* bitpos */
279          complain_overflow_dont, /* complain_on_overflow */
280          NULL,                  /* special_function */
281          "R_XSTORMY16_GNU_VTINHERIT", /* name */
282          FALSE,                 /* partial_inplace */
283          0,                     /* src_mask */
284          0,                     /* dst_mask */
285          FALSE),                /* pcrel_offset */
286 
287   /* GNU extension to record C++ vtable member usage */
288   HOWTO (R_XSTORMY16_GNU_VTENTRY,     /* type */
289          0,                     /* rightshift */
290          2,                     /* size (0 = byte, 1 = short, 2 = long) */
291          0,                     /* bitsize */
292          FALSE,                 /* pc_relative */
293          0,                     /* bitpos */
294          complain_overflow_dont, /* complain_on_overflow */
295          _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
296          "R_XSTORMY16_GNU_VTENTRY",   /* name */
297          FALSE,                 /* partial_inplace */
298          0,                     /* src_mask */
299          0,                     /* dst_mask */
300          FALSE),                /* pcrel_offset */
301 
302 };
303 
304 /* Map BFD reloc types to XSTORMY16 ELF reloc types.  */
305 
306 typedef struct xstormy16_reloc_map
307 {
308   bfd_reloc_code_real_type  bfd_reloc_val;
309   unsigned int              xstormy16_reloc_val;
310   reloc_howto_type *        table;
311 } reloc_map;
312 
313 static const reloc_map xstormy16_reloc_map [] =
314 {
315   { BFD_RELOC_NONE,                 R_XSTORMY16_NONE,          xstormy16_elf_howto_table },
316   { BFD_RELOC_32,                   R_XSTORMY16_32,            xstormy16_elf_howto_table },
317   { BFD_RELOC_16,                   R_XSTORMY16_16,            xstormy16_elf_howto_table },
318   { BFD_RELOC_8,                    R_XSTORMY16_8,             xstormy16_elf_howto_table },
319   { BFD_RELOC_32_PCREL,             R_XSTORMY16_PC32,          xstormy16_elf_howto_table },
320   { BFD_RELOC_16_PCREL,             R_XSTORMY16_PC16,          xstormy16_elf_howto_table },
321   { BFD_RELOC_8_PCREL,              R_XSTORMY16_PC8,           xstormy16_elf_howto_table },
322   { BFD_RELOC_XSTORMY16_REL_12,     R_XSTORMY16_REL_12,        xstormy16_elf_howto_table },
323   { BFD_RELOC_XSTORMY16_24,	    R_XSTORMY16_24,            xstormy16_elf_howto_table },
324   { BFD_RELOC_XSTORMY16_FPTR16,	    R_XSTORMY16_FPTR16,        xstormy16_elf_howto_table },
325   { BFD_RELOC_LO16,                 R_XSTORMY16_LO16,          xstormy16_elf_howto_table },
326   { BFD_RELOC_HI16,                 R_XSTORMY16_HI16,          xstormy16_elf_howto_table },
327   { BFD_RELOC_XSTORMY16_12,         R_XSTORMY16_12,            xstormy16_elf_howto_table },
328   { BFD_RELOC_VTABLE_INHERIT,       R_XSTORMY16_GNU_VTINHERIT, xstormy16_elf_howto_table2 },
329   { BFD_RELOC_VTABLE_ENTRY,         R_XSTORMY16_GNU_VTENTRY,   xstormy16_elf_howto_table2 },
330 };
331 
332 static reloc_howto_type *
333 xstormy16_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED,
334 			     bfd_reloc_code_real_type code)
335 {
336   unsigned int i;
337 
338   for (i = ARRAY_SIZE (xstormy16_reloc_map); --i;)
339     {
340       const reloc_map * entry;
341 
342       entry = xstormy16_reloc_map + i;
343 
344       if (entry->bfd_reloc_val == code)
345 	return entry->table + (entry->xstormy16_reloc_val
346 			       - entry->table[0].type);
347     }
348 
349   return NULL;
350 }
351 
352 static reloc_howto_type *
353 xstormy16_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
354 			     const char *r_name)
355 {
356   unsigned int i;
357 
358   for (i = 0;
359        i < (sizeof (xstormy16_elf_howto_table)
360 	    / sizeof (xstormy16_elf_howto_table[0]));
361        i++)
362     if (xstormy16_elf_howto_table[i].name != NULL
363 	&& strcasecmp (xstormy16_elf_howto_table[i].name, r_name) == 0)
364       return &xstormy16_elf_howto_table[i];
365 
366   for (i = 0;
367        i < (sizeof (xstormy16_elf_howto_table2)
368 	    / sizeof (xstormy16_elf_howto_table2[0]));
369        i++)
370     if (xstormy16_elf_howto_table2[i].name != NULL
371 	&& strcasecmp (xstormy16_elf_howto_table2[i].name, r_name) == 0)
372       return &xstormy16_elf_howto_table2[i];
373 
374   return NULL;
375 }
376 
377 /* Set the howto pointer for an XSTORMY16 ELF reloc.  */
378 
379 static void
380 xstormy16_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED,
381 			      arelent * cache_ptr,
382 			      Elf_Internal_Rela * dst)
383 {
384   unsigned int r_type = ELF32_R_TYPE (dst->r_info);
385 
386   if (r_type <= (unsigned int) R_XSTORMY16_12)
387     cache_ptr->howto = &xstormy16_elf_howto_table [r_type];
388   else if (r_type - R_XSTORMY16_GNU_VTINHERIT
389 	   <= (unsigned int) R_XSTORMY16_GNU_VTENTRY)
390     cache_ptr->howto
391       = &xstormy16_elf_howto_table2 [r_type - R_XSTORMY16_GNU_VTINHERIT];
392   else
393     abort ();
394 }
395 
396 /* We support 16-bit pointers to code above 64k by generating a thunk
397    below 64k containing a JMPF instruction to the final address.  We
398    cannot, unfortunately, minimize the number of thunks unless the
399    -relax switch is given, as otherwise we have no idea where the
400    sections will fall in the address space.  */
401 
402 static bfd_boolean
403 xstormy16_elf_check_relocs (bfd *abfd,
404 			    struct bfd_link_info *info,
405 			    asection *sec,
406 			    const Elf_Internal_Rela *relocs)
407 {
408   const Elf_Internal_Rela *rel, *relend;
409   struct elf_link_hash_entry **sym_hashes;
410   Elf_Internal_Shdr *symtab_hdr;
411   bfd_vma *local_plt_offsets;
412   asection *splt;
413   bfd *dynobj;
414 
415   if (info->relocatable)
416     return TRUE;
417 
418   symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
419   sym_hashes = elf_sym_hashes (abfd);
420   local_plt_offsets = elf_local_got_offsets (abfd);
421   splt = NULL;
422   dynobj = elf_hash_table(info)->dynobj;
423 
424   relend = relocs + sec->reloc_count;
425   for (rel = relocs; rel < relend; ++rel)
426     {
427       unsigned long r_symndx;
428       struct elf_link_hash_entry *h;
429       bfd_vma *offset;
430 
431       r_symndx = ELF32_R_SYM (rel->r_info);
432       if (r_symndx < symtab_hdr->sh_info)
433 	h = NULL;
434       else
435 	{
436 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
437 	  while (h->root.type == bfd_link_hash_indirect
438 		 || h->root.type == bfd_link_hash_warning)
439 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
440 	}
441 
442       switch (ELF32_R_TYPE (rel->r_info))
443         {
444 	  /* This relocation describes a 16-bit pointer to a function.
445 	     We may need to allocate a thunk in low memory; reserve memory
446 	     for it now.  */
447 	case R_XSTORMY16_FPTR16:
448 	  if (rel->r_addend != 0)
449 	    {
450 	      (*info->callbacks->warning)
451 		(info, _("non-zero addend in @fptr reloc"), 0,
452 		 abfd, 0, 0);
453 	    }
454 
455 	  if (dynobj == NULL)
456 	    elf_hash_table (info)->dynobj = dynobj = abfd;
457 	  if (splt == NULL)
458 	    {
459 	      splt = bfd_get_linker_section (dynobj, ".plt");
460 	      if (splt == NULL)
461 		{
462 		  flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
463 				    | SEC_IN_MEMORY | SEC_LINKER_CREATED
464 				    | SEC_READONLY | SEC_CODE);
465 
466 		  splt = bfd_make_section_anyway_with_flags (dynobj, ".plt",
467 							     flags);
468 		  if (splt == NULL
469 		      || ! bfd_set_section_alignment (dynobj, splt, 1))
470 		    return FALSE;
471 		}
472 	    }
473 
474 	  if (h != NULL)
475 	    offset = &h->plt.offset;
476 	  else
477 	    {
478 	      if (local_plt_offsets == NULL)
479 		{
480 		  size_t size;
481 		  unsigned int i;
482 
483 		  size = symtab_hdr->sh_info * sizeof (bfd_vma);
484 		  local_plt_offsets = bfd_alloc (abfd, size);
485 		  if (local_plt_offsets == NULL)
486 		    return FALSE;
487 		  elf_local_got_offsets (abfd) = local_plt_offsets;
488 
489 		  for (i = 0; i < symtab_hdr->sh_info; i++)
490 		    local_plt_offsets[i] = (bfd_vma) -1;
491 		}
492 	      offset = &local_plt_offsets[r_symndx];
493 	    }
494 
495 	  if (*offset == (bfd_vma) -1)
496 	    {
497 	      *offset = splt->size;
498 	      splt->size += 4;
499 	    }
500 	  break;
501 
502 	  /* This relocation describes the C++ object vtable hierarchy.
503 	     Reconstruct it for later use during GC.  */
504         case R_XSTORMY16_GNU_VTINHERIT:
505           if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
506             return FALSE;
507           break;
508 
509 	  /* This relocation describes which C++ vtable entries are actually
510 	     used.  Record for later use during GC.  */
511         case R_XSTORMY16_GNU_VTENTRY:
512           BFD_ASSERT (h != NULL);
513           if (h != NULL
514               && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
515             return FALSE;
516           break;
517 	}
518     }
519 
520   return TRUE;
521 }
522 
523 /* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
524    is within the low 64k, remove any entry for it in the plt.  */
525 
526 struct relax_plt_data
527 {
528   asection *splt;
529   bfd_boolean *again;
530 };
531 
532 static bfd_boolean
533 xstormy16_relax_plt_check (struct elf_link_hash_entry *h, void * xdata)
534 {
535   struct relax_plt_data *data = (struct relax_plt_data *) xdata;
536 
537   if (h->plt.offset != (bfd_vma) -1)
538     {
539       bfd_vma address;
540 
541       if (h->root.type == bfd_link_hash_undefined
542 	  || h->root.type == bfd_link_hash_undefweak)
543 	address = 0;
544       else
545 	address = (h->root.u.def.section->output_section->vma
546 		   + h->root.u.def.section->output_offset
547 		   + h->root.u.def.value);
548 
549       if (address <= 0xffff)
550 	{
551 	  h->plt.offset = -1;
552 	  data->splt->size -= 4;
553 	  *data->again = TRUE;
554 	}
555     }
556 
557   return TRUE;
558 }
559 
560 /* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
561    previously had a plt entry, give it a new entry offset.  */
562 
563 static bfd_boolean
564 xstormy16_relax_plt_realloc (struct elf_link_hash_entry *h, void * xdata)
565 {
566   bfd_vma *entry = (bfd_vma *) xdata;
567 
568   if (h->plt.offset != (bfd_vma) -1)
569     {
570       h->plt.offset = *entry;
571       *entry += 4;
572     }
573 
574   return TRUE;
575 }
576 
577 static bfd_boolean
578 xstormy16_elf_relax_section (bfd *dynobj,
579 			     asection *splt,
580 			     struct bfd_link_info *info,
581 			     bfd_boolean *again)
582 {
583   struct relax_plt_data relax_plt_data;
584   bfd *ibfd;
585 
586   /* Assume nothing changes.  */
587   *again = FALSE;
588 
589   if (info->relocatable)
590     return TRUE;
591 
592   /* We only relax the .plt section at the moment.  */
593   if (dynobj != elf_hash_table (info)->dynobj
594       || strcmp (splt->name, ".plt") != 0)
595     return TRUE;
596 
597   /* Quick check for an empty plt.  */
598   if (splt->size == 0)
599     return TRUE;
600 
601   /* Map across all global symbols; see which ones happen to
602      fall in the low 64k.  */
603   relax_plt_data.splt = splt;
604   relax_plt_data.again = again;
605   elf_link_hash_traverse (elf_hash_table (info), xstormy16_relax_plt_check,
606 			  &relax_plt_data);
607 
608   /* Likewise for local symbols, though that's somewhat less convenient
609      as we have to walk the list of input bfds and swap in symbol data.  */
610   for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link_next)
611     {
612       bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
613       Elf_Internal_Shdr *symtab_hdr;
614       Elf_Internal_Sym *isymbuf = NULL;
615       unsigned int idx;
616 
617       if (! local_plt_offsets)
618 	continue;
619 
620       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
621       if (symtab_hdr->sh_info != 0)
622 	{
623 	  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
624 	  if (isymbuf == NULL)
625 	    isymbuf = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
626 					    symtab_hdr->sh_info, 0,
627 					    NULL, NULL, NULL);
628 	  if (isymbuf == NULL)
629 	    return FALSE;
630 	}
631 
632       for (idx = 0; idx < symtab_hdr->sh_info; ++idx)
633 	{
634 	  Elf_Internal_Sym *isym;
635 	  asection *tsec;
636 	  bfd_vma address;
637 
638 	  if (local_plt_offsets[idx] == (bfd_vma) -1)
639 	    continue;
640 
641 	  isym = &isymbuf[idx];
642 	  if (isym->st_shndx == SHN_UNDEF)
643 	    continue;
644 	  else if (isym->st_shndx == SHN_ABS)
645 	    tsec = bfd_abs_section_ptr;
646 	  else if (isym->st_shndx == SHN_COMMON)
647 	    tsec = bfd_com_section_ptr;
648 	  else
649 	    tsec = bfd_section_from_elf_index (ibfd, isym->st_shndx);
650 
651 	  address = (tsec->output_section->vma
652 		     + tsec->output_offset
653 		     + isym->st_value);
654 	  if (address <= 0xffff)
655 	    {
656 	      local_plt_offsets[idx] = -1;
657 	      splt->size -= 4;
658 	      *again = TRUE;
659 	    }
660 	}
661 
662       if (isymbuf != NULL
663 	  && symtab_hdr->contents != (unsigned char *) isymbuf)
664 	{
665 	  if (! info->keep_memory)
666 	    free (isymbuf);
667 	  else
668 	    {
669 	      /* Cache the symbols for elf_link_input_bfd.  */
670 	      symtab_hdr->contents = (unsigned char *) isymbuf;
671 	    }
672 	}
673     }
674 
675   /* If we changed anything, walk the symbols again to reallocate
676      .plt entry addresses.  */
677   if (*again && splt->size > 0)
678     {
679       bfd_vma entry = 0;
680 
681       elf_link_hash_traverse (elf_hash_table (info),
682 			      xstormy16_relax_plt_realloc, &entry);
683 
684       for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link_next)
685 	{
686 	  bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
687 	  unsigned int nlocals = elf_tdata (ibfd)->symtab_hdr.sh_info;
688 	  unsigned int idx;
689 
690 	  if (! local_plt_offsets)
691 	    continue;
692 
693 	  for (idx = 0; idx < nlocals; ++idx)
694 	    if (local_plt_offsets[idx] != (bfd_vma) -1)
695 	      {
696 	        local_plt_offsets[idx] = entry;
697 		entry += 4;
698 	      }
699 	}
700     }
701 
702   return TRUE;
703 }
704 
705 static bfd_boolean
706 xstormy16_elf_always_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
707 				    struct bfd_link_info *info)
708 {
709   bfd *dynobj;
710   asection *splt;
711 
712   if (info->relocatable)
713     return TRUE;
714 
715   dynobj = elf_hash_table (info)->dynobj;
716   if (dynobj == NULL)
717     return TRUE;
718 
719   splt = bfd_get_linker_section (dynobj, ".plt");
720   BFD_ASSERT (splt != NULL);
721 
722   splt->contents = bfd_zalloc (dynobj, splt->size);
723   if (splt->contents == NULL)
724     return FALSE;
725 
726   return TRUE;
727 }
728 
729 /* Relocate an XSTORMY16 ELF section.
730 
731    The RELOCATE_SECTION function is called by the new ELF backend linker
732    to handle the relocations for a section.
733 
734    The relocs are always passed as Rela structures; if the section
735    actually uses Rel structures, the r_addend field will always be
736    zero.
737 
738    This function is responsible for adjusting the section contents as
739    necessary, and (if using Rela relocs and generating a relocatable
740    output file) adjusting the reloc addend as necessary.
741 
742    This function does not have to worry about setting the reloc
743    address or the reloc symbol index.
744 
745    LOCAL_SYMS is a pointer to the swapped in local symbols.
746 
747    LOCAL_SECTIONS is an array giving the section in the input file
748    corresponding to the st_shndx field of each local symbol.
749 
750    The global hash table entry for the global symbols can be found
751    via elf_sym_hashes (input_bfd).
752 
753    When generating relocatable output, this function must handle
754    STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
755    going to be the section symbol corresponding to the output
756    section, which means that the addend must be adjusted
757    accordingly.  */
758 
759 static bfd_boolean
760 xstormy16_elf_relocate_section (bfd *                   output_bfd ATTRIBUTE_UNUSED,
761 				struct bfd_link_info *  info,
762 				bfd *                   input_bfd,
763 				asection *              input_section,
764 				bfd_byte *              contents,
765 				Elf_Internal_Rela *     relocs,
766 				Elf_Internal_Sym *      local_syms,
767 				asection **             local_sections)
768 {
769   Elf_Internal_Shdr *           symtab_hdr;
770   struct elf_link_hash_entry ** sym_hashes;
771   Elf_Internal_Rela *           rel;
772   Elf_Internal_Rela *           relend;
773   bfd *dynobj;
774   asection *splt;
775 
776   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
777   sym_hashes = elf_sym_hashes (input_bfd);
778   relend     = relocs + input_section->reloc_count;
779 
780   dynobj = elf_hash_table (info)->dynobj;
781   splt = NULL;
782   if (dynobj != NULL)
783     splt = bfd_get_linker_section (dynobj, ".plt");
784 
785   for (rel = relocs; rel < relend; rel ++)
786     {
787       reloc_howto_type *           howto;
788       unsigned long                r_symndx;
789       Elf_Internal_Sym *           sym;
790       asection *                   sec;
791       struct elf_link_hash_entry * h;
792       bfd_vma                      relocation;
793       bfd_reloc_status_type        r;
794       const char *                 name = NULL;
795       int                          r_type;
796 
797       r_type = ELF32_R_TYPE (rel->r_info);
798 
799       if (   r_type == R_XSTORMY16_GNU_VTINHERIT
800 	  || r_type == R_XSTORMY16_GNU_VTENTRY)
801 	continue;
802 
803       r_symndx = ELF32_R_SYM (rel->r_info);
804       howto  = xstormy16_elf_howto_table + ELF32_R_TYPE (rel->r_info);
805       h      = NULL;
806       sym    = NULL;
807       sec    = NULL;
808 
809       if (r_symndx < symtab_hdr->sh_info)
810 	{
811 	  sym = local_syms + r_symndx;
812 	  sec = local_sections [r_symndx];
813 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
814 	}
815       else
816 	{
817 	  bfd_boolean unresolved_reloc, warned;
818 
819 	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
820 				   r_symndx, symtab_hdr, sym_hashes,
821 				   h, sec, relocation,
822 				   unresolved_reloc, warned);
823 	}
824 
825       if (sec != NULL && discarded_section (sec))
826 	RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
827 					 rel, 1, relend, howto, 0, contents);
828 
829       if (info->relocatable)
830 	continue;
831 
832       if (h != NULL)
833 	name = h->root.root.string;
834       else
835 	{
836 	  name = (bfd_elf_string_from_elf_section
837 		  (input_bfd, symtab_hdr->sh_link, sym->st_name));
838 	  if (name == NULL || *name == '\0')
839 	    name = bfd_section_name (input_bfd, sec);
840 	}
841 
842       switch (ELF32_R_TYPE (rel->r_info))
843 	{
844 	case R_XSTORMY16_24:
845 	  {
846 	    bfd_vma reloc = relocation + rel->r_addend;
847 	    unsigned int x;
848 
849 	    x = bfd_get_32 (input_bfd, contents + rel->r_offset);
850 	    x &= 0x0000ff00;
851 	    x |= reloc & 0xff;
852 	    x |= (reloc << 8) & 0xffff0000;
853 	    bfd_put_32 (input_bfd, x, contents + rel->r_offset);
854 
855 	    if (reloc & ~0xffffff)
856 	      r = bfd_reloc_overflow;
857 	    else
858 	      r = bfd_reloc_ok;
859 	    break;
860 	  }
861 
862 	case R_XSTORMY16_FPTR16:
863 	  {
864 	    bfd_vma *plt_offset;
865 
866 	    if (h != NULL)
867 	      plt_offset = &h->plt.offset;
868 	    else
869 	      plt_offset = elf_local_got_offsets (input_bfd) + r_symndx;
870 
871 	    if (relocation <= 0xffff)
872 	      {
873 	        /* If the symbol is in range for a 16-bit address, we should
874 		   have deallocated the plt entry in relax_section.  */
875 	        BFD_ASSERT (*plt_offset == (bfd_vma) -1);
876 	      }
877 	    else
878 	      {
879 		/* If the symbol is out of range for a 16-bit address,
880 		   we must have allocated a plt entry.  */
881 		BFD_ASSERT (*plt_offset != (bfd_vma) -1);
882 
883 		/* If this is the first time we've processed this symbol,
884 		   fill in the plt entry with the correct symbol address.  */
885 		if ((*plt_offset & 1) == 0)
886 		  {
887 		    unsigned int x;
888 
889 		    x = 0x00000200;  /* jmpf */
890 		    x |= relocation & 0xff;
891 		    x |= (relocation << 8) & 0xffff0000;
892 		    bfd_put_32 (input_bfd, x, splt->contents + *plt_offset);
893 		    *plt_offset |= 1;
894 		  }
895 
896 		relocation = (splt->output_section->vma
897 			      + splt->output_offset
898 			      + (*plt_offset & -2));
899 	      }
900 	    r = _bfd_final_link_relocate (howto, input_bfd, input_section,
901 					  contents, rel->r_offset,
902 					  relocation, 0);
903 	    break;
904 	  }
905 
906 	default:
907 	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
908 					contents, rel->r_offset,
909 					relocation, rel->r_addend);
910 	  break;
911 	}
912 
913       if (r != bfd_reloc_ok)
914 	{
915 	  const char * msg = NULL;
916 
917 	  switch (r)
918 	    {
919 	    case bfd_reloc_overflow:
920 	      r = info->callbacks->reloc_overflow
921 		(info, (h ? &h->root : NULL), name, howto->name,
922 		 (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
923 	      break;
924 
925 	    case bfd_reloc_undefined:
926 	      r = info->callbacks->undefined_symbol
927 		(info, name, input_bfd, input_section, rel->r_offset,
928 		 TRUE);
929 	      break;
930 
931 	    case bfd_reloc_outofrange:
932 	      msg = _("internal error: out of range error");
933 	      break;
934 
935 	    case bfd_reloc_notsupported:
936 	      msg = _("internal error: unsupported relocation error");
937 	      break;
938 
939 	    case bfd_reloc_dangerous:
940 	      msg = _("internal error: dangerous relocation");
941 	      break;
942 
943 	    default:
944 	      msg = _("internal error: unknown error");
945 	      break;
946 	    }
947 
948 	  if (msg)
949 	    r = info->callbacks->warning
950 	      (info, msg, name, input_bfd, input_section, rel->r_offset);
951 
952 	  if (! r)
953 	    return FALSE;
954 	}
955     }
956 
957   return TRUE;
958 }
959 
960 /* This must exist if dynobj is ever set.  */
961 
962 static bfd_boolean
963 xstormy16_elf_finish_dynamic_sections (bfd *abfd ATTRIBUTE_UNUSED,
964 				       struct bfd_link_info *info)
965 {
966   bfd *dynobj;
967   asection *splt;
968 
969   /* As an extra sanity check, verify that all plt entries have
970      been filled in.  */
971 
972   if ((dynobj = elf_hash_table (info)->dynobj) != NULL
973       && (splt = bfd_get_linker_section (dynobj, ".plt")) != NULL)
974     {
975       bfd_byte *contents = splt->contents;
976       unsigned int i, size = splt->size;
977 
978       for (i = 0; i < size; i += 4)
979 	{
980 	  unsigned int x = bfd_get_32 (dynobj, contents + i);
981 
982 	  BFD_ASSERT (x != 0);
983 	}
984     }
985 
986   return TRUE;
987 }
988 
989 /* Return the section that should be marked against GC for a given
990    relocation.  */
991 
992 static asection *
993 xstormy16_elf_gc_mark_hook (asection *sec,
994 			    struct bfd_link_info *info,
995 			    Elf_Internal_Rela *rel,
996 			    struct elf_link_hash_entry *h,
997 			    Elf_Internal_Sym *sym)
998 {
999   if (h != NULL)
1000     switch (ELF32_R_TYPE (rel->r_info))
1001       {
1002       case R_XSTORMY16_GNU_VTINHERIT:
1003       case R_XSTORMY16_GNU_VTENTRY:
1004 	return NULL;
1005       }
1006 
1007   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
1008 }
1009 
1010 #define ELF_ARCH		bfd_arch_xstormy16
1011 #define ELF_MACHINE_CODE	EM_XSTORMY16
1012 #define ELF_MAXPAGESIZE		0x100
1013 
1014 #define TARGET_LITTLE_SYM       bfd_elf32_xstormy16_vec
1015 #define TARGET_LITTLE_NAME	"elf32-xstormy16"
1016 
1017 #define elf_info_to_howto_rel			NULL
1018 #define elf_info_to_howto			xstormy16_info_to_howto_rela
1019 #define elf_backend_relocate_section		xstormy16_elf_relocate_section
1020 #define elf_backend_gc_mark_hook		xstormy16_elf_gc_mark_hook
1021 #define elf_backend_check_relocs                xstormy16_elf_check_relocs
1022 #define elf_backend_always_size_sections \
1023   xstormy16_elf_always_size_sections
1024 #define elf_backend_omit_section_dynsym \
1025   ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
1026 #define elf_backend_finish_dynamic_sections \
1027   xstormy16_elf_finish_dynamic_sections
1028 
1029 #define elf_backend_can_gc_sections		1
1030 #define elf_backend_rela_normal			1
1031 
1032 #define bfd_elf32_bfd_reloc_type_lookup		xstormy16_reloc_type_lookup
1033 #define bfd_elf32_bfd_reloc_name_lookup \
1034   xstormy16_reloc_name_lookup
1035 #define bfd_elf32_bfd_relax_section		xstormy16_elf_relax_section
1036 
1037 #include "elf32-target.h"
1038