1 /* D30V-specific support for 32-bit ELF
2    Copyright (C) 1997-2024 Free Software Foundation, Inc.
3    Contributed by Martin Hunt (hunt@cygnus.com).
4 
5    This file is part of BFD, the Binary File Descriptor library.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20    MA 02110-1301, USA.  */
21 
22 #include "sysdep.h"
23 #include "bfd.h"
24 #include "libbfd.h"
25 #include "elf-bfd.h"
26 #include "elf/d30v.h"
27 
28 #define MAX32 ((bfd_signed_vma) 0x7fffffff)
29 #define MIN32 (- MAX32 - 1)
30 
31 static bfd_reloc_status_type
bfd_elf_d30v_reloc(bfd * abfd,arelent * reloc_entry,asymbol * symbol,void * data,asection * input_section,bfd * output_bfd,char ** error_message)32 bfd_elf_d30v_reloc (bfd *abfd,
33                         arelent *reloc_entry,
34                         asymbol *symbol,
35                         void * data,
36                         asection *input_section,
37                         bfd *output_bfd,
38                         char **error_message)
39 {
40   bfd_signed_vma relocation;
41   bfd_vma in1, in2, num;
42   bfd_vma tmp_addr = 0;
43   bfd_reloc_status_type r;
44   asection *reloc_target_output_section;
45   bfd_size_type addr = reloc_entry->address;
46   bfd_reloc_status_type flag = bfd_reloc_ok;
47   bfd_vma output_base = 0;
48   reloc_howto_type *howto = reloc_entry->howto;
49   int make_absolute = 0;
50 
51   if (output_bfd != NULL)
52     {
53       /* Partial linking -- do nothing.  */
54       reloc_entry->address += input_section->output_offset;
55       return bfd_reloc_ok;
56     }
57 
58   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
59                                    input_section, output_bfd, error_message);
60   if (r != bfd_reloc_continue)
61     return r;
62 
63   /* A hacked-up version of bfd_perform_reloc() follows.  */
64  if (bfd_is_und_section (symbol->section)
65       && (symbol->flags & BSF_WEAK) == 0
66       && output_bfd == NULL)
67     flag = bfd_reloc_undefined;
68 
69   /* Is the address of the relocation really within the section?  */
70   if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
71     return bfd_reloc_outofrange;
72 
73   /* Work out which section the relocation is targeted at and the
74      initial relocation command value.  */
75 
76   /* Get symbol value.  (Common symbols are special.)  */
77   if (bfd_is_com_section (symbol->section))
78     relocation = 0;
79   else
80     relocation = symbol->value;
81 
82   reloc_target_output_section = symbol->section->output_section;
83 
84   /* Convert input-section-relative symbol value to absolute.  */
85   output_base = reloc_target_output_section->vma;
86   relocation += output_base + symbol->section->output_offset;
87 
88   /* Add in supplied addend.  */
89   relocation += reloc_entry->addend;
90 
91   /* Here the variable relocation holds the final address of the
92      symbol we are relocating against, plus any addend.  */
93   if (howto->pc_relative)
94     {
95       tmp_addr = input_section->output_section->vma
96           + input_section->output_offset
97           + reloc_entry->address;
98       relocation -= tmp_addr;
99     }
100 
101   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
102   in2 = bfd_get_32 (abfd, (bfd_byte *) data + addr + 4);
103 
104   /* Extract the addend.  */
105   num = ((in2 & 0x3FFFF)
106            | ((in2 & 0xFF00000) >> 2)
107            | ((in1 & 0x3F) << 26));
108   in1 &= 0xFFFFFFC0;
109   in2 = 0x80000000;
110 
111   relocation += num;
112 
113   if (howto->pc_relative && howto->bitsize == 32)
114     {
115       /* The D30V has a PC that doesn't wrap and PC-relative jumps are
116            signed, so a PC-relative jump can't be more than +/- 2^31 bytes.
117            If one exceeds this, change it to an absolute jump.  */
118       if (relocation > MAX32 || relocation < MIN32)
119           {
120             relocation = (relocation + tmp_addr) & 0xffffffff;
121             make_absolute = 1;
122           }
123     }
124 
125   in1 |= (relocation >> 26) & 0x3F;               /* Top 6 bits.  */
126   in2 |= ((relocation & 0x03FC0000) << 2);        /* Next 8 bits.  */
127   in2 |= relocation & 0x0003FFFF;                 /* Bottom 18 bits.  */
128 
129   /* Change a PC-relative instruction to its
130      absolute equivalent with this simple hack.  */
131   if (make_absolute)
132     in1 |= 0x00100000;
133 
134   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
135   bfd_put_32 (abfd, in2, (bfd_byte *) data + addr + 4);
136 
137   return flag;
138 }
139 
140 static bfd_reloc_status_type
bfd_elf_d30v_reloc_21(bfd * abfd,arelent * reloc_entry,asymbol * symbol,void * data,asection * input_section,bfd * output_bfd,char ** error_message)141 bfd_elf_d30v_reloc_21 (bfd *abfd,
142                            arelent *reloc_entry,
143                            asymbol *symbol,
144                            void * data,
145                            asection *input_section,
146                            bfd *output_bfd,
147                            char **error_message)
148 {
149   bfd_vma relocation;
150   bfd_vma in1, num;
151   bfd_reloc_status_type r;
152   asection *reloc_target_output_section;
153   bfd_size_type addr = reloc_entry->address;
154   bfd_reloc_status_type flag = bfd_reloc_ok;
155   bfd_vma output_base = 0;
156   reloc_howto_type *howto = reloc_entry->howto;
157   int mask, max;
158 
159   if (output_bfd != NULL)
160     {
161       /* Partial linking -- do nothing.  */
162       reloc_entry->address += input_section->output_offset;
163       return bfd_reloc_ok;
164     }
165 
166   r = bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data,
167                                    input_section, output_bfd, error_message);
168   if (r != bfd_reloc_continue)
169     return r;
170 
171   /* A hacked-up version of bfd_perform_reloc() follows.  */
172   if (bfd_is_und_section (symbol->section)
173       && (symbol->flags & BSF_WEAK) == 0
174       && output_bfd == NULL)
175     flag = bfd_reloc_undefined;
176 
177   /* Is the address of the relocation really within the section?  */
178   if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
179     return bfd_reloc_outofrange;
180 
181   /* Work out which section the relocation is targeted at and the
182      initial relocation command value.  */
183 
184   /* Get symbol value.  (Common symbols are special.)  */
185   if (bfd_is_com_section (symbol->section))
186     relocation = 0;
187   else
188     relocation = symbol->value;
189 
190   reloc_target_output_section = symbol->section->output_section;
191 
192   /* Convert input-section-relative symbol value to absolute.  */
193   output_base = reloc_target_output_section->vma;
194   relocation += output_base + symbol->section->output_offset;
195 
196   /* Add in supplied addend.  */
197   relocation += reloc_entry->addend;
198 
199   /* Here the variable relocation holds the final address of the
200      symbol we are relocating against, plus any addend.  */
201 
202   if (howto->pc_relative)
203     {
204       relocation -= (input_section->output_section->vma
205                          + input_section->output_offset);
206       if (howto->pcrel_offset)
207           relocation -= reloc_entry->address;
208     }
209 
210   in1 = bfd_get_32 (abfd, (bfd_byte *) data + addr);
211 
212   mask =  (1 << howto->bitsize) - 1;
213   if (howto->bitsize == 6)
214     mask <<= 12;
215   max = (1 << (howto->bitsize + 2)) - 1;
216 
217   /* Extract the addend.  */
218   num = in1 & mask;  /* 18 bits.  */
219   if (howto->bitsize == 6)
220     num >>= 12;
221   num <<= 3; /* shift left 3.  */
222   in1 &= ~mask;  /* Mask out addend.  */
223 
224   relocation += num;
225   if (howto->type == R_D30V_21_PCREL_R
226       || howto->type == R_D30V_15_PCREL_R
227       || howto->type == R_D30V_9_PCREL_R)
228     relocation += 4;
229 
230   if ((int) relocation < 0)
231     {
232       if (~ (int) relocation > max)
233           flag = bfd_reloc_overflow;
234     }
235   else
236     {
237       if ((int) relocation > max)
238           flag = bfd_reloc_overflow;
239     }
240 
241   relocation >>= 3;
242   if (howto->bitsize == 6)
243     in1 |= ((relocation & (mask >> 12)) << 12);
244   else
245     in1 |= relocation & mask;
246 
247   bfd_put_32 (abfd, in1, (bfd_byte *) data + addr);
248 
249   return flag;
250 }
251 
252 static reloc_howto_type elf_d30v_howto_table[] =
253 {
254   /* This reloc does nothing.  */
255   HOWTO (R_D30V_NONE,                   /* Type.  */
256            0,                           /* Rightshift.  */
257            0,                           /* Size.  */
258            0,                           /* Bitsize.  */
259            false,                       /* PC_relative.  */
260            0,                           /* Bitpos.  */
261            complain_overflow_dont, /* Complain_on_overflow.  */
262            bfd_elf_generic_reloc,       /* Special_function.  */
263            "R_D30V_NONE",               /* Name.  */
264            false,                       /* Partial_inplace.  */
265            0,                           /* Src_mask.  */
266            0,                           /* Dst_mask.  */
267            false),            /* PCrel_offset.  */
268 
269   /* A 6 bit absolute relocation.  */
270   HOWTO (R_D30V_6,            /* Type.  */
271            0,                           /* Rightshift.  */
272            4,                           /* Size.  */
273            6,                           /* Bitsize.  */
274            false,                       /* PC_relative.  */
275            0,                           /* Bitpos.  */
276            complain_overflow_bitfield, /* Complain_on_overflow.  */
277            bfd_elf_generic_reloc,       /* Special_function.  */
278            "R_D30V_6",                  /* Name.  */
279            false,                       /* Partial_inplace.  */
280            0x3f,                        /* Src_mask.  */
281            0x3f,                        /* Dst_mask.  */
282            false),            /* PCrel_offset.  */
283 
284   /* A relative 9 bit relocation, right shifted by 3.  */
285   HOWTO (R_D30V_9_PCREL,      /* Type.  */
286            3,                           /* Rightshift.  */
287            4,                           /* Size.  */
288            6,                           /* Bitsize.  */
289            true,                        /* PC_relative.  */
290            0,                           /* Bitpos.  */
291            complain_overflow_signed, /* Complain_on_overflow.  */
292            bfd_elf_d30v_reloc_21,       /* Special_function.  */
293            "R_D30V_9_PCREL",  /* Name.  */
294            false,                       /* Partial_inplace.  */
295            0x3f,                        /* Src_mask.  */
296            0x3f,                        /* Dst_mask.  */
297            true),                       /* PCrel_offset.  */
298 
299   /* A relative 9 bit relocation, right shifted by 3.  */
300   HOWTO (R_D30V_9_PCREL_R,    /* Type.  */
301            3,                           /* Rightshift.  */
302            4,                           /* Size.  */
303            6,                           /* Bitsize.  */
304            true,                        /* PC_relative.  */
305            0,                           /* Bitpos.  */
306            complain_overflow_signed, /* Complain_on_overflow.  */
307            bfd_elf_d30v_reloc_21,       /* Special_function.  */
308            "R_D30V_9_PCREL_R",          /* Name.  */
309            false,                       /* Partial_inplace.  */
310            0x3f,                        /* Src_mask.  */
311            0x3f,                        /* Dst_mask.  */
312            true),                       /* PCrel_offset.  */
313 
314   /* An absolute 15 bit relocation, right shifted by 3.  */
315   HOWTO (R_D30V_15,           /* Type.  */
316            3,                           /* Rightshift.  */
317            4,                           /* Size.  */
318            12,                          /* Bitsize.  */
319            false,                       /* PC_relative.  */
320            0,                           /* Bitpos.  */
321            complain_overflow_signed, /* Complain_on_overflow.  */
322            bfd_elf_generic_reloc,       /* Special_function.  */
323            "R_D30V_15",                 /* Name.  */
324            false,                       /* Partial_inplace.  */
325            0xfff,                       /* Src_mask.  */
326            0xfff,                       /* Dst_mask.  */
327            false),            /* PCrel_offset.  */
328 
329   /* A relative 15 bit relocation, right shifted by 3.  */
330   HOWTO (R_D30V_15_PCREL,     /* Type.  */
331            3,                           /* Rightshift.  */
332            4,                           /* Size.  */
333            12,                          /* Bitsize.  */
334            true,                        /* PC_relative.  */
335            0,                           /* Bitpos.  */
336            complain_overflow_signed, /* Complain_on_overflow.  */
337            bfd_elf_d30v_reloc_21,       /* Special_function.  */
338            "R_D30V_15_PCREL", /* Name.  */
339            false,                       /* Partial_inplace.  */
340            0xfff,                       /* Src_mask.  */
341            0xfff,                       /* Dst_mask.  */
342            true),                       /* PCrel_offset.  */
343 
344   /* A relative 15 bit relocation, right shifted by 3.  */
345   HOWTO (R_D30V_15_PCREL_R,   /* Type.  */
346            3,                           /* Rightshift.  */
347            4,                           /* Size.  */
348            12,                          /* Bitsize.  */
349            true,                        /* PC_relative.  */
350            0,                           /* Bitpos.  */
351            complain_overflow_signed, /* Complain_on_overflow.  */
352            bfd_elf_d30v_reloc_21,       /* Special_function.  */
353            "R_D30V_15_PCREL_R",         /* Name.  */
354            false,                       /* Partial_inplace.  */
355            0xfff,                       /* Src_mask.  */
356            0xfff,                       /* Dst_mask.  */
357            true),                       /* PCrel_offset.  */
358 
359   /* An absolute 21 bit relocation, right shifted by 3.  */
360   HOWTO (R_D30V_21,           /* Type.  */
361            3,                           /* Rightshift.  */
362            4,                           /* Size.  */
363            18,                          /* Bitsize.  */
364            false,                       /* PC_relative.  */
365            0,                           /* Bitpos.  */
366            complain_overflow_signed, /* Complain_on_overflow.  */
367            bfd_elf_generic_reloc,       /* Special_function.  */
368            "R_D30V_21",                 /* Name.  */
369            false,                       /* Partial_inplace.  */
370            0x3ffff,           /* Src_mask.  */
371            0x3ffff,           /* Dst_mask.  */
372            false),            /* PCrel_offset.  */
373 
374   /* A relative 21 bit relocation, right shifted by 3.  */
375   HOWTO (R_D30V_21_PCREL,     /* Type.  */
376            3,                           /* Rightshift.  */
377            4,                           /* Size.  */
378            18,                          /* Bitsize.  */
379            true,                        /* PC_relative.  */
380            0,                           /* Bitpos.  */
381            complain_overflow_signed, /* Complain_on_overflow.  */
382            bfd_elf_d30v_reloc_21,       /* Special_function.  */
383            "R_D30V_21_PCREL", /* Name.  */
384            false,                       /* Partial_inplace.  */
385            0x3ffff,           /* Src_mask.  */
386            0x3ffff,           /* Dst_mask.  */
387            true),                       /* PCrel_offset.  */
388 
389   /* A relative 21 bit relocation, right shifted by 3, in the Right container.  */
390   HOWTO (R_D30V_21_PCREL_R,   /* Type.  */
391            3,                           /* Rightshift.  */
392            4,                           /* Size.  */
393            18,                          /* Bitsize.  */
394            true,                        /* PC_relative.  */
395            0,                           /* Bitpos.  */
396            complain_overflow_signed, /* Complain_on_overflow.  */
397            bfd_elf_d30v_reloc_21,       /* Special_function.  */
398            "R_D30V_21_PCREL_R",         /* Name.  */
399            false,                       /* Partial_inplace.  */
400            0x3ffff,           /* Src_mask.  */
401            0x3ffff,           /* Dst_mask.  */
402            true),                       /* PCrel_offset.  */
403 
404   /* A D30V 32 bit absolute relocation.  */
405   HOWTO (R_D30V_32,           /* Type.  */
406            0,                           /* Rightshift.  */
407            8,                           /* Size.  */
408            32,                          /* Bitsize.  */
409            false,                       /* PC_relative.  */
410            0,                           /* Bitpos.  */
411            complain_overflow_bitfield, /* Complain_on_overflow.  */
412            bfd_elf_d30v_reloc,          /* Special_function.  */
413            "R_D30V_32",                 /* Name.  */
414            false,                       /* Partial_inplace.  */
415            0xffffffff,                  /* Src_mask.  */
416            0xffffffff,                  /* Dst_mask.  */
417            false),            /* PCrel_offset.  */
418 
419   /* A relative 32 bit relocation.  */
420   HOWTO (R_D30V_32_PCREL,     /* Type.  */
421            0,                           /* Rightshift.  */
422            8,                           /* Size.  */
423            32,                          /* Bitsize.  */
424            true,                        /* PC_relative.  */
425            0,                           /* Bitpos.  */
426            complain_overflow_signed, /* Complain_on_overflow.  */
427            bfd_elf_d30v_reloc,          /* Special_function.  */
428            "R_D30V_32_PCREL", /* Name.  */
429            false,                       /* Partial_inplace.  */
430            0xffffffff,                  /* Src_mask.  */
431            0xffffffff,                  /* Dst_mask.  */
432            true),                       /* PCrel_offset.  */
433 
434   /* A regular 32 bit absolute relocation.  */
435   HOWTO (R_D30V_32_NORMAL,    /* Type.  */
436            0,                           /* Rightshift.  */
437            4,                           /* Size.  */
438            32,                          /* Bitsize.  */
439            false,                       /* PC_relative.  */
440            0,                           /* Bitpos.  */
441            complain_overflow_bitfield, /* Complain_on_overflow.  */
442            bfd_elf_generic_reloc,       /* Special_function.  */
443            "R_D30V_32_NORMAL",          /* Name.  */
444            false,                       /* Partial_inplace.  */
445            0xffffffff,                  /* Src_mask.  */
446            0xffffffff,                  /* Dst_mask.  */
447            false),            /* PCrel_offset.  */
448 
449 };
450 
451 /* Map BFD reloc types to D30V ELF reloc types.  */
452 
453 struct d30v_reloc_map
454 {
455   bfd_reloc_code_real_type bfd_reloc_val;
456   unsigned char elf_reloc_val;
457 };
458 
459 static const struct d30v_reloc_map d30v_reloc_map[] =
460 {
461   { BFD_RELOC_NONE, R_D30V_NONE, },
462   { BFD_RELOC_D30V_6, R_D30V_6 },
463   { BFD_RELOC_D30V_9_PCREL, R_D30V_9_PCREL },
464   { BFD_RELOC_D30V_9_PCREL_R, R_D30V_9_PCREL_R },
465   { BFD_RELOC_D30V_15, R_D30V_15 },
466   { BFD_RELOC_D30V_15_PCREL, R_D30V_15_PCREL },
467   { BFD_RELOC_D30V_15_PCREL_R, R_D30V_15_PCREL_R },
468   { BFD_RELOC_D30V_21, R_D30V_21 },
469   { BFD_RELOC_D30V_21_PCREL, R_D30V_21_PCREL },
470   { BFD_RELOC_D30V_21_PCREL_R, R_D30V_21_PCREL_R },
471   { BFD_RELOC_D30V_32, R_D30V_32 },
472   { BFD_RELOC_D30V_32_PCREL, R_D30V_32_PCREL },
473   { BFD_RELOC_32, R_D30V_32_NORMAL },
474 };
475 
476 static reloc_howto_type *
bfd_elf32_bfd_reloc_type_lookup(bfd * abfd ATTRIBUTE_UNUSED,bfd_reloc_code_real_type code)477 bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
478                                          bfd_reloc_code_real_type code)
479 {
480   unsigned int i;
481 
482   for (i = 0;
483        i < sizeof (d30v_reloc_map) / sizeof (struct d30v_reloc_map);
484        i++)
485     {
486       if (d30v_reloc_map[i].bfd_reloc_val == code)
487           return &elf_d30v_howto_table[d30v_reloc_map[i].elf_reloc_val];
488     }
489 
490   return NULL;
491 }
492 
493 static reloc_howto_type *
bfd_elf32_bfd_reloc_name_lookup(bfd * abfd ATTRIBUTE_UNUSED,const char * r_name)494 bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
495                                          const char *r_name)
496 {
497   unsigned int i;
498 
499   for (i = 0;
500        i < sizeof (elf_d30v_howto_table) / sizeof (elf_d30v_howto_table[0]);
501        i++)
502     if (elf_d30v_howto_table[i].name != NULL
503           && strcasecmp (elf_d30v_howto_table[i].name, r_name) == 0)
504       return &elf_d30v_howto_table[i];
505 
506   return NULL;
507 }
508 
509 /* Set the howto pointer for an D30V ELF reloc (type REL).  */
510 
511 static bool
d30v_info_to_howto_rel(bfd * abfd,arelent * cache_ptr,Elf_Internal_Rela * dst)512 d30v_info_to_howto_rel (bfd *abfd,
513                               arelent *cache_ptr,
514                               Elf_Internal_Rela *dst)
515 {
516   unsigned int r_type;
517 
518   r_type = ELF32_R_TYPE (dst->r_info);
519   if (r_type >= (unsigned int) R_D30V_max)
520     {
521       /* xgettext:c-format */
522       _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
523                                 abfd, r_type);
524       bfd_set_error (bfd_error_bad_value);
525       return false;
526     }
527   cache_ptr->howto = &elf_d30v_howto_table[r_type];
528   return true;
529 }
530 
531 /* Set the howto pointer for an D30V ELF reloc (type RELA).  */
532 
533 static bool
d30v_info_to_howto_rela(bfd * abfd,arelent * cache_ptr,Elf_Internal_Rela * dst)534 d30v_info_to_howto_rela (bfd *abfd,
535                                arelent *cache_ptr,
536                                Elf_Internal_Rela *dst)
537 {
538   unsigned int r_type;
539 
540   r_type = ELF32_R_TYPE (dst->r_info);
541   if (r_type >= (unsigned int) R_D30V_max)
542     {
543       /* xgettext:c-format */
544       _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
545                                 abfd, r_type);
546       bfd_set_error (bfd_error_bad_value);
547       return false;
548     }
549   cache_ptr->howto = &elf_d30v_howto_table[r_type];
550   return true;
551 }
552 
553 #define ELF_ARCH              bfd_arch_d30v
554 #define ELF_MACHINE_CODE      EM_D30V
555 #define ELF_MACHINE_ALT1      EM_CYGNUS_D30V
556 #define ELF_MAXPAGESIZE                 0x1000
557 
558 #define TARGET_BIG_SYM                  d30v_elf32_vec
559 #define TARGET_BIG_NAME                 "elf32-d30v"
560 
561 #define elf_info_to_howto     d30v_info_to_howto_rela
562 #define elf_info_to_howto_rel d30v_info_to_howto_rel
563 #define elf_backend_object_p  0
564 
565 #include "elf32-target.h"
566