1258945Sroberto/* XSTORMY16-specific support for 32-bit ELF.
2258945Sroberto   Copyright 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
3258945Sroberto
4258945SrobertoThis file is part of BFD, the Binary File Descriptor library.
5258945Sroberto
6258945SrobertoThis program is free software; you can redistribute it and/or modify
7258945Srobertoit under the terms of the GNU General Public License as published by
8258945Srobertothe Free Software Foundation; either version 2 of the License, or
9258945Sroberto(at your option) any later version.
10258945Sroberto
11258945SrobertoThis program is distributed in the hope that it will be useful,
12258945Srobertobut WITHOUT ANY WARRANTY; without even the implied warranty of
13258945SrobertoMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14258945SrobertoGNU General Public License for more details.
15258945Sroberto
16258945SrobertoYou should have received a copy of the GNU General Public License
17258945Srobertoalong with this program; if not, write to the Free Software
18280849ScyFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19258945Sroberto
20258945Sroberto#include "bfd.h"
21258945Sroberto#include "sysdep.h"
22258945Sroberto#include "libbfd.h"
23258945Sroberto#include "elf-bfd.h"
24258945Sroberto#include "elf/xstormy16.h"
25258945Sroberto#include "libiberty.h"
26258945Sroberto
27258945Sroberto/* Forward declarations.  */
28258945Srobertostatic reloc_howto_type * xstormy16_reloc_type_lookup
29258945Sroberto  PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
30258945Srobertostatic void xstormy16_info_to_howto_rela
31258945Sroberto  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
32258945Srobertostatic bfd_reloc_status_type xstormy16_elf_24_reloc
33258945Sroberto  PARAMS ((bfd *abfd, arelent *reloc_entry, asymbol *symbol,
34258945Sroberto	   PTR data, asection *input_section, bfd *output_bfd,
35258945Sroberto	   char **error_message));
36258945Srobertostatic bfd_boolean xstormy16_elf_check_relocs
37258945Sroberto  PARAMS ((bfd *, struct bfd_link_info *, asection *,
38258945Sroberto	   const Elf_Internal_Rela *));
39258945Srobertostatic bfd_boolean xstormy16_relax_plt_check
40258945Sroberto  PARAMS ((struct elf_link_hash_entry *, PTR));
41258945Srobertostatic bfd_boolean xstormy16_relax_plt_realloc
42258945Sroberto  PARAMS ((struct elf_link_hash_entry *, PTR));
43258945Srobertostatic bfd_boolean xstormy16_elf_relax_section
44258945Sroberto  PARAMS ((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
45258945Sroberto	   bfd_boolean *again));
46258945Srobertostatic bfd_boolean xstormy16_elf_always_size_sections
47258945Sroberto  PARAMS ((bfd *, struct bfd_link_info *));
48258945Srobertostatic bfd_boolean xstormy16_elf_relocate_section
49258945Sroberto  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
50258945Sroberto	   Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
51258945Srobertostatic bfd_boolean xstormy16_elf_finish_dynamic_sections
52258945Sroberto  PARAMS((bfd *, struct bfd_link_info *));
53258945Srobertostatic bfd_boolean xstormy16_elf_gc_sweep_hook
54258945Sroberto  PARAMS ((bfd *, struct bfd_link_info *, asection *,
55258945Sroberto	   const Elf_Internal_Rela *));
56258945Srobertostatic asection * xstormy16_elf_gc_mark_hook
57258945Sroberto  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
58258945Sroberto	   struct elf_link_hash_entry *, Elf_Internal_Sym *));
59258945Sroberto
60258945Srobertostatic reloc_howto_type xstormy16_elf_howto_table [] =
61258945Sroberto{
62258945Sroberto  /* This reloc does nothing.  */
63258945Sroberto  HOWTO (R_XSTORMY16_NONE,	/* type */
64258945Sroberto	 0,			/* rightshift */
65258945Sroberto	 2,			/* size (0 = byte, 1 = short, 2 = long) */
66258945Sroberto	 32,			/* bitsize */
67258945Sroberto	 FALSE,			/* pc_relative */
68258945Sroberto	 0,			/* bitpos */
69258945Sroberto	 complain_overflow_bitfield, /* complain_on_overflow */
70258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
71258945Sroberto	 "R_XSTORMY16_NONE",	/* name */
72258945Sroberto	 FALSE,			/* partial_inplace */
73258945Sroberto	 0,			/* src_mask */
74258945Sroberto	 0,			/* dst_mask */
75258945Sroberto	 FALSE),		/* pcrel_offset */
76258945Sroberto
77258945Sroberto  /* A 32 bit absolute relocation.  */
78258945Sroberto  HOWTO (R_XSTORMY16_32,	/* type */
79258945Sroberto	 0,			/* rightshift */
80258945Sroberto	 2,			/* size (0 = byte, 1 = short, 2 = long) */
81258945Sroberto	 32,			/* bitsize */
82258945Sroberto	 FALSE,			/* pc_relative */
83258945Sroberto	 0,			/* bitpos */
84258945Sroberto	 complain_overflow_dont, /* complain_on_overflow */
85258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
86258945Sroberto	 "R_XSTORMY16_32",	/* name */
87258945Sroberto	 FALSE,			/* partial_inplace */
88258945Sroberto	 0,			/* src_mask */
89258945Sroberto	 0xffffffff,		/* dst_mask */
90258945Sroberto	 FALSE),		/* pcrel_offset */
91258945Sroberto
92258945Sroberto  /* A 16 bit absolute relocation.  */
93258945Sroberto  HOWTO (R_XSTORMY16_16,	/* type */
94258945Sroberto	 0,			/* rightshift */
95258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
96258945Sroberto	 16,			/* bitsize */
97258945Sroberto	 FALSE,			/* pc_relative */
98258945Sroberto	 0,			/* bitpos */
99258945Sroberto	 complain_overflow_bitfield, /* complain_on_overflow */
100258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
101258945Sroberto	 "R_XSTORMY16_16",	/* name */
102258945Sroberto	 FALSE,			/* partial_inplace */
103258945Sroberto	 0,			/* src_mask */
104258945Sroberto	 0xffff,		/* dst_mask */
105258945Sroberto	 FALSE),		/* pcrel_offset */
106258945Sroberto
107258945Sroberto  /* An 8 bit absolute relocation.  */
108258945Sroberto  HOWTO (R_XSTORMY16_8,		/* type */
109258945Sroberto	 0,			/* rightshift */
110258945Sroberto	 0,			/* size (0 = byte, 1 = short, 2 = long) */
111258945Sroberto	 8,			/* bitsize */
112258945Sroberto	 FALSE,			/* pc_relative */
113258945Sroberto	 0,			/* bitpos */
114258945Sroberto	 complain_overflow_unsigned, /* complain_on_overflow */
115258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
116258945Sroberto	 "R_XSTORMY16_8",	/* name */
117258945Sroberto	 FALSE,			/* partial_inplace */
118258945Sroberto	 0,			/* src_mask */
119258945Sroberto	 0xff,			/* dst_mask */
120258945Sroberto	 FALSE),		/* pcrel_offset */
121258945Sroberto
122258945Sroberto  /* A 32 bit pc-relative relocation.  */
123258945Sroberto  HOWTO (R_XSTORMY16_PC32,	/* type */
124258945Sroberto	 0,			/* rightshift */
125258945Sroberto	 2,			/* size (0 = byte, 1 = short, 2 = long) */
126258945Sroberto	 32,			/* bitsize */
127258945Sroberto	 TRUE,			/* pc_relative */
128258945Sroberto	 0,			/* bitpos */
129258945Sroberto	 complain_overflow_dont, /* complain_on_overflow */
130258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
131258945Sroberto	 "R_XSTORMY16_PC32",	/* name */
132258945Sroberto	 FALSE,			/* partial_inplace */
133258945Sroberto	 0,			/* src_mask */
134258945Sroberto	 0xffffffff,		/* dst_mask */
135258945Sroberto	 TRUE),			/* pcrel_offset */
136258945Sroberto
137258945Sroberto  /* A 16 bit pc-relative relocation.  */
138258945Sroberto  HOWTO (R_XSTORMY16_PC16,	/* type */
139258945Sroberto	 0,			/* rightshift */
140258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
141258945Sroberto	 16,			/* bitsize */
142258945Sroberto	 TRUE,			/* pc_relative */
143258945Sroberto	 0,			/* bitpos */
144258945Sroberto	 complain_overflow_signed, /* complain_on_overflow */
145258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
146258945Sroberto	 "R_XSTORMY16_PC16",	/* name */
147258945Sroberto	 FALSE,			/* partial_inplace */
148258945Sroberto	 0,			/* src_mask */
149258945Sroberto	 0xffffffff,		/* dst_mask */
150258945Sroberto	 TRUE),			/* pcrel_offset */
151258945Sroberto
152258945Sroberto  /* An 8 bit pc-relative relocation.  */
153258945Sroberto  HOWTO (R_XSTORMY16_PC8,	/* type */
154258945Sroberto	 0,			/* rightshift */
155258945Sroberto	 0,			/* size (0 = byte, 1 = short, 2 = long) */
156258945Sroberto	 8,			/* bitsize */
157258945Sroberto	 TRUE,			/* pc_relative */
158258945Sroberto	 0,			/* bitpos */
159258945Sroberto	 complain_overflow_signed, /* complain_on_overflow */
160258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
161258945Sroberto	 "R_XSTORMY16_PC8",	/* name */
162258945Sroberto	 FALSE,			/* partial_inplace */
163258945Sroberto	 0,			/* src_mask */
164258945Sroberto	 0xffffffff,		/* dst_mask */
165258945Sroberto	 TRUE),			/* pcrel_offset */
166258945Sroberto
167258945Sroberto  /* A 12-bit pc-relative relocation suitable for the branch instructions.  */
168258945Sroberto  HOWTO (R_XSTORMY16_REL_12,	/* type */
169258945Sroberto	 1,			/* rightshift */
170258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
171258945Sroberto	 11,			/* bitsize */
172258945Sroberto	 TRUE,			/* pc_relative */
173258945Sroberto	 1,			/* bitpos */
174258945Sroberto	 complain_overflow_signed, /* complain_on_overflow */
175258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
176258945Sroberto	 "R_XSTORMY16_REL_12",	/* name */
177258945Sroberto	 FALSE,			/* partial_inplace */
178258945Sroberto	 0,			/* src_mask */
179258945Sroberto	 0x0ffe,		/* dst_mask */
180258945Sroberto	 TRUE),			/* pcrel_offset */
181258945Sroberto
182258945Sroberto  /* A 24-bit absolute relocation suitable for the jump instructions.  */
183258945Sroberto  HOWTO (R_XSTORMY16_24,	/* type */
184258945Sroberto	 0,			/* rightshift */
185258945Sroberto	 2,			/* size (0 = byte, 1 = short, 2 = long) */
186258945Sroberto	 24,			/* bitsize */
187258945Sroberto	 FALSE,			/* pc_relative */
188258945Sroberto	 0,			/* bitpos */
189258945Sroberto	 complain_overflow_unsigned, /* complain_on_overflow */
190258945Sroberto	 xstormy16_elf_24_reloc,	/* special_function */
191258945Sroberto	 "R_XSTORMY16_24",	/* name */
192258945Sroberto	 TRUE,			/* partial_inplace */
193258945Sroberto	 0,			/* src_mask */
194258945Sroberto	 0xffff00ff,		/* dst_mask */
195258945Sroberto	 TRUE),			/* pcrel_offset */
196258945Sroberto
197258945Sroberto  /* A 16 bit absolute relocation to a function pointer.  */
198258945Sroberto  HOWTO (R_XSTORMY16_FPTR16,	/* type */
199258945Sroberto	 0,			/* rightshift */
200258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
201258945Sroberto	 16,			/* bitsize */
202258945Sroberto	 FALSE,			/* pc_relative */
203258945Sroberto	 0,			/* bitpos */
204258945Sroberto	 complain_overflow_bitfield, /* complain_on_overflow */
205258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
206258945Sroberto	 "R_XSTORMY16_FPTR16",	/* name */
207258945Sroberto	 FALSE,			/* partial_inplace */
208258945Sroberto	 0,			/* src_mask */
209258945Sroberto	 0xffffffff,		/* dst_mask */
210258945Sroberto	 FALSE),		/* pcrel_offset */
211258945Sroberto
212258945Sroberto  /* Low order 16 bit value of a high memory address.  */
213258945Sroberto  HOWTO (R_XSTORMY16_LO16,	/* type */
214258945Sroberto	 0,			/* rightshift */
215258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
216258945Sroberto	 16,			/* bitsize */
217258945Sroberto	 FALSE,			/* pc_relative */
218258945Sroberto	 0,			/* bitpos */
219258945Sroberto	 complain_overflow_dont, /* complain_on_overflow */
220258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
221258945Sroberto	 "R_XSTORMY16_LO16",	/* name */
222258945Sroberto	 FALSE,			/* partial_inplace */
223258945Sroberto	 0,			/* src_mask */
224258945Sroberto	 0xffff,		/* dst_mask */
225258945Sroberto	 FALSE),		/* pcrel_offset */
226258945Sroberto
227258945Sroberto  /* High order 16 bit value of a high memory address.  */
228258945Sroberto  HOWTO (R_XSTORMY16_HI16,	/* type */
229258945Sroberto	 16,			/* rightshift */
230258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
231258945Sroberto	 16,			/* bitsize */
232258945Sroberto	 FALSE,			/* pc_relative */
233258945Sroberto	 0,			/* bitpos */
234258945Sroberto	 complain_overflow_dont, /* complain_on_overflow */
235258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
236258945Sroberto	 "R_XSTORMY16_HI16",	/* name */
237258945Sroberto	 FALSE,			/* partial_inplace */
238258945Sroberto	 0,			/* src_mask */
239258945Sroberto	 0xffff,		/* dst_mask */
240258945Sroberto	 FALSE),		/* pcrel_offset */
241258945Sroberto
242258945Sroberto  /* A 12 bit absolute relocation.  */
243258945Sroberto  HOWTO (R_XSTORMY16_12,	/* type */
244258945Sroberto	 0,			/* rightshift */
245258945Sroberto	 1,			/* size (0 = byte, 1 = short, 2 = long) */
246258945Sroberto	 12,			/* bitsize */
247258945Sroberto	 FALSE,			/* pc_relative */
248258945Sroberto	 0,			/* bitpos */
249258945Sroberto	 complain_overflow_signed, /* complain_on_overflow */
250258945Sroberto	 bfd_elf_generic_reloc,	/* special_function */
251258945Sroberto	 "R_XSTORMY16_12",	/* name */
252258945Sroberto	 FALSE,			/* partial_inplace */
253258945Sroberto	 0x0000,		/* src_mask */
254258945Sroberto	 0x0fff,		/* dst_mask */
255258945Sroberto	 FALSE),		/* pcrel_offset */
256258945Sroberto};
257258945Sroberto
258258945Srobertostatic reloc_howto_type xstormy16_elf_howto_table2 [] =
259258945Sroberto{
260258945Sroberto  /* GNU extension to record C++ vtable hierarchy */
261258945Sroberto  HOWTO (R_XSTORMY16_GNU_VTINHERIT, /* type */
262258945Sroberto         0,                     /* rightshift */
263258945Sroberto         2,                     /* size (0 = byte, 1 = short, 2 = long) */
264258945Sroberto         0,                     /* bitsize */
265258945Sroberto         FALSE,                 /* pc_relative */
266258945Sroberto         0,                     /* bitpos */
267258945Sroberto         complain_overflow_dont, /* complain_on_overflow */
268258945Sroberto         NULL,                  /* special_function */
269258945Sroberto         "R_XSTORMY16_GNU_VTINHERIT", /* name */
270258945Sroberto         FALSE,                 /* partial_inplace */
271258945Sroberto         0,                     /* src_mask */
272258945Sroberto         0,                     /* dst_mask */
273258945Sroberto         FALSE),                /* pcrel_offset */
274258945Sroberto
275258945Sroberto  /* GNU extension to record C++ vtable member usage */
276258945Sroberto  HOWTO (R_XSTORMY16_GNU_VTENTRY,     /* type */
277258945Sroberto         0,                     /* rightshift */
278258945Sroberto         2,                     /* size (0 = byte, 1 = short, 2 = long) */
279258945Sroberto         0,                     /* bitsize */
280258945Sroberto         FALSE,                 /* pc_relative */
281258945Sroberto         0,                     /* bitpos */
282258945Sroberto         complain_overflow_dont, /* complain_on_overflow */
283258945Sroberto         _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
284258945Sroberto         "R_XSTORMY16_GNU_VTENTRY",   /* name */
285258945Sroberto         FALSE,                 /* partial_inplace */
286258945Sroberto         0,                     /* src_mask */
287258945Sroberto         0,                     /* dst_mask */
288258945Sroberto         FALSE),                /* pcrel_offset */
289258945Sroberto
290258945Sroberto};
291258945Sroberto
292258945Sroberto/* Map BFD reloc types to XSTORMY16 ELF reloc types.  */
293258945Sroberto
294258945Srobertotypedef struct xstormy16_reloc_map
295258945Sroberto{
296258945Sroberto  bfd_reloc_code_real_type  bfd_reloc_val;
297258945Sroberto  unsigned int              xstormy16_reloc_val;
298258945Sroberto  reloc_howto_type *        table;
299258945Sroberto} reloc_map;
300258945Sroberto
301258945Srobertostatic const reloc_map xstormy16_reloc_map [] =
302258945Sroberto{
303258945Sroberto  { BFD_RELOC_NONE,                 R_XSTORMY16_NONE,          xstormy16_elf_howto_table },
304258945Sroberto  { BFD_RELOC_32,                   R_XSTORMY16_32,            xstormy16_elf_howto_table },
305258945Sroberto  { BFD_RELOC_16,                   R_XSTORMY16_16,            xstormy16_elf_howto_table },
306258945Sroberto  { BFD_RELOC_8,                    R_XSTORMY16_8,             xstormy16_elf_howto_table },
307258945Sroberto  { BFD_RELOC_32_PCREL,             R_XSTORMY16_PC32,          xstormy16_elf_howto_table },
308258945Sroberto  { BFD_RELOC_16_PCREL,             R_XSTORMY16_PC16,          xstormy16_elf_howto_table },
309258945Sroberto  { BFD_RELOC_8_PCREL,              R_XSTORMY16_PC8,           xstormy16_elf_howto_table },
310258945Sroberto  { BFD_RELOC_XSTORMY16_REL_12,     R_XSTORMY16_REL_12,        xstormy16_elf_howto_table },
311258945Sroberto  { BFD_RELOC_XSTORMY16_24,	    R_XSTORMY16_24,            xstormy16_elf_howto_table },
312258945Sroberto  { BFD_RELOC_XSTORMY16_FPTR16,	    R_XSTORMY16_FPTR16,        xstormy16_elf_howto_table },
313258945Sroberto  { BFD_RELOC_LO16,                 R_XSTORMY16_LO16,          xstormy16_elf_howto_table },
314258945Sroberto  { BFD_RELOC_HI16,                 R_XSTORMY16_HI16,          xstormy16_elf_howto_table },
315258945Sroberto  { BFD_RELOC_XSTORMY16_12,         R_XSTORMY16_12,            xstormy16_elf_howto_table },
316258945Sroberto  { BFD_RELOC_VTABLE_INHERIT,       R_XSTORMY16_GNU_VTINHERIT, xstormy16_elf_howto_table2 },
317258945Sroberto  { BFD_RELOC_VTABLE_ENTRY,         R_XSTORMY16_GNU_VTENTRY,   xstormy16_elf_howto_table2 },
318258945Sroberto};
319258945Sroberto
320258945Srobertostatic reloc_howto_type *
321258945Srobertoxstormy16_reloc_type_lookup (abfd, code)
322258945Sroberto     bfd * abfd ATTRIBUTE_UNUSED;
323258945Sroberto     bfd_reloc_code_real_type code;
324258945Sroberto{
325258945Sroberto  unsigned int i;
326258945Sroberto
327258945Sroberto  for (i = ARRAY_SIZE (xstormy16_reloc_map); --i;)
328258945Sroberto    {
329258945Sroberto      const reloc_map * entry;
330258945Sroberto
331258945Sroberto      entry = xstormy16_reloc_map + i;
332258945Sroberto
333258945Sroberto      if (entry->bfd_reloc_val == code)
334258945Sroberto	return entry->table + (entry->xstormy16_reloc_val
335			       - entry->table[0].type);
336    }
337
338  return NULL;
339}
340
341/* Set the howto pointer for an XSTORMY16 ELF reloc.  */
342
343static void
344xstormy16_info_to_howto_rela (abfd, cache_ptr, dst)
345     bfd * abfd ATTRIBUTE_UNUSED;
346     arelent * cache_ptr;
347     Elf_Internal_Rela * dst;
348{
349  unsigned int r_type = ELF32_R_TYPE (dst->r_info);
350
351  if (r_type <= (unsigned int) R_XSTORMY16_12)
352    cache_ptr->howto = &xstormy16_elf_howto_table [r_type];
353  else if (r_type - R_XSTORMY16_GNU_VTINHERIT
354	   <= (unsigned int) R_XSTORMY16_GNU_VTENTRY)
355    cache_ptr->howto
356      = &xstormy16_elf_howto_table2 [r_type - R_XSTORMY16_GNU_VTINHERIT];
357  else
358    abort ();
359}
360
361/* Handle the R_XSTORMY16_24 reloc, which has an odd bit arrangement.  */
362
363static bfd_reloc_status_type
364xstormy16_elf_24_reloc (abfd, reloc_entry, symbol, data, input_section,
365			 output_bfd, error_message)
366     bfd *abfd;
367     arelent *reloc_entry;
368     asymbol *symbol;
369     PTR data;
370     asection *input_section;
371     bfd *output_bfd;
372     char **error_message ATTRIBUTE_UNUSED;
373{
374  bfd_vma relocation, x;
375
376  if (output_bfd != NULL)
377    {
378      reloc_entry->address += input_section->output_offset;
379      return bfd_reloc_ok;
380    }
381
382  if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
383    return bfd_reloc_outofrange;
384
385  if (bfd_is_com_section (symbol->section))
386    relocation = 0;
387  else
388    relocation = symbol->value;
389
390  relocation += symbol->section->output_section->vma;
391  relocation += symbol->section->output_offset;
392  relocation += reloc_entry->addend;
393
394  x = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
395  x &= 0x0000ff00;
396  x |= relocation & 0xff;
397  x |= (relocation << 8) & 0xffff0000;
398  bfd_put_32 (abfd, x, (bfd_byte *) data + reloc_entry->address);
399
400  if (relocation & ~ (bfd_vma) 0xffffff)
401    return bfd_reloc_overflow;
402
403  return bfd_reloc_ok;
404}
405
406/* We support 16-bit pointers to code above 64k by generating a thunk
407   below 64k containing a JMPF instruction to the final address.  We
408   cannot, unfortunately, minimize the number of thunks unless the
409   -relax switch is given, as otherwise we have no idea where the
410   sections will fall in the address space.  */
411
412static bfd_boolean
413xstormy16_elf_check_relocs (abfd, info, sec, relocs)
414     bfd *abfd;
415     struct bfd_link_info *info;
416     asection *sec;
417     const Elf_Internal_Rela *relocs;
418{
419  const Elf_Internal_Rela *rel, *relend;
420  struct elf_link_hash_entry **sym_hashes;
421  Elf_Internal_Shdr *symtab_hdr;
422  bfd_vma *local_plt_offsets;
423  asection *splt;
424  bfd *dynobj;
425
426  if (info->relocatable)
427    return TRUE;
428
429  symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
430  sym_hashes = elf_sym_hashes (abfd);
431  local_plt_offsets = elf_local_got_offsets (abfd);
432  splt = NULL;
433  dynobj = elf_hash_table(info)->dynobj;
434
435  relend = relocs + sec->reloc_count;
436  for (rel = relocs; rel < relend; ++rel)
437    {
438      unsigned long r_symndx;
439      struct elf_link_hash_entry *h;
440      bfd_vma *offset;
441
442      r_symndx = ELF32_R_SYM (rel->r_info);
443      if (r_symndx < symtab_hdr->sh_info)
444	h = NULL;
445      else
446	{
447	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
448	  while (h->root.type == bfd_link_hash_indirect
449		 || h->root.type == bfd_link_hash_warning)
450	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
451	}
452
453      switch (ELF32_R_TYPE (rel->r_info))
454        {
455	  /* This relocation describes a 16-bit pointer to a function.
456	     We may need to allocate a thunk in low memory; reserve memory
457	     for it now.  */
458	case R_XSTORMY16_FPTR16:
459	  if (rel->r_addend != 0)
460	    {
461	      (*info->callbacks->warning)
462		(info, _("non-zero addend in @fptr reloc"), 0,
463		 abfd, 0, 0);
464	    }
465
466	  if (dynobj == NULL)
467	    elf_hash_table (info)->dynobj = dynobj = abfd;
468	  if (splt == NULL)
469	    {
470	      splt = bfd_get_section_by_name (dynobj, ".plt");
471	      if (splt == NULL)
472		{
473		  splt = bfd_make_section (dynobj, ".plt");
474		  if (splt == NULL
475		      || ! bfd_set_section_flags (dynobj, splt,
476						  (SEC_ALLOC
477						   | SEC_LOAD
478						   | SEC_HAS_CONTENTS
479						   | SEC_IN_MEMORY
480						   | SEC_LINKER_CREATED
481						   | SEC_READONLY
482						   | SEC_CODE))
483		      || ! bfd_set_section_alignment (dynobj, splt, 1))
484		    return FALSE;
485		}
486	    }
487
488	  if (h != NULL)
489	    offset = &h->plt.offset;
490	  else
491	    {
492	      if (local_plt_offsets == NULL)
493		{
494		  size_t size;
495		  unsigned int i;
496
497		  size = symtab_hdr->sh_info * sizeof (bfd_vma);
498		  local_plt_offsets = (bfd_vma *) bfd_alloc (abfd, size);
499		  if (local_plt_offsets == NULL)
500		    return FALSE;
501		  elf_local_got_offsets (abfd) = local_plt_offsets;
502
503		  for (i = 0; i < symtab_hdr->sh_info; i++)
504		    local_plt_offsets[i] = (bfd_vma) -1;
505		}
506	      offset = &local_plt_offsets[r_symndx];
507	    }
508
509	  if (*offset == (bfd_vma) -1)
510	    {
511	      *offset = splt->size;
512	      splt->size += 4;
513	    }
514	  break;
515
516	  /* This relocation describes the C++ object vtable hierarchy.
517	     Reconstruct it for later use during GC.  */
518        case R_XSTORMY16_GNU_VTINHERIT:
519          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
520            return FALSE;
521          break;
522
523	  /* This relocation describes which C++ vtable entries are actually
524	     used.  Record for later use during GC.  */
525        case R_XSTORMY16_GNU_VTENTRY:
526          if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
527            return FALSE;
528          break;
529	}
530    }
531
532  return TRUE;
533}
534
535/* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
536   is within the low 64k, remove any entry for it in the plt.  */
537
538struct relax_plt_data
539{
540  asection *splt;
541  bfd_boolean *again;
542};
543
544static bfd_boolean
545xstormy16_relax_plt_check (h, xdata)
546     struct elf_link_hash_entry *h;
547     PTR xdata;
548{
549  struct relax_plt_data *data = (struct relax_plt_data *) xdata;
550
551  if (h->root.type == bfd_link_hash_warning)
552    h = (struct elf_link_hash_entry *) h->root.u.i.link;
553
554  if (h->plt.offset != (bfd_vma) -1)
555    {
556      bfd_vma address;
557
558      if (h->root.type == bfd_link_hash_undefined
559	  || h->root.type == bfd_link_hash_undefweak)
560	address = 0;
561      else
562	address = (h->root.u.def.section->output_section->vma
563		   + h->root.u.def.section->output_offset
564		   + h->root.u.def.value);
565
566      if (address <= 0xffff)
567	{
568	  h->plt.offset = -1;
569	  data->splt->size -= 4;
570	  *data->again = TRUE;
571	}
572    }
573
574  return TRUE;
575}
576
577/* A subroutine of xstormy16_elf_relax_section.  If the global symbol H
578   previously had a plt entry, give it a new entry offset.  */
579
580static bfd_boolean
581xstormy16_relax_plt_realloc (h, xdata)
582     struct elf_link_hash_entry *h;
583     PTR xdata;
584{
585  bfd_vma *entry = (bfd_vma *) xdata;
586
587  if (h->root.type == bfd_link_hash_warning)
588    h = (struct elf_link_hash_entry *) h->root.u.i.link;
589
590  if (h->plt.offset != (bfd_vma) -1)
591    {
592      h->plt.offset = *entry;
593      *entry += 4;
594    }
595
596  return TRUE;
597}
598
599static bfd_boolean
600xstormy16_elf_relax_section (dynobj, splt, info, again)
601     bfd *dynobj;
602     asection *splt;
603     struct bfd_link_info *info;
604     bfd_boolean *again;
605{
606  struct relax_plt_data relax_plt_data;
607  bfd *ibfd;
608
609  /* Assume nothing changes.  */
610  *again = FALSE;
611
612  if (info->relocatable)
613    return TRUE;
614
615  /* We only relax the .plt section at the moment.  */
616  if (dynobj != elf_hash_table (info)->dynobj
617      || strcmp (splt->name, ".plt") != 0)
618    return TRUE;
619
620  /* Quick check for an empty plt.  */
621  if (splt->size == 0)
622    return TRUE;
623
624  /* Map across all global symbols; see which ones happen to
625     fall in the low 64k.  */
626  relax_plt_data.splt = splt;
627  relax_plt_data.again = again;
628  elf_link_hash_traverse (elf_hash_table (info), xstormy16_relax_plt_check,
629			  &relax_plt_data);
630
631  /* Likewise for local symbols, though that's somewhat less convenient
632     as we have to walk the list of input bfds and swap in symbol data.  */
633  for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link_next)
634    {
635      bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
636      Elf_Internal_Shdr *symtab_hdr;
637      Elf_Internal_Sym *isymbuf = NULL;
638      unsigned int idx;
639
640      if (! local_plt_offsets)
641	continue;
642
643      symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
644      if (symtab_hdr->sh_info != 0)
645	{
646	  isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
647	  if (isymbuf == NULL)
648	    isymbuf = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
649					    symtab_hdr->sh_info, 0,
650					    NULL, NULL, NULL);
651	  if (isymbuf == NULL)
652	    return FALSE;
653	}
654
655      for (idx = 0; idx < symtab_hdr->sh_info; ++idx)
656	{
657	  Elf_Internal_Sym *isym;
658	  asection *tsec;
659	  bfd_vma address;
660
661	  if (local_plt_offsets[idx] == (bfd_vma) -1)
662	    continue;
663
664	  isym = &isymbuf[idx];
665	  if (isym->st_shndx == SHN_UNDEF)
666	    continue;
667	  else if (isym->st_shndx == SHN_ABS)
668	    tsec = bfd_abs_section_ptr;
669	  else if (isym->st_shndx == SHN_COMMON)
670	    tsec = bfd_com_section_ptr;
671	  else
672	    tsec = bfd_section_from_elf_index (ibfd, isym->st_shndx);
673
674	  address = (tsec->output_section->vma
675		     + tsec->output_offset
676		     + isym->st_value);
677	  if (address <= 0xffff)
678	    {
679	      local_plt_offsets[idx] = -1;
680	      splt->size -= 4;
681	      *again = TRUE;
682	    }
683	}
684
685      if (isymbuf != NULL
686	  && symtab_hdr->contents != (unsigned char *) isymbuf)
687	{
688	  if (! info->keep_memory)
689	    free (isymbuf);
690	  else
691	    {
692	      /* Cache the symbols for elf_link_input_bfd.  */
693	      symtab_hdr->contents = (unsigned char *) isymbuf;
694	    }
695	}
696    }
697
698  /* If we changed anything, walk the symbols again to reallocate
699     .plt entry addresses.  */
700  if (*again && splt->size > 0)
701    {
702      bfd_vma entry = 0;
703
704      elf_link_hash_traverse (elf_hash_table (info),
705			      xstormy16_relax_plt_realloc, &entry);
706
707      for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link_next)
708	{
709	  bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd);
710	  unsigned int nlocals = elf_tdata (ibfd)->symtab_hdr.sh_info;
711	  unsigned int idx;
712
713	  if (! local_plt_offsets)
714	    continue;
715
716	  for (idx = 0; idx < nlocals; ++idx)
717	    if (local_plt_offsets[idx] != (bfd_vma) -1)
718	      {
719	        local_plt_offsets[idx] = entry;
720		entry += 4;
721	      }
722	}
723    }
724
725  return TRUE;
726}
727
728static bfd_boolean
729xstormy16_elf_always_size_sections (output_bfd, info)
730     bfd *output_bfd ATTRIBUTE_UNUSED;
731     struct bfd_link_info *info;
732{
733  bfd *dynobj;
734  asection *splt;
735
736  if (info->relocatable)
737    return TRUE;
738
739  dynobj = elf_hash_table (info)->dynobj;
740  if (dynobj == NULL)
741    return TRUE;
742
743  splt = bfd_get_section_by_name (dynobj, ".plt");
744  BFD_ASSERT (splt != NULL);
745
746  splt->contents = (bfd_byte *) bfd_zalloc (dynobj, splt->size);
747  if (splt->contents == NULL)
748    return FALSE;
749
750  return TRUE;
751}
752
753/* Relocate an XSTORMY16 ELF section.
754
755   The RELOCATE_SECTION function is called by the new ELF backend linker
756   to handle the relocations for a section.
757
758   The relocs are always passed as Rela structures; if the section
759   actually uses Rel structures, the r_addend field will always be
760   zero.
761
762   This function is responsible for adjusting the section contents as
763   necessary, and (if using Rela relocs and generating a relocatable
764   output file) adjusting the reloc addend as necessary.
765
766   This function does not have to worry about setting the reloc
767   address or the reloc symbol index.
768
769   LOCAL_SYMS is a pointer to the swapped in local symbols.
770
771   LOCAL_SECTIONS is an array giving the section in the input file
772   corresponding to the st_shndx field of each local symbol.
773
774   The global hash table entry for the global symbols can be found
775   via elf_sym_hashes (input_bfd).
776
777   When generating relocatable output, this function must handle
778   STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
779   going to be the section symbol corresponding to the output
780   section, which means that the addend must be adjusted
781   accordingly.  */
782
783static bfd_boolean
784xstormy16_elf_relocate_section (output_bfd, info, input_bfd, input_section,
785			   contents, relocs, local_syms, local_sections)
786     bfd *                   output_bfd ATTRIBUTE_UNUSED;
787     struct bfd_link_info *  info;
788     bfd *                   input_bfd;
789     asection *              input_section;
790     bfd_byte *              contents;
791     Elf_Internal_Rela *     relocs;
792     Elf_Internal_Sym *      local_syms;
793     asection **             local_sections;
794{
795  Elf_Internal_Shdr *           symtab_hdr;
796  struct elf_link_hash_entry ** sym_hashes;
797  Elf_Internal_Rela *           rel;
798  Elf_Internal_Rela *           relend;
799  bfd *dynobj;
800  asection *splt;
801
802  if (info->relocatable)
803    return TRUE;
804
805  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
806  sym_hashes = elf_sym_hashes (input_bfd);
807  relend     = relocs + input_section->reloc_count;
808
809  dynobj = elf_hash_table (info)->dynobj;
810  splt = NULL;
811  if (dynobj != NULL)
812    splt = bfd_get_section_by_name (dynobj, ".plt");
813
814  for (rel = relocs; rel < relend; rel ++)
815    {
816      reloc_howto_type *           howto;
817      unsigned long                r_symndx;
818      Elf_Internal_Sym *           sym;
819      asection *                   sec;
820      struct elf_link_hash_entry * h;
821      bfd_vma                      relocation;
822      bfd_reloc_status_type        r;
823      const char *                 name = NULL;
824      int                          r_type;
825
826      r_type = ELF32_R_TYPE (rel->r_info);
827
828      if (   r_type == R_XSTORMY16_GNU_VTINHERIT
829	  || r_type == R_XSTORMY16_GNU_VTENTRY)
830	continue;
831
832      r_symndx = ELF32_R_SYM (rel->r_info);
833      howto  = xstormy16_elf_howto_table + ELF32_R_TYPE (rel->r_info);
834      h      = NULL;
835      sym    = NULL;
836      sec    = NULL;
837
838      if (r_symndx < symtab_hdr->sh_info)
839	{
840	  sym = local_syms + r_symndx;
841	  sec = local_sections [r_symndx];
842	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
843	}
844      else
845	{
846	  bfd_boolean unresolved_reloc, warned;
847
848	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
849				   r_symndx, symtab_hdr, sym_hashes,
850				   h, sec, relocation,
851				   unresolved_reloc, warned);
852	}
853
854      if (h != NULL)
855	name = h->root.root.string;
856      else
857	{
858	  name = (bfd_elf_string_from_elf_section
859		  (input_bfd, symtab_hdr->sh_link, sym->st_name));
860	  if (name == NULL || *name == '\0')
861	    name = bfd_section_name (input_bfd, sec);
862	}
863
864      switch (ELF32_R_TYPE (rel->r_info))
865	{
866	case R_XSTORMY16_24:
867	  {
868	    bfd_vma reloc = relocation + rel->r_addend;
869	    unsigned int x;
870
871	    x = bfd_get_32 (input_bfd, contents + rel->r_offset);
872	    x &= 0x0000ff00;
873	    x |= reloc & 0xff;
874	    x |= (reloc << 8) & 0xffff0000;
875	    bfd_put_32 (input_bfd, x, contents + rel->r_offset);
876
877	    if (reloc & ~0xffffff)
878	      r = bfd_reloc_overflow;
879	    else
880	      r = bfd_reloc_ok;
881	    break;
882	  }
883
884	case R_XSTORMY16_FPTR16:
885	  {
886	    bfd_vma *plt_offset;
887
888	    if (h != NULL)
889	      plt_offset = &h->plt.offset;
890	    else
891	      plt_offset = elf_local_got_offsets (input_bfd) + r_symndx;
892
893	    if (relocation <= 0xffff)
894	      {
895	        /* If the symbol is in range for a 16-bit address, we should
896		   have deallocated the plt entry in relax_section.  */
897	        BFD_ASSERT (*plt_offset == (bfd_vma) -1);
898	      }
899	    else
900	      {
901		/* If the symbol is out of range for a 16-bit address,
902		   we must have allocated a plt entry.  */
903		BFD_ASSERT (*plt_offset != (bfd_vma) -1);
904
905		/* If this is the first time we've processed this symbol,
906		   fill in the plt entry with the correct symbol address.  */
907		if ((*plt_offset & 1) == 0)
908		  {
909		    unsigned int x;
910
911		    x = 0x00000200;  /* jmpf */
912		    x |= relocation & 0xff;
913		    x |= (relocation << 8) & 0xffff0000;
914		    bfd_put_32 (input_bfd, x, splt->contents + *plt_offset);
915		    *plt_offset |= 1;
916		  }
917
918		relocation = (splt->output_section->vma
919			      + splt->output_offset
920			      + (*plt_offset & -2));
921	      }
922	    r = _bfd_final_link_relocate (howto, input_bfd, input_section,
923					  contents, rel->r_offset,
924					  relocation, 0);
925	    break;
926	  }
927
928	default:
929	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
930					contents, rel->r_offset,
931					relocation, rel->r_addend);
932	  break;
933	}
934
935      if (r != bfd_reloc_ok)
936	{
937	  const char * msg = (const char *) NULL;
938
939	  switch (r)
940	    {
941	    case bfd_reloc_overflow:
942	      r = info->callbacks->reloc_overflow
943		(info, name, howto->name, (bfd_vma) 0,
944		 input_bfd, input_section, rel->r_offset);
945	      break;
946
947	    case bfd_reloc_undefined:
948	      r = info->callbacks->undefined_symbol
949		(info, name, input_bfd, input_section, rel->r_offset,
950		 TRUE);
951	      break;
952
953	    case bfd_reloc_outofrange:
954	      msg = _("internal error: out of range error");
955	      break;
956
957	    case bfd_reloc_notsupported:
958	      msg = _("internal error: unsupported relocation error");
959	      break;
960
961	    case bfd_reloc_dangerous:
962	      msg = _("internal error: dangerous relocation");
963	      break;
964
965	    default:
966	      msg = _("internal error: unknown error");
967	      break;
968	    }
969
970	  if (msg)
971	    r = info->callbacks->warning
972	      (info, msg, name, input_bfd, input_section, rel->r_offset);
973
974	  if (! r)
975	    return FALSE;
976	}
977    }
978
979  return TRUE;
980}
981
982/* This must exist if dynobj is ever set.  */
983
984static bfd_boolean
985xstormy16_elf_finish_dynamic_sections (abfd, info)
986     bfd *abfd ATTRIBUTE_UNUSED;
987     struct bfd_link_info *info;
988{
989  bfd *dynobj;
990  asection *splt;
991
992  /* As an extra sanity check, verify that all plt entries have
993     been filled in.  */
994
995  if ((dynobj = elf_hash_table (info)->dynobj) != NULL
996      && (splt = bfd_get_section_by_name (dynobj, ".plt")) != NULL)
997    {
998      bfd_byte *contents = splt->contents;
999      unsigned int i, size = splt->size;
1000      for (i = 0; i < size; i += 4)
1001	{
1002	  unsigned int x = bfd_get_32 (dynobj, contents + i);
1003	  BFD_ASSERT (x != 0);
1004	}
1005    }
1006
1007  return TRUE;
1008}
1009
1010/* Return the section that should be marked against GC for a given
1011   relocation.  */
1012
1013static asection *
1014xstormy16_elf_gc_mark_hook (sec, info, rel, h, sym)
1015     asection *                   sec;
1016     struct bfd_link_info *       info ATTRIBUTE_UNUSED;
1017     Elf_Internal_Rela *          rel;
1018     struct elf_link_hash_entry * h;
1019     Elf_Internal_Sym *           sym;
1020{
1021  if (h != NULL)
1022    {
1023      switch (ELF32_R_TYPE (rel->r_info))
1024	{
1025	case R_XSTORMY16_GNU_VTINHERIT:
1026	case R_XSTORMY16_GNU_VTENTRY:
1027	  break;
1028
1029	default:
1030	  switch (h->root.type)
1031	    {
1032	    case bfd_link_hash_defined:
1033	    case bfd_link_hash_defweak:
1034	      return h->root.u.def.section;
1035
1036	    case bfd_link_hash_common:
1037	      return h->root.u.c.p->section;
1038
1039	    default:
1040	      break;
1041	    }
1042	}
1043    }
1044  else
1045    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
1046
1047  return NULL;
1048}
1049
1050/* Update the got entry reference counts for the section being removed.  */
1051
1052static bfd_boolean
1053xstormy16_elf_gc_sweep_hook (abfd, info, sec, relocs)
1054     bfd *                     abfd ATTRIBUTE_UNUSED;
1055     struct bfd_link_info *    info ATTRIBUTE_UNUSED;
1056     asection *                sec ATTRIBUTE_UNUSED;
1057     const Elf_Internal_Rela * relocs ATTRIBUTE_UNUSED;
1058{
1059  return TRUE;
1060}
1061
1062#define ELF_ARCH		bfd_arch_xstormy16
1063#define ELF_MACHINE_CODE	EM_XSTORMY16
1064#define ELF_MAXPAGESIZE		0x100
1065
1066#define TARGET_LITTLE_SYM       bfd_elf32_xstormy16_vec
1067#define TARGET_LITTLE_NAME	"elf32-xstormy16"
1068
1069#define elf_info_to_howto_rel			NULL
1070#define elf_info_to_howto			xstormy16_info_to_howto_rela
1071#define elf_backend_relocate_section		xstormy16_elf_relocate_section
1072#define elf_backend_gc_mark_hook		xstormy16_elf_gc_mark_hook
1073#define elf_backend_gc_sweep_hook		xstormy16_elf_gc_sweep_hook
1074#define elf_backend_check_relocs                xstormy16_elf_check_relocs
1075#define elf_backend_always_size_sections \
1076  xstormy16_elf_always_size_sections
1077#define elf_backend_finish_dynamic_sections \
1078  xstormy16_elf_finish_dynamic_sections
1079
1080#define elf_backend_can_gc_sections		1
1081#define elf_backend_rela_normal			1
1082
1083#define bfd_elf32_bfd_reloc_type_lookup		xstormy16_reloc_type_lookup
1084#define bfd_elf32_bfd_relax_section		xstormy16_elf_relax_section
1085
1086#include "elf32-target.h"
1087