hppa_reloc.c revision 1.24 1 /* $NetBSD: hppa_reloc.c,v 1.24 2006/10/17 08:28:06 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette and Nick Hudson.
9 *
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.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: hppa_reloc.c,v 1.24 2006/10/17 08:28:06 skrll Exp $");
42 #endif /* not lint */
43
44 #include <stdlib.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/queue.h>
48
49 #include <string.h>
50
51 #include "rtld.h"
52 #include "debug.h"
53
54 #ifdef RTLD_DEBUG_HPPA
55 #define hdbg(x) xprintf x
56 #else
57 #define hdbg(x) /* nothing */
58 #endif
59
60 caddr_t _rtld_bind(const Obj_Entry *, const Elf_Addr);
61 void _rtld_bind_start(void);
62 void __rtld_setup_hppa_pltgot(const Obj_Entry *, Elf_Addr *);
63
64 /*
65 * It is possible for the compiler to emit relocations for unaligned data.
66 * We handle this situation with these inlines.
67 */
68 #define RELOC_ALIGNED_P(x) \
69 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
70
71 static inline Elf_Addr
72 load_ptr(void *where)
73 {
74 if (__predict_true(RELOC_ALIGNED_P(where)))
75 return *(Elf_Addr *)where;
76 else {
77 Elf_Addr res;
78
79 (void)memcpy(&res, where, sizeof(res));
80 return res;
81 }
82 }
83
84 static inline void
85 store_ptr(void *where, Elf_Addr val)
86 {
87 if (__predict_true(RELOC_ALIGNED_P(where)))
88 *(Elf_Addr *)where = val;
89 else
90 (void)memcpy(where, &val, sizeof(val));
91 }
92
93 /*
94 * In the runtime architecture (ABI), PLABEL function
95 * pointers are distinguished from normal function
96 * pointers by having the next-least-significant bit
97 * set. (This bit is referred to as the L field in
98 * HP documentation). The $$dyncall millicode is
99 * aware of this.
100 */
101 #define RTLD_MAKE_PLABEL(plabel) (((Elf_Addr)(plabel)) | (1 << 1))
102 #define RTLD_IS_PLABEL(addr) (((Elf_Addr)(addr)) & (1 << 1))
103 #define RTLD_GET_PLABEL(addr) ((hppa_plabel *) (((Elf_Addr)addr) & ~3))
104
105 /*
106 * This is the PLABEL structure. The function PC and
107 * shared linkage members must come first, as they are
108 * the actual PLABEL.
109 */
110 typedef struct _hppa_plabel {
111 Elf_Addr hppa_plabel_pc;
112 Elf_Addr hppa_plabel_sl;
113 SLIST_ENTRY(_hppa_plabel) hppa_plabel_next;
114 } hppa_plabel;
115
116 /*
117 * For now allocated PLABEL structures are tracked on a
118 * singly linked list. This maybe should be revisited.
119 */
120 static SLIST_HEAD(hppa_plabel_head, _hppa_plabel) hppa_plabel_list
121 = SLIST_HEAD_INITIALIZER(hppa_plabel_list);
122
123 /*
124 * Because I'm hesitant to use NEW while relocating self,
125 * this is a small pool of preallocated PLABELs.
126 */
127 #define HPPA_PLABEL_PRE (12)
128 static hppa_plabel hppa_plabel_pre[HPPA_PLABEL_PRE];
129 static int hppa_plabel_pre_next = 0;
130
131 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
132 int _rtld_relocate_plt_objects(const Obj_Entry *);
133 /*
134 * This bootstraps the dynamic linker by relocating its GOT.
135 * On the hppa, unlike on other architectures, static strings
136 * are found through the GOT. Static strings are essential
137 * for RTLD_DEBUG, and I suspect they're used early even when
138 * !defined(RTLD_DEBUG), making relocating the GOT essential.
139 *
140 * It gets worse. Relocating the GOT doesn't mean just walking
141 * it and adding the relocbase to all of the entries. You must
142 * find and use the GOT relocations, since those RELA relocations
143 * have the necessary addends - the GOT comes initialized as
144 * zeroes.
145 */
146 void
147 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
148 {
149 const Elf_Rela *relafirst, *rela, *relalim;
150 Elf_Addr relasz;
151 void *where;
152 Elf_Addr *pltgot;
153 const Elf_Rela *plabel_relocs[HPPA_PLABEL_PRE];
154 int nplabel_relocs = 0;
155 int i;
156 const Elf_Sym *symtab, *sym;
157 unsigned long symnum;
158 hppa_plabel *plabel;
159
160 /*
161 * Process the DYNAMIC section, looking for the non-PLT relocations.
162 */
163 relafirst = NULL;
164 relasz = 0;
165 symtab = NULL;
166 pltgot = NULL;
167 for (; dynp->d_tag != DT_NULL; ++dynp) {
168 switch (dynp->d_tag) {
169
170 case DT_RELA:
171 relafirst = (const Elf_Rela *)
172 (relocbase + dynp->d_un.d_ptr);
173 break;
174
175 case DT_RELASZ:
176 relasz = dynp->d_un.d_val;
177 break;
178
179 case DT_SYMTAB:
180 symtab = (const Elf_Sym *)
181 (relocbase + dynp->d_un.d_ptr);
182 break;
183
184 case DT_PLTGOT:
185 pltgot = (Elf_Addr *)
186 (relocbase + dynp->d_un.d_ptr);
187 break;
188 }
189 }
190 relalim = (const Elf_Rela *)((caddr_t)relafirst + relasz);
191
192 for (rela = relafirst; rela < relalim; rela++) {
193 symnum = ELF_R_SYM(rela->r_info);
194 where = (void *)(relocbase + rela->r_offset);
195
196 switch (ELF_R_TYPE(rela->r_info)) {
197 case R_TYPE(DIR32):
198 if (symnum == 0)
199 store_ptr(where,
200 relocbase + rela->r_addend);
201 else {
202 sym = symtab + symnum;
203 store_ptr(where,
204 relocbase + rela->r_addend + sym->st_value);
205 }
206 break;
207
208 case R_TYPE(PLABEL32):
209 /*
210 * PLABEL32 relocation processing is done in two phases
211 *
212 * i) local function relocations (symbol number == 0)
213 * can be resolved immediately.
214 *
215 * ii) external function relocations are deferred until
216 * we finish all other relocations so that global
217 * data isn't accessed until all other non-PLT
218 * relocations have been done.
219 */
220 if (symnum == 0)
221 *((Elf_Addr *)where) =
222 relocbase + rela->r_addend;
223 else
224 plabel_relocs[nplabel_relocs++] = rela;
225 break;
226
227 default:
228 break;
229 }
230 }
231
232 assert(nplabel_relocs < HPPA_PLABEL_PRE);
233 for (i = 0; i < nplabel_relocs; i++) {
234 rela = plabel_relocs[i];
235 where = (void *)(relocbase + rela->r_offset);
236 sym = symtab + ELF_R_SYM(rela->r_info);
237
238 plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
239
240 plabel->hppa_plabel_pc = (Elf_Addr)
241 (relocbase + sym->st_value + rela->r_addend);
242 plabel->hppa_plabel_sl = (Elf_Addr)pltgot;
243
244 SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
245 *((Elf_Addr *)where) = (Elf_Addr)(RTLD_MAKE_PLABEL(plabel));
246 }
247
248 #if defined(RTLD_DEBUG_HPPA)
249 for (rela = relafirst; rela < relalim; rela++) {
250 where = (void *)(relocbase + rela->r_offset);
251
252 switch (ELF_R_TYPE(rela->r_info)) {
253 case R_TYPE(DIR32):
254 hdbg(("DIR32 rela @%p(%p) -> %p(%p)\n",
255 (void *)rela->r_offset,
256 (void *)where,
257 (void *)rela->r_addend,
258 (void *)*((Elf_Addr *)where) ));
259 break;
260
261 case R_TYPE(PLABEL32):
262 symnum = ELF_R_SYM(rela->r_info);
263 if (symnum == 0) {
264 hdbg(("PLABEL rela @%p(%p) -> %p(%p)\n",
265 (void *)rela->r_offset,
266 (void *)where,
267 (void *)rela->r_addend,
268 (void *)*((Elf_Addr *)where) ));
269 } else {
270 sym = symtab + symnum;
271
272 hdbg(("PLABEL32 rela @%p(%p), symnum=%ld(%p) -> %p(%p)\n",
273 (void *)rela->r_offset,
274 (void *)where,
275 symnum,
276 (void *)sym->st_value,
277 (void *)rela->r_addend,
278 (void *)*((Elf_Addr *)where) ));
279 }
280 break;
281 default:
282 hdbg(("rela XXX reloc\n"));
283 break;
284 }
285 }
286 #endif /* RTLD_DEBUG_HPPA */
287 }
288
289 /*
290 * This allocates a PLABEL. If called with a non-NULL def, the
291 * plabel is for the function associated with that definition
292 * in the defining object defobj, plus the given addend. If
293 * called with a NULL def, the plabel is for the function at
294 * the (unrelocated) address in addend in the object defobj.
295 */
296 Elf_Addr
297 _rtld_function_descriptor_alloc(const Obj_Entry *defobj, const Elf_Sym *def,
298 Elf_Addr addend)
299 {
300 Elf_Addr func_pc, func_sl;
301 hppa_plabel *plabel;
302
303 if (def != NULL) {
304
305 /*
306 * We assume that symbols of type STT_NOTYPE
307 * are undefined. Return NULL for these.
308 */
309 if (ELF_ST_TYPE(def->st_info) == STT_NOTYPE)
310 return (Elf_Addr)NULL;
311
312 /* Otherwise assert that this symbol must be a function. */
313 assert(ELF_ST_TYPE(def->st_info) == STT_FUNC);
314
315 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
316 addend);
317 } else
318 func_pc = (Elf_Addr)(defobj->relocbase + addend);
319
320 /*
321 * Search the existing PLABELs for one matching
322 * this function. If there is one, return it.
323 */
324 func_sl = (Elf_Addr)(defobj->pltgot);
325 SLIST_FOREACH(plabel, &hppa_plabel_list, hppa_plabel_next)
326 if (plabel->hppa_plabel_pc == func_pc &&
327 plabel->hppa_plabel_sl == func_sl)
328 return RTLD_MAKE_PLABEL(plabel);
329
330 /*
331 * Once we've used up the preallocated set, we start
332 * using NEW to allocate plabels.
333 */
334 if (hppa_plabel_pre_next < HPPA_PLABEL_PRE)
335 plabel = &hppa_plabel_pre[hppa_plabel_pre_next++];
336 else {
337 plabel = NEW(hppa_plabel);
338 if (plabel == NULL)
339 return (Elf_Addr)-1;
340 }
341
342 /* Fill the new entry and insert it on the list. */
343 plabel->hppa_plabel_pc = func_pc;
344 plabel->hppa_plabel_sl = func_sl;
345 SLIST_INSERT_HEAD(&hppa_plabel_list, plabel, hppa_plabel_next);
346
347 return RTLD_MAKE_PLABEL(plabel);
348 }
349
350 /*
351 * If a pointer is a PLABEL, this unwraps it.
352 */
353 const void *
354 _rtld_function_descriptor_function(const void *addr)
355 {
356 return (RTLD_IS_PLABEL(addr) ?
357 (const void *) RTLD_GET_PLABEL(addr)->hppa_plabel_pc :
358 addr);
359 }
360
361 /* This sets up an object's GOT. */
362 void
363 _rtld_setup_pltgot(const Obj_Entry *obj)
364 {
365 __rtld_setup_hppa_pltgot(obj, obj->pltgot);
366 }
367
368 int
369 _rtld_relocate_nonplt_objects(const Obj_Entry *obj)
370 {
371 const Elf_Rela *rela;
372
373 for (rela = obj->rela; rela < obj->relalim; rela++) {
374 Elf_Addr *where;
375 const Elf_Sym *def;
376 const Obj_Entry *defobj;
377 Elf_Addr tmp;
378 unsigned long symnum;
379
380 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
381 symnum = ELF_R_SYM(rela->r_info);
382
383 switch (ELF_R_TYPE(rela->r_info)) {
384 case R_TYPE(NONE):
385 break;
386
387 case R_TYPE(DIR32):
388 if (symnum) {
389 /*
390 * This is either a DIR32 against a symbol
391 * (def->st_name != 0), or against a local
392 * section (def->st_name == 0).
393 */
394 def = obj->symtab + symnum;
395 defobj = obj;
396 if (def->st_name != 0)
397 def = _rtld_find_symdef(symnum, obj,
398 &defobj, false);
399 if (def == NULL)
400 return -1;
401
402 tmp = (Elf_Addr)(defobj->relocbase +
403 def->st_value + rela->r_addend);
404
405 if (load_ptr(where) != tmp)
406 store_ptr(where, tmp);
407 rdbg(("DIR32 %s in %s --> %p in %s",
408 obj->strtab + obj->symtab[symnum].st_name,
409 obj->path, (void *)load_ptr(where), defobj->path));
410 } else {
411 tmp = (Elf_Addr)(obj->relocbase +
412 rela->r_addend);
413
414 if (load_ptr(where) != tmp)
415 store_ptr(where, tmp);
416 rdbg(("DIR32 in %s --> %p", obj->path,
417 (void *)load_ptr(where)));
418 }
419 break;
420
421 case R_TYPE(PLABEL32):
422 if (symnum) {
423 def = _rtld_find_symdef(symnum, obj, &defobj,
424 false);
425 if (def == NULL)
426 return -1;
427
428 tmp = _rtld_function_descriptor_alloc(defobj,
429 def, rela->r_addend);
430 if (tmp == (Elf_Addr)-1)
431 return -1;
432
433 if (*where != tmp)
434 *where = tmp;
435 rdbg(("PLABEL32 %s in %s --> %p in %s",
436 obj->strtab + obj->symtab[symnum].st_name,
437 obj->path, (void *)*where, defobj->path));
438 } else {
439 /*
440 * This is a PLABEL for a static function, and
441 * the dynamic linker has both allocated a PLT
442 * entry for this function and told us where it
443 * is. We can safely use the PLT entry as the
444 * PLABEL because there should be no other
445 * PLABEL reloc referencing this function.
446 * This object should also have an IPLT
447 * relocation to initialize the PLT entry.
448 *
449 * The dynamic linker should also have ensured
450 * that the addend has the
451 * next-least-significant bit set; the
452 * $$dyncall millicode uses this to distinguish
453 * a PLABEL pointer from a plain function
454 * pointer.
455 */
456 tmp = (Elf_Addr)
457 (obj->relocbase + rela->r_addend);
458
459 if (*where != tmp)
460 *where = tmp;
461 rdbg(("PLABEL32 in %s --> %p", obj->path,
462 (void *)*where));
463 }
464 break;
465
466 case R_TYPE(COPY):
467 /*
468 * These are deferred until all other relocations have
469 * been done. All we do here is make sure that the
470 * COPY relocation is not in a shared library. They
471 * are allowed only in executable files.
472 */
473 if (obj->isdynamic) {
474 _rtld_error(
475 "%s: Unexpected R_COPY relocation in shared library",
476 obj->path);
477 return -1;
478 }
479 rdbg(("COPY (avoid in main)"));
480 break;
481
482 default:
483 rdbg(("sym = %lu, type = %lu, offset = %p, "
484 "addend = %p, contents = %p, symbol = %s",
485 symnum, (u_long)ELF_R_TYPE(rela->r_info),
486 (void *)rela->r_offset, (void *)rela->r_addend,
487 (void *)load_ptr(where),
488 obj->strtab + obj->symtab[symnum].st_name));
489 _rtld_error("%s: Unsupported relocation type %ld "
490 "in non-PLT relocations\n",
491 obj->path, (u_long) ELF_R_TYPE(rela->r_info));
492 return -1;
493 }
494 }
495 return 0;
496 }
497
498 int
499 _rtld_relocate_plt_lazy(const Obj_Entry *obj)
500 {
501 const Elf_Rela *rela;
502
503 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
504 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
505 Elf_Addr func_pc, func_sl;
506
507 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
508
509 /*
510 * If this is an IPLT reloc for a static function,
511 * fully resolve the PLT entry now.
512 */
513 if (ELF_R_SYM(rela->r_info) == 0) {
514 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
515 func_sl = (Elf_Addr)(obj->pltgot);
516 }
517
518 /*
519 * Otherwise set up for lazy binding.
520 */
521 else {
522 /*
523 * This function pointer points to the PLT
524 * stub added by the linker, and instead of
525 * a shared linkage value, we stash this
526 * relocation's offset. The PLT stub has
527 * already been set up to transfer to
528 * _rtld_bind_start.
529 */
530 func_pc = ((Elf_Addr)(obj->pltgot)) - 16;
531 func_sl = (Elf_Addr)
532 ((caddr_t)rela - (caddr_t)(obj->pltrela));
533 }
534 rdbg(("lazy bind %s(%p) --> old=(%p,%p) new=(%p,%p)",
535 obj->path,
536 (void *)where,
537 (void *)where[0], (void *)where[1],
538 (void *)func_pc, (void *)func_sl));
539
540 /*
541 * Fill this PLT entry and return.
542 */
543 where[0] = func_pc;
544 where[1] = func_sl;
545 }
546 return 0;
547 }
548
549 caddr_t
550 _rtld_bind(const Obj_Entry *obj, Elf_Addr reloff)
551 {
552 const Elf_Rela *rela = (const Elf_Rela *)((caddr_t)obj->pltrela + reloff);
553 Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
554 const Elf_Sym *def;
555 const Obj_Entry *defobj;
556 Elf_Addr func_pc, func_sl;
557
558 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
559 assert(ELF_R_SYM(rela->r_info) != 0);
560
561 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true);
562 if (def == NULL)
563 _rtld_die();
564
565 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
566 func_sl = (Elf_Addr)(defobj->pltgot);
567
568 rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
569 defobj->strtab + def->st_name,
570 (void *)where[0], (void *)where[1],
571 (void *)func_pc, (void *)func_sl));
572 /*
573 * Fill this PLT entry and return.
574 */
575 if (where[0] != func_pc)
576 where[0] = func_pc;
577 if (where[1] != func_sl)
578 where[1] = func_sl;
579
580 return (caddr_t)where;
581 }
582
583 int
584 _rtld_relocate_plt_objects(const Obj_Entry *obj)
585 {
586 const Elf_Rela *rela;
587
588 for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
589 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
590 const Elf_Sym *def;
591 const Obj_Entry *defobj;
592 Elf_Addr func_pc, func_sl;
593
594 assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
595
596 if (ELF_R_SYM(rela->r_info) == 0) {
597 func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
598 func_sl = (Elf_Addr)(obj->pltgot);
599 } else {
600 def = _rtld_find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
601 true);
602 if (def == NULL)
603 return -1;
604 func_pc = (Elf_Addr)(defobj->relocbase + def->st_value +
605 rela->r_addend);
606 func_sl = (Elf_Addr)(defobj->pltgot);
607 rdbg(("bind now/fixup in %s --> old=(%p,%p) new=(%p,%p)",
608 defobj->strtab + def->st_name,
609 (void *)where[0], (void *)where[1],
610 (void *)func_pc, (void *)func_sl));
611 }
612
613 /*
614 * Fill this PLT entry and return.
615 */
616 if (where[0] != func_pc)
617 where[0] = func_pc;
618 if (where[1] != func_sl)
619 where[1] = func_sl;
620 }
621 return 0;
622 }
623