No empty .Rs/.Re
[netbsd-mini2440.git] / libexec / ld.elf_so / arch / hppa / hppa_reloc.c
bloba4e97976e0e4d58c52db5d64789dbc2ca93e4f56
1 /* $NetBSD: hppa_reloc.c,v 1.29 2009/08/29 13:46:54 jmmv Exp $ */
3 /*-
4 * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette and Nick Hudson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: hppa_reloc.c,v 1.29 2009/08/29 13:46:54 jmmv Exp $");
35 #endif /* not lint */
37 #include <stdlib.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/queue.h>
42 #include <string.h>
44 #include "rtld.h"
45 #include "debug.h"
47 #ifdef RTLD_DEBUG_HPPA
48 #define hdbg(x) xprintf x
49 #else
50 #define hdbg(x) /* nothing */
51 #endif
53 caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr);
54 void _rtld_bind_start(void);
55 void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *);
58 * It is possible for the compiler to emit relocations for unaligned data.
59 * We handle this situation with these inlines.
61 #define RELOC_ALIGNED_P(x) \
62 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
64 static inline Elf_Addr
65 load_ptr(void *where)
67 if (__predict_true(RELOC_ALIGNED_P(where)))
68 return *(Elf_Addr *)where;
69 else {
70 Elf_Addr res;
72 (void)memcpy(&res, where, sizeof(res));
73 return res;
77 static inline void
78 store_ptr(void *where, Elf_Addr val)
80 if (__predict_true(RELOC_ALIGNED_P(where)))
81 *(Elf_Addr *)where = val;
82 else
83 (void)memcpy(where, &val, sizeof(val));
87 * In the runtime architecture (ABI), PLABEL function
88 * pointers are distinguished from normal function
89 * pointers by having the next-least-significant bit
90 * set. (This bit is referred to as the L field in
91 * HP documentation). The $$dyncall millicode is
92 * aware of this.
94 #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1))
95 #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1))
96 #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3))
99 * This is the PLABEL structure. The function PC and
100 * shared linkage members must come first, as they are
101 * the actual PLABEL.
103 typedef struct _hppa_plabel {
104 Elf_Addr hppa_plabel_pc;
105 Elf_Addr hppa_plabel_sl;
106 SLIST_ENTRY(_hppa_plabel) hppa_plabel_next;
107 } hppa_plabel;
110 * For now allocated PLABEL structures are tracked on a
111 * singly linked list. This maybe should be revisited.
113 static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list
114 = SLIST_HEAD_INITIALIZER(hppa_plabel_list);
117 * Because I'm hesitant to use NEW while relocating self,
118 * this is a small pool of preallocated PLABELs.
120 #define HPPA_PLABEL_PRE (14)
121 static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE];
122 static int hppa_plabel_pre_next = 0;
124 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
125 int _rtld_relocate_plt_objects(const Obj_Entry *);
126 static inline int _rtld_relocate_plt_object(const Obj_Entry *,
127 const Elf_Rela *, Elf_Addr *);
130 * This bootstraps the dynamic linker by relocating its GOT.
131 * On the hppa, unlike on other architectures, static strings
132 * are found through the GOT. Static strings are essential
133 * for RTLD_DEBUG, and I suspect they're used early even when
134 * !defined(RTLD_DEBUG), making relocating the GOT essential.
136 * It gets worse. Relocating the GOT doesn't mean just walking
137 * it and adding the relocbase to all of the entries. You must
138 * find and use the GOT relocations, since those RELA relocations
139 * have the necessary addends - the GOT comes initialized as
140 * zeroes.
142 void
143 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
145 const Elf_Rela *relafirst, *rela, *relalim;
146 Elf_Addr relasz;
147 void *where;
148 Elf_Addr *pltgot;
149 const Elf_Rela *plabel_relocs[HPPA_PLABEL_PRE];
150 int nplabel_relocs = 0;
151 int i;
152 const Elf_Sym *symtab, *sym;
153 unsigned long symnum;
154 hppa_plabel *plabel;
157 * Process the DYNAMIC section, looking for the non-PLT relocations.
159 relafirst = NULL;
160 relasz = 0;
161 symtab = NULL;
162 pltgot = NULL;
163 for (; dynp->d_tag != DT_NULL; ++dynp) {
164 switch (dynp->d_tag) {
166 case DT_RELA:
167 relafirst = (const Elf_Rela *)
168 (relocbase + dynp->d_un.d_ptr);
169 break;
171 case DT_RELASZ:
172 relasz = dynp->d_un.d_val;
173 break;
175 case DT_SYMTAB:
176 symtab = (const Elf_Sym *)
177 (relocbase + dynp->d_un.d_ptr);
178 break;
180 case DT_PLTGOT:
181 pltgot = (Elf_Addr *)
182 (relocbase + dynp->d_un.d_ptr);
183 break;
186 relalim = (const Elf_Rela *)((const char *)relafirst + relasz);
188 for (rela = relafirst; rela < relalim; rela++) {
189 symnum = ELF_R_SYM(rela->r_info);
190 where = (void *)(relocbase + rela->r_offset);
192 switch (ELF_R_TYPE(rela->r_info)) {
193 case R_TYPE(DIR32):
194 if (symnum == 0)
195 store_ptr(where,
196 relocbase + rela->r_addend);
197 else {
198 sym = symtab + symnum;
199 store_ptr(where,
200 relocbase + rela->r_addend + sym->st_value);
202 break;
204 case R_TYPE(PLABEL32):
206 * PLABEL32 relocation processing is done in two phases
208 * i) local function relocations (symbol number == 0)
209 * can be resolved immediately.
211 * ii) external function relocations are deferred until
212 * we finish all other relocations so that global
213 * data isn't accessed until all other non-PLT
214 * relocations have been done.
216 if (symnum == 0)
217 *((Elf_Addr *)where) =
218 relocbase + rela->r_addend;
219 else
220 plabel_relocs[nplabel_relocs++] = rela;
221 break;
223 default:
224 break;
228 assert(nplabel_relocs < HPPA_PLABEL_PRE);
229 for (i = 0; i < nplabel_relocs; i++) {
230 rela = plabel_relocs[i];
231 where = (void *)(relocbase + rela->r_offset);
232 sym = symtab + ELF_R_SYM(rela->r_info);
234 plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
236 plabel->hppa_plabel_pc = (Elf_Addr)
237 (relocbase + sym->st_value + rela->r_addend);
238 plabel->hppa_plabel_sl = (Elf_Addr)pltgot;
240 SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
241 *((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel));
244 #if defined(RTLD_DEBUG_HPPA)
245 for (rela = relafirst; rela < relalim; rela++) {
246 where = (void *)(relocbase + rela->r_offset);
248 switch (ELF_R_TYPE(rela->r_info)) {
249 case R_TYPE(DIR32):
250 hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
251 (void *)rela->r_offset,
252 (void *)where,
253 (void *)rela->r_addend,
254 (void *)*((Elf_Addr *)where) ));
255 break;
257 case R_TYPE(PLABEL32):
258 symnum = ELF_R_SYM(rela->r_info);
259 if (symnum == 0) {
260 hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
261 (void *)rela->r_offset,
262 (void *)where,
263 (void *)rela->r_addend,
264 (void *)*((Elf_Addr *)where) ));
265 } else {
266 sym = symtab + symnum;
268 hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
269 (void *)rela->r_offset,
270 (void *)where,
271 symnum,
272 (void *)sym->st_value,
273 (void *)rela->r_addend,
274 (void *)*((Elf_Addr *)where) ));
276 break;
277 default:
278 hdbg(("rela XXX reloc\n"));
279 break;
282 #endif /* RTLD_DEBUG_HPPA */
286 * This allocates a PLABEL. If called with a non-NULL def, the
287 * plabel is for the function associated with that definition
288 * in the defining object defobj, plus the given addend. If
289 * called with a NULL def, the plabel is for the function at
290 * the (unrelocated) address in addend in the object defobj.
292 Elf_Addr
293 _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def,
294 Elf_Addr addend)
296 Elf_Addr func_pc, func_sl;
297 hppa_plabel *plabel;
299 if (def != NULL) {
302 * We assume that symbols of type STT_NOTYPE
303 * are undefined. Return NULL for these.
305 if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE)
306 return (Elf_Addr)NULL;
308 /* Otherwise assert that this symbol must be a function. */
309 assert(ELF_ST_TYPE(def->st_info) == STT_FUNC);
311 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
312 addend);
313 } else
314 func_pc = (Elf_Addr)(defobj->relocbase + addend);
317 * Search the existing PLABELs for one matching
318 * this function. If there is one, return it.
320 func_sl = (Elf_Addr)(defobj->pltgot);
321 SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next)
322 if (plabel->hppa_plabel_pc == func_pc &&
323 plabel->hppa_plabel_sl == func_sl)
324 return RTLD_MAKE_PLABEL(plabel);
327 * Once we've used up the preallocated set, we start
328 * using NEW to allocate plabels.
330 if (hppa_plabel_pre_next < HPPA_PLABEL_PRE)
331 plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
332 else {
333 plabel = NEW(hppa_plabel);
334 if (plabel == NULL)
335 return (Elf_Addr)-1;
338 /* Fill the new entry and insert it on the list. */
339 plabel->hppa_plabel_pc = func_pc;
340 plabel->hppa_plabel_sl = func_sl;
341 SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
343 return RTLD_MAKE_PLABEL(plabel);
347 * If a pointer is a PLABEL, this unwraps it.
349 const void *
350 _rtld_function_descriptor_function(const void *addr)
352 return (RTLD_IS_PLABEL(addr) ?
353 (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc :
354 addr);
357 /* This sets up an object's GOT. */
358 void
359 _rtld_setup_pltgot(const Obj_Entry *obj)
361 __rtld_setup_hppa_pltgot(obj, obj->pltgot);
365 _rtld_relocate_nonplt_objects(const Obj_Entry *obj)
367 const Elf_Rela *rela;
369 for (rela = obj->rela; rela < obj->relalim; rela++) {
370 Elf_Addr *where;
371 const Elf_Sym *def;
372 const Obj_Entry *defobj;
373 Elf_Addr tmp;
374 unsigned long symnum;
376 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
377 symnum = ELF_R_SYM(rela->r_info);
379 switch (ELF_R_TYPE(rela->r_info)) {
380 case R_TYPE(NONE):
381 break;
383 case R_TYPE(DIR32):
384 if (symnum) {
386 * This is either a DIR32 against a symbol
387 * (def->st_name != 0), or against a local
388 * section (def->st_name == 0).
390 def = obj->symtab + symnum;
391 defobj = obj;
392 if (def->st_name != 0)
393 def = _rtld_find_symdef(symnum, obj,
394 &defobj, false);
395 if (def == NULL)
396 return -1;
398 tmp = (Elf_Addr)(defobj->relocbase +
399 def->st_value + rela->r_addend);
401 if (load_ptr(where) != tmp)
402 store_ptr(where, tmp);
403 rdbg(("DIR32 %s in %s --> %p in %s",
404 obj->strtab + obj->symtab[symnum].st_name,
405 obj->path, (void *)load_ptr(where), defobj->path));
406 } else {
407 tmp = (Elf_Addr)(obj->relocbase +
408 rela->r_addend);
410 if (load_ptr(where) != tmp)
411 store_ptr(where, tmp);
412 rdbg(("DIR32 in %s --> %p", obj->path,
413 (void *)load_ptr(where)));
415 break;
417 case R_TYPE(PLABEL32):
418 if (symnum) {
419 def = _rtld_find_symdef(symnum, obj, &defobj,
420 false);
421 if (def == NULL)
422 return -1;
424 tmp = _rtld_function_descriptor_alloc(defobj,
425 def, rela->r_addend);
426 if (tmp == (Elf_Addr)-1)
427 return -1;
429 if (*where != tmp)
430 *where = tmp;
431 rdbg(("PLABEL32 %s in %s --> %p in %s",
432 obj->strtab + obj->symtab[symnum].st_name,
433 obj->path, (void *)*where, defobj->path));
434 } else {
436 * This is a PLABEL for a static function, and
437 * the dynamic linker has both allocated a PLT
438 * entry for this function and told us where it
439 * is. We can safely use the PLT entry as the
440 * PLABEL because there should be no other
441 * PLABEL reloc referencing this function.
442 * This object should also have an IPLT
443 * relocation to initialize the PLT entry.
445 * The dynamic linker should also have ensured
446 * that the addend has the
447 * next-least-significant bit set; the
448 * $$dyncall millicode uses this to distinguish
449 * a PLABEL pointer from a plain function
450 * pointer.
452 tmp = (Elf_Addr)
453 (obj->relocbase + rela->r_addend);
455 if (*where != tmp)
456 *where = tmp;
457 rdbg(("PLABEL32 in %s --> %p", obj->path,
458 (void *)*where));
460 break;
462 case R_TYPE(COPY):
464 * These are deferred until all other relocations have
465 * been done. All we do here is make sure that the
466 * COPY relocation is not in a shared library. They
467 * are allowed only in executable files.
469 if (obj->isdynamic) {
470 _rtld_error(
471 "%s: Unexpected R_COPY relocation in shared library",
472 obj->path);
473 return -1;
475 rdbg(("COPY (avoid in main)"));
476 break;
478 default:
479 rdbg(("sym = %lu, type = %lu, offset = %p, "
480 "addend = %p, contents = %p, symbol = %s",
481 symnum, (u_long)ELF_R_TYPE(rela->r_info),
482 (void *)rela->r_offset, (void *)rela->r_addend,
483 (void *)load_ptr(where),
484 obj->strtab + obj->symtab[symnum].st_name));
485 _rtld_error("%s: Unsupported relocation type %ld "
486 "in non-PLT relocations",
487 obj->path, (u_long) ELF_R_TYPE(rela->r_info));
488 return -1;
491 return 0;
495 _rtld_relocate_plt_lazy(const Obj_Entry *obj)
497 const Elf_Rela *rela;
499 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
500 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
501 Elf_Addr func_pc, func_sl;
503 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
506 * If this is an IPLT reloc for a static function,
507 * fully resolve the PLT entry now.
509 if (ELF_R_SYM(rela->r_info) == 0) {
510 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
511 func_sl = (Elf_Addr)(obj->pltgot);
515 * Otherwise set up for lazy binding.
517 else {
519 * This function pointer points to the PLT
520 * stub added by the linker, and instead of
521 * a shared linkage value, we stash this
522 * relocation's offset. The PLT stub has
523 * already been set up to transfer to
524 * _rtld_bind_start.
526 func_pc = ((Elf_Addr)(obj->pltgot)) - 16;
527 func_sl = (Elf_Addr)
528 ((const char *)rela - (const char *)(obj->pltrela));
530 rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
531 obj->path,
532 (void *)where,
533 (void *)where[0], (void *)where[1],
534 (void *)func_pc, (void *)func_sl));
537 * Fill this PLT entry and return.
539 where[0] = func_pc;
540 where[1] = func_sl;
542 return 0;
545 static inline int
546 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *tp)
548 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
549 const Elf_Sym *def;
550 const Obj_Entry *defobj;
551 Elf_Addr func_pc, func_sl;
553 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
555 if (ELF_R_SYM(rela->r_info) == 0) {
556 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
557 func_sl = (Elf_Addr)(obj->pltgot);
558 } else {
559 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true);
560 if (def == NULL)
561 return -1;
563 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
564 func_sl = (Elf_Addr)(defobj->pltgot);
566 rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
567 defobj->strtab + def->st_name,
568 (void *)where[0], (void *)where[1],
569 (void *)func_pc, (void *)func_sl));
572 * Fill this PLT entry and return.
574 if (where[0] != func_pc)
575 where[0] = func_pc;
576 if (where[1] != func_sl)
577 where[1] = func_sl;
579 if (tp)
580 *tp = (Elf_Addr)where;
582 return 0;
585 caddr_t
586 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
588 const Elf_Rela *rela;
589 Elf_Addr new_value;
590 int err;
592 rela = (const Elf_Rela *)((const char *)obj->pltrela + reloff);
594 assert(ELF_R_SYM(rela->r_info) != 0);
596 err = _rtld_relocate_plt_object(obj, rela, &new_value);
597 if (err || new_value == 0)
598 _rtld_die();
600 return (caddr_t)new_value;
604 _rtld_relocate_plt_objects(const Obj_Entry *obj)
606 const Elf_Rela *rela = obj->pltrela;
608 for (; rela < obj->pltrelalim; rela++) {
609 if (_rtld_relocate_plt_object(obj, rela, NULL) < 0)
610 return -1;
612 return 0;