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