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