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