]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/rtld-elf/arm/reloc.c
Update llvm to trunk r256633.
[FreeBSD/FreeBSD.git] / libexec / rtld-elf / arm / reloc.c
1 /*      $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */
2
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 #include <sys/param.h>
6 #include <sys/mman.h>
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include "machine/sysarch.h"
15
16 #include "debug.h"
17 #include "rtld.h"
18 #include "paths.h"
19
20 void
21 init_pltgot(Obj_Entry *obj)
22 {       
23         if (obj->pltgot != NULL) {
24                 obj->pltgot[1] = (Elf_Addr) obj;
25                 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
26         }
27 }
28
29 int             
30 do_copy_relocations(Obj_Entry *dstobj)
31 {
32         const Elf_Rel *rellim;
33         const Elf_Rel *rel;
34
35         assert(dstobj->mainprog);       /* COPY relocations are invalid elsewhere */
36
37         rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
38         for (rel = dstobj->rel;  rel < rellim;  rel++) {
39                 if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) {
40                         void *dstaddr;
41                         const Elf_Sym *dstsym;
42                         const char *name;
43                         size_t size;
44                         const void *srcaddr;
45                         const Elf_Sym *srcsym;
46                         const Obj_Entry *srcobj, *defobj;
47                         SymLook req;
48                         int res;
49                         
50                         dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
51                         dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
52                         name = dstobj->strtab + dstsym->st_name;
53                         size = dstsym->st_size;
54
55                         symlook_init(&req, name);
56                         req.ventry = fetch_ventry(dstobj,
57                             ELF_R_SYM(rel->r_info));
58                         req.flags = SYMLOOK_EARLY;
59
60                         for (srcobj = dstobj->next;  srcobj != NULL; 
61                              srcobj = srcobj->next) {
62                                 res = symlook_obj(&req, srcobj);
63                                 if (res == 0) {
64                                         srcsym = req.sym_out;
65                                         defobj = req.defobj_out;
66                                         break;
67                                 }
68                         }                       
69                         if (srcobj == NULL) {
70                                 _rtld_error(
71 "Undefined symbol \"%s\" referenced from COPY relocation in %s",
72                                     name, dstobj->path);
73                                 return (-1);
74                         }
75                         
76                         srcaddr = (const void *)(defobj->relocbase +
77                             srcsym->st_value);
78                         memcpy(dstaddr, srcaddr, size);
79                 }
80         }
81         return 0;                            
82 }
83
84 void _rtld_bind_start(void);
85 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
86
87 int open();
88 int _open();
89 void
90 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
91 {
92         const Elf_Rel *rel = 0, *rellim;
93         Elf_Addr relsz = 0;
94         Elf_Addr *where;
95         uint32_t size;
96
97         for (; dynp->d_tag != DT_NULL; dynp++) {
98                 switch (dynp->d_tag) {
99                 case DT_REL:
100                         rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
101                         break;
102                 case DT_RELSZ:
103                         relsz = dynp->d_un.d_val;
104                         break;
105                 }
106         }
107         rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
108         size = (rellim - 1)->r_offset - rel->r_offset;
109         for (; rel < rellim; rel++) {
110                 where = (Elf_Addr *)(relocbase + rel->r_offset);
111                 
112                 *where += (Elf_Addr)relocbase;
113         }
114 }
115 /*
116  * It is possible for the compiler to emit relocations for unaligned data.
117  * We handle this situation with these inlines.
118  */
119 #define RELOC_ALIGNED_P(x) \
120         (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
121
122 static __inline Elf_Addr
123 load_ptr(void *where)
124 {
125         Elf_Addr res;
126
127         memcpy(&res, where, sizeof(res));
128
129         return (res);
130 }
131
132 static __inline void
133 store_ptr(void *where, Elf_Addr val)
134 {
135
136         memcpy(where, &val, sizeof(val));
137 }
138
139 static int
140 reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
141     int flags, RtldLockState *lockstate)
142 {
143         Elf_Addr        *where;
144         const Elf_Sym   *def;
145         const Obj_Entry *defobj;
146         Elf_Addr         tmp;
147         unsigned long    symnum;
148
149         where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
150         symnum = ELF_R_SYM(rel->r_info);
151
152         switch (ELF_R_TYPE(rel->r_info)) {
153         case R_ARM_NONE:
154                 break;
155                 
156 #if 1 /* XXX should not occur */
157         case R_ARM_PC24: {      /* word32 S - P + A */
158                 Elf32_Sword addend;
159                 
160                 /*
161                  * Extract addend and sign-extend if needed.
162                  */
163                 addend = *where;
164                 if (addend & 0x00800000)
165                         addend |= 0xff000000;
166                 
167                 def = find_symdef(symnum, obj, &defobj, flags, cache,
168                     lockstate);
169                 if (def == NULL)
170                                 return -1;
171                         tmp = (Elf_Addr)obj->relocbase + def->st_value
172                             - (Elf_Addr)where + (addend << 2);
173                         if ((tmp & 0xfe000000) != 0xfe000000 &&
174                             (tmp & 0xfe000000) != 0) {
175                                 _rtld_error(
176                                 "%s: R_ARM_PC24 relocation @ %p to %s failed "
177                                 "(displacement %ld (%#lx) out of range)",
178                                     obj->path, where,
179                                     obj->strtab + obj->symtab[symnum].st_name,
180                                     (long) tmp, (long) tmp);
181                                 return -1;
182                         }
183                         tmp >>= 2;
184                         *where = (*where & 0xff000000) | (tmp & 0x00ffffff);
185                         dbg("PC24 %s in %s --> %p @ %p in %s",
186                             obj->strtab + obj->symtab[symnum].st_name,
187                             obj->path, (void *)*where, where, defobj->path);
188                         break;
189                 }
190 #endif
191
192                 case R_ARM_ABS32:       /* word32 B + S + A */
193                 case R_ARM_GLOB_DAT:    /* word32 B + S */
194                         def = find_symdef(symnum, obj, &defobj, flags, cache,
195                             lockstate);
196                         if (def == NULL)
197                                 return -1;
198                         if (__predict_true(RELOC_ALIGNED_P(where))) {
199                                 tmp =  *where + (Elf_Addr)defobj->relocbase +
200                                     def->st_value;
201                                 *where = tmp;
202                         } else {
203                                 tmp = load_ptr(where) +
204                                     (Elf_Addr)defobj->relocbase +
205                                     def->st_value;
206                                 store_ptr(where, tmp);
207                         }
208                         dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
209                             obj->strtab + obj->symtab[symnum].st_name,
210                             obj->path, (void *)tmp, where, defobj->path);
211                         break;
212
213                 case R_ARM_RELATIVE:    /* word32 B + A */
214                         if (__predict_true(RELOC_ALIGNED_P(where))) {
215                                 tmp = *where + (Elf_Addr)obj->relocbase;
216                                 *where = tmp;
217                         } else {
218                                 tmp = load_ptr(where) +
219                                     (Elf_Addr)obj->relocbase;
220                                 store_ptr(where, tmp);
221                         }
222                         dbg("RELATIVE in %s --> %p", obj->path,
223                             (void *)tmp);
224                         break;
225
226                 case R_ARM_COPY:
227                         /*
228                          * These are deferred until all other relocations have
229                          * been done.  All we do here is make sure that the
230                          * COPY relocation is not in a shared library.  They
231                          * are allowed only in executable files.
232                          */
233                         if (!obj->mainprog) {
234                                 _rtld_error(
235                         "%s: Unexpected R_COPY relocation in shared library",
236                                     obj->path);
237                                 return -1;
238                         }
239                         dbg("COPY (avoid in main)");
240                         break;
241
242                 case R_ARM_TLS_DTPOFF32:
243                         def = find_symdef(symnum, obj, &defobj, flags, cache,
244                             lockstate);
245                         if (def == NULL)
246                                 return -1;
247
248                         tmp = (Elf_Addr)(def->st_value);
249                         if (__predict_true(RELOC_ALIGNED_P(where)))
250                                 *where = tmp;
251                         else
252                                 store_ptr(where, tmp);
253
254                         dbg("TLS_DTPOFF32 %s in %s --> %p",
255                             obj->strtab + obj->symtab[symnum].st_name,
256                             obj->path, (void *)tmp);
257
258                         break;
259                 case R_ARM_TLS_DTPMOD32:
260                         def = find_symdef(symnum, obj, &defobj, flags, cache,
261                             lockstate);
262                         if (def == NULL)
263                                 return -1;
264
265                         tmp = (Elf_Addr)(defobj->tlsindex);
266                         if (__predict_true(RELOC_ALIGNED_P(where)))
267                                 *where = tmp;
268                         else
269                                 store_ptr(where, tmp);
270
271                         dbg("TLS_DTPMOD32 %s in %s --> %p",
272                             obj->strtab + obj->symtab[symnum].st_name,
273                             obj->path, (void *)tmp);
274
275                         break;
276
277                 case R_ARM_TLS_TPOFF32:
278                         def = find_symdef(symnum, obj, &defobj, flags, cache,
279                             lockstate);
280                         if (def == NULL)
281                                 return -1;
282
283                         if (!defobj->tls_done && allocate_tls_offset(obj))
284                                 return -1;
285
286                         /* XXX: FIXME */
287                         tmp = (Elf_Addr)def->st_value + defobj->tlsoffset +
288                             TLS_TCB_SIZE;
289                         if (__predict_true(RELOC_ALIGNED_P(where)))
290                                 *where = tmp;
291                         else
292                                 store_ptr(where, tmp);
293                         dbg("TLS_TPOFF32 %s in %s --> %p",
294                             obj->strtab + obj->symtab[symnum].st_name,
295                             obj->path, (void *)tmp);
296                         break;
297
298
299                 default:
300                         dbg("sym = %lu, type = %lu, offset = %p, "
301                             "contents = %p, symbol = %s",
302                             symnum, (u_long)ELF_R_TYPE(rel->r_info),
303                             (void *)rel->r_offset, (void *)load_ptr(where),
304                             obj->strtab + obj->symtab[symnum].st_name);
305                         _rtld_error("%s: Unsupported relocation type %ld "
306                             "in non-PLT relocations\n",
307                             obj->path, (u_long) ELF_R_TYPE(rel->r_info));
308                         return -1;
309         }
310         return 0;
311 }
312
313 /*
314  *  * Process non-PLT relocations
315  *   */
316 int
317 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
318     RtldLockState *lockstate)
319 {
320         const Elf_Rel *rellim;
321         const Elf_Rel *rel;
322         SymCache *cache;
323         int r = -1;
324         
325         /* The relocation for the dynamic loader has already been done. */
326         if (obj == obj_rtld)
327                 return (0);
328         if ((flags & SYMLOOK_IFUNC) != 0)
329                 /* XXX not implemented */
330                 return (0);
331
332         /*
333          * The dynamic loader may be called from a thread, we have
334          * limited amounts of stack available so we cannot use alloca().
335          */
336         cache = calloc(obj->dynsymcount, sizeof(SymCache));
337         /* No need to check for NULL here */
338
339         rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
340         for (rel = obj->rel; rel < rellim; rel++) {
341                 if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0)
342                         goto done;
343         }
344         r = 0;
345 done:
346         if (cache != NULL)
347                 free(cache);
348         return (r);
349 }
350
351 /*
352  *  * Process the PLT relocations.
353  *   */
354 int
355 reloc_plt(Obj_Entry *obj)
356 {
357         const Elf_Rel *rellim;
358         const Elf_Rel *rel;
359                 
360         rellim = (const Elf_Rel *)((char *)obj->pltrel +
361             obj->pltrelsize);
362         for (rel = obj->pltrel;  rel < rellim;  rel++) {
363                 Elf_Addr *where;
364
365                 assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
366                 
367                 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
368                 *where += (Elf_Addr )obj->relocbase;
369         }
370         
371         return (0);
372 }
373
374 /*
375  *  * LD_BIND_NOW was set - force relocation for all jump slots
376  *   */
377 int
378 reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
379 {
380         const Obj_Entry *defobj;
381         const Elf_Rel *rellim;
382         const Elf_Rel *rel;
383         const Elf_Sym *def;
384         Elf_Addr *where;
385         Elf_Addr target;
386         
387         rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
388         for (rel = obj->pltrel; rel < rellim; rel++) {
389                 assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
390                 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
391                 def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
392                     SYMLOOK_IN_PLT | flags, NULL, lockstate);
393                 if (def == NULL) {
394                         dbg("reloc_jmpslots: sym not found");
395                         return (-1);
396                 }
397                 
398                 target = (Elf_Addr)(defobj->relocbase + def->st_value);         
399                 reloc_jmpslot(where, target, defobj, obj,
400                     (const Elf_Rel *) rel);
401         }
402         
403         obj->jmpslots_done = true;
404         
405         return (0);
406 }
407
408 int
409 reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
410 {
411
412         /* XXX not implemented */
413         return (0);
414 }
415
416 int
417 reloc_gnu_ifunc(Obj_Entry *obj, int flags,
418     struct Struct_RtldLockState *lockstate)
419 {
420
421         /* XXX not implemented */
422         return (0);
423 }
424
425 Elf_Addr
426 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
427                 const Obj_Entry *obj, const Elf_Rel *rel)
428 {
429
430         assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
431
432         if (*where != target)
433                 *where = target;
434
435         return target;
436 }
437
438 void
439 allocate_initial_tls(Obj_Entry *objs)
440 {
441 #ifdef ARM_TP_ADDRESS
442         void **_tp = (void **)ARM_TP_ADDRESS;
443 #endif
444
445         /*
446         * Fix the size of the static TLS block by using the maximum
447         * offset allocated so far and adding a bit for dynamic modules to
448         * use.
449         */
450
451         tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
452
453 #ifdef ARM_TP_ADDRESS
454         (*_tp) = (void *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8);
455 #else
456         sysarch(ARM_SET_TP, allocate_tls(objs, NULL, TLS_TCB_SIZE, 8));
457 #endif
458 }
459
460 void *
461 __tls_get_addr(tls_index* ti)
462 {
463         char *p;
464 #ifdef ARM_TP_ADDRESS
465         void **_tp = (void **)ARM_TP_ADDRESS;
466
467         p = tls_get_addr_common((Elf_Addr **)(*_tp), ti->ti_module, ti->ti_offset);
468 #else
469         void *_tp;
470         __asm __volatile("mrc  p15, 0, %0, c13, c0, 3"          \
471             : "=r" (_tp));
472         p = tls_get_addr_common((Elf_Addr **)(_tp), ti->ti_module, ti->ti_offset);
473 #endif
474
475         return (p);
476 }