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
30static bfd_reloc_status_type
31xstormy16_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
71static 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
269static 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
305typedef 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
312static 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
331static reloc_howto_type *
332xstormy16_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
351static reloc_howto_type *
352xstormy16_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
378static bool
379xstormy16_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
409static bool
410xstormy16_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
528struct relax_plt_data
529{
530  asection *splt;
531  bool *again;
532};
533
534static bool
535xstormy16_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
565static bool
566xstormy16_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
579static bool
580xstormy16_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
708static bool
709xstormy16_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
762static int
763xstormy16_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
957static bool
958xstormy16_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
986static asection *
987xstormy16_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