xref: /netbsd-src/libexec/ld.elf_so/reloc.c (revision eeb15e760e07c8b8355b87091939d54d2157b4fa)
1 /*	$NetBSD: reloc.c,v 1.118 2023/07/30 09:20:14 riastradh 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.118 2023/07/30 09:20:14 riastradh 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 "hash.h"
60 #include "rtld.h"
61 
62 #ifndef RTLD_INHIBIT_COPY_RELOCS
63 static int _rtld_do_copy_relocation(const Obj_Entry *, const Elf_Rela *);
64 
65 static int
_rtld_do_copy_relocation(const Obj_Entry * dstobj,const Elf_Rela * rela)66 _rtld_do_copy_relocation(const Obj_Entry *dstobj, const Elf_Rela *rela)
67 {
68 	void           *dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
69 	const Elf_Sym  *dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
70 	const char     *name = dstobj->strtab + dstsym->st_name;
71 	Elf_Hash        hash;
72 	size_t          size = dstsym->st_size;
73 	const void     *srcaddr;
74 	const Elf_Sym  *srcsym = NULL;
75 	Obj_Entry      *srcobj;
76 
77 	hash.sysv = _rtld_sysv_hash(name);
78 	hash.gnu = _rtld_gnu_hash(name);
79 
80 	if (__predict_false(size == 0)) {
81 #if defined(__powerpc__) && !defined(__LP64) /* PR port-macppc/47464 */
82 		if (strcmp(name, "_SDA_BASE_") == 0
83 		    || strcmp(name, "_SDA2_BASE_") == 0)
84 		{
85 			rdbg(("COPY %s %s --> ignoring old binutils bug",
86 			      dstobj->path, name));
87 			return 0;
88 		}
89 #endif
90 #if 0 /* shall we warn? */
91 		xwarnx("%s: zero size COPY relocation for \"%s\"",
92 		       dstobj->path, name);
93 #endif
94 	}
95 
96 	for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
97 		srcsym = _rtld_symlook_obj(name, &hash, srcobj, 0,
98 		    _rtld_fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)));
99 		if (srcsym != NULL)
100 			break;
101 	}
102 
103 	if (srcobj == NULL) {
104 		_rtld_error("Undefined symbol \"%s\" referenced from COPY"
105 		    " relocation in %s", name, dstobj->path);
106 		return (-1);
107 	}
108 	srcaddr = (const void *)(srcobj->relocbase + srcsym->st_value);
109 	rdbg(("COPY %s %s %s --> src=%p dst=%p size %ld",
110 	    dstobj->path, srcobj->path, name, srcaddr,
111 	    (void *)dstaddr, (long)size));
112 	(void)memcpy(dstaddr, srcaddr, size);
113 	return (0);
114 }
115 #endif /* RTLD_INHIBIT_COPY_RELOCS */
116 
117 
118 /*
119  * Process the special R_xxx_COPY relocations in the main program.  These
120  * copy data from a shared object into a region in the main program's BSS
121  * segment.
122  *
123  * Returns 0 on success, -1 on failure.
124  */
125 int
_rtld_do_copy_relocations(const Obj_Entry * dstobj)126 _rtld_do_copy_relocations(const Obj_Entry *dstobj)
127 {
128 #ifndef RTLD_INHIBIT_COPY_RELOCS
129 
130 	/* COPY relocations are invalid elsewhere */
131 	assert(!dstobj->isdynamic);
132 
133 	if (dstobj->rel != NULL) {
134 		const Elf_Rel  *rel;
135 		for (rel = dstobj->rel; rel < dstobj->rellim; ++rel) {
136 			if (ELF_R_TYPE(rel->r_info) == R_TYPE(COPY)) {
137 				Elf_Rela        ourrela;
138 				ourrela.r_info = rel->r_info;
139 				ourrela.r_offset = rel->r_offset;
140 				ourrela.r_addend = 0;
141 				if (_rtld_do_copy_relocation(dstobj,
142 				    &ourrela) < 0)
143 					return (-1);
144 			}
145 		}
146 	}
147 	if (dstobj->rela != NULL) {
148 		const Elf_Rela *rela;
149 		for (rela = dstobj->rela; rela < dstobj->relalim; ++rela) {
150 			if (ELF_R_TYPE(rela->r_info) == R_TYPE(COPY)) {
151 				if (_rtld_do_copy_relocation(dstobj, rela) < 0)
152 					return (-1);
153 			}
154 		}
155 	}
156 #ifdef GNU_RELRO
157 	if (_rtld_relro(dstobj, true) == -1)
158 		return -1;
159 #endif
160 #endif /* RTLD_INHIBIT_COPY_RELOCS */
161 
162 	return (0);
163 }
164 
165 /*
166  * Relocate newly-loaded shared objects.  The argument is a pointer to
167  * the Obj_Entry for the first such object.  All objects from the first
168  * to the end of the list of objects are relocated.  Returns 0 on success,
169  * or -1 on failure.
170  */
171 int
_rtld_relocate_objects(Obj_Entry * first,bool bind_now)172 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
173 {
174 	Obj_Entry *obj;
175 	int ok = 1;
176 
177 	for (obj = first; obj != NULL; obj = obj->next) {
178 		if ((!obj->sysv_hash && !obj->gnu_hash) ||
179 		    obj->symtab == NULL || obj->strtab == NULL) {
180 			_rtld_error("%s: Shared object has no run-time"
181 			    " symbol table", obj->path);
182 			return -1;
183 		}
184 		if (obj->nbuckets == UINT32_MAX) {
185 			_rtld_error("%s: Symbol table too large", obj->path);
186 			return -1;
187 		}
188 		rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
189 		    obj->path,
190 		    (long)(obj->rellim - obj->rel),
191 		    (long)(obj->relalim - obj->rela),
192 		    (long)(obj->pltrellim - obj->pltrel),
193 		    (long)(obj->pltrelalim - obj->pltrela)));
194 
195 		if (obj->textrel) {
196 			xwarnx("%s: text relocations", obj->path);
197 			/*
198 			 * There are relocations to the write-protected text
199 			 * segment.
200 			 */
201 			if (mprotect(obj->mapbase, obj->textsize,
202 				PROT_READ | PROT_WRITE) == -1) {
203 				_rtld_error("%s: Cannot write-enable text "
204 				    "segment: %s", obj->path, xstrerror(errno));
205 				return -1;
206 			}
207 		}
208 		dbg(("doing non-PLT relocations"));
209 		if (_rtld_relocate_nonplt_objects(obj) < 0)
210 			ok = 0;
211 		if (obj->textrel) {	/* Re-protected the text segment. */
212 			if (mprotect(obj->mapbase, obj->textsize,
213 				     PROT_READ | PROT_EXEC) == -1) {
214 				_rtld_error("%s: Cannot write-protect text "
215 				    "segment: %s", obj->path, xstrerror(errno));
216 				return -1;
217 			}
218 		}
219 		dbg(("doing lazy PLT binding"));
220 		if (_rtld_relocate_plt_lazy(obj) < 0)
221 			ok = 0;
222 		if (obj->z_now || bind_now) {
223 			dbg(("doing immediate PLT binding"));
224 			if (_rtld_relocate_plt_objects(obj) < 0)
225 				ok = 0;
226 		}
227 		if (!ok)
228 			return -1;
229 
230 		dbg(("fixing up PLTGOT"));
231 		/* Set the special PLTGOT entries. */
232 		if (obj->pltgot != NULL)
233 			_rtld_setup_pltgot(obj);
234 #ifdef GNU_RELRO
235 		if (_rtld_relro(obj, false) == -1)
236 			return -1;
237 #endif
238 	}
239 	return 0;
240 }
241 
242 Elf_Addr
_rtld_resolve_ifunc(const Obj_Entry * obj,const Elf_Sym * def)243 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
244 {
245 	Elf_Addr target;
246 
247 	_rtld_shared_exit();
248 	target = _rtld_resolve_ifunc2(obj,
249 	    (Elf_Addr)obj->relocbase + def->st_value);
250 	_rtld_shared_enter();
251 	return target;
252 }
253 
254 Elf_Addr
_rtld_resolve_ifunc2(const Obj_Entry * obj,Elf_Addr addr)255 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
256 {
257 	Elf_Addr target;
258 
259 	target = _rtld_call_function_addr(obj, addr);
260 
261 	return target;
262 }
263 
264 #if \
265     !defined(RTLD_COMMON_CALL_IFUNC_RELA) && \
266     !defined(RTLD_COMMON_CALL_IFUNC_REL) && \
267     !defined(RTLD_ARCH_CALL_IFUNC)
268 void
_rtld_call_ifunc(Obj_Entry * obj,sigset_t * mask,u_int cur_objgen)269 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
270 {
271 }
272 #endif
273 
274 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
275 #  ifdef __sparc__
276 #  include <machine/elf_support.h>
277 #  endif
278 
279 void
_rtld_call_ifunc(Obj_Entry * obj,sigset_t * mask,u_int cur_objgen)280 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
281 {
282 	const Elf_Rela *rela;
283 	Elf_Addr *where;
284 #ifdef __sparc__
285 	Elf_Word *where2;
286 #endif
287 	Elf_Addr target;
288 
289 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
290 		rela = obj->pltrelalim - obj->ifunc_remaining--;
291 #ifdef __sparc__
292 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
293 #else
294 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
295 #endif
296 		if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
297 			continue;
298 #ifdef __sparc__
299 		where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
300 #else
301 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
302 #endif
303 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
304 		_rtld_exclusive_exit(mask);
305 		target = _rtld_resolve_ifunc2(obj, target);
306 		_rtld_exclusive_enter(mask);
307 #ifdef __sparc__
308 		sparc_write_branch(where2 + 1, (void *)target);
309 #else
310 		if (*where != target)
311 			*where = target;
312 #endif
313 	}
314 
315 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
316 		rela = obj->relalim - obj->ifunc_remaining_nonplt--;
317 		if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
318 			continue;
319 		where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
320 		target = (Elf_Addr)(obj->relocbase + rela->r_addend);
321 		_rtld_exclusive_exit(mask);
322 		target = _rtld_resolve_ifunc2(obj, target);
323 		_rtld_exclusive_enter(mask);
324 		if (*where != target)
325 			*where = target;
326 	}
327 }
328 #endif
329 
330 #ifdef RTLD_COMMON_CALL_IFUNC_REL
331 void
_rtld_call_ifunc(Obj_Entry * obj,sigset_t * mask,u_int cur_objgen)332 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
333 {
334 	const Elf_Rel *rel;
335 	Elf_Addr *where, target;
336 
337 	while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
338 		rel = obj->pltrellim - obj->ifunc_remaining;
339 		--obj->ifunc_remaining;
340 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
341 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
342 			_rtld_exclusive_exit(mask);
343 			target = _rtld_resolve_ifunc2(obj, *where);
344 			_rtld_exclusive_enter(mask);
345 			if (*where != target)
346 				*where = target;
347 		}
348 	}
349 
350 	while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
351 		rel = obj->rellim - obj->ifunc_remaining_nonplt--;
352 		if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
353 			where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
354 			_rtld_exclusive_exit(mask);
355 			target = _rtld_resolve_ifunc2(obj, *where);
356 			_rtld_exclusive_enter(mask);
357 			if (*where != target)
358 				*where = target;
359 		}
360 	}
361 }
362 #endif
363