Update QNX IPC loader to RIDL 2.1.0
[ipc/ipcdev.git] / qnx / src / ipc3x_dev / ti / syslink / procMgr / hlos / knl / loaders / Elf / Qnx / DLOAD / C60_DLOAD_REL / c60_reloc.c
1 /*
2 * c60_reloc.c
3 *
4 * Process C6x-specific dynamic relocations for core dynamic loader.
5 *
6 * Copyright (C) 2009-2015 Texas Instruments Incorporated - http://www.ti.com/
7 *
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the
19 * distribution.
20 *
21 * Neither the name of Texas Instruments Incorporated nor the names of
22 * its contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 */
39 #include <limits.h>
40 #include "relocate.h"
41 #include "symtab.h"
42 #include "c60_elf32.h"
43 #include "dload_api.h"
44 #include "util.h"
45 #include "dload_endian.h"
46 #include "c60_reloc.h"
48 #define MASK(n,s) (((1 << n) - 1) << s)
50 /*---------------------------------------------------------------------------*/
51 /* C6x Relocations Supported                                                 */
52 /*                                                                           */
53 /* See the C6000 ELF ABI Specification for more details.                     */
54 /*                                                                           */
55 /* R_C6000_ABS32          |   .field    X,32                                 */
56 /* R_C6000_ABS16          |   .field    X,16                                 */
57 /* R_C6000_ABS8           |   .field    X,8                                  */
58 /* R_C6000_PCR_S21        |   B         foo                                  */
59 /*                            CALLP     foo, B3                              */
60 /* R_C6000_PCR_S12        |   BNOP      foo                                  */
61 /* R_C6000_PCR_S10        |   BPOS      foo, A10                             */
62 /*                            BDEC      foo, A1                              */
63 /* R_C6000_PCR_S7         |   ADDKPC    foo, B3, 4                           */
64 /* R_C6000_ABS_S16        |   MVK       sym, A0                              */
65 /* R_C6000_ABS_L16        |   MVKL      sym, A0                              */
66 /*                            MVKLH     sym, A0                              */
67 /* R_C6000_ABS_H16        |   MVKH      sym, A0                              */
68 /* R_C6000_SBR_U15_B      |   LDB   *+B14(sym), A1                           */
69 /*                            ADDAB   B14, sym, A1                           */
70 /* R_C6000_SBR_U15_H      |   LDH   *+B14(sym), A1                           */
71 /*                            ADDAH   B14, sym, A1                           */
72 /* R_C6000_SBR_U15_W      |   LDW   *+B14(sym), A1                           */
73 /*                            ADDAW   B14, sym, A1                           */
74 /* R_C6000_SBR_S16        |   MVK     sym-$bss, A0                           */
75 /* R_C6000_SBR_L16_B      |   MVKL (sym-$bss),  A0                           */
76 /* R_C6000_SBR_L16_H      |   MVKL (sym-$bss)/2,A0                           */
77 /* R_C6000_SBR_L16_W      |   MVKL (sym-$bss)/4,A0                           */
78 /* R_C6000_SBR_H16_B      |   MVKH (sym-$bss),  A0                           */
79 /* R_C6000_SBR_H16_H      |   MVKH (sym-$bss)/2,A0                           */
80 /* R_C6000_SBR_H16_W      |   MVKH (sym-$bss)/4,A0                           */
81 /* R_C6000_SBR_GOT_U15_W  |   LDW *+B14[GOT(sym)],A0                         */
82 /* R_C6000_SBR_GOT_L16_W  |   MVKL $DPR_GOT(sym), A0                         */
83 /* R_C6000_SBR_GOT_H16_W  |   MVKH $DPR_GOT(sym), A0                         */
84 /* R_C6000_DSBT_INDEX     |   LDW *+B14[$DSBT_index()], DP                   */
85 /*                                                                           */
86 /*---------------------------------------------------------------------------*/
88 /*****************************************************************************/
89 /* WRITE_RELOC_R() - Perform a relocation into a buffered segment.           */
90 /*****************************************************************************/
91 static void write_reloc_r(uint8_t* buffered_segment,
92                           uint32_t segment_offset,
93                           int r_type, uint32_t r)
94 {
95    uint32_t* rel_field_ptr = (uint32_t*)(buffered_segment + segment_offset);
97 #if LOADER_DEBUG
98    /*------------------------------------------------------------------------*/
99    /* Print some details about the relocation we are about to process.       */
100    /*------------------------------------------------------------------------*/
101    if(debugging_on)
102    {
103           DLIF_trace("RWRT: segment_offset: %d\n", segment_offset);
104           DLIF_trace("RWRT: buffered_segment: 0x%x\n",
105                                              (uint32_t)buffered_segment);
106           DLIF_trace("RWRT: rel_field_ptr: 0x%x\n", (uint32_t)rel_field_ptr);
107           DLIF_trace("RWRT: result: 0x%x\n", r);
108    }
109 #endif
112    /*------------------------------------------------------------------------*/
113    /* Given the relocation type, carry out relocation into a 4 byte packet   */
114    /* within the buffered segment.                                           */
115    /*------------------------------------------------------------------------*/
116    switch(r_type)
117    {
118       case R_C6000_ABS32:
119           *rel_field_ptr = r;
120           break;
121       case R_C6000_PREL31:
122           *rel_field_ptr = (*rel_field_ptr & ~MASK(30,0)) | r;
123           break;
124       case R_C6000_ABS16:
125           *((uint16_t*)(buffered_segment + segment_offset)) = r;
126           break;
127       case R_C6000_ABS8:
128           *((uint8_t*)(buffered_segment + segment_offset)) = r;
129           break;
130      case R_C6000_PCR_S21:
131           *rel_field_ptr = (*rel_field_ptr & ~MASK(21,7)) | (r << 7);
132           break;
133       case R_C6000_PCR_S12:
134           *rel_field_ptr = (*rel_field_ptr & ~MASK(12,16)) | (r << 16);
135           break;
136       case R_C6000_PCR_S10:
137           *rel_field_ptr = (*rel_field_ptr & ~MASK(10,13)) | (r << 13);
138           break;
139       case R_C6000_PCR_S7:
140           *rel_field_ptr = (*rel_field_ptr & ~MASK(7,16)) | (r << 16);
141           break;
143       case R_C6000_ABS_S16:
144           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
145           break;
146       case R_C6000_ABS_L16:
147           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
148           break;
149       case R_C6000_ABS_H16:
150           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
151           break;
153       case R_C6000_SBR_U15_B:
154           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
155           break;
156       case R_C6000_SBR_U15_H:
157           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
158           break;
159       case R_C6000_SBR_U15_W:
160       case R_C6000_DSBT_INDEX:
161           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
162           break;
164       case R_C6000_SBR_S16:
165       case R_C6000_SBR_L16_B:
166       case R_C6000_SBR_L16_H:
167       case R_C6000_SBR_L16_W:
168       case R_C6000_SBR_H16_B:
169       case R_C6000_SBR_H16_H:
170       case R_C6000_SBR_H16_W:
171           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
172           break;
174       /*---------------------------------------------------------------------*/
175       /* Linux "import-as-own" copy relocations are not yet supported.       */
176       /*---------------------------------------------------------------------*/
177       case R_C6000_COPY:
179       default:
180           DLIF_error(DLET_RELOC,
181                      "write_reloc_r called with invalid relocation type!\n");
182   }
184 #if LOADER_DEBUG
185   if (debugging_on)
186      DLIF_trace("reloc_field 0x%x\n", *rel_field_ptr);
187 #endif
190 /*****************************************************************************/
191 /* PACK_RESULT() - Pack the result of a relocation calculation for storage   */
192 /*      in the relocation field.                                             */
193 /*****************************************************************************/
194 static int32_t pack_result(int32_t unpacked_result, int r_type)
196    switch(r_type)
197    {
198       case R_C6000_ABS32:
199       case R_C6000_ABS16:
200       case R_C6000_ABS8:
201       case R_C6000_ABS_S16:
202       case R_C6000_ABS_L16:
203       case R_C6000_SBR_U15_B:
204       case R_C6000_SBR_S16:
205       case R_C6000_SBR_L16_B:
206           return unpacked_result;
208       case R_C6000_SBR_U15_H:
209       case R_C6000_SBR_L16_H:
210       case R_C6000_PREL31:
211           return unpacked_result >> 1;
213       case R_C6000_PCR_S21:
214       case R_C6000_PCR_S12:
215       case R_C6000_PCR_S10:
216       case R_C6000_PCR_S7:
217       case R_C6000_SBR_U15_W:
218       case R_C6000_SBR_L16_W:
219       case R_C6000_DSBT_INDEX:
220           return unpacked_result >> 2;
222       case R_C6000_ABS_H16:
223       case R_C6000_SBR_H16_B:
224           return unpacked_result >> 16;
226       case R_C6000_SBR_H16_H:
227           return unpacked_result >> 17;
229       case R_C6000_SBR_H16_W:
230           return unpacked_result >> 18;
232       /*---------------------------------------------------------------------*/
233       /* Linux "import-as-own" copy relocations are not yet supported.       */
234       /*---------------------------------------------------------------------*/
235       case R_C6000_COPY:
237       default:
238           DLIF_error(DLET_RELOC,
239                      "pack_result called with invalid relocation type!\n");
240           return 0;
241    }
244 /*****************************************************************************/
245 /* MASK_RESULT() - Mask the result of a relocation calculation so that it    */
246 /*      fits the size of the relocation type's field.                        */
247 /*****************************************************************************/
248 static int32_t mask_result(int32_t unmasked_result, int r_type)
250    switch(r_type)
251    {
252       case R_C6000_ABS8:
253          return unmasked_result & 0xFF;
255       case R_C6000_ABS32:
256          return unmasked_result;
258       case R_C6000_ABS16:
259       case R_C6000_ABS_S16:
260       case R_C6000_ABS_L16:
261       case R_C6000_ABS_H16:
262       case R_C6000_SBR_S16:
263       case R_C6000_SBR_L16_B:
264       case R_C6000_SBR_L16_H:
265       case R_C6000_SBR_L16_W:
266       case R_C6000_SBR_H16_B:
267       case R_C6000_SBR_H16_H:
268       case R_C6000_SBR_H16_W:
269           return unmasked_result & 0xFFFF;
271       case R_C6000_PCR_S21:
272           return unmasked_result & 0x1FFFFF;
274       case R_C6000_PCR_S12:
275           return unmasked_result & 0xFFF;
277       case R_C6000_PCR_S10:
278           return unmasked_result & 0x3FF;
280       case R_C6000_PCR_S7:
281           return unmasked_result & 0x7F;
283       case R_C6000_SBR_U15_B:
284       case R_C6000_SBR_U15_H:
285       case R_C6000_SBR_U15_W:
286       case R_C6000_DSBT_INDEX:
287           return unmasked_result & 0x7FFF;
289       case R_C6000_PREL31:
290           return unmasked_result & 0x7FFFFFFF;
292       /*---------------------------------------------------------------------*/
293       /* Linux "import-as-own" copy relocations are not yet supported.       */
294       /*---------------------------------------------------------------------*/
295       case R_C6000_COPY:
297       default:
298          DLIF_error(DLET_RELOC,
299                     "mask_result called with invalid relocation type!\n");
300          return 0;
301    }
304 /*****************************************************************************/
305 /* REL_OVERFLOW()                                                            */
306 /*                                                                           */
307 /*    Check relocation value against the range associated with a given       */
308 /*    relocation type field size and signedness.                             */
309 /*                                                                           */
310 /*****************************************************************************/
311 static BOOL rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value)
313    /*------------------------------------------------------------------------*/
314    /* Select appropriate range check based on relocation type.               */
315    /*------------------------------------------------------------------------*/
316    switch(r_type)
317    {
318       case R_C6000_ABS16:       return ((reloc_value > 65535) ||
319                                         (reloc_value < -32768));
320       case R_C6000_ABS8:        return ((reloc_value > 255) ||
321                                         (reloc_value < -128));
322       case R_C6000_PCR_S21:     return ((reloc_value >= 0x400000) ||
323                                         (reloc_value < -0x400000));
324       case R_C6000_PCR_S12:     return ((reloc_value >= 0x2000) ||
325                                         (reloc_value < -0x2000));
326       case R_C6000_PCR_S10:     return ((reloc_value >= 0x800) ||
327                                         (reloc_value < -0x800));
328       case R_C6000_PCR_S7:      return ((reloc_value >= 0x100) ||
329                                         (reloc_value < -0x100));
330       case R_C6000_SBR_S16:
331       case R_C6000_ABS_S16:     return ((reloc_value >= 0x8000) ||
332                                         (reloc_value < -0x8000));
333       case R_C6000_SBR_U15_B:   return (((uint32_t)reloc_value) >= 0x8000);
334       case R_C6000_SBR_U15_H:   return (((uint32_t)reloc_value) >= 0xFFFF);
335       case R_C6000_DSBT_INDEX:
336       case R_C6000_SBR_U15_W:   return (((uint32_t)reloc_value) >= 0x1FFFD);
339       /*---------------------------------------------------------------------*/
340       /* Some relocation types suppress overflow checking at link-time.      */
341       /*---------------------------------------------------------------------*/
342       case R_C6000_ABS_L16:
343       case R_C6000_ABS_H16:
344       case R_C6000_SBR_L16_B:
345       case R_C6000_SBR_L16_H:
346       case R_C6000_SBR_L16_W:
347       case R_C6000_SBR_H16_B:
348       case R_C6000_SBR_H16_H:
349       case R_C6000_SBR_H16_W:
350          return 0;
352       /*---------------------------------------------------------------------*/
353       /* 32-bit relocation field values are not checked for overflow.        */
354       /*---------------------------------------------------------------------*/
355       case R_C6000_ABS32:
356       case R_C6000_PREL31:
357          return 0;
359       /*---------------------------------------------------------------------*/
360       /* If relocation type did not appear in the above switch, then we      */
361       /* didn't expect to see it.                                            */
362       /*---------------------------------------------------------------------*/
363       default:
364          DLIF_error(DLET_RELOC,
365                     "rel_overflow called with invalid relocation type!\n");
366    }
368    return 1;
371 #if LOADER_DEBUG || LOADER_PROFILE
372 extern int DLREL_relocations;
373 extern time_t DLREL_total_reloc_time;
374 #endif
376 /*****************************************************************************/
377 /* RELOC_DO() - Process a single relocation entry.                           */
378 /*****************************************************************************/
379 static void reloc_do(C60_RELOC_TYPE r_type,
380                      uint32_t segment_vaddr,
381                      uint8_t *segment_buffer,
382                      uint32_t addend,
383                      uint32_t symval,
384                      uint32_t spc,
385                      int      wrong_endian,
386                      uint32_t base_pointer,
387                      int32_t  dsbt_index)
389    int32_t reloc_value = 0;
391 #if LOADER_DEBUG && LOADER_PROFILE
392    /*------------------------------------------------------------------------*/
393    /* In debug mode, keep a count of the number of relocations processed.    */
394    /* In profile mode, start the clock on a given relocation.                */
395    /*------------------------------------------------------------------------*/
396    int start_time = 0;
397    if (debugging_on || profiling_on)
398    {
399       DLREL_relocations++;
400       if (profiling_on) start_time = clock();
401    }
402 #endif
404    /*------------------------------------------------------------------------*/
405    /* Calculate the relocation value according to the rules associated with  */
406    /* the given relocation type.                                             */
407    /*------------------------------------------------------------------------*/
408    switch(r_type)
409    {
410       /*---------------------------------------------------------------------*/
411       /* Straight-Up Address relocations (address references).               */
412       /*---------------------------------------------------------------------*/
413       case R_C6000_ABS32:
414       case R_C6000_ABS16:
415       case R_C6000_ABS8:
416       case R_C6000_ABS_S16:
417       case R_C6000_ABS_L16:
418       case R_C6000_ABS_H16:
419          reloc_value = symval + addend;
420          break;
422       /*---------------------------------------------------------------------*/
423       /* PC-Relative relocations (calls and branches).                       */
424       /*---------------------------------------------------------------------*/
425       case R_C6000_PCR_S21:
426       case R_C6000_PCR_S12:
427       case R_C6000_PCR_S10:
428       case R_C6000_PCR_S7:
429       {
430          /*------------------------------------------------------------------*/
431          /* Add SPC to segment address to get the PC. Mask for exec-packet   */
432          /* boundary.                                                        */
433          /*------------------------------------------------------------------*/
434          int32_t opnd_p = (spc + segment_vaddr) & 0xffffffe0;
435          reloc_value = symval + addend - opnd_p;
436          break;
437       }
439       /*---------------------------------------------------------------------*/
440       /* "Place"-relative relocations (TDEH).                                */
441       /*---------------------------------------------------------------------*/
442       /* These relocations occur in data and refer to a label that occurs    */
443       /* at some signed 32-bit offset from the place where the relocation    */
444       /* occurs.                                                             */
445       /*---------------------------------------------------------------------*/
446       case R_C6000_PREL31:
447       {
448          /*------------------------------------------------------------------*/
449          /* Compute location of relocation entry and subtract it from the    */
450          /* address of the location being referenced (it is computed very    */
451          /* much like a PC-relative relocation, but it occurs in data and    */
452          /* is called a "place"-relative relocation).                        */
453          /*------------------------------------------------------------------*/
454          /* If this is an Elf32_Rel type relocation, then addend is assumed  */
455          /* to have been scaled when it was unpacked (field << 1).           */
456          /*------------------------------------------------------------------*/
457          /* For Elf32_Rela type relocations the addend is assumed to be a    */
458          /* signed 32-bit integer value.                                     */
459          /*------------------------------------------------------------------*/
460          /* Offset is not fetch-packet relative; doesn't need to be masked.  */
461          /*------------------------------------------------------------------*/
462          int32_t opnd_p = (spc + segment_vaddr);
463          reloc_value = symval + addend - opnd_p;
464          break;
465       }
467       /*---------------------------------------------------------------------*/
468       /* Static-Base Relative relocations (near-DP).                         */
469       /*---------------------------------------------------------------------*/
470       case R_C6000_SBR_U15_B:
471       case R_C6000_SBR_U15_H:
472       case R_C6000_SBR_U15_W:
473       case R_C6000_SBR_S16:
474       case R_C6000_SBR_L16_B:
475       case R_C6000_SBR_L16_H:
476       case R_C6000_SBR_L16_W:
477       case R_C6000_SBR_H16_B:
478       case R_C6000_SBR_H16_H:
479       case R_C6000_SBR_H16_W:
480          reloc_value = symval + addend - base_pointer;
481          break;
483       /*---------------------------------------------------------------------*/
484       /* R_C6000_DSBT_INDEX - uses value assigned by the dynamic loader to   */
485       /*    be the DSBT index for this module as a scaled offset when        */
486       /*    referencing the DSBT. The DSBT base address is in symval and the */
487       /*    static base is in base_pointer. DP-relative offset to slot in    */
488       /*    DSBT is the offset of the DSBT relative to the DP plus the       */
489       /*    scaled DSBT index into the DSBT.                                 */
490       /*---------------------------------------------------------------------*/
491       case R_C6000_DSBT_INDEX:
492          reloc_value = ((symval + addend) - base_pointer) + (dsbt_index << 2);
493          break;
495       /*---------------------------------------------------------------------*/
496       /* Linux "import-as-own" copy relocation: after DSO initialization,    */
497       /* copy the named object from the DSO into the executable's BSS        */
498       /*---------------------------------------------------------------------*/
499       /* Linux "import-as-own" copy relocations are not yet supported.       */
500       /*---------------------------------------------------------------------*/
501       case R_C6000_COPY:
503       /*---------------------------------------------------------------------*/
504       /* Unrecognized relocation type.                                       */
505       /*---------------------------------------------------------------------*/
506       default:
507          DLIF_error(DLET_RELOC,
508                     "reloc_do called with invalid relocation type!\n");
509          break;
510    }
512    /*------------------------------------------------------------------------*/
513    /* Overflow checking.  Is relocation value out of range for the size and  */
514    /* type of the current relocation?                                        */
515    /*------------------------------------------------------------------------*/
516    if (rel_overflow(r_type, reloc_value))
517       DLIF_error(DLET_RELOC, "relocation overflow!\n");
519    /*------------------------------------------------------------------------*/
520    /* Move relocation value to appropriate offset for relocation field's     */
521    /* location.                                                              */
522    /*------------------------------------------------------------------------*/
523    reloc_value = pack_result(reloc_value, r_type);
525    /*------------------------------------------------------------------------*/
526    /* Mask packed result to the size of the relocation field.                */
527    /*------------------------------------------------------------------------*/
528    reloc_value = mask_result(reloc_value, r_type);
530    /*------------------------------------------------------------------------*/
531    /* If necessary, Swap endianness of data at relocation address.           */
532    /*------------------------------------------------------------------------*/
533    if (wrong_endian)
534       DLIMP_change_endian32((int32_t*)(segment_buffer + spc));
536    /*------------------------------------------------------------------------*/
537    /* Write the relocated 4-byte packet back to the segment buffer.          */
538    /*------------------------------------------------------------------------*/
539    write_reloc_r(segment_buffer, spc, r_type, reloc_value);
541    /*------------------------------------------------------------------------*/
542    /* Change endianness of segment address back to original.                 */
543    /*------------------------------------------------------------------------*/
544    if (wrong_endian)
545       DLIMP_change_endian32((int32_t*)(segment_buffer + spc));
547 #if LOADER_DEBUG && LOADER_PROFILE
548    /*------------------------------------------------------------------------*/
549    /* In profile mode, add elapsed time for this relocation to total time    */
550    /* spent doing relocations.                                               */
551    /*------------------------------------------------------------------------*/
552    if (profiling_on)
553       DLREL_total_reloc_time += (clock() - start_time);
554    if (debugging_on)
555       DLIF_trace("reloc_value = 0x%x\n", reloc_value);
556 #endif
559 /*****************************************************************************/
560 /* REL_UNPACK_ADDEND()                                                       */
561 /*                                                                           */
562 /*    Unpack addend value from the relocation field.                         */
563 /*                                                                           */
564 /*****************************************************************************/
565 static void rel_unpack_addend(C60_RELOC_TYPE r_type,
566                               uint8_t *address,
567                   uint32_t *addend)
569    /*------------------------------------------------------------------------*/
570    /* C6000 does not support Elf32_Rel type relocations in the dynamic       */
571    /* loader core.  We will emit an internal error and abort until this      */
572    /* support is added.  I abort here because this is necessarily a target-  */
573    /* specific part of the relocation infrastructure.                        */
574    /*------------------------------------------------------------------------*/
575    *addend = 0;
577    DLIF_error(DLET_RELOC,
578               "Internal Error: unpacking addend values from the relocation "
579           "field is not supported in the C6000 dynamic loader at this "
580           "time; aborting\n");
581    DLIF_exit(1);
584 /*****************************************************************************/
585 /* REL_SWAP_ENDIAN()                                                         */
586 /*                                                                           */
587 /*    Return TRUE if we should change the endianness of a relocation field.  */
588 /*                                                                           */
589 /*****************************************************************************/
590 static BOOL rel_swap_endian(DLIMP_Dynamic_Module *dyn_module,
591                             C60_RELOC_TYPE r_type)
593    if (dyn_module->wrong_endian) return TRUE;
595    return FALSE;
598 /*****************************************************************************/
599 /* REL_CHANGE_ENDIAN()                                                       */
600 /*                                                                           */
601 /*    Change the endianness of the relocation field at the specified address */
602 /*    in the segment's data.                                                 */
603 /*                                                                           */
604 /*****************************************************************************/
605 static void rel_change_endian(C60_RELOC_TYPE r_type, uint8_t *address)
607    /*------------------------------------------------------------------------*/
608    /* On C6000, all instructions are 32-bits wide.                           */
609    /*------------------------------------------------------------------------*/
610    DLIMP_change_endian32((int32_t *)address);
613 /*****************************************************************************/
614 /* READ_REL_TABLE()                                                          */
615 /*                                                                           */
616 /*    Read in an Elf32_Rel type relocation table.  This function allocates   */
617 /*    host memory for the table.                                             */
618 /*                                                                           */
619 /*****************************************************************************/
620 static void read_rel_table(struct Elf32_Rel **rel_table,
621                            int32_t table_offset,
622                uint32_t relnum, uint32_t relent,
623                LOADER_FILE_DESC *fd, BOOL wrong_endian)
625    if (relnum == 0) { *rel_table = NULL; return; }
627    *rel_table = (struct Elf32_Rel *)DLIF_malloc(relnum * relent);
628    if (NULL == *rel_table) {
629        DLIF_error(DLET_MEMORY, "Failed to Allocate read_rel_table\n");
630        return;
631    }
632    DLIF_fseek(fd, table_offset, LOADER_SEEK_SET);
633    DLIF_fread(*rel_table, relnum, relent, fd);
635    if (wrong_endian)
636    {
637       int i;
638       for (i = 0; i < relnum; i++)
639          DLIMP_change_rel_endian(*rel_table + i);
640    }
643 /*****************************************************************************/
644 /* PROCESS_REL_TABLE()                                                       */
645 /*                                                                           */
646 /*    Process table of Elf32_Rel type relocations.                           */
647 /*                                                                           */
648 /*****************************************************************************/
649 static void process_rel_table(DLOAD_HANDLE handle,
650                               DLIMP_Loaded_Segment* seg,
651                               struct Elf32_Rel *rel_table,
652                   uint32_t relnum,
653                   int32_t *start_relidx,
654                   uint32_t ti_static_base,
655                   DLIMP_Dynamic_Module* dyn_module)
657    Elf32_Addr seg_start_addr = seg->input_vaddr;
658    Elf32_Addr seg_end_addr   = seg_start_addr + seg->phdr.p_memsz;
659    BOOL found = FALSE;
660    int32_t relidx = *start_relidx;
662    /*------------------------------------------------------------------------*/
663    /* If the given start reloc index is out of range, then start from the    */
664    /* beginning of the given table.                                          */
665    /*------------------------------------------------------------------------*/
666    if (relidx >= relnum) relidx = 0;
668    /*------------------------------------------------------------------------*/
669    /* Spin through Elf32_Rel type relocation table.                          */
670    /*------------------------------------------------------------------------*/
671    for ( ; relidx < relnum; relidx++)
672    {
673       /*---------------------------------------------------------------------*/
674       /* If the relocation offset falls within the segment, process it.      */
675       /*---------------------------------------------------------------------*/
676       if (rel_table[relidx].r_offset >= seg_start_addr &&
677           rel_table[relidx].r_offset < seg_end_addr)
678       {
679          Elf32_Addr     r_symval = 0;
680      C60_RELOC_TYPE r_type  =
681                    (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info);
682      int32_t        r_symid = ELF32_R_SYM(rel_table[relidx].r_info);
684      uint8_t *reloc_address = NULL;
685      uint32_t pc     = 0;
686      uint32_t addend = 0;
688      BOOL     change_endian = FALSE;
690      found = TRUE;
692          /*------------------------------------------------------------------*/
693      /* If symbol definition is not found, don't do the relocation.      */
694      /* An error is generated by the lookup function.                    */
695      /*------------------------------------------------------------------*/
696          if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval))
697             continue;
699          /*------------------------------------------------------------------*/
700      /* Addend value is stored in the relocation field.                  */
701      /* We'll need to unpack it from the data for the segment that is    */
702      /* currently being relocated.                                       */
703          /*------------------------------------------------------------------*/
704          pc            = rel_table[relidx].r_offset - seg->input_vaddr;
705      reloc_address = (uint8_t *)seg->host_address + pc;
707      change_endian = rel_swap_endian(dyn_module, r_type);
708      if (change_endian)
709         rel_change_endian(r_type, reloc_address);
711      rel_unpack_addend(
712                     (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info),
713                                                    reloc_address, &addend);
715          /*------------------------------------------------------------------*/
716      /* Perform actual relocation.  This is a really wide function       */
717      /* interface and could do with some encapsulation.                  */
718      /*------------------------------------------------------------------*/
719          reloc_do(r_type,
720                   seg->phdr.p_vaddr,
721                   seg->host_address,
722                   addend,
723                   r_symval,
724                   pc,
725                   dyn_module->wrong_endian,
726                   ti_static_base,
727                   dyn_module->dsbt_index);
729       }
731       else if (found)
732          break;
733    }
736 /*****************************************************************************/
737 /* READ_RELA_TABLE()                                                         */
738 /*                                                                           */
739 /*    Read in an Elf32_Rela type relocation table.  This function allocates  */
740 /*    host memory for the table.                                             */
741 /*                                                                           */
742 /*****************************************************************************/
743 static void read_rela_table(struct Elf32_Rela **rela_table,
744                             int32_t table_offset,
745                 uint32_t relanum, uint32_t relaent,
746                 LOADER_FILE_DESC *fd, BOOL wrong_endian)
748    if (relanum == 0) { *rela_table = NULL; return; }
749    *rela_table = (struct Elf32_Rela *)DLIF_malloc(relanum * relaent);
750    if (NULL == *rela_table) {
751        DLIF_error(DLET_MEMORY, "Failed to Allocate read_rela_table\n");
752        return;
753    }
754    DLIF_fseek(fd, table_offset, LOADER_SEEK_SET);
755    DLIF_fread(*rela_table, relanum, relaent, fd);
757    if (wrong_endian)
758    {
759       int i;
760       for (i = 0; i < relanum; i++)
761          DLIMP_change_rela_endian(*rela_table + i);
762    }
765 /*****************************************************************************/
766 /* PROCESS_RELA_TABLE()                                                      */
767 /*                                                                           */
768 /*    Process a table of Elf32_Rela type relocations.                        */
769 /*                                                                           */
770 /*****************************************************************************/
771 static void process_rela_table(DLOAD_HANDLE handle,
772                                DLIMP_Loaded_Segment *seg,
773                                struct Elf32_Rela *rela_table,
774                    uint32_t relanum,
775                    int32_t *start_relidx,
776                    uint32_t ti_static_base,
777                    DLIMP_Dynamic_Module *dyn_module)
779     Elf32_Addr seg_start_addr = seg->input_vaddr;
780     Elf32_Addr seg_end_addr   = seg_start_addr + seg->phdr.p_memsz;
781     BOOL       found          = FALSE;
782     int32_t    relidx         = *start_relidx;
784     /*-----------------------------------------------------------------------*/
785     /* If the given start reloc index is out of range, then start from       */
786     /* the beginning of the given table.                                     */
787     /*-----------------------------------------------------------------------*/
788     if (relidx > relanum) relidx = 0;
790     /*-----------------------------------------------------------------------*/
791     /* Spin through RELA relocation table.                                   */
792     /*-----------------------------------------------------------------------*/
793     for ( ; relidx < relanum; relidx++)
794     {
795         /*-------------------------------------------------------------------*/
796         /* If the relocation offset falls within the segment, process it.    */
797         /*-------------------------------------------------------------------*/
798         if (rela_table[relidx].r_offset >= seg_start_addr &&
799             rela_table[relidx].r_offset < seg_end_addr)
800         {
801         Elf32_Addr     r_symval;
802         C60_RELOC_TYPE r_type  =
803                   (C60_RELOC_TYPE)ELF32_R_TYPE(rela_table[relidx].r_info);
804         int32_t        r_symid = ELF32_R_SYM(rela_table[relidx].r_info);
806         found = TRUE;
808             /*---------------------------------------------------------------*/
809         /* If symbol definition is not found, don't do the relocation.   */
810         /* An error is generated by the lookup function.                 */
811             /*---------------------------------------------------------------*/
812             if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval))
813                 continue;
815             /*---------------------------------------------------------------*/
816             /* Perform actual relocation.  This is a really wide function    */
817             /* interface and could do with some encapsulation.               */
818             /*---------------------------------------------------------------*/
819             reloc_do(r_type,
820                      seg->phdr.p_vaddr,
821                      seg->host_address,
822                      rela_table[relidx].r_addend,
823                      r_symval,
824                      rela_table[relidx].r_offset - seg->input_vaddr,
825                      dyn_module->wrong_endian,
826                      ti_static_base,
827                      dyn_module->dsbt_index);
828         }
830         else if (found)
831             break;
832     }
835 /*****************************************************************************/
836 /* PROCESS_GOT_RELOCS()                                                      */
837 /*                                                                           */
838 /*    Process all GOT relocations.  It is possible to have both Elf32_Rel    */
839 /*    and Elf32_Rela type relocations in the same file, so we handle tham    */
840 /*    both.                                                                  */
841 /*                                                                           */
842 /*****************************************************************************/
843 static void process_got_relocs(DLOAD_HANDLE handle,
844                                struct Elf32_Rel* rel_table, uint32_t relnum,
845                                struct Elf32_Rela* rela_table, uint32_t relanum,
846                    DLIMP_Dynamic_Module* dyn_module)
848    DLIMP_Loaded_Segment *seg =
849        (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf);
850    uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size;
851    int32_t  rel_relidx = 0;
852    int32_t  rela_relidx = 0;
853    uint32_t seg_idx = 0;
854    uint32_t ti_static_base = 0;
856    /*------------------------------------------------------------------------*/
857    /* Get the value of the static base (__TI_STATIC_BASE) which will be      */
858    /* passed into the relocation table processing functions.                 */
859    /*------------------------------------------------------------------------*/
860    if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab,
861                              dyn_module->symnum, &ti_static_base))
862       DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n");
864    /*------------------------------------------------------------------------*/
865    /* Process relocations segment by segment.                                */
866    /*------------------------------------------------------------------------*/
867    for (seg_idx = 0; seg_idx < num_segs; seg_idx++)
868    {
869       /*---------------------------------------------------------------------*/
870       /* Relocations should not occur in uninitialized segments.             */
871       /*---------------------------------------------------------------------*/
872       if (!seg[seg_idx].phdr.p_filesz) continue;
874       if (rela_table)
875          process_rela_table(handle, (seg + seg_idx),
876                         rela_table, relanum, &rela_relidx,
877                 ti_static_base, dyn_module);
879       if (rel_table)
880          process_rel_table(handle, (seg + seg_idx),
881                         rel_table, relnum, &rel_relidx,
882                 ti_static_base, dyn_module);
883    }
886 /*****************************************************************************/
887 /* PROCESS_PLTGOT_RELOCS()                                                   */
888 /*                                                                           */
889 /*    Process all PLTGOT relocation entries.  The PLTGOT relocation table    */
890 /*    can be either Elf32_Rel or Elf32_Rela type.  All PLTGOT relocations    */
891 /*    ar guaranteed to belong to the same segment.                           */
892 /*                                                                           */
893 /*****************************************************************************/
894 static void process_pltgot_relocs(DLOAD_HANDLE handle,
895                                   void* plt_reloc_table,
896                                   int reltype,
897                                   uint32_t pltnum,
898                   DLIMP_Dynamic_Module* dyn_module)
900    Elf32_Addr r_offset = (reltype == DT_REL) ?
901                              ((struct Elf32_Rel *)plt_reloc_table)->r_offset :
902                  ((struct Elf32_Rela *)plt_reloc_table)->r_offset;
904    DLIMP_Loaded_Segment* seg =
905       (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf);
907    uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size;
908    int32_t  plt_relidx = 0;
909    uint32_t seg_idx = 0;
910    uint32_t ti_static_base = 0;
912    /*------------------------------------------------------------------------*/
913    /* Get the value of the static base (__TI_STATIC_BASE) which will be      */
914    /* passed into the relocation table processing functions.                 */
915    /*------------------------------------------------------------------------*/
916    if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab,
917                              dyn_module->symnum, &ti_static_base))
918       DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n");
920    /*------------------------------------------------------------------------*/
921    /* For each segment s, check if the relocation falls within s. If so,     */
922    /* then all other relocations are guaranteed to fall with s. Process      */
923    /* all relocations and then return.                                       */
924    /*------------------------------------------------------------------------*/
925    for (seg_idx = 0; seg_idx < num_segs; seg_idx++)
926    {
927       Elf32_Addr seg_start_addr = seg[seg_idx].input_vaddr;
928       Elf32_Addr seg_end_addr   = seg_start_addr + seg[seg_idx].phdr.p_memsz;
930       /*---------------------------------------------------------------------*/
931       /* Relocations should not occur in uninitialized segments.             */
932       /*---------------------------------------------------------------------*/
933       if(!seg[seg_idx].phdr.p_filesz) continue;
935       if (r_offset >= seg_start_addr &&
936           r_offset < seg_end_addr)
937       {
938          if (reltype == DT_REL)
939         process_rel_table(handle, (seg + seg_idx),
940                           (struct Elf32_Rel *)plt_reloc_table,
941                   pltnum, &plt_relidx,
942                   ti_static_base, dyn_module);
943      else
944         process_rela_table(handle, (seg + seg_idx),
945                            (struct Elf32_Rela *)plt_reloc_table,
946                    pltnum, &plt_relidx,
947                    ti_static_base, dyn_module);
949          break;
950       }
951    }
954 /*****************************************************************************/
955 /* RELOCATE() - Perform RELA and REL type relocations for given ELF object   */
956 /*      file that we are in the process of loading and relocating.           */
957 /*****************************************************************************/
958 void DLREL_c60_relocate(DLOAD_HANDLE handle,
959                         LOADER_FILE_DESC *fd, DLIMP_Dynamic_Module *dyn_module)
961    struct Elf32_Dyn  *dyn_nugget = dyn_module->dyntab;
962    struct Elf32_Rela *rela_table = NULL;
963    struct Elf32_Rel  *rel_table  = NULL;
964    struct Elf32_Rela *rela_plt_table = NULL;
965    struct Elf32_Rel  *rel_plt_table  = NULL;
967    /*------------------------------------------------------------------------*/
968    /* Read the size of the relocation table (DT_RELASZ) and the size per     */
969    /* relocation (DT_RELAENT) from the dynamic segment.                      */
970    /*------------------------------------------------------------------------*/
971    uint32_t relasz  = DLIMP_get_first_dyntag(DT_RELASZ, dyn_nugget);
972    uint32_t relaent = DLIMP_get_first_dyntag(DT_RELAENT, dyn_nugget);
973    uint32_t relanum = 0;
975    /*------------------------------------------------------------------------*/
976    /* Read the size of the relocation table (DT_RELSZ) and the size per      */
977    /* relocation (DT_RELENT) from the dynamic segment.                       */
978    /*------------------------------------------------------------------------*/
979    uint32_t relsz  = DLIMP_get_first_dyntag(DT_RELSZ, dyn_nugget);
980    uint32_t relent = DLIMP_get_first_dyntag(DT_RELENT, dyn_nugget);
981    uint32_t relnum = 0;
983    /*------------------------------------------------------------------------*/
984    /* Read the size of the relocation table (DT_PLTRELSZ) and the type of    */
985    /* of the PLTGOT relocation table (DT_PLTREL): one of DT_REL or DT_RELA   */
986    /*------------------------------------------------------------------------*/
987    uint32_t pltrelsz  = DLIMP_get_first_dyntag(DT_PLTRELSZ, dyn_nugget);
988    int      pltreltyp = DLIMP_get_first_dyntag(DT_PLTREL, dyn_nugget);
989    uint32_t pltnum    = 0;
991    /*------------------------------------------------------------------------*/
992    /* Find/record DSBT index associated with this module.                    */
993    /*------------------------------------------------------------------------*/
994    if (is_dsbt_module(dyn_module) &&
995        (dyn_module->dsbt_index == DSBT_INDEX_INVALID))
996       dyn_module->dsbt_index =
997                    DLIF_get_dsbt_index(dyn_module->loaded_module->file_handle);
999    /*------------------------------------------------------------------------*/
1000    /* Read the PLTGOT relocation table from the file                         */
1001    /* The PLTGOT table is a subsection at the end of either the DT_REL or    */
1002    /* DT_RELA table.  The size of the table it belongs to DT_REL(A)SZ        */
1003    /* includes the size of the PLTGOT table.  So it must be adjusted so that */
1004    /* the GOT relocation tables only contain actual GOT relocations.         */
1005    /*------------------------------------------------------------------------*/
1006    if (pltrelsz != INT_MAX && pltrelsz != 0)
1007    {
1008       if (pltreltyp == DT_REL)
1009       {
1010          pltnum = pltrelsz/relent;
1011          relsz -= pltrelsz;
1012          read_rel_table((&rel_plt_table),
1013              DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget),
1014              pltnum, relent, fd, dyn_module->wrong_endian);
1015       }
1017       else if (pltreltyp == DT_RELA)
1018       {
1019          pltnum = pltrelsz/relaent;
1020          relasz -= pltrelsz;
1021          read_rela_table((&rela_plt_table),
1022              DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget),
1023              pltnum, relaent, fd, dyn_module->wrong_endian);
1024       }
1026       else
1027       {
1028          DLIF_error(DLET_RELOC,
1029              "DT_PLTREL is invalid: must be either %d or %d\n",
1030              DT_REL, DT_RELA);
1031       }
1032    }
1034    /*------------------------------------------------------------------------*/
1035    /* Read the DT_RELA GOT relocation table from the file                    */
1036    /*------------------------------------------------------------------------*/
1037    if (relasz != INT_MAX && relasz != 0)
1038    {
1039       relanum = relasz/relaent;
1040       read_rela_table(&rela_table, DLIMP_get_first_dyntag(DT_RELA, dyn_nugget),
1041                       relanum, relaent, fd, dyn_module->wrong_endian);
1042    }
1044    /*------------------------------------------------------------------------*/
1045    /* Read the DT_REL GOT relocation table from the file                     */
1046    /*------------------------------------------------------------------------*/
1047    if (relsz != INT_MAX && relsz != 0)
1048    {
1049       relnum = relsz/relent;
1050       read_rel_table(&rel_table, DLIMP_get_first_dyntag(DT_REL, dyn_nugget),
1051                      relnum, relent, fd, dyn_module->wrong_endian);
1052    }
1054    /*------------------------------------------------------------------------*/
1055    /* Process the PLTGOT relocations                                         */
1056    /*------------------------------------------------------------------------*/
1057    if (rela_plt_table)
1058       process_pltgot_relocs(handle, rela_plt_table, pltreltyp, pltnum,
1059                             dyn_module);
1061    if (rel_plt_table)
1062       process_pltgot_relocs(handle, rel_plt_table, pltreltyp, pltnum,
1063                             dyn_module);
1065    /*------------------------------------------------------------------------*/
1066    /* Process the GOT relocations                                            */
1067    /*------------------------------------------------------------------------*/
1068    if (rel_table || rela_table)
1069       process_got_relocs(handle, rel_table, relnum, rela_table, relanum,
1070                          dyn_module);
1072    /*-------------------------------------------------------------------------*/
1073    /* Free memory used for ELF relocation table copies.                       */
1074    /*-------------------------------------------------------------------------*/
1075    if (rela_table) DLIF_free(rela_table);
1076    if (rel_table)  DLIF_free(rel_table);
1077    if (rela_plt_table) DLIF_free(rela_plt_table);
1078    if (rel_plt_table)  DLIF_free(rel_plt_table);
1081 /*****************************************************************************/
1082 /* UNIT TESTING INTERFACE                                                    */
1083 /*****************************************************************************/
1084 #ifdef UNIT_TEST
1085 void unit_c60_reloc_do(C60_RELOC_TYPE r_type,
1086                        uint8_t *address_space,
1087                        uint32_t addend, uint32_t symval, uint32_t pc,
1088                        uint32_t static_base, int wrong_endian,
1089                        int32_t dsbt_index)
1091     reloc_do(r_type, (uint32_t)address_space, address_space,
1092              addend, symval, pc, FALSE, static_base, dsbt_index);
1095 #if 0 /* RELA TYPE RELOCATIONS HAVE ADDEND IN RELOCATION ENTRY */
1096 void unit_c60_rel_unpack_addend(C60_RELOC_TYPE r_type,
1097                                 uint8_t* address,
1098                 uint32_t* addend)
1100     rel_unpack_addend(r_type, address, addend);
1102 #endif
1104 BOOL unit_c60_rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value)
1106    return rel_overflow(r_type, reloc_value);
1108 #endif