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