1129204Scognet/*	$NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $	*/
2129204Scognet
3129204Scognet#include <sys/cdefs.h>
4129204Scognet__FBSDID("$FreeBSD$");
5129204Scognet#include <sys/param.h>
6129204Scognet#include <sys/mman.h>
7129204Scognet
8129204Scognet#include <errno.h>
9129204Scognet#include <stdio.h>
10129204Scognet#include <stdlib.h>
11129204Scognet#include <string.h>
12129204Scognet#include <unistd.h>
13237394Smarius
14237394Smarius#include "machine/sysarch.h"
15237394Smarius
16129204Scognet#include "debug.h"
17129204Scognet#include "rtld.h"
18129204Scognet
19129204Scognetvoid
20129204Scognetinit_pltgot(Obj_Entry *obj)
21129204Scognet{
22129204Scognet	if (obj->pltgot != NULL) {
23129204Scognet		obj->pltgot[1] = (Elf_Addr) obj;
24129204Scognet		obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
25129204Scognet	}
26129204Scognet}
27129204Scognet
28129204Scognetint
29129204Scognetdo_copy_relocations(Obj_Entry *dstobj)
30129204Scognet{
31129204Scognet	const Elf_Rel *rellim;
32129204Scognet	const Elf_Rel *rel;
33129204Scognet
34129204Scognet	assert(dstobj->mainprog);	/* COPY relocations are invalid elsewhere */
35129204Scognet
36129204Scognet   	rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
37129204Scognet	for (rel = dstobj->rel;  rel < rellim;  rel++) {
38129204Scognet		if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) {
39129204Scognet	    		void *dstaddr;
40129204Scognet			const Elf_Sym *dstsym;
41129204Scognet			const char *name;
42129204Scognet			size_t size;
43129204Scognet			const void *srcaddr;
44129204Scognet			const Elf_Sym *srcsym;
45216695Skib			const Obj_Entry *srcobj, *defobj;
46216695Skib			SymLook req;
47216695Skib			int res;
48129204Scognet
49129204Scognet			dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
50129204Scognet			dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
51129204Scognet			name = dstobj->strtab + dstsym->st_name;
52129204Scognet			size = dstsym->st_size;
53216695Skib
54216695Skib			symlook_init(&req, name);
55216695Skib			req.ventry = fetch_ventry(dstobj,
56216695Skib			    ELF_R_SYM(rel->r_info));
57233831Skib			req.flags = SYMLOOK_EARLY;
58233831Skib
59216695Skib			for (srcobj = dstobj->next;  srcobj != NULL;
60216695Skib			     srcobj = srcobj->next) {
61216695Skib				res = symlook_obj(&req, srcobj);
62216695Skib				if (res == 0) {
63216695Skib					srcsym = req.sym_out;
64216695Skib					defobj = req.defobj_out;
65129204Scognet					break;
66216695Skib				}
67216695Skib			}
68129204Scognet			if (srcobj == NULL) {
69216695Skib				_rtld_error(
70216695Skib"Undefined symbol \"%s\" referenced from COPY relocation in %s",
71216695Skib				    name, dstobj->path);
72216695Skib				return (-1);
73129204Scognet			}
74129204Scognet
75216695Skib			srcaddr = (const void *)(defobj->relocbase +
76216695Skib			    srcsym->st_value);
77129204Scognet			memcpy(dstaddr, srcaddr, size);
78129204Scognet		}
79129204Scognet	}
80129204Scognet	return 0;
81129204Scognet}
82129204Scognet
83129204Scognetvoid _rtld_bind_start(void);
84129204Scognetvoid _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
85129204Scognet
86129204Scognetint open();
87129204Scognetint _open();
88129204Scognetvoid
89129204Scognet_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
90129204Scognet{
91129204Scognet	const Elf_Rel *rel = 0, *rellim;
92129204Scognet	Elf_Addr relsz = 0;
93129204Scognet	Elf_Addr *where;
94129204Scognet	uint32_t size;
95129204Scognet
96129204Scognet	for (; dynp->d_tag != DT_NULL; dynp++) {
97129204Scognet		switch (dynp->d_tag) {
98129204Scognet		case DT_REL:
99129204Scognet			rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
100129204Scognet			break;
101129204Scognet		case DT_RELSZ:
102129204Scognet			relsz = dynp->d_un.d_val;
103129204Scognet			break;
104129204Scognet		}
105129204Scognet	}
106129204Scognet	rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
107129204Scognet	size = (rellim - 1)->r_offset - rel->r_offset;
108129204Scognet	for (; rel < rellim; rel++) {
109129204Scognet		where = (Elf_Addr *)(relocbase + rel->r_offset);
110129204Scognet
111129204Scognet		*where += (Elf_Addr)relocbase;
112129204Scognet	}
113129204Scognet}
114129204Scognet/*
115129204Scognet * It is possible for the compiler to emit relocations for unaligned data.
116129204Scognet * We handle this situation with these inlines.
117129204Scognet */
118129204Scognet#define	RELOC_ALIGNED_P(x) \
119129204Scognet	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
120129204Scognet
121129204Scognetstatic __inline Elf_Addr
122129204Scognetload_ptr(void *where)
123129204Scognet{
124129204Scognet	Elf_Addr res;
125129204Scognet
126129204Scognet	memcpy(&res, where, sizeof(res));
127129204Scognet
128129204Scognet	return (res);
129129204Scognet}
130129204Scognet
131129204Scognetstatic __inline void
132129204Scognetstore_ptr(void *where, Elf_Addr val)
133129204Scognet{
134129204Scognet
135129204Scognet	memcpy(where, &val, sizeof(val));
136129204Scognet}
137129204Scognet
138129204Scognetstatic int
139216695Skibreloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
140233831Skib    int flags, RtldLockState *lockstate)
141129204Scognet{
142129204Scognet	Elf_Addr        *where;
143129204Scognet	const Elf_Sym   *def;
144129204Scognet	const Obj_Entry *defobj;
145129204Scognet	Elf_Addr         tmp;
146129204Scognet	unsigned long	 symnum;
147129204Scognet
148129204Scognet	where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
149129204Scognet	symnum = ELF_R_SYM(rel->r_info);
150129204Scognet
151129204Scognet	switch (ELF_R_TYPE(rel->r_info)) {
152129204Scognet	case R_ARM_NONE:
153129204Scognet		break;
154129204Scognet
155129204Scognet#if 1 /* XXX should not occur */
156129204Scognet	case R_ARM_PC24: {	/* word32 S - P + A */
157129204Scognet		Elf32_Sword addend;
158129204Scognet
159129204Scognet		/*
160129204Scognet		 * Extract addend and sign-extend if needed.
161129204Scognet		 */
162129204Scognet		addend = *where;
163129204Scognet		if (addend & 0x00800000)
164129204Scognet			addend |= 0xff000000;
165129204Scognet
166233831Skib		def = find_symdef(symnum, obj, &defobj, flags, cache,
167216695Skib		    lockstate);
168129204Scognet		if (def == NULL)
169129204Scognet				return -1;
170129204Scognet			tmp = (Elf_Addr)obj->relocbase + def->st_value
171129204Scognet			    - (Elf_Addr)where + (addend << 2);
172129204Scognet			if ((tmp & 0xfe000000) != 0xfe000000 &&
173129204Scognet			    (tmp & 0xfe000000) != 0) {
174129204Scognet				_rtld_error(
175129204Scognet				"%s: R_ARM_PC24 relocation @ %p to %s failed "
176129204Scognet				"(displacement %ld (%#lx) out of range)",
177129204Scognet				    obj->path, where,
178129204Scognet				    obj->strtab + obj->symtab[symnum].st_name,
179129204Scognet				    (long) tmp, (long) tmp);
180129204Scognet				return -1;
181129204Scognet			}
182129204Scognet			tmp >>= 2;
183129204Scognet			*where = (*where & 0xff000000) | (tmp & 0x00ffffff);
184129204Scognet			dbg("PC24 %s in %s --> %p @ %p in %s",
185129204Scognet			    obj->strtab + obj->symtab[symnum].st_name,
186129204Scognet			    obj->path, (void *)*where, where, defobj->path);
187129204Scognet			break;
188129204Scognet		}
189129204Scognet#endif
190129204Scognet
191129204Scognet		case R_ARM_ABS32:	/* word32 B + S + A */
192129204Scognet		case R_ARM_GLOB_DAT:	/* word32 B + S */
193233831Skib			def = find_symdef(symnum, obj, &defobj, flags, cache,
194216695Skib			    lockstate);
195129204Scognet			if (def == NULL)
196129204Scognet				return -1;
197129204Scognet			if (__predict_true(RELOC_ALIGNED_P(where))) {
198129204Scognet				tmp =  *where + (Elf_Addr)defobj->relocbase +
199129204Scognet				    def->st_value;
200129204Scognet				*where = tmp;
201129204Scognet			} else {
202129204Scognet				tmp = load_ptr(where) +
203129204Scognet				    (Elf_Addr)defobj->relocbase +
204129204Scognet				    def->st_value;
205129204Scognet				store_ptr(where, tmp);
206129204Scognet			}
207129204Scognet			dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
208129204Scognet			    obj->strtab + obj->symtab[symnum].st_name,
209129204Scognet			    obj->path, (void *)tmp, where, defobj->path);
210129204Scognet			break;
211129204Scognet
212129204Scognet		case R_ARM_RELATIVE:	/* word32 B + A */
213129204Scognet			if (__predict_true(RELOC_ALIGNED_P(where))) {
214129204Scognet				tmp = *where + (Elf_Addr)obj->relocbase;
215129204Scognet				*where = tmp;
216129204Scognet			} else {
217129204Scognet				tmp = load_ptr(where) +
218129204Scognet				    (Elf_Addr)obj->relocbase;
219129204Scognet				store_ptr(where, tmp);
220129204Scognet			}
221129204Scognet			dbg("RELATIVE in %s --> %p", obj->path,
222129204Scognet			    (void *)tmp);
223129204Scognet			break;
224129204Scognet
225129204Scognet		case R_ARM_COPY:
226129204Scognet			/*
227129204Scognet			 * These are deferred until all other relocations have
228129204Scognet			 * been done.  All we do here is make sure that the
229129204Scognet			 * COPY relocation is not in a shared library.  They
230129204Scognet			 * are allowed only in executable files.
231129204Scognet			 */
232129204Scognet			if (!obj->mainprog) {
233129204Scognet				_rtld_error(
234129204Scognet			"%s: Unexpected R_COPY relocation in shared library",
235129204Scognet				    obj->path);
236129204Scognet				return -1;
237129204Scognet			}
238129204Scognet			dbg("COPY (avoid in main)");
239129204Scognet			break;
240129204Scognet
241237394Smarius		case R_ARM_TLS_DTPOFF32:
242237394Smarius			def = find_symdef(symnum, obj, &defobj, flags, cache,
243237394Smarius			    lockstate);
244237394Smarius			if (def == NULL)
245237394Smarius				return -1;
246237394Smarius
247237394Smarius			tmp = (Elf_Addr)(def->st_value);
248237394Smarius			if (__predict_true(RELOC_ALIGNED_P(where)))
249237394Smarius				*where = tmp;
250237394Smarius			else
251237394Smarius				store_ptr(where, tmp);
252237394Smarius
253237394Smarius			dbg("TLS_DTPOFF32 %s in %s --> %p",
254237394Smarius			    obj->strtab + obj->symtab[symnum].st_name,
255237394Smarius			    obj->path, (void *)tmp);
256237394Smarius
257237394Smarius			break;
258237394Smarius		case R_ARM_TLS_DTPMOD32:
259237394Smarius			def = find_symdef(symnum, obj, &defobj, flags, cache,
260237394Smarius			    lockstate);
261237394Smarius			if (def == NULL)
262237394Smarius				return -1;
263237394Smarius
264237394Smarius			tmp = (Elf_Addr)(defobj->tlsindex);
265237394Smarius			if (__predict_true(RELOC_ALIGNED_P(where)))
266237394Smarius				*where = tmp;
267237394Smarius			else
268237394Smarius				store_ptr(where, tmp);
269237394Smarius
270237394Smarius			dbg("TLS_DTPMOD32 %s in %s --> %p",
271237394Smarius			    obj->strtab + obj->symtab[symnum].st_name,
272237394Smarius			    obj->path, (void *)tmp);
273237394Smarius
274237394Smarius			break;
275237394Smarius
276237394Smarius		case R_ARM_TLS_TPOFF32:
277237394Smarius			def = find_symdef(symnum, obj, &defobj, flags, cache,
278237394Smarius			    lockstate);
279237394Smarius			if (def == NULL)
280237394Smarius				return -1;
281237394Smarius
282237394Smarius			if (!defobj->tls_done && allocate_tls_offset(obj))
283237394Smarius				return -1;
284237394Smarius
285237394Smarius			/* XXX: FIXME */
286237394Smarius			tmp = (Elf_Addr)def->st_value + defobj->tlsoffset +
287237394Smarius			    TLS_TCB_SIZE;
288237394Smarius			if (__predict_true(RELOC_ALIGNED_P(where)))
289237394Smarius				*where = tmp;
290237394Smarius			else
291237394Smarius				store_ptr(where, tmp);
292237394Smarius			dbg("TLS_TPOFF32 %s in %s --> %p",
293237394Smarius			    obj->strtab + obj->symtab[symnum].st_name,
294237394Smarius			    obj->path, (void *)tmp);
295237394Smarius			break;
296237394Smarius
297237394Smarius
298129204Scognet		default:
299129204Scognet			dbg("sym = %lu, type = %lu, offset = %p, "
300129204Scognet			    "contents = %p, symbol = %s",
301129204Scognet			    symnum, (u_long)ELF_R_TYPE(rel->r_info),
302129204Scognet			    (void *)rel->r_offset, (void *)load_ptr(where),
303129204Scognet			    obj->strtab + obj->symtab[symnum].st_name);
304129204Scognet			_rtld_error("%s: Unsupported relocation type %ld "
305129204Scognet			    "in non-PLT relocations\n",
306129204Scognet			    obj->path, (u_long) ELF_R_TYPE(rel->r_info));
307129204Scognet			return -1;
308129204Scognet	}
309129204Scognet	return 0;
310129204Scognet}
311129204Scognet
312129204Scognet/*
313129204Scognet *  * Process non-PLT relocations
314129204Scognet *   */
315129204Scognetint
316233831Skibreloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
317233831Skib    RtldLockState *lockstate)
318129204Scognet{
319129204Scognet	const Elf_Rel *rellim;
320129204Scognet	const Elf_Rel *rel;
321129204Scognet	SymCache *cache;
322129204Scognet	int r = -1;
323129204Scognet
324135883Scognet	/* The relocation for the dynamic loader has already been done. */
325135883Scognet	if (obj == obj_rtld)
326135883Scognet		return (0);
327129204Scognet	/*
328129204Scognet 	 * The dynamic loader may be called from a thread, we have
329129204Scognet	 * limited amounts of stack available so we cannot use alloca().
330135883Scognet	 */
331235396Skib	cache = calloc(obj->dynsymcount, sizeof(SymCache));
332208256Srdivacky	/* No need to check for NULL here */
333208256Srdivacky
334129204Scognet	rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
335129204Scognet	for (rel = obj->rel; rel < rellim; rel++) {
336233831Skib		if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0)
337129204Scognet			goto done;
338129204Scognet	}
339129204Scognet	r = 0;
340129204Scognetdone:
341208256Srdivacky	if (cache != NULL)
342208256Srdivacky		free(cache);
343129204Scognet	return (r);
344129204Scognet}
345129204Scognet
346129204Scognet/*
347129204Scognet *  * Process the PLT relocations.
348129204Scognet *   */
349129204Scognetint
350129204Scognetreloc_plt(Obj_Entry *obj)
351129204Scognet{
352129204Scognet	const Elf_Rel *rellim;
353129204Scognet	const Elf_Rel *rel;
354129204Scognet
355129204Scognet	rellim = (const Elf_Rel *)((char *)obj->pltrel +
356129204Scognet	    obj->pltrelsize);
357129204Scognet	for (rel = obj->pltrel;  rel < rellim;  rel++) {
358129204Scognet		Elf_Addr *where;
359129204Scognet
360129204Scognet		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
361129204Scognet
362129204Scognet		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
363129204Scognet		*where += (Elf_Addr )obj->relocbase;
364129204Scognet	}
365129204Scognet
366129204Scognet	return (0);
367129204Scognet}
368129204Scognet
369129204Scognet/*
370129204Scognet *  * LD_BIND_NOW was set - force relocation for all jump slots
371129204Scognet *   */
372129204Scognetint
373233831Skibreloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
374129204Scognet{
375129204Scognet	const Obj_Entry *defobj;
376129204Scognet	const Elf_Rel *rellim;
377129204Scognet	const Elf_Rel *rel;
378129204Scognet	const Elf_Sym *def;
379129204Scognet	Elf_Addr *where;
380129204Scognet	Elf_Addr target;
381129204Scognet
382129204Scognet	rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
383129204Scognet	for (rel = obj->pltrel; rel < rellim; rel++) {
384129204Scognet		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
385129204Scognet		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
386129204Scognet		def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
387233831Skib		    SYMLOOK_IN_PLT | flags, NULL, lockstate);
388129204Scognet		if (def == NULL) {
389129204Scognet			dbg("reloc_jmpslots: sym not found");
390129204Scognet			return (-1);
391129204Scognet		}
392129204Scognet
393129204Scognet		target = (Elf_Addr)(defobj->relocbase + def->st_value);
394129204Scognet		reloc_jmpslot(where, target, defobj, obj,
395129204Scognet		    (const Elf_Rel *) rel);
396129204Scognet	}
397129204Scognet
398129204Scognet	obj->jmpslots_done = true;
399129204Scognet
400129204Scognet	return (0);
401129204Scognet}
402129204Scognet
403229503Skibint
404229503Skibreloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
405229503Skib{
406229503Skib
407229503Skib	/* XXX not implemented */
408229503Skib	return (0);
409229503Skib}
410229503Skib
411229503Skibint
412233831Skibreloc_gnu_ifunc(Obj_Entry *obj, int flags,
413233831Skib    struct Struct_RtldLockState *lockstate)
414229503Skib{
415229503Skib
416229503Skib	/* XXX not implemented */
417229503Skib	return (0);
418229503Skib}
419229503Skib
420129204ScognetElf_Addr
421129204Scognetreloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
422129204Scognet    		const Obj_Entry *obj, const Elf_Rel *rel)
423129204Scognet{
424129204Scognet
425129204Scognet	assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
426129204Scognet
427129204Scognet	if (*where != target)
428129204Scognet		*where = target;
429129204Scognet
430129204Scognet	return target;
431129204Scognet}
432129204Scognet
433135680Scognetvoid
434135680Scognetallocate_initial_tls(Obj_Entry *objs)
435135680Scognet{
436237394Smarius	void **_tp = (void **)ARM_TP_ADDRESS;
437237394Smarius
438237394Smarius	/*
439237394Smarius	* Fix the size of the static TLS block by using the maximum
440237394Smarius	* offset allocated so far and adding a bit for dynamic modules to
441237394Smarius	* use.
442237394Smarius	*/
443237394Smarius
444237394Smarius	tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
445237394Smarius
446237394Smarius	(*_tp) = (void *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8);
447135680Scognet}
448135680Scognet
449135680Scognetvoid *
450135680Scognet__tls_get_addr(tls_index* ti)
451135680Scognet{
452237394Smarius	void **_tp = (void **)ARM_TP_ADDRESS;
453237394Smarius	char *p;
454237394Smarius
455237394Smarius	p = tls_get_addr_common((Elf_Addr **)(*_tp), ti->ti_module, ti->ti_offset);
456237394Smarius
457237394Smarius	return (p);
458135680Scognet}
459