xref: /netbsd-src/libexec/ld.elf_so/reloc.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: reloc.c,v 1.112 2018/04/03 21:10:27 joerg Exp $	 */
2 
3 /*
4  * Copyright 1996 John D. Polstra.
5  * Copyright 1996 Matt Thomas <matt@3am-software.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by John Polstra.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Dynamic linker for ELF.
36  *
37  * John Polstra <jdp@polstra.com>.
38  */
39 
40 #include <sys/cdefs.h>
41 #ifndef lint
42 __RCSID("$NetBSD: reloc.c,v 1.112 2018/04/03 21:10:27 joerg Exp $");
43 #endif /* not lint */
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <sys/types.h>
54 #include <sys/mman.h>
55 #include <sys/bitops.h>
56 #include <dirent.h>
57 
58 #include "debug.h"
59 #include "rtld.h"
60 
61 #ifndef RTLD_INHIBIT_COPY_RELOCS
62 static int _rtld_do_copy_relocation(const Obj_Entry *, const Elf_Rela *);
63 
64 static int
65 _rtld_do_copy_relocation(const Obj_Entry *dstobj, const Elf_Rela *rela)
66 {
67 	void           *dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
68 	const Elf_Sym  *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
69 	const char     *name = dstobj->strtab + dstsym->st_name;
70 	unsigned long   hash = _rtld_elf_hash(name);
71 	size_t          size = dstsym->st_size;
72 	const void     *srcaddr;
73 	const Elf_Sym  *srcsym = NULL;
74 	Obj_Entry      *srcobj;
75 
76 	if (__predict_false(size == 0)) {
77 #if defined(__powerpc__) && !defined(__LP64) /* PR port-macppc/47464 */
78 		if (strcmp(name, "_SDA_BASE_") == 0
79 		    || strcmp(name, "_SDA2_BASE_") == 0)
80 		{
81 			rdbg(("COPY %s %s --> ignoring old binutils bug",
82 			      dstobj->path, name));
83 			return 0;
84 		}
85 #endif
86 #if 0 /* shall we warn? */
87 		xwarnx("%s: zero size COPY relocation for \"%s\"",
88 		       dstobj->path, name);
89 #endif
90 	}
91 
92 	for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
93 		srcsym = _rtld_symlook_obj(name, hash, srcobj, 0,
94 		    _rtld_fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)));
95 		if (srcsym != NULL)
96 			break;
97 	}
98 
99 	if (srcobj == NULL) {
100 		_rtld_error("Undefined symbol \"%s\" referenced from COPY"
101 		    " relocation in %s", name, dstobj->path);
102 		return (-1);
103 	}
104 	srcaddr = (const void *)(srcobj->relocbase + srcsym->st_value);
105 	(void)memcpy(dstaddr, srcaddr, size);
106 	rdbg(("COPY %s %s %s --> src=%p dst=%p size %ld",
107 	    dstobj->path, srcobj->path, name, srcaddr,
108 	    (void *)dstaddr, (long)size));
109 	return (0);
110 }
111 #endif /* RTLD_INHIBIT_COPY_RELOCS */
112 
113 
114 /*
115  * Process the special R_xxx_COPY relocations in the main program.  These
116  * copy data from a shared object into a region in the main program's BSS
117  * segment.
118  *
119  * Returns 0 on success, -1 on failure.
120  */
121 int
122 _rtld_do_copy_relocations(const Obj_Entry *dstobj)
123 {
124 #ifndef RTLD_INHIBIT_COPY_RELOCS
125 
126 	/* COPY relocations are invalid elsewhere */
127 	assert(!dstobj->isdynamic);
128 
129 	if (dstobj->rel != NULL) {
130 		const Elf_Rel  *rel;
131 		for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
132 			if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
133 				Elf_Rela        ourrela;
134 				ourrela.r_info = rel->r_info;
135 				ourrela.r_offset = rel->r_offset;
136 				ourrela.r_addend = 0;
137 				if (_rtld_do_copy_relocation(dstobj,
138 				    &ourrela) < 0)
139 					return (-1);
140 			}
141 		}
142 	}
143 	if (dstobj->rela != NULL) {
144 		const Elf_Rela *rela;
145 		for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
146 			if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
147 				if (_rtld_do_copy_relocation(dstobj, rela) < 0)
148 					return (-1);
149 			}
150 		}
151 	}
152 #endif /* RTLD_INHIBIT_COPY_RELOCS */
153 
154 	return (0);
155 }
156 
157 /*
158  * Relocate newly-loaded shared objects.  The argument is a pointer to
159  * the Obj_Entry for the first such object.  All objects from the first
160  * to the end of the list of objects are relocated.  Returns 0 on success,
161  * or -1 on failure.
162  */
163 int
164 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
165 {
166 	Obj_Entry *obj;
167 	int ok = 1;
168 
169 	for (obj = first; obj != NULL; obj = obj->next) {
170 		if (obj->nbuckets == 0 || obj->nchains == 0 ||
171 		    obj->buckets == NULL || obj->symtab == NULL ||
172 		    obj->strtab == NULL) {
173 			_rtld_error("%s: Shared object has no run-time"
174 			    " symbol table", obj->path);
175 			return -1;
176 		}
177 		if (obj->nbuckets == UINT32_MAX) {
178 			_rtld_error("%s: Symbol table too large", obj->path);
179 			return -1;
180 		}
181 		rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
182 		    obj->path,
183 		    (long)(obj->rellim - obj->rel),
184 		    (long)(obj->relalim - obj->rela),
185 		    (long)(obj->pltrellim - obj->pltrel),
186 		    (long)(obj->pltrelalim - obj->pltrela)));
187 
188 		if (obj->textrel) {
189 			xwarnx("%s: text relocations", obj->path);
190 			/*
191 			 * There are relocations to the write-protected text
192 			 * segment.
193 			 */
194 			if (mprotect(obj->mapbase, obj->textsize,
195 				PROT_READ | PROT_WRITE) == -1) {
196 				_rtld_error("%s: Cannot write-enable text "
197 				    "segment: %s", obj->path, xstrerror(errno));
198 				return -1;
199 			}
200 		}
201 		dbg(("doing non-PLT relocations"));
202 		if (_rtld_relocate_nonplt_objects(obj) < 0)
203 			ok = 0;
204 		if (obj->textrel) {	/* Re-protected the text segment. */
205 			if (mprotect(obj->mapbase, obj->textsize,
206 				     PROT_READ | PROT_EXEC) == -1) {
207 				_rtld_error("%s: Cannot write-protect text "
208 				    "segment: %s", obj->path, xstrerror(errno));
209 				return -1;
210 			}
211 		}
212 		dbg(("doing lazy PLT binding"));
213 		if (_rtld_relocate_plt_lazy(obj) < 0)
214 			ok = 0;
215 		if (obj->z_now || bind_now) {
216 			dbg(("doing immediate PLT binding"));
217 			if (_rtld_relocate_plt_objects(obj) < 0)
218 				ok = 0;
219 		}
220 		if (!ok)
221 			return -1;
222 
223 		/* Set some sanity-checking numbers in the Obj_Entry. */
224 		obj->magic = RTLD_MAGIC;
225 		obj->version = RTLD_VERSION;
226 
227 		/*
228 		 * Fill in the backwards compatibility dynamic linker entry points.
229 		 *
230 		 * DO NOT ADD TO THIS LIST
231 		 */
232 		obj->dlopen = dlopen;
233 		obj->dlsym = dlsym;
234 		obj->dlerror = dlerror;
235 		obj->dlclose = dlclose;
236 		obj->dladdr = dladdr;
237 
238 		dbg(("fixing up PLTGOT"));
239 		/* Set the special PLTGOT entries. */
240 		if (obj->pltgot != NULL)
241 			_rtld_setup_pltgot(obj);
242 #ifdef GNU_RELRO
243 		if (obj->relro_size > 0) {
244 			if (mprotect(obj->relro_page, obj->relro_size,
245 			    PROT_READ) == -1) {
246 				_rtld_error("%s: Cannot enforce relro "
247 				    "protection: %s", obj->path,
248 				    xstrerror(errno));
249 				return -1;
250 			}
251 		}
252 #endif
253 	}
254 
255 	return 0;
256 }
257 
258 Elf_Addr
259 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
260 {
261 	Elf_Addr target;
262 
263 	_rtld_shared_exit();
264 	target = _rtld_resolve_ifunc2(obj,
265 	    (Elf_Addr)obj->relocbase + def->st_value);
266 	_rtld_shared_enter();
267 	return target;
268 }
269 
270 Elf_Addr
271 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
272 {
273 	Elf_Addr target;
274 
275 	target = _rtld_call_function_addr(obj, addr);
276 
277 	return target;
278 }
279 
280 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
281 #  ifdef __sparc__
282 #  include <machine/elf_support.h>
283 #  endif
284 
285 void
286 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
287 {
288 	const Elf_Rela *rela;
289 	Elf_Addr *where;
290 #ifdef __sparc__
291 	Elf_Word *where2;
292 #endif
293 	Elf_Addr target;
294 
295 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
296 		rela = obj->pltrelalim - obj->ifunc_remaining--;
297 #ifdef __sparc__
298 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
299 #else
300 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
301 #endif
302 		if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
303 			continue;
304 #ifdef __sparc__
305 		where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
306 #else
307 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
308 #endif
309 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
310 		_rtld_exclusive_exit(mask);
311 		target = _rtld_resolve_ifunc2(obj, target);
312 		_rtld_exclusive_enter(mask);
313 #ifdef __sparc__
314 		sparc_write_branch(where2 + 1, (void *)target);
315 #else
316 		if (*where != target)
317 			*where = target;
318 #endif
319 	}
320 
321 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
322 		rela = obj->relalim - obj->ifunc_remaining_nonplt--;
323 		if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
324 			continue;
325 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
326 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
327 		_rtld_exclusive_exit(mask);
328 		target = _rtld_resolve_ifunc2(obj, target);
329 		_rtld_exclusive_enter(mask);
330 		if (*where != target)
331 			*where = target;
332 	}
333 }
334 #endif
335 
336 #ifdef RTLD_COMMON_CALL_IFUNC_REL
337 void
338 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
339 {
340 	const Elf_Rel *rel;
341 	Elf_Addr *where, target;
342 
343 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
344 		rel = obj->pltrellim - obj->ifunc_remaining;
345 		--obj->ifunc_remaining;
346 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
347 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
348 			_rtld_exclusive_exit(mask);
349 			target = _rtld_resolve_ifunc2(obj, *where);
350 			_rtld_exclusive_enter(mask);
351 			if (*where != target)
352 				*where = target;
353 		}
354 	}
355 
356 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
357 		rel = obj->rellim - obj->ifunc_remaining_nonplt--;
358 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
359 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
360 			_rtld_exclusive_exit(mask);
361 			target = _rtld_resolve_ifunc2(obj, *where);
362 			_rtld_exclusive_enter(mask);
363 			if (*where != target)
364 				*where = target;
365 		}
366 	}
367 }
368 #endif
369