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