7a67d121bf4afe4975836b98452a676a648b9290
[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 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 #if defined (__KERNEL__)
40 #include <linux/limits.h>
41 #else
42 #include <limits.h>
43 #endif
44 #include "relocate.h"
45 #include "c60_elf32.h"
46 #include "dload_api.h"
47 #include "util.h"
48 #include "dload_endian.h"
49 #include "symtab.h"
51 #define MASK(n,s) (((1 << n) - 1) << s)
53 /*---------------------------------------------------------------------------*/
54 /* C6x Relocations Supported                                                 */
55 /*                                                                           */
56 /* See the C6000 ELF ABI Specification for more details.                     */
57 /*                                                                           */
58 /* R_C6000_ABS32          |   .field    X,32                                 */
59 /* R_C6000_ABS16          |   .field    X,16                                 */
60 /* R_C6000_ABS8           |   .field    X,8                                  */
61 /* R_C6000_PCR_S21        |   B         foo                                  */
62 /*                            CALLP     foo, B3                              */
63 /* R_C6000_PCR_S12        |   BNOP      foo                                  */
64 /* R_C6000_PCR_S10        |   BPOS      foo, A10                             */
65 /*                            BDEC      foo, A1                              */
66 /* R_C6000_PCR_S7         |   ADDKPC    foo, B3, 4                           */
67 /* R_C6000_ABS_S16        |   MVK       sym, A0                              */
68 /* R_C6000_ABS_L16        |   MVKL      sym, A0                              */
69 /*                            MVKLH     sym, A0                              */
70 /* R_C6000_ABS_H16        |   MVKH      sym, A0                              */
71 /* R_C6000_SBR_U15_B      |   LDB   *+B14(sym), A1                           */
72 /*                            ADDAB   B14, sym, A1                           */
73 /* R_C6000_SBR_U15_H      |   LDH   *+B14(sym), A1                           */
74 /*                            ADDAH   B14, sym, A1                           */
75 /* R_C6000_SBR_U15_W      |   LDW   *+B14(sym), A1                           */
76 /*                            ADDAW   B14, sym, A1                           */
77 /* R_C6000_SBR_S16        |   MVK     sym-$bss, A0                           */
78 /* R_C6000_SBR_L16_B      |   MVKL (sym-$bss),  A0                           */
79 /* R_C6000_SBR_L16_H      |   MVKL (sym-$bss)/2,A0                           */
80 /* R_C6000_SBR_L16_W      |   MVKL (sym-$bss)/4,A0                           */
81 /* R_C6000_SBR_H16_B      |   MVKH (sym-$bss),  A0                           */
82 /* R_C6000_SBR_H16_H      |   MVKH (sym-$bss)/2,A0                           */
83 /* R_C6000_SBR_H16_W      |   MVKH (sym-$bss)/4,A0                           */
84 /* R_C6000_SBR_GOT_U15_W  |   LDW *+B14[GOT(sym)],A0                         */
85 /* R_C6000_SBR_GOT_L16_W  |   MVKL $DPR_GOT(sym), A0                         */
86 /* R_C6000_SBR_GOT_H16_W  |   MVKH $DPR_GOT(sym), A0                         */
87 /* R_C6000_DSBT_INDEX     |   LDW *+B14[$DSBT_index()], DP                   */
88 /*                                                                           */
89 /*---------------------------------------------------------------------------*/
91 /*****************************************************************************/
92 /* WRITE_RELOC_R() - Perform a relocation into a buffered segment.           */
93 /*****************************************************************************/
94 static void write_reloc_r(uint8_t* buffered_segment,
95                           uint32_t segment_offset,
96                           int r_type, uint32_t r)
97 {
98    uint32_t* rel_field_ptr = (uint32_t*)(buffered_segment + segment_offset);
100 #if LOADER_DEBUG
101    /*------------------------------------------------------------------------*/
102    /* Print some details about the relocation we are about to process.       */
103    /*------------------------------------------------------------------------*/
104    if(debugging_on)
105    {
106           DLIF_trace("RWRT: segment_offset: %d\n", segment_offset);
107           DLIF_trace("RWRT: buffered_segment: 0x%x\n", buffered_segment);
108           DLIF_trace("RWRT: rel_field_ptr: 0x%x\n", rel_field_ptr);
109           DLIF_trace("RWRT: result: 0x%x\n", r);
110    }
111 #endif
114    /*------------------------------------------------------------------------*/
115    /* Given the relocation type, carry out relocation into a 4 byte packet   */
116    /* within the buffered segment.                                           */
117    /*------------------------------------------------------------------------*/
118    switch(r_type)
119    {
120       case R_C6000_ABS32:
121           *rel_field_ptr = r;
122           break;
123       case R_C6000_PREL31:
124           *rel_field_ptr = (*rel_field_ptr & ~MASK(30,0)) | r;
125           break;
126       case R_C6000_ABS16:
127           *((uint16_t*)(buffered_segment + segment_offset)) = r;
128           break;
129       case R_C6000_ABS8:
130           *((uint8_t*)(buffered_segment + segment_offset)) = r;
131           break;
132      case R_C6000_PCR_S21:
133           *rel_field_ptr = (*rel_field_ptr & ~MASK(21,7)) | (r << 7);
134           break;
135       case R_C6000_PCR_S12:
136           *rel_field_ptr = (*rel_field_ptr & ~MASK(12,16)) | (r << 16);
137           break;
138       case R_C6000_PCR_S10:
139           *rel_field_ptr = (*rel_field_ptr & ~MASK(10,13)) | (r << 13);
140           break;
141       case R_C6000_PCR_S7:
142           *rel_field_ptr = (*rel_field_ptr & ~MASK(7,16)) | (r << 16);
143           break;
145       case R_C6000_ABS_S16:
146           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
147           break;
148       case R_C6000_ABS_L16:
149           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
150           break;
151       case R_C6000_ABS_H16:
152           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
153           break;
155       case R_C6000_SBR_U15_B:
156           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
157           break;
158       case R_C6000_SBR_U15_H:
159           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
160           break;
161       case R_C6000_SBR_U15_W:
162       case R_C6000_DSBT_INDEX:
163           *rel_field_ptr = (*rel_field_ptr & ~MASK(15,8)) | (r << 8);
164           break;
166       case R_C6000_SBR_S16:
167       case R_C6000_SBR_L16_B:
168       case R_C6000_SBR_L16_H:
169       case R_C6000_SBR_L16_W:
170       case R_C6000_SBR_H16_B:
171       case R_C6000_SBR_H16_H:
172       case R_C6000_SBR_H16_W:
173           *rel_field_ptr = (*rel_field_ptr & ~MASK(16,7)) | (r << 7);
174           break;
176       /*---------------------------------------------------------------------*/
177       /* Linux "import-as-own" copy relocations are not yet supported.       */
178       /*---------------------------------------------------------------------*/
179       case R_C6000_COPY:
181       default:
182           DLIF_error(DLET_RELOC,
183                      "write_reloc_r called with invalid relocation type!\n");
184   }
186 #if LOADER_DEBUG
187   if (debugging_on)
188      DLIF_trace("reloc_field 0x%x\n", *rel_field_ptr);
189 #endif
192 /*****************************************************************************/
193 /* PACK_RESULT() - Pack the result of a relocation calculation for storage   */
194 /*      in the relocation field.                                             */
195 /*****************************************************************************/
196 static int32_t pack_result(int32_t unpacked_result, int r_type)
198    switch(r_type)
199    {
200       case R_C6000_ABS32:
201       case R_C6000_ABS16:
202       case R_C6000_ABS8:
203       case R_C6000_ABS_S16:
204       case R_C6000_ABS_L16:
205       case R_C6000_SBR_U15_B:
206       case R_C6000_SBR_S16:
207       case R_C6000_SBR_L16_B:
208           return unpacked_result;
210       case R_C6000_SBR_U15_H:
211       case R_C6000_SBR_L16_H:
212       case R_C6000_PREL31:
213           return unpacked_result >> 1;
215       case R_C6000_PCR_S21:
216       case R_C6000_PCR_S12:
217       case R_C6000_PCR_S10:
218       case R_C6000_PCR_S7:
219       case R_C6000_SBR_U15_W:
220       case R_C6000_SBR_L16_W:
221       case R_C6000_DSBT_INDEX:
222           return unpacked_result >> 2;
224       case R_C6000_ABS_H16:
225       case R_C6000_SBR_H16_B:
226           return unpacked_result >> 16;
228       case R_C6000_SBR_H16_H:
229           return unpacked_result >> 17;
231       case R_C6000_SBR_H16_W:
232           return unpacked_result >> 18;
234       /*---------------------------------------------------------------------*/
235       /* Linux "import-as-own" copy relocations are not yet supported.       */
236       /*---------------------------------------------------------------------*/
237       case R_C6000_COPY:
239       default:
240           DLIF_error(DLET_RELOC,
241                      "pack_result called with invalid relocation type!\n");
242           return 0;
243    }
246 /*****************************************************************************/
247 /* MASK_RESULT() - Mask the result of a relocation calculation so that it    */
248 /*      fits the size of the relocation type's field.                        */
249 /*****************************************************************************/
250 static int32_t mask_result(int32_t unmasked_result, int r_type)
252    switch(r_type)
253    {
254       case R_C6000_ABS8:
255          return unmasked_result & 0xFF;
257       case R_C6000_ABS32:
258          return unmasked_result;
260       case R_C6000_ABS16:
261       case R_C6000_ABS_S16:
262       case R_C6000_ABS_L16:
263       case R_C6000_ABS_H16:
264       case R_C6000_SBR_S16:
265       case R_C6000_SBR_L16_B:
266       case R_C6000_SBR_L16_H:
267       case R_C6000_SBR_L16_W:
268       case R_C6000_SBR_H16_B:
269       case R_C6000_SBR_H16_H:
270       case R_C6000_SBR_H16_W:
271           return unmasked_result & 0xFFFF;
273       case R_C6000_PCR_S21:
274           return unmasked_result & 0x1FFFFF;
276       case R_C6000_PCR_S12:
277           return unmasked_result & 0xFFF;
279       case R_C6000_PCR_S10:
280           return unmasked_result & 0x3FF;
282       case R_C6000_PCR_S7:
283           return unmasked_result & 0x7F;
285       case R_C6000_SBR_U15_B:
286       case R_C6000_SBR_U15_H:
287       case R_C6000_SBR_U15_W:
288       case R_C6000_DSBT_INDEX:
289           return unmasked_result & 0x7FFF;
291       case R_C6000_PREL31:
292           return unmasked_result & 0x7FFFFFFF;
294       /*---------------------------------------------------------------------*/
295       /* Linux "import-as-own" copy relocations are not yet supported.       */
296       /*---------------------------------------------------------------------*/
297       case R_C6000_COPY:
299       default:
300          DLIF_error(DLET_RELOC,
301                     "mask_result called with invalid relocation type!\n");
302          return 0;
303    }
306 /*****************************************************************************/
307 /* REL_OVERFLOW()                                                            */
308 /*                                                                           */
309 /*    Check relocation value against the range associated with a given       */
310 /*    relocation type field size and signedness.                             */
311 /*                                                                           */
312 /*****************************************************************************/
313 static BOOL rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value)
315    /*------------------------------------------------------------------------*/
316    /* Select appropriate range check based on relocation type.               */
317    /*------------------------------------------------------------------------*/
318    switch(r_type)
319    {
320       case R_C6000_ABS16:       return ((reloc_value > 65535) ||
321                                         (reloc_value < -32768));
322       case R_C6000_ABS8:        return ((reloc_value > 255) ||
323                                         (reloc_value < -128));
324       case R_C6000_PCR_S21:     return ((reloc_value >= 0x400000) ||
325                                         (reloc_value < -0x400000));
326       case R_C6000_PCR_S12:     return ((reloc_value >= 0x2000) ||
327                                         (reloc_value < -0x2000));
328       case R_C6000_PCR_S10:     return ((reloc_value >= 0x800) ||
329                                         (reloc_value < -0x800));
330       case R_C6000_PCR_S7:      return ((reloc_value >= 0x100) ||
331                                         (reloc_value < -0x100));
332       case R_C6000_SBR_S16:
333       case R_C6000_ABS_S16:     return ((reloc_value >= 0x8000) ||
334                                         (reloc_value < -0x8000));
335       case R_C6000_SBR_U15_B:   return (((uint32_t)reloc_value) >= 0x8000);
336       case R_C6000_SBR_U15_H:   return (((uint32_t)reloc_value) >= 0xFFFF);
337       case R_C6000_DSBT_INDEX:
338       case R_C6000_SBR_U15_W:   return (((uint32_t)reloc_value) >= 0x1FFFD);
341       /*---------------------------------------------------------------------*/
342       /* Some relocation types suppress overflow checking at link-time.      */
343       /*---------------------------------------------------------------------*/
344       case R_C6000_ABS_L16:
345       case R_C6000_ABS_H16:
346       case R_C6000_SBR_L16_B:
347       case R_C6000_SBR_L16_H:
348       case R_C6000_SBR_L16_W:
349       case R_C6000_SBR_H16_B:
350       case R_C6000_SBR_H16_H:
351       case R_C6000_SBR_H16_W:
352          return 0;
354       /*---------------------------------------------------------------------*/
355       /* 32-bit relocation field values are not checked for overflow.        */
356       /*---------------------------------------------------------------------*/
357       case R_C6000_ABS32:
358       case R_C6000_PREL31:
359          return 0;
361       /*----------------------------------------------------------------------*/
362       /* If relocation type did not appear in the above cases, then we didn't */
363       /* expect to see it.                                                    */
364       /*----------------------------------------------------------------------*/
365       default:
366         DLIF_error(DLET_RELOC,
367                  "rel_overflow called with invalid relocation type!\n");
368         return 1;
369         }
372 /*****************************************************************************/
373 /* RELOC_DO() - Process a single relocation entry.                           */
374 /*****************************************************************************/
375 static void reloc_do(C60_RELOC_TYPE r_type,
376                      uint8_t *segment_address,
377                      uint32_t addend,
378                      uint32_t symval,
379                      uint32_t spc,
380                      int      wrong_endian,
381                      uint32_t base_pointer,
382                      int32_t  dsbt_index)
384    int32_t reloc_value = 0;
386 #if LOADER_DEBUG && LOADER_PROFILE
387    /*------------------------------------------------------------------------*/
388    /* In debug mode, keep a count of the number of relocations processed.    */
389    /* In profile mode, start the clock on a given relocation.                */
390    /*------------------------------------------------------------------------*/
391    int start_time;
392    if (debugging_on || profiling_on)
393    {
394       DLREL_relocations++;
395       if (profiling_on) start_time = clock();
396    }
397 #endif
399    /*------------------------------------------------------------------------*/
400    /* Calculate the relocation value according to the rules associated with  */
401    /* the given relocation type.                                             */
402    /*------------------------------------------------------------------------*/
403    switch(r_type)
404    {
405       /*---------------------------------------------------------------------*/
406       /* Straight-Up Address relocations (address references).               */
407       /*---------------------------------------------------------------------*/
408       case R_C6000_ABS32:
409       case R_C6000_ABS16:
410       case R_C6000_ABS8:
411       case R_C6000_ABS_S16:
412       case R_C6000_ABS_L16:
413       case R_C6000_ABS_H16:
414          reloc_value = symval + addend;
415          break;
417       /*---------------------------------------------------------------------*/
418       /* PC-Relative relocations (calls and branches).                       */
419       /*---------------------------------------------------------------------*/
420       case R_C6000_PCR_S21:
421       case R_C6000_PCR_S12:
422       case R_C6000_PCR_S10:
423       case R_C6000_PCR_S7:
424       {
425          /*------------------------------------------------------------------*/
426          /* Add SPC to segment address to get the PC. Mask for exec-packet   */
427          /* boundary.                                                        */
428          /*------------------------------------------------------------------*/
429          int32_t opnd_p = (spc + (int32_t)segment_address) & 0xffffffe0;
430          reloc_value = symval + addend - opnd_p;
431          break;
432       }
434       /*---------------------------------------------------------------------*/
435       /* "Place"-relative relocations (TDEH).                                */
436       /*---------------------------------------------------------------------*/
437       /* These relocations occur in data and refer to a label that occurs    */
438       /* at some signed 32-bit offset from the place where the relocation    */
439       /* occurs.                                                             */
440       /*---------------------------------------------------------------------*/
441       case R_C6000_PREL31:
442       {
443          /*------------------------------------------------------------------*/
444          /* Compute location of relocation entry and subtract it from the    */
445          /* address of the location being referenced (it is computed very    */
446          /* much like a PC-relative relocation, but it occurs in data and    */
447          /* is called a "place"-relative relocation).                        */
448          /*------------------------------------------------------------------*/
449          /* If this is an Elf32_Rel type relocation, then addend is assumed  */
450          /* to have been scaled when it was unpacked (field << 1).           */
451          /*------------------------------------------------------------------*/
452          /* For Elf32_Rela type relocations the addend is assumed to be a    */
453          /* signed 32-bit integer value.                                     */
454          /*------------------------------------------------------------------*/
455          /* Offset is not fetch-packet relative; doesn't need to be masked.  */
456          /*------------------------------------------------------------------*/
457          int32_t opnd_p = (spc + (int32_t)segment_address);
458          reloc_value = symval + addend - opnd_p;
459          break;
460       }
462       /*---------------------------------------------------------------------*/
463       /* Static-Base Relative relocations (near-DP).                         */
464       /*---------------------------------------------------------------------*/
465       case R_C6000_SBR_U15_B:
466       case R_C6000_SBR_U15_H:
467       case R_C6000_SBR_U15_W:
468       case R_C6000_SBR_S16:
469       case R_C6000_SBR_L16_B:
470       case R_C6000_SBR_L16_H:
471       case R_C6000_SBR_L16_W:
472       case R_C6000_SBR_H16_B:
473       case R_C6000_SBR_H16_H:
474       case R_C6000_SBR_H16_W:
475          reloc_value = symval + addend - base_pointer;
476          break;
478       /*---------------------------------------------------------------------*/
479       /* R_C6000_DSBT_INDEX - uses value assigned by the dynamic loader to   */
480       /*    be the DSBT index for this module as a scaled offset when        */
481       /*    referencing the DSBT. The DSBT base address is in symval and the */
482       /*    static base is in base_pointer. DP-relative offset to slot in    */
483       /*    DSBT is the offset of the DSBT relative to the DP plus the       */
484       /*    scaled DSBT index into the DSBT.                                 */
485       /*---------------------------------------------------------------------*/
486       case R_C6000_DSBT_INDEX:
487          reloc_value = ((symval + addend) - base_pointer) + (dsbt_index << 2);
488          break;
490       /*---------------------------------------------------------------------*/
491       /* Linux "import-as-own" copy relocation: after DSO initialization,    */
492       /* copy the named object from the DSO into the executable's BSS        */
493       /*---------------------------------------------------------------------*/
494       /* Linux "import-as-own" copy relocations are not yet supported.       */
495       /*---------------------------------------------------------------------*/
496       case R_C6000_COPY:
498       /*---------------------------------------------------------------------*/
499       /* Unrecognized relocation type.                                       */
500       /*---------------------------------------------------------------------*/
501       default:
502          DLIF_error(DLET_RELOC,
503                     "reloc_do called with invalid relocation type!\n");
504          break;
505    }
507    /*------------------------------------------------------------------------*/
508    /* Overflow checking.  Is relocation value out of range for the size and  */
509    /* type of the current relocation?                                        */
510    /*------------------------------------------------------------------------*/
511    if (rel_overflow(r_type, reloc_value))
512       DLIF_error(DLET_RELOC, "relocation overflow!\n");
514    /*------------------------------------------------------------------------*/
515    /* Move relocation value to appropriate offset for relocation field's     */
516    /* location.                                                              */
517    /*------------------------------------------------------------------------*/
518    reloc_value = pack_result(reloc_value, r_type);
520    /*------------------------------------------------------------------------*/
521    /* Mask packed result to the size of the relocation field.                */
522    /*------------------------------------------------------------------------*/
523    reloc_value = mask_result(reloc_value, r_type);
525    /*------------------------------------------------------------------------*/
526    /* If necessary, Swap endianness of data at relocation address.           */
527    /*------------------------------------------------------------------------*/
528    if (wrong_endian)
529       DLIMP_change_endian32((int32_t*)(segment_address + spc));
531    /*------------------------------------------------------------------------*/
532    /* Write the relocated 4-byte packet back to the segment buffer.          */
533    /*------------------------------------------------------------------------*/
534    write_reloc_r(segment_address, spc, r_type, reloc_value);
536    /*------------------------------------------------------------------------*/
537    /* Change endianness of segment address back to original.                 */
538    /*------------------------------------------------------------------------*/
539    if (wrong_endian)
540       DLIMP_change_endian32((int32_t*)(segment_address + spc));
542 #if LOADER_DEBUG && LOADER_PROFILE
543    /*------------------------------------------------------------------------*/
544    /* In profile mode, add elapsed time for this relocation to total time    */
545    /* spent doing relocations.                                               */
546    /*------------------------------------------------------------------------*/
547    if (profiling_on)
548       DLREL_total_reloc_time += (clock() - start_time);
549    if (debugging_on)
550       DLIF_trace("reloc_value = 0x%x\n", reloc_value);
551 #endif
555 /*****************************************************************************/
556 /* REL_UNPACK_ADDEND()                                                       */
557 /*                                                                           */
558 /*    Unpack addend value from the relocation field.                         */
559 /*                                                                           */
560 /*****************************************************************************/
561 static void rel_unpack_addend(C60_RELOC_TYPE r_type,
562                               uint8_t *address,
563                               uint32_t *addend)
565    /*------------------------------------------------------------------------*/
566    /* C6000 does not support Elf32_Rel type relocations in the dynamic       */
567    /* loader core.  We will emit an internal error and abort until this      */
568    /* support is added.  I abort here because this is necessarily a target-  */
569    /* specific part of the relocation infrastructure.                        */
570    /*------------------------------------------------------------------------*/
571    *addend = 0;
573    DLIF_error(DLET_RELOC,
574               "Internal Error: unpacking addend values from the relocation "
575               "field is not supported in the C6000 dynamic loader at this "
576               "time; aborting\n");
577 #if !defined (__KERNEL__)
578    exit(1);
579 #endif
582 /*****************************************************************************/
583 /* REL_SWAP_ENDIAN()                                                         */
584 /*                                                                           */
585 /*    Return TRUE if we should change the endianness of a relocation field.  */
586 /*                                                                           */
587 /*****************************************************************************/
588 static BOOL rel_swap_endian(DLIMP_Dynamic_Module *dyn_module,
589                             C60_RELOC_TYPE r_type)
591    if (dyn_module->wrong_endian) return TRUE;
593    return FALSE;
596 /*****************************************************************************/
597 /* REL_CHANGE_ENDIAN()                                                       */
598 /*                                                                           */
599 /*    Change the endianness of the relocation field at the specified address */
600 /*    in the segment's data.                                                 */
601 /*                                                                           */
602 /*****************************************************************************/
603 static void rel_change_endian(C60_RELOC_TYPE r_type, uint8_t *address)
605    /*------------------------------------------------------------------------*/
606    /* On C6000, all instructions are 32-bits wide.                           */
607    /*------------------------------------------------------------------------*/
608    DLIMP_change_endian32((int32_t *)address);
611 /*****************************************************************************/
612 /* READ_REL_TABLE()                                                          */
613 /*                                                                           */
614 /*    Read in an Elf32_Rel type relocation table.  This function allocates   */
615 /*    host memory for the table.                                             */
616 /*                                                                           */
617 /*****************************************************************************/
618 static void read_rel_table(struct Elf32_Rel **rel_table,
619                            int32_t table_offset,
620                            uint32_t relnum, uint32_t relent,
621                            LOADER_FILE_DESC *fd, BOOL wrong_endian)
623    *rel_table = (struct Elf32_Rel *)DLIF_malloc(relnum * relent);
624    if (NULL == *rel_table) {
625        DLIF_error(DLET_MEMORY, "Failed to Allocate read_rel_table\n");
626        return;
627    }
628    DLIF_fseek(fd, table_offset, LOADER_SEEK_SET);
629    DLIF_fread(*rel_table, relnum, relent, fd);
631    if (wrong_endian)
632    {
633       int i;
634       for (i = 0; i < relnum; i++)
635          DLIMP_change_rel_endian(*rel_table + i);
636    }
639 /*****************************************************************************/
640 /* PROCESS_REL_TABLE()                                                       */
641 /*                                                                           */
642 /*    Process table of Elf32_Rel type relocations.                           */
643 /*                                                                           */
644 /*****************************************************************************/
645 static void process_rel_table(DLOAD_HANDLE handle, DLIMP_Loaded_Segment* seg,
646                               struct Elf32_Rel *rel_table,
647                               uint32_t relnum,
648                               int32_t *start_relidx,
649                               uint32_t ti_static_base,
650                               DLIMP_Dynamic_Module* dyn_module)
652    Elf32_Addr seg_start_addr = seg->input_vaddr;
653    Elf32_Addr seg_end_addr   = seg_start_addr + seg->phdr.p_memsz;
654    BOOL found = FALSE;
655    int32_t relidx = *start_relidx;
657    /*------------------------------------------------------------------------*/
658    /* If the given start reloc index is out of range, then start from the    */
659    /* beginning of the given table.                                          */
660    /*------------------------------------------------------------------------*/
661    if (relidx >= relnum) relidx = 0;
663    /*------------------------------------------------------------------------*/
664    /* Spin through Elf32_Rel type relocation table.                          */
665    /*------------------------------------------------------------------------*/
666    for ( ; relidx < relnum; relidx++)
667    {
668       /*---------------------------------------------------------------------*/
669       /* If the relocation offset falls within the segment, process it.      */
670       /*---------------------------------------------------------------------*/
671       if (rel_table[relidx].r_offset >= seg_start_addr &&
672           rel_table[relidx].r_offset < seg_end_addr)
673       {
674          Elf32_Addr     r_symval = 0;
675          C60_RELOC_TYPE r_type  =
676                        (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info);
677          int32_t        r_symid = ELF32_R_SYM(rel_table[relidx].r_info);
679          uint8_t *reloc_address = NULL;
680          uint32_t pc     = 0;
681          uint32_t addend = 0;
683          BOOL     change_endian = FALSE;
685          found = TRUE;
687          /*------------------------------------------------------------------*/
688          /* If symbol definition is not found, don't do the relocation.      */
689          /* An error is generated by the lookup function.                    */
690          /*------------------------------------------------------------------*/
691          if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval))
692             continue;
694          /*------------------------------------------------------------------*/
695          /* Addend value is stored in the relocation field.                  */
696          /* We'll need to unpack it from the data for the segment that is    */
697          /* currently being relocated.                                       */
698          /*------------------------------------------------------------------*/
699          reloc_address =
700                        (((uint8_t *)(seg->phdr.p_vaddr) + seg->reloc_offset) +
701                         rel_table[relidx].r_offset - seg->input_vaddr);
702          pc = (uint32_t)reloc_address;
704          change_endian = rel_swap_endian(dyn_module, r_type);
705          if (change_endian)
706             rel_change_endian(r_type, reloc_address);
708          rel_unpack_addend(
709                         (C60_RELOC_TYPE)ELF32_R_TYPE(rel_table[relidx].r_info),
710                                                        reloc_address, &addend);
712          /*------------------------------------------------------------------*/
713          /* Perform actual relocation.  This is a really wide function       */
714          /* interface and could do with some encapsulation.                  */
715          /*------------------------------------------------------------------*/
716          reloc_do(r_type,
717                   reloc_address,
718                   addend,
719                   r_symval,
720                   pc,
721                   dyn_module->wrong_endian,
722                   ti_static_base,
723                   dyn_module->dsbt_index);
725       }
727       else if (found)
728          break;
729    }
732 /*****************************************************************************/
733 /* READ_RELA_TABLE()                                                         */
734 /*                                                                           */
735 /*    Read in an Elf32_Rela type relocation table.  This function allocates  */
736 /*    host memory for the table.                                             */
737 /*                                                                           */
738 /*****************************************************************************/
739 static void read_rela_table(struct Elf32_Rela **rela_table,
740                             int32_t table_offset,
741                             uint32_t relanum, uint32_t relaent,
742                             LOADER_FILE_DESC *fd, BOOL wrong_endian)
744    *rela_table = (struct Elf32_Rela *)DLIF_malloc(relanum * relaent);
745    if (NULL == *rela_table) {
746        DLIF_error(DLET_MEMORY, "Failed to Allocate read_rela_table\n");
747        return;
748    }
749    DLIF_fseek(fd, table_offset, LOADER_SEEK_SET);
750    DLIF_fread(*rela_table, relanum, relaent, fd);
752    if (wrong_endian)
753    {
754       int i;
755       for (i = 0; i < relanum; i++)
756          DLIMP_change_rela_endian(*rela_table + i);
757    }
760 /*****************************************************************************/
761 /* PROCESS_RELA_TABLE()                                                      */
762 /*                                                                           */
763 /*    Process a table of Elf32_Rela type relocations.                        */
764 /*                                                                           */
765 /*****************************************************************************/
766 static void process_rela_table(DLOAD_HANDLE handle, DLIMP_Loaded_Segment *seg,
767                                struct Elf32_Rela *rela_table,
768                                uint32_t relanum,
769                                int32_t *start_relidx,
770                                uint32_t ti_static_base,
771                                DLIMP_Dynamic_Module *dyn_module)
773     Elf32_Addr seg_start_addr = seg->input_vaddr;
774     Elf32_Addr seg_end_addr   = seg_start_addr + seg->phdr.p_memsz;
775     BOOL       found          = FALSE;
776     int32_t    relidx         = *start_relidx;
778     /*-----------------------------------------------------------------------*/
779     /* If the given start reloc index is out of range, then start from       */
780     /* the beginning of the given table.                                     */
781     /*-----------------------------------------------------------------------*/
782     if (relidx > relanum) relidx = 0;
784     /*-----------------------------------------------------------------------*/
785     /* Spin through RELA relocation table.                                   */
786     /*-----------------------------------------------------------------------*/
787     for ( ; relidx < relanum; relidx++)
788     {
789         /*-------------------------------------------------------------------*/
790         /* If the relocation offset falls within the segment, process it.    */
791         /*-------------------------------------------------------------------*/
792         if (rela_table[relidx].r_offset >= seg_start_addr &&
793             rela_table[relidx].r_offset < seg_end_addr)
794         {
795             Elf32_Addr     r_symval;
796             C60_RELOC_TYPE r_type  =
797                       (C60_RELOC_TYPE)ELF32_R_TYPE(rela_table[relidx].r_info);
798             int32_t        r_symid = ELF32_R_SYM(rela_table[relidx].r_info);
800             found = TRUE;
802             /*---------------------------------------------------------------*/
803             /* If symbol definition is not found, don't do the relocation.   */
804             /* An error is generated by the lookup function.                 */
805             /*---------------------------------------------------------------*/
806             if (!DLSYM_canonical_lookup(handle, r_symid, dyn_module, &r_symval))
807                 continue;
809             /*---------------------------------------------------------------*/
810             /* Perform actual relocation.  This is a really wide function    */
811             /* interface and could do with some encapsulation.               */
812             /*---------------------------------------------------------------*/
813             reloc_do(r_type,
814                      (uint8_t*)(seg->phdr.p_vaddr) + seg->reloc_offset,
815                      rela_table[relidx].r_addend,
816                      r_symval,
817                      rela_table[relidx].r_offset - seg->input_vaddr,
818                      dyn_module->wrong_endian,
819                      ti_static_base,
820                      dyn_module->dsbt_index);
821         }
823         else if (found)
824             break;
825     }
828 /*****************************************************************************/
829 /* PROCESS_GOT_RELOCS()                                                      */
830 /*                                                                           */
831 /*    Process all GOT relocations.  It is possible to have both Elf32_Rel    */
832 /*    and Elf32_Rela type relocations in the same file, so we handle tham    */
833 /*    both.                                                                  */
834 /*                                                                           */
835 /*****************************************************************************/
836 static void process_got_relocs(DLOAD_HANDLE handle,
837                                struct Elf32_Rel* rel_table, uint32_t relnum,
838                                struct Elf32_Rela* rela_table, uint32_t relanum,
839                                DLIMP_Dynamic_Module* dyn_module)
841    DLIMP_Loaded_Segment *seg =
842        (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf);
843    uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size;
844    int32_t  rel_relidx = 0;
845    int32_t  rela_relidx = 0;
846    uint32_t seg_idx = 0;
847    uint32_t ti_static_base = 0;
849    /*------------------------------------------------------------------------*/
850    /* Get the value of the static base (__TI_STATIC_BASE) which will be      */
851    /* passed into the relocation table processing functions.                 */
852    /*------------------------------------------------------------------------*/
853    if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab,
854                              dyn_module->symnum, &ti_static_base))
855       DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n");
857    /*------------------------------------------------------------------------*/
858    /* Process relocations segment by segment.                                */
859    /*------------------------------------------------------------------------*/
860    for (seg_idx = 0; seg_idx < num_segs; seg_idx++)
861    {
862       /*---------------------------------------------------------------------*/
863       /* Relocations should not occur in uninitialized segments.             */
864       /*---------------------------------------------------------------------*/
865       if (!seg[seg_idx].phdr.p_filesz) continue;
867       if (rela_table)
868          process_rela_table(handle, (seg + seg_idx),
869                             rela_table, relanum, &rela_relidx,
870                             ti_static_base, dyn_module);
872       if (rel_table)
873          process_rel_table(handle, (seg + seg_idx),
874                             rel_table, relnum, &rel_relidx,
875                             ti_static_base, dyn_module);
876    }
879 /*****************************************************************************/
880 /* PROCESS_PLTGOT_RELOCS()                                                   */
881 /*                                                                           */
882 /*    Process all PLTGOT relocation entries.  The PLTGOT relocation table    */
883 /*    can be either Elf32_Rel or Elf32_Rela type.  All PLTGOT relocations    */
884 /*    ar guaranteed to belong to the same segment.                           */
885 /*                                                                           */
886 /*****************************************************************************/
887 static void process_pltgot_relocs(DLOAD_HANDLE handle, void* plt_reloc_table,
888                                   int reltype,
889                                   uint32_t pltnum,
890                                   DLIMP_Dynamic_Module* dyn_module)
892    Elf32_Addr r_offset = (reltype == DT_REL) ?
893                              ((struct Elf32_Rel *)plt_reloc_table)->r_offset :
894                              ((struct Elf32_Rela *)plt_reloc_table)->r_offset;
896    DLIMP_Loaded_Segment* seg =
897       (DLIMP_Loaded_Segment*)(dyn_module->loaded_module->loaded_segments.buf);
899    uint32_t num_segs = dyn_module->loaded_module->loaded_segments.size;
900    int32_t  plt_relidx = 0;
901    uint32_t seg_idx = 0;
902    uint32_t ti_static_base = 0;
904    /*------------------------------------------------------------------------*/
905    /* Get the value of the static base (__TI_STATIC_BASE) which will be      */
906    /* passed into the relocation table processing functions.                 */
907    /*------------------------------------------------------------------------*/
908    if (!DLSYM_lookup_local_symtab("__TI_STATIC_BASE", dyn_module->symtab,
909                              dyn_module->symnum, &ti_static_base))
910       DLIF_error(DLET_RELOC, "Could not resolve value of __TI_STATIC_BASE\n");
912    /*------------------------------------------------------------------------*/
913    /* For each segment s, check if the relocation falls within s. If so,     */
914    /* then all other relocations are guaranteed to fall with s. Process      */
915    /* all relocations and then return.                                       */
916    /*------------------------------------------------------------------------*/
917    for (seg_idx = 0; seg_idx < num_segs; seg_idx++)
918    {
919       Elf32_Addr seg_start_addr = seg[seg_idx].input_vaddr;
920       Elf32_Addr seg_end_addr   = seg_start_addr + seg[seg_idx].phdr.p_memsz;
922       /*---------------------------------------------------------------------*/
923       /* Relocations should not occur in uninitialized segments.             */
924       /*---------------------------------------------------------------------*/
925       if(!seg[seg_idx].phdr.p_filesz) continue;
927       if (r_offset >= seg_start_addr &&
928           r_offset < seg_end_addr)
929       {
930          if (reltype == DT_REL)
931             process_rel_table(handle, (seg + seg_idx),
932                               (struct Elf32_Rel *)plt_reloc_table,
933                               pltnum, &plt_relidx,
934                               ti_static_base, dyn_module);
935          else
936             process_rela_table(handle, (seg + seg_idx),
937                                (struct Elf32_Rela *)plt_reloc_table,
938                                pltnum, &plt_relidx,
939                                ti_static_base, dyn_module);
941          break;
942       }
943    }
946 /*****************************************************************************/
947 /* RELOCATE() - Perform RELA and REL type relocations for given ELF object   */
948 /*      file that we are in the process of loading and relocating.           */
949 /*****************************************************************************/
950 void DLREL_relocate_c60(DLOAD_HANDLE handle, LOADER_FILE_DESC *fd,
951                         DLIMP_Dynamic_Module *dyn_module)
953    struct Elf32_Dyn  *dyn_nugget = dyn_module->dyntab;
954    struct Elf32_Rela *rela_table = NULL;
955    struct Elf32_Rel  *rel_table  = NULL;
956    void              *plt_table  = NULL;
958    /*------------------------------------------------------------------------*/
959    /* Read the size of the relocation table (DT_RELASZ) and the size per     */
960    /* relocation (DT_RELAENT) from the dynamic segment.                      */
961    /*------------------------------------------------------------------------*/
962    uint32_t relasz  = DLIMP_get_first_dyntag(DT_RELASZ, dyn_nugget);
963    uint32_t relaent = DLIMP_get_first_dyntag(DT_RELAENT, dyn_nugget);
964    uint32_t relanum = 0;
966    /*------------------------------------------------------------------------*/
967    /* Read the size of the relocation table (DT_RELSZ) and the size per      */
968    /* relocation (DT_RELENT) from the dynamic segment.                       */
969    /*------------------------------------------------------------------------*/
970    uint32_t relsz  = DLIMP_get_first_dyntag(DT_RELSZ, dyn_nugget);
971    uint32_t relent = DLIMP_get_first_dyntag(DT_RELENT, dyn_nugget);
972    uint32_t relnum = 0;
974    /*------------------------------------------------------------------------*/
975    /* Read the size of the relocation table (DT_PLTRELSZ) and the type of    */
976    /* of the PLTGOT relocation table (DT_PLTREL): one of DT_REL or DT_RELA   */
977    /*------------------------------------------------------------------------*/
978    uint32_t pltrelsz  = DLIMP_get_first_dyntag(DT_PLTRELSZ, dyn_nugget);
979    int      pltreltyp = DLIMP_get_first_dyntag(DT_PLTREL, dyn_nugget);
980    uint32_t pltnum    = 0;
982    /*------------------------------------------------------------------------*/
983    /* Find/record DSBT index associated with this module.                    */
984    /*------------------------------------------------------------------------*/
985    if (is_dsbt_module(dyn_module) &&
986        (dyn_module->dsbt_index == DSBT_INDEX_INVALID))
987       dyn_module->dsbt_index =
988                    DLIF_get_dsbt_index(dyn_module->loaded_module->file_handle);
990    /*------------------------------------------------------------------------*/
991    /* Read the PLTGOT relocation table from the file                         */
992    /* The PLTGOT table is a subsection at the end of either the DT_REL or    */
993    /* DT_RELA table.  The size of the table it belongs to DT_REL(A)SZ        */
994    /* includes the size of the PLTGOT table.  So it must be adjusted so that */
995    /* the GOT relocation tables only contain actual GOT relocations.         */
996    /*------------------------------------------------------------------------*/
997    if (pltrelsz != INT_MAX)
998    {
999       if (pltreltyp == DT_REL)
1000       {
1001          pltnum = pltrelsz/relent;
1002          relsz -= pltrelsz;
1003          read_rel_table(((struct Elf32_Rel**) &plt_table),
1004                         DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget),
1005                         pltnum, relent, fd, dyn_module->wrong_endian);
1006       }
1008       else if (pltreltyp == DT_RELA)
1009       {
1010          pltnum = pltrelsz/relaent;
1011          relasz -= pltrelsz;
1012          read_rela_table(((struct Elf32_Rela**) &plt_table),
1013                          DLIMP_get_first_dyntag(DT_JMPREL, dyn_nugget),
1014                          pltnum, relaent, fd, dyn_module->wrong_endian);
1015       }
1017       else
1018       {
1019          DLIF_error(DLET_RELOC,
1020                     "DT_PLTREL is invalid: must be either %d or %d\n",
1021                     DT_REL, DT_RELA);
1022       }
1023    }
1025    /*------------------------------------------------------------------------*/
1026    /* Read the DT_RELA GOT relocation table from the file                    */
1027    /*------------------------------------------------------------------------*/
1028    if (relasz != INT_MAX)
1029    {
1030       relanum = relasz/relaent;
1031       read_rela_table(&rela_table, DLIMP_get_first_dyntag(DT_RELA, dyn_nugget),
1032                       relanum, relaent, fd, dyn_module->wrong_endian);
1033    }
1035    /*------------------------------------------------------------------------*/
1036    /* Read the DT_REL GOT relocation table from the file                     */
1037    /*------------------------------------------------------------------------*/
1038    if (relsz != INT_MAX)
1039    {
1040       relnum = relsz/relent;
1041       read_rel_table(&rel_table, DLIMP_get_first_dyntag(DT_REL, dyn_nugget),
1042                      relnum, relent, fd, dyn_module->wrong_endian);
1043    }
1045    /*------------------------------------------------------------------------*/
1046    /* Process the PLTGOT relocations                                         */
1047    /*------------------------------------------------------------------------*/
1048    if (plt_table)
1049       process_pltgot_relocs(handle, plt_table, pltreltyp, pltnum, dyn_module);
1051    /*------------------------------------------------------------------------*/
1052    /* Process the GOT relocations                                            */
1053    /*------------------------------------------------------------------------*/
1054    if (rel_table || rela_table)
1055       process_got_relocs(handle, rel_table, relnum, rela_table, relanum, dyn_module);
1057    /*-------------------------------------------------------------------------*/
1058    /* Free memory used for ELF relocation table copies.                       */
1059    /*-------------------------------------------------------------------------*/
1060    if (rela_table) DLIF_free(rela_table);
1061    if (rel_table)  DLIF_free(rel_table);
1062    if (plt_table)  DLIF_free(plt_table);
1065 /*****************************************************************************/
1066 /* UNIT TESTING INTERFACE                                                    */
1067 /*****************************************************************************/
1068 #ifdef UNIT_TEST
1069 void unit_c60_reloc_do(C60_RELOC_TYPE r_type,
1070                        uint8_t *address_space,
1071                        uint32_t addend, uint32_t symval, uint32_t pc,
1072                        uint32_t static_base, int wrong_endian,
1073                        int32_t dsbt_index)
1075     reloc_do(r_type, address_space, addend, symval, pc, FALSE, static_base, dsbt_index);
1078 #if 0 /* RELA TYPE RELOCATIONS HAVE ADDEND IN RELOCATION ENTRY */
1079 void unit_c60_rel_unpack_addend(C60_RELOC_TYPE r_type,
1080                                 uint8_t* address,
1081                                 uint32_t* addend)
1083     rel_unpack_addend(r_type, address, addend);
1085 #endif
1087 BOOL unit_c60_rel_overflow(C60_RELOC_TYPE r_type, int32_t reloc_value)
1089    return rel_overflow(r_type, reloc_value);
1091 #endif