xref: /freebsd/libexec/rtld-elf/arm/reloc.c (revision f0adf7f5cdd241db2f2c817683191a6ef64a4e95)
1 /*	$NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $	*/
2 
3 #include <sys/cdefs.h>
4 __FBSDID("$FreeBSD$");
5 #include <sys/param.h>
6 #include <sys/mman.h>
7 
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include "debug.h"
14 #include "rtld.h"
15 
16 void
17 init_pltgot(Obj_Entry *obj)
18 {
19 	if (obj->pltgot != NULL) {
20 		obj->pltgot[1] = (Elf_Addr) obj;
21 		obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
22 	}
23 }
24 
25 int
26 do_copy_relocations(Obj_Entry *dstobj)
27 {
28 	const Elf_Rel *rellim;
29 	const Elf_Rel *rel;
30 
31 	assert(dstobj->mainprog);	/* COPY relocations are invalid elsewhere */
32 
33    	rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
34 	for (rel = dstobj->rel;  rel < rellim;  rel++) {
35 		if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) {
36 	    		void *dstaddr;
37 			const Elf_Sym *dstsym;
38 			const char *name;
39 			unsigned long hash;
40 			size_t size;
41 			const void *srcaddr;
42 			const Elf_Sym *srcsym;
43 			Obj_Entry *srcobj;
44 
45 			dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
46 			dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
47 			name = dstobj->strtab + dstsym->st_name;
48 			hash = elf_hash(name);
49 			size = dstsym->st_size;
50 
51 			for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
52 				if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL)
53 					break;
54 
55 			if (srcobj == NULL) {
56 				_rtld_error("Undefined symbol \"%s\" referenced from COPY"
57 				    " relocation in %s", name, dstobj->path);
58 				return -1;
59 			}
60 
61 			srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
62 			memcpy(dstaddr, srcaddr, size);
63 		}
64 	}
65 	return 0;
66 }
67 
68 void _rtld_bind_start(void);
69 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
70 
71 int open();
72 int _open();
73 void
74 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
75 {
76 	const Elf_Rel *rel = 0, *rellim;
77 	Elf_Addr relsz = 0;
78 	Elf_Addr *where;
79 	uint32_t size;
80 
81 	for (; dynp->d_tag != DT_NULL; dynp++) {
82 		switch (dynp->d_tag) {
83 		case DT_REL:
84 			rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
85 			break;
86 		case DT_RELSZ:
87 			relsz = dynp->d_un.d_val;
88 			break;
89 		}
90 	}
91 	rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
92 	size = (rellim - 1)->r_offset - rel->r_offset;
93 	mprotect((void*)relocbase, size, PROT_READ|PROT_WRITE|PROT_EXEC);
94 	for (; rel < rellim; rel++) {
95 		where = (Elf_Addr *)(relocbase + rel->r_offset);
96 
97 		*where += (Elf_Addr)relocbase;
98 	}
99 	mprotect((void*)relocbase, size, PROT_READ|PROT_EXEC);
100 }
101 /*
102  * It is possible for the compiler to emit relocations for unaligned data.
103  * We handle this situation with these inlines.
104  */
105 #define	RELOC_ALIGNED_P(x) \
106 	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
107 
108 static __inline Elf_Addr
109 load_ptr(void *where)
110 {
111 	Elf_Addr res;
112 
113 	memcpy(&res, where, sizeof(res));
114 
115 	return (res);
116 }
117 
118 static __inline void
119 store_ptr(void *where, Elf_Addr val)
120 {
121 
122 	memcpy(where, &val, sizeof(val));
123 }
124 
125 static int
126 reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache)
127 {
128 	Elf_Addr        *where;
129 	const Elf_Sym   *def;
130 	const Obj_Entry *defobj;
131 	Elf_Addr         tmp;
132 	unsigned long	 symnum;
133 
134 	where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
135 	symnum = ELF_R_SYM(rel->r_info);
136 
137 	switch (ELF_R_TYPE(rel->r_info)) {
138 	case R_ARM_NONE:
139 		break;
140 
141 #if 1 /* XXX should not occur */
142 	case R_ARM_PC24: {	/* word32 S - P + A */
143 		Elf32_Sword addend;
144 
145 		/*
146 		 * Extract addend and sign-extend if needed.
147 		 */
148 		addend = *where;
149 		if (addend & 0x00800000)
150 			addend |= 0xff000000;
151 
152 		def = find_symdef(symnum, obj, &defobj, false, cache);
153 		if (def == NULL)
154 				return -1;
155 			tmp = (Elf_Addr)obj->relocbase + def->st_value
156 			    - (Elf_Addr)where + (addend << 2);
157 			if ((tmp & 0xfe000000) != 0xfe000000 &&
158 			    (tmp & 0xfe000000) != 0) {
159 				_rtld_error(
160 				"%s: R_ARM_PC24 relocation @ %p to %s failed "
161 				"(displacement %ld (%#lx) out of range)",
162 				    obj->path, where,
163 				    obj->strtab + obj->symtab[symnum].st_name,
164 				    (long) tmp, (long) tmp);
165 				return -1;
166 			}
167 			tmp >>= 2;
168 			*where = (*where & 0xff000000) | (tmp & 0x00ffffff);
169 			dbg("PC24 %s in %s --> %p @ %p in %s",
170 			    obj->strtab + obj->symtab[symnum].st_name,
171 			    obj->path, (void *)*where, where, defobj->path);
172 			break;
173 		}
174 #endif
175 
176 		case R_ARM_ABS32:	/* word32 B + S + A */
177 		case R_ARM_GLOB_DAT:	/* word32 B + S */
178 			def = find_symdef(symnum, obj, &defobj, false, cache);
179 			if (def == NULL)
180 				return -1;
181 			if (__predict_true(RELOC_ALIGNED_P(where))) {
182 				tmp =  *where + (Elf_Addr)defobj->relocbase +
183 				    def->st_value;
184 				*where = tmp;
185 			} else {
186 				tmp = load_ptr(where) +
187 				    (Elf_Addr)defobj->relocbase +
188 				    def->st_value;
189 				store_ptr(where, tmp);
190 			}
191 			dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
192 			    obj->strtab + obj->symtab[symnum].st_name,
193 			    obj->path, (void *)tmp, where, defobj->path);
194 			break;
195 
196 		case R_ARM_RELATIVE:	/* word32 B + A */
197 			if (__predict_true(RELOC_ALIGNED_P(where))) {
198 				tmp = *where + (Elf_Addr)obj->relocbase;
199 				*where = tmp;
200 			} else {
201 				tmp = load_ptr(where) +
202 				    (Elf_Addr)obj->relocbase;
203 				store_ptr(where, tmp);
204 			}
205 			dbg("RELATIVE in %s --> %p", obj->path,
206 			    (void *)tmp);
207 			break;
208 
209 		case R_ARM_COPY:
210 			/*
211 			 * These are deferred until all other relocations have
212 			 * been done.  All we do here is make sure that the
213 			 * COPY relocation is not in a shared library.  They
214 			 * are allowed only in executable files.
215 			 */
216 			if (!obj->mainprog) {
217 				_rtld_error(
218 			"%s: Unexpected R_COPY relocation in shared library",
219 				    obj->path);
220 				return -1;
221 			}
222 			dbg("COPY (avoid in main)");
223 			break;
224 
225 		default:
226 			dbg("sym = %lu, type = %lu, offset = %p, "
227 			    "contents = %p, symbol = %s",
228 			    symnum, (u_long)ELF_R_TYPE(rel->r_info),
229 			    (void *)rel->r_offset, (void *)load_ptr(where),
230 			    obj->strtab + obj->symtab[symnum].st_name);
231 			_rtld_error("%s: Unsupported relocation type %ld "
232 			    "in non-PLT relocations\n",
233 			    obj->path, (u_long) ELF_R_TYPE(rel->r_info));
234 			return -1;
235 	}
236 	return 0;
237 }
238 
239 /*
240  *  * Process non-PLT relocations
241  *   */
242 int
243 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
244 {
245 	const Elf_Rel *rellim;
246 	const Elf_Rel *rel;
247 	SymCache *cache;
248 	int bytes = obj->nchains * sizeof(SymCache);
249 	int r = -1;
250 
251 	/*
252  	 * The dynamic loader may be called from a thread, we have
253 	 * limited amounts of stack available so we cannot use alloca().
254 	 * 	 	 	 */
255 	cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
256 	if (cache == MAP_FAILED)
257 		cache = NULL;
258 
259 	rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
260 	for (rel = obj->rel; rel < rellim; rel++) {
261 		if (reloc_nonplt_object(obj, rel, cache) < 0)
262 			goto done;
263 	}
264 	r = 0;
265 done:
266 	if (cache) {
267 		munmap(cache, bytes);
268 	}
269 	return (r);
270 }
271 
272 /*
273  *  * Process the PLT relocations.
274  *   */
275 int
276 reloc_plt(Obj_Entry *obj)
277 {
278 	const Elf_Rel *rellim;
279 	const Elf_Rel *rel;
280 
281 	rellim = (const Elf_Rel *)((char *)obj->pltrel +
282 	    obj->pltrelsize);
283 	for (rel = obj->pltrel;  rel < rellim;  rel++) {
284 		Elf_Addr *where;
285 
286 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
287 
288 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
289 		*where += (Elf_Addr )obj->relocbase;
290 	}
291 
292 	return (0);
293 }
294 
295 /*
296  *  * LD_BIND_NOW was set - force relocation for all jump slots
297  *   */
298 int
299 reloc_jmpslots(Obj_Entry *obj)
300 {
301 	const Obj_Entry *defobj;
302 	const Elf_Rel *rellim;
303 	const Elf_Rel *rel;
304 	const Elf_Sym *def;
305 	Elf_Addr *where;
306 	Elf_Addr target;
307 
308 	rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
309 	for (rel = obj->pltrel; rel < rellim; rel++) {
310 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
311 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
312 		def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
313 		    true, NULL);
314 		if (def == NULL) {
315 			dbg("reloc_jmpslots: sym not found");
316 			return (-1);
317 		}
318 
319 		target = (Elf_Addr)(defobj->relocbase + def->st_value);
320 		reloc_jmpslot(where, target, defobj, obj,
321 		    (const Elf_Rel *) rel);
322 	}
323 
324 	obj->jmpslots_done = true;
325 
326 	return (0);
327 }
328 
329 Elf_Addr
330 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
331     		const Obj_Entry *obj, const Elf_Rel *rel)
332 {
333 
334 	assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
335 
336 	if (*where != target)
337 		*where = target;
338 
339 	return target;
340 }
341 
342