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