xref: /freebsd/libexec/rtld-elf/arm/reloc.c (revision f11c7f63056671247335df83a3fe80b94c6616ac)
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 			size_t size;
40 			const void *srcaddr;
41 			const Elf_Sym *srcsym;
42 			const Obj_Entry *srcobj, *defobj;
43 			SymLook req;
44 			int res;
45 
46 			dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
47 			dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
48 			name = dstobj->strtab + dstsym->st_name;
49 			size = dstsym->st_size;
50 
51 			symlook_init(&req, name);
52 			req.ventry = fetch_ventry(dstobj,
53 			    ELF_R_SYM(rel->r_info));
54 			for (srcobj = dstobj->next;  srcobj != NULL;
55 			     srcobj = srcobj->next) {
56 				res = symlook_obj(&req, srcobj);
57 				if (res == 0) {
58 					srcsym = req.sym_out;
59 					defobj = req.defobj_out;
60 					break;
61 				}
62 			}
63 			if (srcobj == NULL) {
64 				_rtld_error(
65 "Undefined symbol \"%s\" referenced from COPY relocation in %s",
66 				    name, dstobj->path);
67 				return (-1);
68 			}
69 
70 			srcaddr = (const void *)(defobj->relocbase +
71 			    srcsym->st_value);
72 			memcpy(dstaddr, srcaddr, size);
73 		}
74 	}
75 	return 0;
76 }
77 
78 void _rtld_bind_start(void);
79 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
80 
81 int open();
82 int _open();
83 void
84 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
85 {
86 	const Elf_Rel *rel = 0, *rellim;
87 	Elf_Addr relsz = 0;
88 	Elf_Addr *where;
89 	uint32_t size;
90 
91 	for (; dynp->d_tag != DT_NULL; dynp++) {
92 		switch (dynp->d_tag) {
93 		case DT_REL:
94 			rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
95 			break;
96 		case DT_RELSZ:
97 			relsz = dynp->d_un.d_val;
98 			break;
99 		}
100 	}
101 	rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
102 	size = (rellim - 1)->r_offset - rel->r_offset;
103 	for (; rel < rellim; rel++) {
104 		where = (Elf_Addr *)(relocbase + rel->r_offset);
105 
106 		*where += (Elf_Addr)relocbase;
107 	}
108 }
109 /*
110  * It is possible for the compiler to emit relocations for unaligned data.
111  * We handle this situation with these inlines.
112  */
113 #define	RELOC_ALIGNED_P(x) \
114 	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
115 
116 static __inline Elf_Addr
117 load_ptr(void *where)
118 {
119 	Elf_Addr res;
120 
121 	memcpy(&res, where, sizeof(res));
122 
123 	return (res);
124 }
125 
126 static __inline void
127 store_ptr(void *where, Elf_Addr val)
128 {
129 
130 	memcpy(where, &val, sizeof(val));
131 }
132 
133 static int
134 reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
135     RtldLockState *lockstate)
136 {
137 	Elf_Addr        *where;
138 	const Elf_Sym   *def;
139 	const Obj_Entry *defobj;
140 	Elf_Addr         tmp;
141 	unsigned long	 symnum;
142 
143 	where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
144 	symnum = ELF_R_SYM(rel->r_info);
145 
146 	switch (ELF_R_TYPE(rel->r_info)) {
147 	case R_ARM_NONE:
148 		break;
149 
150 #if 1 /* XXX should not occur */
151 	case R_ARM_PC24: {	/* word32 S - P + A */
152 		Elf32_Sword addend;
153 
154 		/*
155 		 * Extract addend and sign-extend if needed.
156 		 */
157 		addend = *where;
158 		if (addend & 0x00800000)
159 			addend |= 0xff000000;
160 
161 		def = find_symdef(symnum, obj, &defobj, false, cache,
162 		    lockstate);
163 		if (def == NULL)
164 				return -1;
165 			tmp = (Elf_Addr)obj->relocbase + def->st_value
166 			    - (Elf_Addr)where + (addend << 2);
167 			if ((tmp & 0xfe000000) != 0xfe000000 &&
168 			    (tmp & 0xfe000000) != 0) {
169 				_rtld_error(
170 				"%s: R_ARM_PC24 relocation @ %p to %s failed "
171 				"(displacement %ld (%#lx) out of range)",
172 				    obj->path, where,
173 				    obj->strtab + obj->symtab[symnum].st_name,
174 				    (long) tmp, (long) tmp);
175 				return -1;
176 			}
177 			tmp >>= 2;
178 			*where = (*where & 0xff000000) | (tmp & 0x00ffffff);
179 			dbg("PC24 %s in %s --> %p @ %p in %s",
180 			    obj->strtab + obj->symtab[symnum].st_name,
181 			    obj->path, (void *)*where, where, defobj->path);
182 			break;
183 		}
184 #endif
185 
186 		case R_ARM_ABS32:	/* word32 B + S + A */
187 		case R_ARM_GLOB_DAT:	/* word32 B + S */
188 			def = find_symdef(symnum, obj, &defobj, false, cache,
189 			    lockstate);
190 			if (def == NULL)
191 				return -1;
192 			if (__predict_true(RELOC_ALIGNED_P(where))) {
193 				tmp =  *where + (Elf_Addr)defobj->relocbase +
194 				    def->st_value;
195 				*where = tmp;
196 			} else {
197 				tmp = load_ptr(where) +
198 				    (Elf_Addr)defobj->relocbase +
199 				    def->st_value;
200 				store_ptr(where, tmp);
201 			}
202 			dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
203 			    obj->strtab + obj->symtab[symnum].st_name,
204 			    obj->path, (void *)tmp, where, defobj->path);
205 			break;
206 
207 		case R_ARM_RELATIVE:	/* word32 B + A */
208 			if (__predict_true(RELOC_ALIGNED_P(where))) {
209 				tmp = *where + (Elf_Addr)obj->relocbase;
210 				*where = tmp;
211 			} else {
212 				tmp = load_ptr(where) +
213 				    (Elf_Addr)obj->relocbase;
214 				store_ptr(where, tmp);
215 			}
216 			dbg("RELATIVE in %s --> %p", obj->path,
217 			    (void *)tmp);
218 			break;
219 
220 		case R_ARM_COPY:
221 			/*
222 			 * These are deferred until all other relocations have
223 			 * been done.  All we do here is make sure that the
224 			 * COPY relocation is not in a shared library.  They
225 			 * are allowed only in executable files.
226 			 */
227 			if (!obj->mainprog) {
228 				_rtld_error(
229 			"%s: Unexpected R_COPY relocation in shared library",
230 				    obj->path);
231 				return -1;
232 			}
233 			dbg("COPY (avoid in main)");
234 			break;
235 
236 		default:
237 			dbg("sym = %lu, type = %lu, offset = %p, "
238 			    "contents = %p, symbol = %s",
239 			    symnum, (u_long)ELF_R_TYPE(rel->r_info),
240 			    (void *)rel->r_offset, (void *)load_ptr(where),
241 			    obj->strtab + obj->symtab[symnum].st_name);
242 			_rtld_error("%s: Unsupported relocation type %ld "
243 			    "in non-PLT relocations\n",
244 			    obj->path, (u_long) ELF_R_TYPE(rel->r_info));
245 			return -1;
246 	}
247 	return 0;
248 }
249 
250 /*
251  *  * Process non-PLT relocations
252  *   */
253 int
254 reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate)
255 {
256 	const Elf_Rel *rellim;
257 	const Elf_Rel *rel;
258 	SymCache *cache;
259 	int r = -1;
260 
261 	/* The relocation for the dynamic loader has already been done. */
262 	if (obj == obj_rtld)
263 		return (0);
264 	/*
265  	 * The dynamic loader may be called from a thread, we have
266 	 * limited amounts of stack available so we cannot use alloca().
267 	 */
268 	cache = calloc(obj->nchains, sizeof(SymCache));
269 	/* No need to check for NULL here */
270 
271 	rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
272 	for (rel = obj->rel; rel < rellim; rel++) {
273 		if (reloc_nonplt_object(obj, rel, cache, lockstate) < 0)
274 			goto done;
275 	}
276 	r = 0;
277 done:
278 	if (cache != NULL)
279 		free(cache);
280 	return (r);
281 }
282 
283 /*
284  *  * Process the PLT relocations.
285  *   */
286 int
287 reloc_plt(Obj_Entry *obj)
288 {
289 	const Elf_Rel *rellim;
290 	const Elf_Rel *rel;
291 
292 	rellim = (const Elf_Rel *)((char *)obj->pltrel +
293 	    obj->pltrelsize);
294 	for (rel = obj->pltrel;  rel < rellim;  rel++) {
295 		Elf_Addr *where;
296 
297 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
298 
299 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
300 		*where += (Elf_Addr )obj->relocbase;
301 	}
302 
303 	return (0);
304 }
305 
306 /*
307  *  * LD_BIND_NOW was set - force relocation for all jump slots
308  *   */
309 int
310 reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate)
311 {
312 	const Obj_Entry *defobj;
313 	const Elf_Rel *rellim;
314 	const Elf_Rel *rel;
315 	const Elf_Sym *def;
316 	Elf_Addr *where;
317 	Elf_Addr target;
318 
319 	rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
320 	for (rel = obj->pltrel; rel < rellim; rel++) {
321 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
322 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
323 		def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
324 		    true, NULL, lockstate);
325 		if (def == NULL) {
326 			dbg("reloc_jmpslots: sym not found");
327 			return (-1);
328 		}
329 
330 		target = (Elf_Addr)(defobj->relocbase + def->st_value);
331 		reloc_jmpslot(where, target, defobj, obj,
332 		    (const Elf_Rel *) rel);
333 	}
334 
335 	obj->jmpslots_done = true;
336 
337 	return (0);
338 }
339 
340 int
341 reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
342 {
343 
344 	/* XXX not implemented */
345 	return (0);
346 }
347 
348 int
349 reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
350 {
351 
352 	/* XXX not implemented */
353 	return (0);
354 }
355 
356 Elf_Addr
357 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
358     		const Obj_Entry *obj, const Elf_Rel *rel)
359 {
360 
361 	assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
362 
363 	if (*where != target)
364 		*where = target;
365 
366 	return target;
367 }
368 
369 void
370 allocate_initial_tls(Obj_Entry *objs)
371 {
372 
373 }
374 
375 void *
376 __tls_get_addr(tls_index* ti)
377 {
378 	return (NULL);
379 }
380