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