source: rtems/cpukit/libdl/rtl-mdreloc-powerpc.c @ ec1dd51a

Last change on this file since ec1dd51a was ec1dd51a, checked in by Chris Johns <chrisj@…>, on Mar 6, 2019 at 10:15:56 AM

libdl: Add small data support to the remaining PowerPC BSPs.

Updates #3687

  • Property mode set to 100644
File size: 14.2 KB
Line 
1/*
2 * Taken from NetBSD and stripped of the relocations not needed on RTEMS.
3 */
4
5/*  $NetBSD: ppc_reloc.c,v 1.44 2010/01/13 20:17:22 christos Exp $  */
6
7#include <sys/cdefs.h>
8
9#include <errno.h>
10#include <inttypes.h>
11#include <stdio.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14
15#include <rtems/rtl/rtl.h>
16#include "rtl-bit-alloc.h"
17#include "rtl-elf.h"
18#include "rtl-error.h"
19#include <rtems/rtl/rtl-trace.h>
20#include "rtl-unwind.h"
21#include "rtl-unwind-dw2.h"
22
23#define ha(x) ((((u_int32_t)(x) & 0x8000) ? \
24                 ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
25#define l(x) ((u_int32_t)(x) & 0xffff)
26
27/*
28 * SDATA allocator.
29 */
30static rtems_rtl_bit_alloc* sdata;
31
32static Elf_Addr
33get_sda_base (void)
34{
35  uint32_t sda_base;
36  __asm__ volatile (" mr %0, 13\n" : "=r" (sda_base));
37  return sda_base;
38}
39
40/*
41 *  Access the variables via asm statements to avoid any fix up issues
42 *  generated by the C compiler which thinks they belong in the .sdata
43 *  section.
44 */
45
46#define GET_ADDR(_l, _v) \
47  __asm__ volatile (" lis %0, " #_l "@h\n" \
48                    " ori %0, %0, " #_l "@l\n" : "=r" (_v))
49
50static void*
51get_sdata_start (void)
52{
53#if _ARCH_PPC64
54  return NULL;
55#else
56  Elf_Addr addr;
57  GET_ADDR(__SDATA_START__, addr);
58  return (void*) addr;
59#endif
60}
61
62#if !_ARCH_PPC64
63static size_t
64get_sdata_sbss_size (void)
65{
66  Elf_Addr sdata_begin;
67  Elf_Addr sbss_end;
68  GET_ADDR(bsp_section_sdata_begin, sdata_begin);
69  GET_ADDR(bsp_section_sbss_end, sbss_end);
70  return sbss_end - sdata_begin;
71}
72
73static size_t
74get_sdata_libdl_size (void)
75{
76  Elf_Addr begin;
77  Elf_Addr end;
78  GET_ADDR(bsp_section_sdata_libdl_begin, begin);
79  GET_ADDR(bsp_section_sdata_libdl_end, end);
80  return end - begin;
81}
82#endif
83
84uint32_t
85rtems_rtl_elf_section_flags (const rtems_rtl_obj* obj,
86                             const Elf_Shdr*      shdr)
87{
88  return 0;
89}
90
91uint32_t
92rtems_rtl_elf_arch_parse_section (const rtems_rtl_obj* obj,
93                                  int                  section,
94                                  const char*          name,
95                                  const Elf_Shdr*      shdr,
96                                  const uint32_t       flags)
97{
98#if !_ARCH_PPC64
99  struct {
100    const char* label;
101    size_t      len;
102  } prefix[] = {
103    #define SEPARATED_PREFIX(_p) { _p, sizeof (_p) - 1 }
104    SEPARATED_PREFIX (".sdata"),
105    SEPARATED_PREFIX (".sbss"),
106  };
107  const size_t prefixes = sizeof (prefix) / sizeof (prefix[0]);
108  size_t       p;
109  for (p = 0; p < prefixes; ++p)
110  {
111    if (strncmp (name, prefix[p].label, prefix[p].len) == 0)
112      return flags | RTEMS_RTL_OBJ_SECT_ARCH_ALLOC;
113  }
114#endif
115  return flags;
116}
117
118bool
119rtems_rtl_elf_arch_section_alloc (const rtems_rtl_obj* obj,
120                                  rtems_rtl_obj_sect*  sect)
121{
122#if _ARCH_PPC64
123    rtems_rtl_set_error (ENOMEM, ".sdata no supported by ABI");
124    return false;
125#else
126  if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
127    printf ("rtl: section: arch: alloc: name=%s size=%zu flags=%08" PRIx32 \
128            " order=%i link=%d info=%d\n",
129            sect->name, sect->size, sect->flags, sect->load_order,
130            sect->link, sect->info);
131
132  if (sdata == NULL)
133  {
134    sdata = rtems_rtl_bit_alloc_open (get_sdata_start (),
135                                      get_sdata_libdl_size (),
136                                      sizeof (uint32_t),
137                                      get_sdata_sbss_size ());
138    if (sdata == NULL)
139    {
140      rtems_rtl_set_error (ENOMEM, "no memory for sdata allocator");
141      return false;
142    }
143  }
144
145  sect->base = rtems_rtl_bit_alloc_balloc (sdata, sect->size);
146  if (sect->base == NULL)
147  {
148    rtems_rtl_set_error (ENOMEM, "no .sdata memory: %s", sect->name);
149    return false;
150  }
151
152  return true;
153#endif
154}
155
156bool
157rtems_rtl_elf_arch_section_free (const rtems_rtl_obj* obj,
158                                  rtems_rtl_obj_sect*  sect)
159{
160#if !_ARCH_PPC64
161  if (rtems_rtl_trace (RTEMS_RTL_TRACE_DETAIL))
162    printf ("rtl: section: arch: free: name=%s size=%zu\n", sect->name, sect->size);
163  if (sdata != NULL)
164    rtems_rtl_bit_alloc_bfree (sdata, sect->base, sect->size);
165#endif
166  return true;
167}
168
169bool
170rtems_rtl_elf_rel_resolve_sym (Elf_Word type)
171{
172  return true;
173}
174
175size_t
176rtems_rtl_elf_relocate_tramp_max_size (void)
177{
178  /*
179   * We have 4 instructions and each instruction is 32bits.
180   */
181  return 4 * 4;
182}
183
184static void*
185set_veneer (void* tramopline, Elf_Addr target)
186{
187  /*
188   * http://shell-storm.org/online/Online-Assembler-and-Disassembler/
189   *
190   *  lis   12,0x1234
191   *  ori   12,12,0x5678
192   *  mtctr 12
193   *  bctr
194   */
195#if COMPILE_ASM
196  asm volatile (" lis   12,0x1234\n" \
197                " ori   12,12,0x5678\n" \
198                " mtctr 12\n" \
199                " bctr\n");
200#endif
201  uint32_t* tramp = (uint32_t*) tramopline;
202  *tramp++ = 0x3d800000 | (target >> 16);
203  *tramp++ = 0x618c0000 | (target & 0xffff);
204  *tramp++ = 0x7d8903a6;
205  *tramp++ = 0x4e800420;
206  return tramp;
207}
208
209static size_t
210get_veneer_size (int type)
211{
212  (void) type;
213  return rtems_rtl_elf_relocate_tramp_max_size ();
214}
215
216static bool
217rtems_rtl_elf_reloc_rela (rtems_rtl_obj*            obj,
218                          const Elf_Rela*           rela,
219                          const rtems_rtl_obj_sect* sect,
220                          const char*               symname,
221                          const Elf_Byte            syminfo,
222                          const Elf_Word            symvalue,
223                          const bool                parsing)
224{
225  Elf_Addr* where;
226  Elf_Word tmp;
227  uint32_t mask = 0;
228  uint32_t bits = 0;
229
230  where = (Elf_Addr *)(sect->base + rela->r_offset);
231  switch (ELF_R_TYPE(rela->r_info)) {
232    case R_TYPE(NONE):
233      break;
234
235    case R_TYPE(32):
236      /*
237       * value:1; Field: word32; Expression: S + A
238       */
239      if (!parsing) {
240        *where = symvalue + rela->r_addend;
241        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
242          printf ("rtl: ADDR32 %p @ %p in %s\n",
243                  (void *)*(where), where, rtems_rtl_obj_oname (obj));
244      }
245      break;
246
247    case R_TYPE(14):
248      /*
249       * value:7; Field: low14*; Expression: (S + A) >> 2
250       */
251    case R_TYPE(24):
252      /*
253       * value:2; Field: low24*; Expression: (S + A) >> 2
254       */
255      if (ELF_R_TYPE(rela->r_info) == R_TYPE(14)) {
256        bits = 14;
257        mask = 0xfffc;
258      } else {
259        bits = 24;
260        mask = 0x3fffffc;
261      }
262      tmp = (symvalue + rela->r_addend) >> 2;
263      if (tmp > ((1<<bits) - 1 )) {
264        Elf_Word tramp_addr;
265        size_t   tramp_size = get_veneer_size(ELF_R_TYPE(rela->r_info));
266        if (parsing) {
267          obj->tramp_size += tramp_size;
268          return true;
269        }
270        tramp_addr = (Elf_Addr) obj->tramp_brk;
271        obj->tramp_brk = set_veneer(obj->tramp_brk,
272                                    symvalue + rela->r_addend);
273        tmp = *where;
274        tmp &= ~mask;
275        tmp |= (tramp_addr + rela->r_addend) & mask;
276      }
277      else {
278        tmp = *where;
279        tmp &= ~mask;
280        tmp |= (symvalue + rela->r_addend) & mask;
281      }
282
283      if (!parsing) {
284        *where = tmp;
285        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
286          printf ("rtl: ADDR14/ADDR24 %p @ %p in %s\n",
287                  (void *)*where, where, rtems_rtl_obj_oname (obj));
288      }
289      break;
290
291    case R_TYPE(16_HA):
292      /*
293       * value:6; Field:half16; Expression: #ha(S+A)
294       */
295      if (!parsing) {
296        tmp = symvalue + rela->r_addend;
297        *(uint16_t *)where = (((tmp >> 16) + ((tmp & 0x8000) ? 1: 0)) & 0xffff);
298        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
299          printf ("rtl: 16_HA %p @ %p in %s\n",
300                  (void *)*(where), where, rtems_rtl_obj_oname (obj));
301      }
302      break;
303
304    case R_TYPE(16_HI):
305      /*
306       * value:5; Field:half16; Expression: #hi(S+A)
307       */
308      if (!parsing) {
309        *(uint16_t *)where = ((symvalue + rela->r_addend) >> 16) & 0xffff;
310        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
311          printf ("rtl: 16_HI %p @ %p in %s\n",
312                  (void *)*where, where, rtems_rtl_obj_oname (obj));
313      }
314      break;
315    case R_TYPE(16_LO):
316      /*
317       * value:4; Field:half16; Expression: #lo(S+A)
318       */
319      if (!parsing) {
320        *(uint16_t *)where = (symvalue + (rela->r_addend)) & 0xffff;
321        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
322          printf ("rtl: 16_LO %p @ %p in %s\n",
323                  (void *)*where, where, rtems_rtl_obj_oname (obj));
324      }
325      break;
326
327    case R_TYPE(REL14):
328      /*
329       * value:11; Field:low14*; Expression:(S+A-P)>>2
330       */
331    case R_TYPE(REL24):
332      /*
333       * value:10; Field:low24*; Expression:(S+A-P)>>2
334       */
335      if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL24)) {
336        mask = 0x3fffffc;
337        bits = 24;
338      }
339      else if (ELF_R_TYPE(rela->r_info) == R_TYPE(REL14)) {
340        mask = 0xfffc;
341        bits = 14;
342      }
343
344      tmp =((int) (symvalue + rela->r_addend - (Elf_Addr)where)) >> 2;
345      if (((Elf_Sword)tmp > ((1<<(bits-1)) - 1)) ||
346          ((Elf_Sword)tmp < -(1<<(bits-1)))) {
347        Elf_Word tramp_addr;
348        size_t   tramp_size = get_veneer_size(ELF_R_TYPE(rela->r_info));
349        if (parsing) {
350          obj->tramp_size += tramp_size;
351          return true;
352        }
353        tramp_addr = (Elf_Addr) obj->tramp_brk;
354        obj->tramp_brk = set_veneer(obj->tramp_brk,
355                                    symvalue + rela->r_addend);
356        tmp = *where;
357        tmp &= ~mask;
358        tmp |= (tramp_addr + rela->r_addend - (Elf_Addr)where) & mask;
359      }
360      else
361      {
362        tmp = *where;
363        tmp &= ~mask;
364        tmp |= (symvalue + rela->r_addend - (Elf_Addr)where) & mask;
365      }
366
367      if (!parsing) {
368        *where = tmp;
369        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
370          printf ("rtl: REL24/REL14 %p @ %p in %s\n",
371                  (void *)*where, where, rtems_rtl_obj_oname (obj));
372      }
373      break;
374
375    case R_TYPE(REL32):
376      /*
377       * value:26; Field:word32*; Expression:S+A-P
378       */
379      if (!parsing) {
380        *where = symvalue + rela->r_addend - (Elf_Addr)where;
381        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
382          printf ("rtl: REL32 %p @ %p in %s\n",
383                  (void *)*where, where, rtems_rtl_obj_oname (obj));
384      }
385      break;
386
387    case R_TYPE(SDAREL16):
388      /*
389       * A sign-extended 16 bit value relative to _SDA_BASE_, for use with
390       * small data items.
391       */
392      if (!parsing) {
393        Elf_Addr sda_base = get_sda_base ();
394        mask = 0xffff;
395        tmp = *((Elf32_Half*) where);
396        tmp &= ~mask;
397        tmp |= (symvalue + rela->r_addend - sda_base) & mask;
398        *((Elf32_Half*) where) = tmp;
399        if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC))
400          printf ("rtl: SDAREL16 %p @ %p in %s\n",
401                  (void *) (uintptr_t) *((Elf32_Half*) where),
402                  where, rtems_rtl_obj_oname (obj));
403      }
404      break;
405
406    default:
407      printf ("rtl: reloc unknown: sym = %" PRIu32 ", type = %" PRIu32 ", offset = %p, "
408              "contents = %p\n",
409              ELF_R_SYM(rela->r_info), (uint32_t) ELF_R_TYPE(rela->r_info),
410              (void *)rela->r_offset, (void *)*where);
411      rtems_rtl_set_error (EINVAL,
412                           "%s: Unsupported relocation type %" PRId32
413                           "in non-PLT relocations",
414                           sect->name, (uint32_t) ELF_R_TYPE(rela->r_info));
415      return false;
416  }
417  return true;
418}
419
420bool
421rtems_rtl_elf_relocate_rela_tramp (rtems_rtl_obj*            obj,
422                                   const Elf_Rela*           rela,
423                                   const rtems_rtl_obj_sect* sect,
424                                   const char*               symname,
425                                   const Elf_Byte            syminfo,
426                                   const Elf_Word            symvalue)
427{
428  return rtems_rtl_elf_reloc_rela (obj,
429                                   rela,
430                                   sect,
431                                   symname,
432                                   syminfo,
433                                   symvalue,
434                                   true);
435}
436
437bool
438rtems_rtl_elf_relocate_rela (rtems_rtl_obj*            obj,
439                             const Elf_Rela*           rela,
440                             const rtems_rtl_obj_sect* sect,
441                             const char*               symname,
442                             const Elf_Byte            syminfo,
443                             const Elf_Word            symvalue)
444{
445  return rtems_rtl_elf_reloc_rela (obj,
446                                   rela,
447                                   sect,
448                                   symname,
449                                   syminfo,
450                                   symvalue,
451                                   false);
452}
453
454bool
455rtems_rtl_elf_relocate_rel_tramp (rtems_rtl_obj*            obj,
456                                  const Elf_Rel*            rel,
457                                  const rtems_rtl_obj_sect* sect,
458                                  const char*               symname,
459                                  const Elf_Byte            syminfo,
460                                  const Elf_Word            symvalue)
461{
462  (void) obj;
463  (void) rel;
464  (void) sect;
465  (void) symname;
466  (void) syminfo;
467  (void) symvalue;
468  rtems_rtl_set_error (EINVAL, "rel type record not supported");
469  return false;
470}
471
472bool
473rtems_rtl_elf_relocate_rel (rtems_rtl_obj*            obj,
474                            const Elf_Rel*            rel,
475                            const rtems_rtl_obj_sect* sect,
476                            const char*               symname,
477                            const Elf_Byte            syminfo,
478                            const Elf_Word            symvalue)
479{
480  (void) obj;
481  (void) rel;
482  (void) sect;
483  (void) symname;
484  (void) syminfo;
485  (void) symvalue;
486  rtems_rtl_set_error (EINVAL, "rel type record not supported");
487  return false;
488}
489
490bool
491rtems_rtl_elf_unwind_parse (const rtems_rtl_obj* obj,
492                            const char*          name,
493                            uint32_t             flags)
494{
495  return rtems_rtl_elf_unwind_dw2_parse (obj, name, flags);
496}
497
498bool
499rtems_rtl_elf_unwind_register (rtems_rtl_obj* obj)
500{
501  return rtems_rtl_elf_unwind_dw2_register (obj);
502}
503
504bool
505rtems_rtl_elf_unwind_deregister (rtems_rtl_obj* obj)
506{
507  return rtems_rtl_elf_unwind_dw2_deregister (obj);
508}
Note: See TracBrowser for help on using the repository browser.