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