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