xref: /freebsd/libexec/rtld-elf/arm/reloc.c (revision 531c890b8aecbf157fe3491503b5ca62c0b01093)
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 			const Ver_Entry *ve;
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 			hash = elf_hash(name);
50 			size = dstsym->st_size;
51 			ve = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info));
52 
53 			for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
54 				if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL)
55 					break;
56 
57 			if (srcobj == NULL) {
58 				_rtld_error("Undefined symbol \"%s\" referenced from COPY"
59 				    " relocation in %s", name, dstobj->path);
60 				return -1;
61 			}
62 
63 			srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
64 			memcpy(dstaddr, srcaddr, size);
65 		}
66 	}
67 	return 0;
68 }
69 
70 void _rtld_bind_start(void);
71 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
72 
73 int open();
74 int _open();
75 void
76 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
77 {
78 	const Elf_Rel *rel = 0, *rellim;
79 	Elf_Addr relsz = 0;
80 	Elf_Addr *where;
81 	uint32_t size;
82 
83 	for (; dynp->d_tag != DT_NULL; dynp++) {
84 		switch (dynp->d_tag) {
85 		case DT_REL:
86 			rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
87 			break;
88 		case DT_RELSZ:
89 			relsz = dynp->d_un.d_val;
90 			break;
91 		}
92 	}
93 	rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
94 	size = (rellim - 1)->r_offset - rel->r_offset;
95 	for (; rel < rellim; rel++) {
96 		where = (Elf_Addr *)(relocbase + rel->r_offset);
97 
98 		*where += (Elf_Addr)relocbase;
99 	}
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 	/* The relocation for the dynamic loader has already been done. */
252 	if (obj == obj_rtld)
253 		return (0);
254 	/*
255  	 * The dynamic loader may be called from a thread, we have
256 	 * limited amounts of stack available so we cannot use alloca().
257 	 */
258 	cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
259 	if (cache == MAP_FAILED)
260 		cache = NULL;
261 
262 	rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
263 	for (rel = obj->rel; rel < rellim; rel++) {
264 		if (reloc_nonplt_object(obj, rel, cache) < 0)
265 			goto done;
266 	}
267 	r = 0;
268 done:
269 	if (cache) {
270 		munmap(cache, bytes);
271 	}
272 	return (r);
273 }
274 
275 /*
276  *  * Process the PLT relocations.
277  *   */
278 int
279 reloc_plt(Obj_Entry *obj)
280 {
281 	const Elf_Rel *rellim;
282 	const Elf_Rel *rel;
283 
284 	rellim = (const Elf_Rel *)((char *)obj->pltrel +
285 	    obj->pltrelsize);
286 	for (rel = obj->pltrel;  rel < rellim;  rel++) {
287 		Elf_Addr *where;
288 
289 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
290 
291 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
292 		*where += (Elf_Addr )obj->relocbase;
293 	}
294 
295 	return (0);
296 }
297 
298 /*
299  *  * LD_BIND_NOW was set - force relocation for all jump slots
300  *   */
301 int
302 reloc_jmpslots(Obj_Entry *obj)
303 {
304 	const Obj_Entry *defobj;
305 	const Elf_Rel *rellim;
306 	const Elf_Rel *rel;
307 	const Elf_Sym *def;
308 	Elf_Addr *where;
309 	Elf_Addr target;
310 
311 	rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
312 	for (rel = obj->pltrel; rel < rellim; rel++) {
313 		assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
314 		where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
315 		def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
316 		    true, NULL);
317 		if (def == NULL) {
318 			dbg("reloc_jmpslots: sym not found");
319 			return (-1);
320 		}
321 
322 		target = (Elf_Addr)(defobj->relocbase + def->st_value);
323 		reloc_jmpslot(where, target, defobj, obj,
324 		    (const Elf_Rel *) rel);
325 	}
326 
327 	obj->jmpslots_done = true;
328 
329 	return (0);
330 }
331 
332 Elf_Addr
333 reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
334     		const Obj_Entry *obj, const Elf_Rel *rel)
335 {
336 
337 	assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
338 
339 	if (*where != target)
340 		*where = target;
341 
342 	return target;
343 }
344 
345 void
346 allocate_initial_tls(Obj_Entry *objs)
347 {
348 
349 }
350 
351 void *
352 __tls_get_addr(tls_index* ti)
353 {
354 	return (NULL);
355 }
356