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