1 /*	$NetBSD: hppa_reloc.c,v 1.15 2002/09/26 20:42:11 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)		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 __P((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(obj)
325 	const Obj_Entry *obj;
326 {
327 	const Elf_Rela *rela;
328 
329 	for (rela = obj->rela; rela < obj->relalim; rela++) {
330 		Elf_Addr        *where;
331 		const Elf_Sym   *def;
332 		const Obj_Entry *defobj;
333 		Elf_Addr         tmp;
334 		unsigned long	 symnum;
335 
336 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
337 		symnum = ELF_R_SYM(rela->r_info);
338 
339 		switch (ELF_R_TYPE(rela->r_info)) {
340 		case R_TYPE(NONE):
341 			break;
342 
343 		case R_TYPE(DIR32):
344 			if (symnum) {
345 				/*
346 				 * This is either a DIR32 against a symbol
347 				 * (def->st_name != 0), or against a local
348 				 * section (def->st_name == 0).
349 				 */
350 				def = obj->symtab + symnum;
351 				defobj = obj;
352 				if (def->st_name != 0)
353 					/*
354 			 		 * While we're relocating self,
355 					 * _rtld_objlist is NULL, so we just
356 					 * pass in self.
357 					 */
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 				extern Elf_Addr	_GLOBAL_OFFSET_TABLE_[];
373 				extern Elf_Addr	_GOT_END_[];
374 
375 				tmp = (Elf_Addr)(obj->relocbase +
376 				    rela->r_addend);
377 
378 				/* This is the ...iffy hueristic. */
379 				if (!self ||
380 				    (caddr_t)where < (caddr_t)_GLOBAL_OFFSET_TABLE_ ||
381 				    (caddr_t)where >= (caddr_t)_GOT_END_) {
382 					if (*where != tmp)
383 						*where = tmp;
384 					rdbg(("DIR32 in %s --> %p", obj->path,
385 					    (void *)*where));
386 				} else
387 					rdbg(("DIR32 in %s stays at %p",
388 					    obj->path, (void *)*where));
389 			}
390 			break;
391 
392 		case R_TYPE(PLABEL32):
393 			if (symnum) {
394 				/*
395 		 		 * While we're relocating self, _rtld_objlist
396 				 * is NULL, so we just pass in self.
397 				 */
398 				def = _rtld_find_symdef(symnum, obj, &defobj,
399 				    false);
400 				if (def == NULL)
401 					return -1;
402 
403 				tmp = _rtld_function_descriptor_alloc(defobj, def,
404 				    rela->r_addend);
405 				if (tmp == (Elf_Addr)-1)
406 					return -1;
407 
408 				if (*where != tmp)
409 					*where = tmp;
410 				rdbg(("PLABEL32 %s in %s --> %p in %s",
411 				    obj->strtab + obj->symtab[symnum].st_name,
412 				    obj->path, (void *)*where, defobj->path));
413 			} else {
414 				/*
415 				 * This is a PLABEL for a static function, and
416 				 * the dynamic linker has both allocated a PLT
417 				 * entry for this function and told us where it
418 				 * is.  We can safely use the PLT entry as the
419 				 * PLABEL because there should be no other
420 				 * PLABEL reloc referencing this function.
421 				 * This object should also have an IPLT
422 				 * relocation to initialize the PLT entry.
423 				 *
424 				 * The dynamic linker should also have ensured
425 				 * that the addend has the
426 				 * next-least-significant bit set; the
427 				 * $$dyncall millicode uses this to distinguish
428 				 * a PLABEL pointer from a plain function
429 				 * pointer.
430 				 */
431 				tmp = (Elf_Addr)(obj->relocbase + rela->r_addend);
432 
433 				if (*where != tmp)
434 					*where = tmp;
435 				rdbg(("PLABEL32 in %s --> %p", obj->path,
436 				    (void *)*where));
437 			}
438 			break;
439 
440 		case R_TYPE(COPY):
441 			/*
442 			 * These are deferred until all other relocations have
443 			 * been done.  All we do here is make sure that the
444 			 * COPY relocation is not in a shared library.  They
445 			 * are allowed only in executable files.
446 			 */
447 			if (obj->isdynamic) {
448 				_rtld_error(
449 			"%s: Unexpected R_COPY relocation in shared library",
450 				    obj->path);
451 				return -1;
452 			}
453 			rdbg(("COPY (avoid in main)"));
454 			break;
455 
456 		default:
457 			rdbg(("sym = %lu, type = %lu, offset = %p, "
458 			    "addend = %p, contents = %p, symbol = %s",
459 			    symnum, (u_long)ELF_R_TYPE(rela->r_info),
460 			    (void *)rela->r_offset, (void *)rela->r_addend,
461 			    (void *)*where,
462 			    obj->strtab + obj->symtab[symnum].st_name));
463 			_rtld_error("%s: Unsupported relocation type %ld "
464 			    "in non-PLT relocations\n",
465 			    obj->path, (u_long) ELF_R_TYPE(rela->r_info));
466 			return -1;
467 		}
468 	}
469 	return 0;
470 }
471 
472 int
473 _rtld_relocate_plt_lazy(obj)
474 	const Obj_Entry *obj;
475 {
476 	const Elf_Rela *rela;
477 
478 	for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) {
479 		Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
480 		Elf_Addr func_pc, func_sl;
481 
482 		assert(ELF_R_TYPE(rela->r_info) == R_TYPE(IPLT));
483 
484 		/*
485 		 * If this is an IPLT reloc for a static function,
486 		 * fully resolve the PLT entry now.
487 		 */
488 		if (ELF_R_SYM(rela->r_info) == 0) {
489 			func_pc = (Elf_Addr)(obj->relocbase + rela->r_addend);
490 			func_sl = (Elf_Addr)HPPA_OBJ_SL(obj);
491 		}
492 
493 		/*
494 		 * Otherwise set up for lazy binding.
495 		 */
496 		else {
497 			/*
498 			 * This function pointer points to the PLT
499 			 * stub added by the linker, and instead of
500 			 * a shared linkage value, we stash this
501 			 * relocation's offset.  The PLT stub has
502 			 * already been set up to transfer to
503 			 * _rtld_bind_start.
504 			 */
505 			func_pc = ((Elf_Addr)HPPA_OBJ_GOT(obj)) - 16;
506 			func_sl = (Elf_Addr)((caddr_t)rela - (caddr_t)obj->pltrela);
507 		}
508 
509 		/*
510 		 * Fill this PLT entry and return.
511 		 */
512 		where[0] = func_pc;
513 		where[1] = func_sl;
514 	}
515 	return 0;
516 }
517