xref: /netbsd-src/external/gpl3/binutils/dist/bfd/elf32-xstormy16.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Xstormy16-specific support for 32-bit ELF.
2    Copyright (C) 2000-2024 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
xstormy16_elf_24_reloc(bfd * abfd,arelent * reloc_entry,asymbol * symbol,void * data,asection * input_section,bfd * output_bfd,char ** error_message ATTRIBUTE_UNUSED)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 	 0,			/* size */
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 	 4,			/* size */
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 	 2,			/* size */
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 	 1,			/* size */
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 	 4,			/* size */
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 	 2,			/* size */
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 	 1,			/* size */
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 	 2,			/* size */
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 	 4,			/* size */
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 	 2,			/* size */
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 	 2,			/* size */
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 	 2,			/* size */
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 	 2,			/* size */
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 	 4,			/* size */
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 	 4,			/* size */
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 *
xstormy16_reloc_type_lookup(bfd * abfd ATTRIBUTE_UNUSED,bfd_reloc_code_real_type code)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 *
xstormy16_reloc_name_lookup(bfd * abfd ATTRIBUTE_UNUSED,const char * r_name)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 bool
xstormy16_info_to_howto_rela(bfd * abfd,arelent * cache_ptr,Elf_Internal_Rela * dst)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 bool
xstormy16_elf_check_relocs(bfd * abfd,struct bfd_link_info * info,asection * sec,const Elf_Internal_Rela * relocs)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 (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 	  if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
517 	    return false;
518 	  break;
519 	}
520     }
521 
522   return true;
523 }
524 
525 /* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
526    is within the low 64k, remove any entry for it in the plt.  */
527 
528 struct relax_plt_data
529 {
530   asection *splt;
531   bool *again;
532 };
533 
534 static bool
xstormy16_relax_plt_check(struct elf_link_hash_entry * h,void * xdata)535 xstormy16_relax_plt_check (struct elf_link_hash_entry *h, void * xdata)
536 {
537   struct relax_plt_data *data = (struct relax_plt_data *) xdata;
538 
539   if (h->plt.offset != (bfd_vma) -1)
540     {
541       bfd_vma address;
542 
543       if (h->root.type == bfd_link_hash_undefined
544 	  || h->root.type == bfd_link_hash_undefweak)
545 	address = 0;
546       else
547 	address = (h->root.u.def.section->output_section->vma
548 		   + h->root.u.def.section->output_offset
549 		   + h->root.u.def.value);
550 
551       if (address <= 0xffff)
552 	{
553 	  h->plt.offset = -1;
554 	  data->splt->size -= 4;
555 	  *data->again = true;
556 	}
557     }
558 
559   return true;
560 }
561 
562 /* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
563    previously had a plt entry, give it a new entry offset.  */
564 
565 static bool
xstormy16_relax_plt_realloc(struct elf_link_hash_entry * h,void * xdata)566 xstormy16_relax_plt_realloc (struct elf_link_hash_entry *h, void * xdata)
567 {
568   bfd_vma *entry = (bfd_vma *) xdata;
569 
570   if (h->plt.offset != (bfd_vma) -1)
571     {
572       h->plt.offset = *entry;
573       *entry += 4;
574     }
575 
576   return true;
577 }
578 
579 static bool
xstormy16_elf_relax_section(bfd * dynobj,asection * splt,struct bfd_link_info * info,bool * again)580 xstormy16_elf_relax_section (bfd *dynobj,
581 			     asection *splt,
582 			     struct bfd_link_info *info,
583 			     bool *again)
584 {
585   struct relax_plt_data relax_plt_data;
586   bfd *ibfd;
587 
588   /* Assume nothing changes.  */
589   *again = false;
590 
591   if (bfd_link_relocatable (info)
592       || !is_elf_hash_table (info->hash))
593     return true;
594 
595   /* We only relax the .plt section at the moment.  */
596   if (dynobj != elf_hash_table (info)->dynobj
597       || strcmp (splt->name, ".plt") != 0)
598     return true;
599 
600   /* Quick check for an empty plt.  */
601   if (splt->size == 0)
602     return true;
603 
604   /* Map across all global symbols; see which ones happen to
605      fall in the low 64k.  */
606   relax_plt_data.splt = splt;
607   relax_plt_data.again = again;
608   elf_link_hash_traverse (elf_hash_table (info), xstormy16_relax_plt_check,
609 			  &relax_plt_data);
610 
611   /* Likewise for local symbols, though that's somewhat less convenient
612      as we have to walk the list of input bfds and swap in symbol data.  */
613   for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link.next)
614     {
615       bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
616       Elf_Internal_Shdr *symtab_hdr;
617       Elf_Internal_Sym *isymbuf = NULL;
618       unsigned int idx;
619 
620       if (! local_plt_offsets)
621 	continue;
622 
623       symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
624       if (symtab_hdr->sh_info != 0)
625 	{
626 	  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
627 	  if (isymbuf == NULL)
628 	    isymbuf = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
629 					    symtab_hdr->sh_info, 0,
630 					    NULL, NULL, NULL);
631 	  if (isymbuf == NULL)
632 	    return false;
633 	}
634 
635       for (idx = 0; idx < symtab_hdr->sh_info; ++idx)
636 	{
637 	  Elf_Internal_Sym *isym;
638 	  asection *tsec;
639 	  bfd_vma address;
640 
641 	  if (local_plt_offsets[idx] == (bfd_vma) -1)
642 	    continue;
643 
644 	  isym = &isymbuf[idx];
645 	  if (isym->st_shndx == SHN_UNDEF)
646 	    continue;
647 	  else if (isym->st_shndx == SHN_ABS)
648 	    tsec = bfd_abs_section_ptr;
649 	  else if (isym->st_shndx == SHN_COMMON)
650 	    tsec = bfd_com_section_ptr;
651 	  else
652 	    tsec = bfd_section_from_elf_index (ibfd, isym->st_shndx);
653 
654 	  address = (tsec->output_section->vma
655 		     + tsec->output_offset
656 		     + isym->st_value);
657 	  if (address <= 0xffff)
658 	    {
659 	      local_plt_offsets[idx] = -1;
660 	      splt->size -= 4;
661 	      *again = true;
662 	    }
663 	}
664 
665       if (isymbuf != NULL
666 	  && symtab_hdr->contents != (unsigned char *) isymbuf)
667 	{
668 	  if (! info->keep_memory)
669 	    free (isymbuf);
670 	  else
671 	    {
672 	      /* Cache the symbols for elf_link_input_bfd.  */
673 	      symtab_hdr->contents = (unsigned char *) isymbuf;
674 	    }
675 	}
676     }
677 
678   /* If we changed anything, walk the symbols again to reallocate
679      .plt entry addresses.  */
680   if (*again && splt->size > 0)
681     {
682       bfd_vma entry = 0;
683 
684       elf_link_hash_traverse (elf_hash_table (info),
685 			      xstormy16_relax_plt_realloc, &entry);
686 
687       for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link.next)
688 	{
689 	  bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
690 	  unsigned int nlocals = elf_tdata (ibfd)->symtab_hdr.sh_info;
691 	  unsigned int idx;
692 
693 	  if (! local_plt_offsets)
694 	    continue;
695 
696 	  for (idx = 0; idx < nlocals; ++idx)
697 	    if (local_plt_offsets[idx] != (bfd_vma) -1)
698 	      {
699 		local_plt_offsets[idx] = entry;
700 		entry += 4;
701 	      }
702 	}
703     }
704 
705   return true;
706 }
707 
708 static bool
xstormy16_elf_always_size_sections(bfd * output_bfd ATTRIBUTE_UNUSED,struct bfd_link_info * info)709 xstormy16_elf_always_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
710 				    struct bfd_link_info *info)
711 {
712   bfd *dynobj;
713   asection *splt;
714 
715   if (bfd_link_relocatable (info))
716     return true;
717 
718   dynobj = elf_hash_table (info)->dynobj;
719   if (dynobj == NULL)
720     return true;
721 
722   splt = elf_hash_table (info)->splt;
723   BFD_ASSERT (splt != NULL);
724 
725   splt->contents = bfd_zalloc (dynobj, splt->size);
726   if (splt->contents == NULL)
727     return false;
728 
729   return true;
730 }
731 
732 /* Relocate an XSTORMY16 ELF section.
733 
734    The RELOCATE_SECTION function is called by the new ELF backend linker
735    to handle the relocations for a section.
736 
737    The relocs are always passed as Rela structures; if the section
738    actually uses Rel structures, the r_addend field will always be
739    zero.
740 
741    This function is responsible for adjusting the section contents as
742    necessary, and (if using Rela relocs and generating a relocatable
743    output file) adjusting the reloc addend as necessary.
744 
745    This function does not have to worry about setting the reloc
746    address or the reloc symbol index.
747 
748    LOCAL_SYMS is a pointer to the swapped in local symbols.
749 
750    LOCAL_SECTIONS is an array giving the section in the input file
751    corresponding to the st_shndx field of each local symbol.
752 
753    The global hash table entry for the global symbols can be found
754    via elf_sym_hashes (input_bfd).
755 
756    When generating relocatable output, this function must handle
757    STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
758    going to be the section symbol corresponding to the output
759    section, which means that the addend must be adjusted
760    accordingly.  */
761 
762 static int
xstormy16_elf_relocate_section(bfd * output_bfd ATTRIBUTE_UNUSED,struct bfd_link_info * info,bfd * input_bfd,asection * input_section,bfd_byte * contents,Elf_Internal_Rela * relocs,Elf_Internal_Sym * local_syms,asection ** local_sections)763 xstormy16_elf_relocate_section (bfd *			output_bfd ATTRIBUTE_UNUSED,
764 				struct bfd_link_info *	info,
765 				bfd *			input_bfd,
766 				asection *		input_section,
767 				bfd_byte *		contents,
768 				Elf_Internal_Rela *	relocs,
769 				Elf_Internal_Sym *	local_syms,
770 				asection **		local_sections)
771 {
772   Elf_Internal_Shdr *		symtab_hdr;
773   struct elf_link_hash_entry ** sym_hashes;
774   Elf_Internal_Rela *		rel;
775   Elf_Internal_Rela *		relend;
776   asection *splt;
777 
778   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
779   sym_hashes = elf_sym_hashes (input_bfd);
780   relend     = relocs + input_section->reloc_count;
781 
782   splt = elf_hash_table (info)->splt;
783 
784   for (rel = relocs; rel < relend; rel ++)
785     {
786       reloc_howto_type *	   howto;
787       unsigned long		   r_symndx;
788       Elf_Internal_Sym *	   sym;
789       asection *		   sec;
790       struct elf_link_hash_entry * h;
791       bfd_vma			   relocation;
792       bfd_reloc_status_type	   r;
793       const char *		   name = NULL;
794       int			   r_type;
795 
796       r_type = ELF32_R_TYPE (rel->r_info);
797 
798       if (   r_type == R_XSTORMY16_GNU_VTINHERIT
799 	  || r_type == R_XSTORMY16_GNU_VTENTRY)
800 	continue;
801 
802       r_symndx = ELF32_R_SYM (rel->r_info);
803       howto  = xstormy16_elf_howto_table + ELF32_R_TYPE (rel->r_info);
804       h      = NULL;
805       sym    = NULL;
806       sec    = NULL;
807 
808       if (r_symndx < symtab_hdr->sh_info)
809 	{
810 	  sym = local_syms + r_symndx;
811 	  sec = local_sections [r_symndx];
812 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
813 	}
814       else
815 	{
816 	  bool unresolved_reloc, warned, ignored;
817 
818 	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
819 				   r_symndx, symtab_hdr, sym_hashes,
820 				   h, sec, relocation,
821 				   unresolved_reloc, warned, ignored);
822 	}
823 
824       if (sec != NULL && discarded_section (sec))
825 	RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
826 					 rel, 1, relend, howto, 0, contents);
827 
828       if (bfd_link_relocatable (info))
829 	continue;
830 
831       if (h != NULL)
832 	name = h->root.root.string;
833       else
834 	{
835 	  name = (bfd_elf_string_from_elf_section
836 		  (input_bfd, symtab_hdr->sh_link, sym->st_name));
837 	  if (name == NULL || *name == '\0')
838 	    name = bfd_section_name (sec);
839 	}
840 
841       switch (ELF32_R_TYPE (rel->r_info))
842 	{
843 	case R_XSTORMY16_24:
844 	  {
845 	    bfd_vma reloc = relocation + rel->r_addend;
846 	    unsigned int x;
847 
848 	    x = bfd_get_32 (input_bfd, contents + rel->r_offset);
849 	    x &= 0x0000ff00;
850 	    x |= reloc & 0xff;
851 	    x |= (reloc << 8) & 0xffff0000;
852 	    bfd_put_32 (input_bfd, x, contents + rel->r_offset);
853 
854 	    if (reloc & ~0xffffff)
855 	      r = bfd_reloc_overflow;
856 	    else
857 	      r = bfd_reloc_ok;
858 	    break;
859 	  }
860 
861 	case R_XSTORMY16_FPTR16:
862 	  {
863 	    bfd_vma *plt_offset;
864 
865 	    if (h != NULL)
866 	      plt_offset = &h->plt.offset;
867 	    else
868 	      plt_offset = elf_local_got_offsets (input_bfd) + r_symndx;
869 
870 	    if (relocation <= 0xffff)
871 	      {
872 		/* If the symbol is in range for a 16-bit address, we should
873 		   have deallocated the plt entry in relax_section.  */
874 		BFD_ASSERT (*plt_offset == (bfd_vma) -1);
875 	      }
876 	    else
877 	      {
878 		/* If the symbol is out of range for a 16-bit address,
879 		   we must have allocated a plt entry.  */
880 		BFD_ASSERT (*plt_offset != (bfd_vma) -1);
881 
882 		/* If this is the first time we've processed this symbol,
883 		   fill in the plt entry with the correct symbol address.  */
884 		if ((*plt_offset & 1) == 0)
885 		  {
886 		    unsigned int x;
887 
888 		    x = 0x00000200;  /* jmpf */
889 		    x |= relocation & 0xff;
890 		    x |= (relocation << 8) & 0xffff0000;
891 		    bfd_put_32 (input_bfd, x, splt->contents + *plt_offset);
892 		    *plt_offset |= 1;
893 		  }
894 
895 		relocation = (splt->output_section->vma
896 			      + splt->output_offset
897 			      + (*plt_offset & -2));
898 	      }
899 	    r = _bfd_final_link_relocate (howto, input_bfd, input_section,
900 					  contents, rel->r_offset,
901 					  relocation, 0);
902 	    break;
903 	  }
904 
905 	default:
906 	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
907 					contents, rel->r_offset,
908 					relocation, rel->r_addend);
909 	  break;
910 	}
911 
912       if (r != bfd_reloc_ok)
913 	{
914 	  const char * msg = NULL;
915 
916 	  switch (r)
917 	    {
918 	    case bfd_reloc_overflow:
919 	      (*info->callbacks->reloc_overflow)
920 		(info, (h ? &h->root : NULL), name, howto->name,
921 		 (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
922 	      break;
923 
924 	    case bfd_reloc_undefined:
925 	      (*info->callbacks->undefined_symbol)
926 		(info, name, input_bfd, input_section, rel->r_offset, true);
927 	      break;
928 
929 	    case bfd_reloc_outofrange:
930 	      msg = _("internal error: out of range error");
931 	      break;
932 
933 	    case bfd_reloc_notsupported:
934 	      msg = _("internal error: unsupported relocation error");
935 	      break;
936 
937 	    case bfd_reloc_dangerous:
938 	      msg = _("internal error: dangerous relocation");
939 	      break;
940 
941 	    default:
942 	      msg = _("internal error: unknown error");
943 	      break;
944 	    }
945 
946 	  if (msg)
947 	    (*info->callbacks->warning) (info, msg, name, input_bfd,
948 					 input_section, rel->r_offset);
949 	}
950     }
951 
952   return true;
953 }
954 
955 /* This must exist if dynobj is ever set.  */
956 
957 static bool
xstormy16_elf_finish_dynamic_sections(bfd * abfd ATTRIBUTE_UNUSED,struct bfd_link_info * info)958 xstormy16_elf_finish_dynamic_sections (bfd *abfd ATTRIBUTE_UNUSED,
959 				       struct bfd_link_info *info)
960 {
961   bfd *dynobj = elf_hash_table (info)->dynobj;
962   asection *splt = elf_hash_table (info)->splt;
963 
964   /* As an extra sanity check, verify that all plt entries have
965      been filled in.  */
966 
967   if (dynobj != NULL && splt != NULL)
968     {
969       bfd_byte *contents = splt->contents;
970       unsigned int i, size = splt->size;
971 
972       for (i = 0; i < size; i += 4)
973 	{
974 	  unsigned int x = bfd_get_32 (dynobj, contents + i);
975 
976 	  BFD_ASSERT (x != 0);
977 	}
978     }
979 
980   return true;
981 }
982 
983 /* Return the section that should be marked against GC for a given
984    relocation.  */
985 
986 static asection *
xstormy16_elf_gc_mark_hook(asection * sec,struct bfd_link_info * info,Elf_Internal_Rela * rel,struct elf_link_hash_entry * h,Elf_Internal_Sym * sym)987 xstormy16_elf_gc_mark_hook (asection *sec,
988 			    struct bfd_link_info *info,
989 			    Elf_Internal_Rela *rel,
990 			    struct elf_link_hash_entry *h,
991 			    Elf_Internal_Sym *sym)
992 {
993   if (h != NULL)
994     switch (ELF32_R_TYPE (rel->r_info))
995       {
996       case R_XSTORMY16_GNU_VTINHERIT:
997       case R_XSTORMY16_GNU_VTENTRY:
998 	return NULL;
999       }
1000 
1001   return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
1002 }
1003 
1004 #define ELF_ARCH		bfd_arch_xstormy16
1005 #define ELF_MACHINE_CODE	EM_XSTORMY16
1006 #define ELF_MAXPAGESIZE		0x100
1007 
1008 #define TARGET_LITTLE_SYM       xstormy16_elf32_vec
1009 #define TARGET_LITTLE_NAME	"elf32-xstormy16"
1010 
1011 #define elf_info_to_howto_rel			NULL
1012 #define elf_info_to_howto			xstormy16_info_to_howto_rela
1013 #define elf_backend_relocate_section		xstormy16_elf_relocate_section
1014 #define elf_backend_gc_mark_hook		xstormy16_elf_gc_mark_hook
1015 #define elf_backend_check_relocs		xstormy16_elf_check_relocs
1016 #define elf_backend_always_size_sections \
1017   xstormy16_elf_always_size_sections
1018 #define elf_backend_omit_section_dynsym \
1019   _bfd_elf_omit_section_dynsym_all
1020 #define elf_backend_finish_dynamic_sections \
1021   xstormy16_elf_finish_dynamic_sections
1022 
1023 #define elf_backend_can_gc_sections		1
1024 #define elf_backend_rela_normal			1
1025 
1026 #define bfd_elf32_bfd_reloc_type_lookup		xstormy16_reloc_type_lookup
1027 #define bfd_elf32_bfd_reloc_name_lookup \
1028   xstormy16_reloc_name_lookup
1029 #define bfd_elf32_bfd_relax_section		xstormy16_elf_relax_section
1030 
1031 #include "elf32-target.h"
1032