source: rtems/cpukit/libdl/rtl-mdreloc-arm.c @ 6c9f017

Last change on this file since 6c9f017 was 6c9f017, checked in by Chris Johns <chrisj@…>, on Feb 2, 2019 at 4:09:53 AM

libdl: Add powerpc large memory and small data support.

  • Add support for architecure sections that can be handled by the architecture back end.
  • Add trampoline/fixup support for PowerPC. This means the PowerPC now supports large memory loading of applications.
  • Add a bit allocator to manage small block based regions of memory.
  • Add small data (sdata/sbss) support for the PowerPC. The support makes the linker allocated small data region of memory a global resource available to libdl loaded object files.

Updates #3687
Updates #3685

  • Property mode set to 100644
File size: 18.6 KB
Line 
1/*
2 * Taken from NetBSD and stripped of the relocations not needed on RTEMS.
3 */
4
5/*  $NetBSD: mdreloc.c,v 1.33 2010/01/14 12:12:07 skrll Exp $  */
6
7#include <sys/cdefs.h>
8
9#include <errno.h>
10#include <inttypes.h>
11#include <stdio.h>
12#include <string.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <unwind.h>
16#include <unwind-arm-common.h>
17
18#include <rtems/rtl/rtl.h>
19#include "rtl-elf.h"
20#include "rtl-error.h"
21#include <rtems/rtl/rtl-trace.h>
22#include "rtl-unwind.h"
23
24/*
25 * Set to 1 to allow untested relocations. If you tested one and it
26 * works or you fixed the relocation please remove the guard.
27 */
28#define ALLOW_UNTESTED_RELOCS 1
29
30/*
31 * It is possible for the compiler to emit relocations for unaligned data.
32 * We handle this situation with these inlines.
33 */
34#define RELOC_ALIGNED_P(x) \
35        (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
36
37#define SHT_ARM_EXIDX  0x70000001 /* Section holds ARM unwind info. */
38
39static inline Elf_Addr
40load_ptr(void *where)
41{
42  Elf_Addr res;
43
44  memcpy(&res, where, sizeof(res));
45
46  return (res);
47}
48
49static inline void
50store_ptr(void *where, Elf_Addr val)
51{
52  memcpy(where, &val, sizeof(val));
53}
54
55/*
56 * The address of Thumb function symbols is it's real address plus one.
57 * This is done by compiler, thus do not consider symtype here.
58 */
59static inline int
60isThumb(Elf_Word symvalue)
61{
62  if ((symvalue & 0x1) == 0x1)
63    return true;
64  else return false;
65}
66
67static inline Elf_SOff
68sign_extend31(Elf_Addr val)
69{
70  if (0x40000000 & val)
71    val =  ~((Elf_Addr)0x7fffffff) | (0x7fffffff & val);
72  return 0x7fffffff & val;
73}
74
75static void*
76set_veneer(void* tramopline, Elf_Addr target)
77{
78  /*
79   * http://shell-storm.org/online/Online-Assembler-and-Disassembler/
80   *
81   *  ldr.w pc, [pc]
82   */
83  uint32_t* tramp = (uint32_t*) tramopline;
84  *tramp++ = 0xf000f8df;
85  *tramp++ = (uint32_t) target;
86  return tramp;
87}
88
89static size_t
90get_veneer_size(int type)
91{
92  (void) type;
93  return 8;
94}
95
96size_t
97rtems_rtl_elf_relocate_tramp_max_size (void)
98{
99  return 8;
100}
101
102uint32_t
103rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
104                             const Elf_Shdr*      shdr)
105{
106  uint32_t flags = 0;
107  if (shdr->sh_type == SHT_ARM_EXIDX)
108    flags = RTEMS_RTL_OBJ_SECT_EH | RTEMS_RTL_OBJ_SECT_LOAD;
109  return flags;
110}
111
112uint32_t
113rtems_rtl_elf_arch_parse_section (const rtems_rtl_obj* obj,
114                                  int                  section,
115                                  const char*          name,
116                                  const Elf_Shdr*      shdr,
117                                  const uint32_t       flags)
118{
119  (void) obj;
120  (void) section;
121  (void) name;
122  (void) shdr;
123  return flags;
124}
125
126bool
127rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj,
128                                  rtems_rtl_obj_sect*  sect)
129{
130  (void) obj;
131  (void) sect;
132  return false;
133}
134
135bool
136rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj,
137                                  rtems_rtl_obj_sect*  sect)
138{
139  (void) obj;
140  (void) sect;
141  return false;
142}
143
144bool
145rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
146{
147  return true;
148}
149
150bool
151rtems_rtl_elf_relocate_rela_tramp (rtems_rtl_obj*            obj,
152                                   const Elf_Rela*           rela,
153                                   const rtems_rtl_obj_sect* sect,
154                                   const char*               symname,
155                                   const Elf_Byte            syminfo,
156                                   const Elf_Word            symvalue)
157{
158  (void) obj;
159  (void) rela;
160  (void) sect;
161  (void) symname;
162  (void) syminfo;
163  (void) symvalue;
164  rtems_rtl_set_error (EINVAL, "rela type record not supported");
165  return false;
166}
167
168bool
169rtems_rtl_elf_relocate_rela (rtems_rtl_obj*            obj,
170                             const Elf_Rela*           rela,
171                             const rtems_rtl_obj_sect* sect,
172                             const char*               symname,
173                             const Elf_Byte            syminfo,
174                             const Elf_Word            symvalue)
175{
176  (void) obj;
177  (void) rela;
178  (void) sect;
179  (void) symname;
180  (void) syminfo;
181  (void) symvalue;
182  rtems_rtl_set_error (EINVAL, "rela type record not supported");
183  return false;
184}
185
186static bool
187rtems_rtl_elf_reloc_rel (rtems_rtl_obj*            obj,
188                         const Elf_Rel*            rel,
189                         const rtems_rtl_obj_sect* sect,
190                         const char*               symname,
191                         const Elf_Byte            syminfo,
192                         const Elf_Word            symvalue,
193                         const bool                parsing)
194{
195  Elf_Addr *where;
196  Elf_Addr tmp;
197  Elf_Word insn, addend;
198  Elf_Word sign, i1, i2;
199  uint16_t lower_insn, upper_insn;
200
201  where = (Elf_Addr *)(sect->base + rel->r_offset);
202
203  switch (ELF_R_TYPE(rel->r_info)) {
204    case R_TYPE(NONE):
205      if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
206        printf ("rtl: NONE %p in %s\n", where, rtems_rtl_obj_oname (obj));
207      }
208      break;
209
210    case R_TYPE(CALL):    /* BL/BLX */
211    case R_TYPE(JUMP24):  /* B/BL<cond> */
212      insn = *where;
213
214      if (insn & 0x00800000)
215        addend = insn | 0xff000000;
216      else addend = insn & 0x00ffffff;
217
218      if (isThumb(symvalue)) {
219        if ((insn & 0xfe000000) == 0xfa000000);         /* Already blx */
220        else {
221          if ((insn & 0xff000000) == 0xeb000000) {      /* BL <label> */
222            *where = (insn & 0x00ffffff) | 0xfa000000;  /* BL-->BLX */
223          } else {
224            printf("JUMP24 is not suppored from arm to thumb\n");
225            return false;
226          }
227        }
228      }
229
230      tmp = symvalue + (addend << 2) - (Elf_Addr)where;
231      tmp = (Elf_Sword)tmp >> 2;
232
233      if (((Elf_Sword)tmp > 0x7fffff) || ((Elf_Sword)tmp < -0x800000)) {
234        Elf_Word tramp_addr;
235        size_t   tramp_size = get_veneer_size(ELF_R_TYPE(rel->r_info));
236
237        if (parsing) {
238          obj->tramp_size += tramp_size;
239          return true;
240        }
241
242        if (!rtems_rtl_obj_has_ramp_space (obj, tramp_size)) {
243          rtems_rtl_set_error (EINVAL,
244                               "%s: CALL/JUMP24: overflow: no tramp memory",
245                               sect->name);
246          return false;
247        }
248
249        tramp_addr = ((Elf_Addr) obj->tramp_brk) | (symvalue & 1);
250        obj->tramp_brk = set_veneer(obj->tramp_brk, symvalue);
251
252        tmp = tramp_addr + (addend << 2) - (Elf_Addr)where;
253        tmp = (Elf_Sword)tmp >> 2;
254      }
255
256      if (!parsing) {
257        *where = (*where & 0xff000000) | (tmp & 0xffffff);
258
259        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
260          printf ("rtl: JUMP24/PC24/CALL %p @ %p in %s\n",
261                  (void *)*where, where, rtems_rtl_obj_oname (obj));
262      }
263      break;
264
265    case R_TYPE(V4BX):
266      /* Miscellaneous, ignore */
267      if (!parsing && rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
268        printf ("rtl: V4BX %p @ %p in %s\n",
269                (void *)*where, where, rtems_rtl_obj_oname (obj));
270      }
271      break;
272
273    case R_TYPE(MOVT_ABS):
274    case R_TYPE(MOVW_ABS_NC):
275      insn = *where;
276
277      addend = ((insn >> 4) & 0xf000) | (insn & 0x0fff);
278      if (addend & 0x8000)
279        addend |= 0xffff0000;
280
281      tmp = symvalue + addend;
282
283      if (ELF_R_TYPE(rel->r_info) == R_TYPE(MOVW_ABS_NC))
284        tmp &= 0xffff;
285      else {
286        tmp = (Elf_Sword)tmp >> 16;
287        if (((Elf_Sword)tmp > 0x7fff) || ((Elf_Sword)tmp < -0x8000)) {
288          printf("MOVT_ABS Overflow\n");
289          return false;
290        }
291      }
292
293      if (!parsing) {
294        *where = (insn & 0xfff0f000) | ((tmp & 0xf000) << 4) | (tmp & 0xfff);
295
296        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
297          printf ("rtl: MOVT_ABS/MOVW_ABS_NC %p @ %p in %s\n",
298                  (void *)*where, where, rtems_rtl_obj_oname (obj));
299      }
300      break;
301
302    case R_TYPE(REL32):     /* word32 (S + A) | T - P */
303    case R_TYPE(ABS32):     /* word32 (S + A) | T */
304    case R_TYPE(GLOB_DAT):  /* word32 (S + A) | T */
305    case R_TYPE(PREL31):    /* word32 (S + A) | T - P */
306    case R_TYPE(TARGET1):   /* Equivalent to ABS32 */
307    case R_TYPE(TARGET2):   /* Equivalent to REL32 */
308      if (!parsing) {
309        if (__predict_true(RELOC_ALIGNED_P(where))) {
310          tmp = *where + symvalue;
311          if (isThumb(symvalue))
312            tmp |= 1;
313          if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
314              ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
315            tmp -= (Elf_Addr)where;
316          else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
317            tmp = sign_extend31(tmp - (Elf_Addr)where);
318          if (!parsing) {
319            *where = tmp;
320          }
321        } else {
322          tmp = load_ptr(where) + symvalue;
323          if (isThumb(symvalue))
324            tmp |= 1;
325          if (ELF_R_TYPE(rel->r_info) == R_TYPE(REL32) ||
326              ELF_R_TYPE(rel->r_info) == R_TYPE(TARGET2))
327            tmp -= (Elf_Addr)where;
328          else if (ELF_R_TYPE(rel->r_info) == R_TYPE(PREL31))
329            tmp = sign_extend31(tmp - (Elf_Addr)where);
330          store_ptr(where, tmp);
331        }
332
333        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
334          printf ("rtl: REL32/ABS32/GLOB_DAT/PREL31/TARGET2 %p @ %p in %s\n",
335                  (void *)tmp, where, rtems_rtl_obj_oname (obj));
336      }
337      break;
338
339    case R_TYPE(THM_MOVT_ABS):
340    case R_TYPE(THM_MOVW_ABS_NC):
341      if (!parsing) {
342        upper_insn = *(uint16_t *)where;
343        lower_insn = *((uint16_t *)where + 1);
344
345        addend = ((upper_insn & 0x000f) << 12) | ((upper_insn & 0x0400) << 1) |
346          ((lower_insn & 0x7000) >> 4) | (lower_insn & 0x00ff);
347        addend = (addend ^ 0x8000) - 0x8000;
348
349        tmp = addend + symvalue;
350        if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS)
351          tmp >>= 16;
352
353        *(uint16_t *)where = (uint16_t)((upper_insn & 0xfbf0) |
354                                        ((tmp & 0xf000) >> 12) |
355                                        ((tmp & 0x0800) >> 1));
356        *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0x8f00) |
357                                              ((tmp & 0x0700) << 4) |
358                                              (tmp & 0x00ff));
359
360        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) {
361          printf ("rtl: THM_MOVT_ABS/THM_MOVW_ABS_NC %p @ %p in %s\n",
362                  (void *)*where, where, rtems_rtl_obj_oname (obj));
363        }
364      }
365      break;
366
367    case R_TYPE(THM_JUMP24):
368      /* same as THM_PC22; insn b.w */
369    case R_TYPE(THM_PC22):
370      upper_insn = *(uint16_t *)where;
371      lower_insn = *((uint16_t *)where + 1);
372      sign = (upper_insn & (1 << 10)) >> 10;
373      i1 = ((lower_insn >> 13) & 1) ^ sign ? 0 : 1;
374      i2 = ((lower_insn >> 11) & 1) ^ sign ? 0 : 1;
375      tmp = (i1 << 23) | (i2 << 22) | ((upper_insn & 0x3ff) << 12) | ((lower_insn & 0x7ff) << 1);
376      addend = (tmp | ((sign ? 0 : 1) << 24)) - (1 << 24);
377
378      if (isThumb(symvalue)) ;/*Thumb to Thumb call, nothing to care */
379      else {
380        if (ELF_R_TYPE(rel->r_info) == R_TYPE(THM_JUMP24)) {
381          tmp = (tmp + 2) & ~3; /* aligned to 4 bytes only for JUMP24 */
382#if !ALLOW_UNTESTED_RELOCS
383          printf("THM_JUMP24 to arm not supported\n");
384          return false;
385#endif
386        }
387        else {
388          /* THM_CALL bl-->blx */
389          lower_insn &=~(1<<12);
390        }
391      }
392
393      tmp = symvalue + addend;
394      tmp = tmp - (Elf_Addr)where;
395
396      if (((Elf_Sword)tmp > 0x7fffff) || ((Elf_Sword)tmp < -0x800000)) {
397          Elf_Word tramp_addr;
398          size_t   tramp_size = get_veneer_size(ELF_R_TYPE(rel->r_info));
399
400        if (parsing) {
401          obj->tramp_size += tramp_size;
402          return true;
403        }
404
405        if (!rtems_rtl_obj_has_ramp_space (obj, tramp_size)) {
406          rtems_rtl_set_error (EINVAL,
407                               "%s: THM_CALL/JUMP24: overflow: no tramp memory",
408                               sect->name);
409          return false;
410        }
411
412        tramp_addr = ((Elf_Addr) obj->tramp_brk) | (symvalue & 1);
413        obj->tramp_brk = set_veneer(obj->tramp_brk, symvalue);
414
415
416        tmp = tramp_addr + addend;
417        tmp = tmp - (Elf_Addr)where;
418      }
419
420      if (!parsing) {
421        sign = (tmp >> 24) & 1;
422        *(uint16_t *)where = (uint16_t)((upper_insn & 0xf800) | (sign << 10) |
423                                        ((tmp >> 12) & 0x3ff));
424
425        *((uint16_t *)where + 1) = (uint16_t)((lower_insn & 0xd000)|
426                                              ((sign ^ (~(tmp >> 23) & 1)) << 13) |
427                                              ((sign ^ (~(tmp >> 22) & 1)) << 11) |
428                                              ((tmp >> 1) & 0x7ff));
429
430        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)){
431          printf ("rtl: THM_CALL/JUMP24 %p @ %p in %s\n",
432                  (void *)*where, where, rtems_rtl_obj_oname (obj));
433        }
434      }
435      break;
436
437    case R_TYPE(THM_JUMP19):
438
439      if (!isThumb(symvalue)) {
440        printf("THM_JUMP19 to arm not supported\n");
441        return false;
442      }
443
444      upper_insn = *(uint16_t *)where;
445      lower_insn = *((uint16_t *)where + 1);
446      sign = (upper_insn >> 10) & 0x1;
447
448      if ((((upper_insn & 0x3f) >> 7) & 0x7) == 0x7) {
449        printf("THM_JUMP19 failed\n");
450        return false; /*if cond <3:1> == '111', see Related codings in armv7a manual */
451      }
452
453      i1 = (lower_insn >> 13) & 0x1;
454      i2 = (lower_insn >> 11) & 0x1;
455
456      tmp = ((i2 << 19) | (i1 << 18) | ((upper_insn & 0x3f) << 12) | ((lower_insn & 0x7ff) << 1));
457      addend = (tmp | ((sign ? 0 : 1) << 20)) - (1 << 20);
458      tmp = symvalue + addend;
459
460      tmp = tmp - (Elf_Addr)where;
461
462      if (((Elf_Sword)tmp > 0x7ffffe) || ((Elf_Sword)tmp < -0x800000)) {
463        rtems_rtl_set_error (EINVAL, "%s: Overflow %" PRIu32 " "
464                             "THM_JUMP19 relocations",
465                             sect->name, (uint32_t) ELF_R_TYPE(rel->r_info));
466        return true;
467        return false;
468      }
469
470      sign = (tmp >> 20) & 0x1;
471      i2 = (tmp >> 19) & 0x1;
472      i1 = (tmp >> 18) & 0x1;
473
474      *(uint16_t*)where = (upper_insn & 0xfbc0) | (sign << 10) | ((tmp >> 12) & 0x3f);
475      *((uint16_t*)where + 1) = (lower_insn & 0xd000) | (i1 << 13) |
476                                (i2 << 11) | ((tmp >> 1) & 0x7ff);
477
478      if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
479        printf ("rtl: THM_JUMP19 %p @ %p in %s\n",
480                (void *)*where, where, rtems_rtl_obj_oname (obj));
481      break;
482
483    case R_TYPE(TLS_LE32):
484#if ALLOW_UNTESTED_RELOCS
485      if (!parsing) {
486        addend = *where;
487        *where = symvalue + addend;
488        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
489          printf ("rtl: TLS_LE32 %p @ %p in %s\n",
490                  (void *)*where, where, rtems_rtl_obj_oname (obj));
491      }
492      break;
493#endif
494    case R_TYPE(TLS_GD32):
495    case R_TYPE(TLS_LDM32):
496    case R_TYPE(TLS_LDO32):
497    case R_TYPE(TLS_IE32):
498    case R_TYPE(TLS_LDO12):
499    case R_TYPE(TLS_LE12):
500    case R_TYPE(TLS_IE12GP):
501      printf("TSL relocations not supported\n");
502
503    default:
504      printf ("rtl: reloc unknown: sym = %" PRIu32 ", type = %" PRIu32 ", offset = %p",
505              ELF_R_SYM(rel->r_info), (uint32_t) ELF_R_TYPE(rel->r_info),
506                (void *)rel->r_offset);
507      if (!parsing)
508        printf("contents = %p", (void *)*where);
509      printf("\n");
510      rtems_rtl_set_error (EINVAL,
511                           "%s: Unsupported relocation type %" PRIu32 " "
512                           "in non-PLT relocations",
513                           sect->name, (uint32_t) ELF_R_TYPE(rel->r_info));
514      return false;
515  }
516
517  return true;
518}
519
520bool
521rtems_rtl_elf_relocate_rel_tramp (rtems_rtl_obj*            obj,
522                                  const Elf_Rel*            rel,
523                                  const rtems_rtl_obj_sect* sect,
524                                  const char*               symname,
525                                  const Elf_Byte            syminfo,
526                                  const Elf_Word            symvalue)
527{
528  return rtems_rtl_elf_reloc_rel (obj,
529                                  rel,
530                                  sect,
531                                  symname,
532                                  syminfo,
533                                  symvalue,
534                                  true);
535}
536
537bool
538rtems_rtl_elf_relocate_rel (rtems_rtl_obj*            obj,
539                            const Elf_Rel*            rel,
540                            const rtems_rtl_obj_sect* sect,
541                            const char*               symname,
542                            const Elf_Byte            syminfo,
543                            const Elf_Word            symvalue)
544{
545  return rtems_rtl_elf_reloc_rel (obj,
546                                  rel,
547                                  sect,
548                                  symname,
549                                  syminfo,
550                                  symvalue,
551                                  false);
552}
553
554bool
555rtems_rtl_elf_unwind_parse (const rtems_rtl_obj* obj,
556                            const char*          name,
557                            uint32_t             flags)
558{
559  /*
560   * We location the EH sections in section flags.
561   */
562  return false;
563}
564
565bool
566rtems_rtl_elf_unwind_register (rtems_rtl_obj* obj)
567{
568  return true;
569}
570
571bool
572rtems_rtl_elf_unwind_deregister (rtems_rtl_obj* obj)
573{
574  obj->loader = NULL;
575  return true;
576}
577
578/* An exception index table entry.  */
579typedef struct __EIT_entry
580{
581  _uw fnoffset;
582  _uw content;
583} __EIT_entry;
584
585/* The exception index table location in the base module */
586extern __EIT_entry __exidx_start;
587extern __EIT_entry __exidx_end;
588
589/*
590 * A weak reference is in libgcc, provide a real version and provide a way to
591 * manage loaded modules.
592 *
593 * Passed in the return address and a reference to the number of records
594 * found. We set the start of the exidx data and the number of records.
595 */
596_Unwind_Ptr __gnu_Unwind_Find_exidx (_Unwind_Ptr return_address,
597                                     int*        nrec) __attribute__ ((__noinline__,
598                                                                       __used__,
599                                                                       __noclone__));
600
601_Unwind_Ptr __gnu_Unwind_Find_exidx (_Unwind_Ptr return_address,
602                                     int*        nrec)
603{
604  rtems_rtl_data*   rtl;
605  rtems_chain_node* node;
606  __EIT_entry*      exidx_start = &__exidx_start;
607  __EIT_entry*      exidx_end = &__exidx_end;
608
609  rtl = rtems_rtl_lock ();
610
611  node = rtems_chain_first (&rtl->objects);
612  while (!rtems_chain_is_tail (&rtl->objects, node)) {
613    rtems_rtl_obj* obj = (rtems_rtl_obj*) node;
614    if (rtems_rtl_obj_text_inside (obj, (void*) return_address)) {
615      exidx_start = (__EIT_entry*) obj->eh_base;
616      exidx_end = (__EIT_entry*) (obj->eh_base + obj->eh_size);
617      break;
618    }
619    node = rtems_chain_next (node);
620  }
621
622  rtems_rtl_unlock ();
623
624  *nrec = exidx_end - exidx_start;
625
626  return (_Unwind_Ptr) exidx_start;
627}
Note: See TracBrowser for help on using the repository browser.