* elf32-v850.c (bfd_elf32_v850_reloc): Fix handling of
[external/binutils.git] / bfd / elf32-v850.c
1 /* V850-specific support for 32-bit ELF
2    Copyright (C) 1994, 1995 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "bfd.h"
21 #include "sysdep.h"
22 #include "libbfd.h"
23 #include "elf-bfd.h"
24
25 static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
26   PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
27 static void v850_info_to_howto_rel
28   PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *));
29 static bfd_reloc_status_type bfd_elf32_v850_reloc
30   PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
31
32
33
34 /* Try to minimize the amount of space occupied by relocation tables
35    on the ROM (not that the ROM won't be swamped by other ELF overhead).  */
36 #define USE_REL
37
38 enum reloc_type
39 {
40   R_V850_NONE = 0,
41   R_V850_9_PCREL,
42   R_V850_22_PCREL,
43   R_V850_HI16_S,
44   R_V850_HI16,
45   R_V850_LO16,
46   R_V850_32,
47   R_V850_16,
48   R_V850_8,
49   R_V850_max
50 };
51
52 static reloc_howto_type elf_v850_howto_table[] =
53 {
54   /* This reloc does nothing.  */
55   HOWTO (R_V850_NONE,           /* type */
56          0,                     /* rightshift */
57          2,                     /* size (0 = byte, 1 = short, 2 = long) */
58          32,                    /* bitsize */
59          false,                 /* pc_relative */
60          0,                     /* bitpos */
61          complain_overflow_bitfield, /* complain_on_overflow */
62          bfd_elf_generic_reloc, /* special_function */
63          "R_V850_NONE",         /* name */
64          false,                 /* partial_inplace */
65          0,                     /* src_mask */
66          0,                     /* dst_mask */
67          false),                /* pcrel_offset */
68
69   /* A PC relative 9 bit branch. */
70   HOWTO (R_V850_9_PCREL,        /* type */
71          2,                     /* rightshift */
72          2,                     /* size (0 = byte, 1 = short, 2 = long) */
73          26,                    /* bitsize */
74          true,                  /* pc_relative */
75          0,                     /* bitpos */
76          complain_overflow_bitfield, /* complain_on_overflow */
77          bfd_elf32_v850_reloc,  /* special_function */
78          "R_V850_9_PCREL",      /* name */
79          false,                 /* partial_inplace */
80          0x00ffffff,            /* src_mask */
81          0x00ffffff,            /* dst_mask */
82          true),                 /* pcrel_offset */
83
84   /* A PC relative 22 bit branch. */
85   HOWTO (R_V850_22_PCREL,       /* type */
86          2,                     /* rightshift */
87          2,                     /* size (0 = byte, 1 = short, 2 = long) */
88          22,                    /* bitsize */
89          true,                  /* pc_relative */
90          7,                     /* bitpos */
91          complain_overflow_signed, /* complain_on_overflow */
92          bfd_elf32_v850_reloc,  /* special_function */
93          "R_V850_22_PCREL",     /* name */
94          false,                 /* partial_inplace */
95          0x07ffff80,            /* src_mask */
96          0x07ffff80,            /* dst_mask */
97          true),                 /* pcrel_offset */
98
99   /* High 16 bits of symbol value.  */
100   HOWTO (R_V850_HI16_S,         /* type */
101          0,                     /* rightshift */
102          1,                     /* size (0 = byte, 1 = short, 2 = long) */
103          16,                    /* bitsize */
104          false,                 /* pc_relative */
105          0,                    /* bitpos */
106          complain_overflow_dont,/* complain_on_overflow */
107          bfd_elf32_v850_reloc, /* special_function */
108          "R_V850_HI16_S",       /* name */
109          true,                  /* partial_inplace */
110          0xffff,                /* src_mask */
111          0xffff,                /* dst_mask */
112          false),                /* pcrel_offset */
113
114   /* High 16 bits of symbol value.  */
115   HOWTO (R_V850_HI16,           /* type */
116          0,                     /* rightshift */
117          1,                     /* size (0 = byte, 1 = short, 2 = long) */
118          16,                    /* bitsize */
119          false,                 /* pc_relative */
120          0,                    /* bitpos */
121          complain_overflow_dont,/* complain_on_overflow */
122          bfd_elf32_v850_reloc, /* special_function */
123          "R_V850_HI16",         /* name */
124          true,                  /* partial_inplace */
125          0xffff,                /* src_mask */
126          0xffff,                /* dst_mask */
127          false),                /* pcrel_offset */
128
129   /* Low 16 bits of symbol value.  */
130   HOWTO (R_V850_LO16,           /* type */
131          0,                     /* rightshift */
132          1,                     /* size (0 = byte, 1 = short, 2 = long) */
133          16,                    /* bitsize */
134          false,                 /* pc_relative */
135          0,                     /* bitpos */
136          complain_overflow_dont,/* complain_on_overflow */
137          bfd_elf_generic_reloc, /* special_function */
138          "R_V850_LO16",         /* name */
139          true,                  /* partial_inplace */
140          0xffff,                /* src_mask */
141          0xffff,                /* dst_mask */
142          false),                /* pcrel_offset */
143
144   /* Simple 32bit reloc.  */
145   HOWTO (R_V850_32,           /* type */
146          0,                     /* rightshift */
147          2,                     /* size (0 = byte, 1 = short, 2 = long) */
148          32,                    /* bitsize */
149          false,                 /* pc_relative */
150          0,                     /* bitpos */
151          complain_overflow_dont,/* complain_on_overflow */
152          bfd_elf_generic_reloc, /* special_function */
153          "R_V850_32",         /* name */
154          true,                  /* partial_inplace */
155          0xffffffff,                /* src_mask */
156          0xffffffff,                /* dst_mask */
157          false),                /* pcrel_offset */
158
159   /* Simple 16bit reloc.  */
160   HOWTO (R_V850_16,           /* type */
161          0,                     /* rightshift */
162          1,                     /* size (0 = byte, 1 = short, 2 = long) */
163          16,                    /* bitsize */
164          false,                 /* pc_relative */
165          0,                     /* bitpos */
166          complain_overflow_dont,/* complain_on_overflow */
167          bfd_elf_generic_reloc, /* special_function */
168          "R_V850_16",         /* name */
169          true,                  /* partial_inplace */
170          0xffff,                /* src_mask */
171          0xffff,                /* dst_mask */
172          false),                /* pcrel_offset */
173
174   /* Simple 8bit reloc.  */
175   HOWTO (R_V850_8,           /* type */
176          0,                     /* rightshift */
177          0,                     /* size (0 = byte, 1 = short, 2 = long) */
178          8,                    /* bitsize */
179          false,                 /* pc_relative */
180          0,                     /* bitpos */
181          complain_overflow_dont,/* complain_on_overflow */
182          bfd_elf_generic_reloc, /* special_function */
183          "R_V850_8",         /* name */
184          true,                  /* partial_inplace */
185          0xff,                /* src_mask */
186          0xff,                /* dst_mask */
187          false),                /* pcrel_offset */
188 };
189
190 /* Map BFD reloc types to V850 ELF reloc types.  */
191
192 struct v850_reloc_map
193 {
194   unsigned char bfd_reloc_val;
195   unsigned char elf_reloc_val;
196 };
197
198 static const struct v850_reloc_map v850_reloc_map[] =
199 {
200   { BFD_RELOC_NONE, R_V850_NONE, },
201   { BFD_RELOC_V850_9_PCREL, R_V850_9_PCREL, },
202   { BFD_RELOC_V850_22_PCREL, R_V850_22_PCREL, },
203   { BFD_RELOC_HI16_S, R_V850_HI16_S, },
204   { BFD_RELOC_HI16, R_V850_HI16, },
205   { BFD_RELOC_LO16, R_V850_LO16, },
206   { BFD_RELOC_32, R_V850_32, },
207   { BFD_RELOC_16, R_V850_16, },
208   { BFD_RELOC_8, R_V850_8, },
209 };
210
211 static reloc_howto_type *
212 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
213      bfd *abfd;
214      bfd_reloc_code_real_type code;
215 {
216   unsigned int i;
217
218   for (i = 0;
219        i < sizeof (v850_reloc_map) / sizeof (struct v850_reloc_map);
220        i++)
221     {
222       if (v850_reloc_map[i].bfd_reloc_val == code)
223         return &elf_v850_howto_table[v850_reloc_map[i].elf_reloc_val];
224     }
225
226   return NULL;
227 }
228
229 /* Set the howto pointer for an V850 ELF reloc.  */
230
231 static void
232 v850_info_to_howto_rel (abfd, cache_ptr, dst)
233      bfd *abfd;
234      arelent *cache_ptr;
235      Elf32_Internal_Rel *dst;
236 {
237   unsigned int r_type;
238
239   r_type = ELF32_R_TYPE (dst->r_info);
240   BFD_ASSERT (r_type < (unsigned int) R_V850_max);
241   cache_ptr->howto = &elf_v850_howto_table[r_type];
242 }
243
244 static bfd_reloc_status_type
245 bfd_elf32_v850_reloc (abfd, reloc, symbol, data, isection, obfd, err)
246      bfd *abfd;
247      arelent *reloc;
248      asymbol *symbol;
249      PTR data;
250      asection *isection;
251      bfd *obfd;
252      char **err;
253 {
254   if (obfd != (bfd *) NULL
255       && (symbol->flags & BSF_SECTION_SYM) == 0
256       && (! reloc->howto->partial_inplace
257           || reloc->addend == 0))
258     {
259       reloc->address += isection->output_offset;
260       return bfd_reloc_ok;
261     }
262   else if (obfd != NULL)
263     {
264       return bfd_reloc_continue;
265     }
266
267   /* We handle final linking of some relocs ourselves.  */
268     {
269       long relocation, insn;
270
271       /* Is the address of the relocation really within the section?  */
272       if (reloc->address > isection->_cooked_size)
273         return bfd_reloc_outofrange;
274
275       /* Work out which section the relocation is targetted at and the
276          initial relocation command value.  */
277
278       /* Get symbol value.  (Common symbols are special.)  */
279       if (bfd_is_com_section (symbol->section))
280         relocation = 0;
281       else
282         relocation = symbol->value;
283
284       /* Convert input-section-relative symbol value to absolute + addend.  */
285       relocation += symbol->section->output_section->vma;
286       relocation += symbol->section->output_offset;
287       relocation += reloc->addend;
288
289       if (reloc->howto->pc_relative == true)
290         {
291           /* Here the variable relocation holds the final address of the
292              symbol we are relocating against, plus any addend.  */
293           relocation -= isection->output_section->vma + isection->output_offset;
294
295           /* Deal with pcrel_offset */
296           relocation -= reloc->address;
297         }
298
299       /* I've got no clue... */
300       reloc->addend = 0;        
301
302       if (reloc->howto->type == R_V850_22_PCREL)
303         {
304           if (relocation > 0x1ffff || relocation < -0x200000)
305             return bfd_reloc_overflow;
306
307           if ((relocation % 2) != 0)
308             return bfd_reloc_dangerous;
309
310           insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc->address);
311           insn |= (((relocation & 0xfffe) << 16)
312                    | ((relocation & 0x3f0000) >> 16));
313           bfd_put_32 (abfd, insn, (bfd_byte *)data + reloc->address);
314           return bfd_reloc_ok;
315         }
316       else if (reloc->howto->type == R_V850_9_PCREL)
317         {
318           if (relocation > 0xff || relocation < -0x100)
319             return bfd_reloc_overflow;
320
321           if ((relocation % 2) != 0)
322             return bfd_reloc_dangerous;
323
324           insn = bfd_get_16 (abfd, (bfd_byte *) data + reloc->address);
325           insn |= ((relocation & 0x1f0) << 7) | ((relocation & 0x0e) << 3);
326           bfd_put_16 (abfd, insn, (bfd_byte *)data + reloc->address);
327           return bfd_reloc_ok;
328         }
329       else if (reloc->howto->type == R_V850_HI16_S)
330         {
331           relocation = (relocation >> 16) + ((relocation & 0x8000) != 0);
332           bfd_put_16 (abfd, relocation, (bfd_byte *)data + reloc->address);
333           return bfd_reloc_ok;
334         }
335       else if (reloc->howto->type == R_V850_HI16)
336         {
337           relocation = (relocation >> 16);
338           bfd_put_16 (abfd, relocation, (bfd_byte *)data + reloc->address);
339           return bfd_reloc_ok;
340         }
341     }
342
343   return bfd_reloc_continue;
344 }
345
346 #define TARGET_LITTLE_SYM               bfd_elf32_v850_vec
347 #define TARGET_LITTLE_NAME              "elf32-v850"
348 #define ELF_ARCH                bfd_arch_v850
349 #define ELF_MACHINE_CODE        EM_CYGNUS_V850
350 #define ELF_MAXPAGESIZE         0x1000
351
352 #define elf_info_to_howto       0
353 #define elf_info_to_howto_rel   v850_info_to_howto_rel
354
355 #include "elf32-target.h"