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