reloc.c revision 1.119 1 /* $NetBSD: reloc.c,v 1.119 2025/04/18 02:16:16 riastradh Exp $ */
2
3 /*
4 * Copyright 1996 John D. Polstra.
5 * Copyright 1996 Matt Thomas <matt (at) 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 (at) polstra.com>.
38 */
39
40 #include <sys/cdefs.h>
41 #ifndef lint
42 __RCSID("$NetBSD: reloc.c,v 1.119 2025/04/18 02:16:16 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
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
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 /*
158 * If the main program is lazily bound (default -- whether or
159 * not LD_BINDNOW is set in the calling environment), we are
160 * now done writing to anything covered by RELRO and we can
161 * safely make it read-only. There may still be ifunc
162 * resolution to do later; it will happen in a read/write
163 * segment and will not be made read-only.
164 *
165 * But if the main program is eagerly bound (i.e., the object
166 * has DF_1_NOW set in DT_FLAGS_1, whether or not LD_BIND_NOW
167 * is set in the calling environment), we delay protecting the
168 * RELRO region as read-only until we have resolved ifuncs --
169 * at which point we will make the ifunc resolution read-only
170 * too.
171 */
172 if (!dstobj->z_now && _rtld_relro(dstobj, true) == -1)
173 return -1;
174 #endif
175 #endif /* RTLD_INHIBIT_COPY_RELOCS */
176
177 return (0);
178 }
179
180 /*
181 * Relocate newly-loaded shared objects. The argument is a pointer to
182 * the Obj_Entry for the first such object. All objects from the first
183 * to the end of the list of objects are relocated. Returns 0 on success,
184 * or -1 on failure.
185 */
186 int
187 _rtld_relocate_objects(Obj_Entry *first, bool bind_now)
188 {
189 Obj_Entry *obj;
190 int ok = 1;
191
192 for (obj = first; obj != NULL; obj = obj->next) {
193 if ((!obj->sysv_hash && !obj->gnu_hash) ||
194 obj->symtab == NULL || obj->strtab == NULL) {
195 _rtld_error("%s: Shared object has no run-time"
196 " symbol table", obj->path);
197 return -1;
198 }
199 if (obj->nbuckets == UINT32_MAX) {
200 _rtld_error("%s: Symbol table too large", obj->path);
201 return -1;
202 }
203 rdbg((" relocating %s (%ld/%ld rel/rela, %ld/%ld plt rel/rela)",
204 obj->path,
205 (long)(obj->rellim - obj->rel),
206 (long)(obj->relalim - obj->rela),
207 (long)(obj->pltrellim - obj->pltrel),
208 (long)(obj->pltrelalim - obj->pltrela)));
209
210 if (obj->textrel) {
211 xwarnx("%s: text relocations", obj->path);
212 /*
213 * There are relocations to the write-protected text
214 * segment.
215 */
216 if (mprotect(obj->mapbase, obj->textsize,
217 PROT_READ | PROT_WRITE) == -1) {
218 _rtld_error("%s: Cannot write-enable text "
219 "segment: %s", obj->path, xstrerror(errno));
220 return -1;
221 }
222 }
223 dbg(("doing non-PLT relocations"));
224 if (_rtld_relocate_nonplt_objects(obj) < 0)
225 ok = 0;
226 if (obj->textrel) { /* Re-protected the text segment. */
227 if (mprotect(obj->mapbase, obj->textsize,
228 PROT_READ | PROT_EXEC) == -1) {
229 _rtld_error("%s: Cannot write-protect text "
230 "segment: %s", obj->path, xstrerror(errno));
231 return -1;
232 }
233 }
234 dbg(("doing lazy PLT binding"));
235 if (_rtld_relocate_plt_lazy(obj) < 0)
236 ok = 0;
237 if (obj->z_now || bind_now) {
238 dbg(("doing immediate PLT binding"));
239 if (_rtld_relocate_plt_objects(obj) < 0)
240 ok = 0;
241 }
242 if (!ok)
243 return -1;
244
245 dbg(("fixing up PLTGOT"));
246 /* Set the special PLTGOT entries. */
247 if (obj->pltgot != NULL)
248 _rtld_setup_pltgot(obj);
249 #ifdef GNU_RELRO
250 if (_rtld_relro(obj, false) == -1)
251 return -1;
252 #endif
253 }
254 return 0;
255 }
256
257 Elf_Addr
258 _rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
259 {
260 Elf_Addr target;
261
262 _rtld_shared_exit();
263 target = _rtld_resolve_ifunc2(obj,
264 (Elf_Addr)obj->relocbase + def->st_value);
265 _rtld_shared_enter();
266 return target;
267 }
268
269 Elf_Addr
270 _rtld_resolve_ifunc2(const Obj_Entry *obj, Elf_Addr addr)
271 {
272 Elf_Addr target;
273
274 target = _rtld_call_function_addr(obj, addr);
275
276 return target;
277 }
278
279 #if \
280 !defined(RTLD_COMMON_CALL_IFUNC_RELA) && \
281 !defined(RTLD_COMMON_CALL_IFUNC_REL) && \
282 !defined(RTLD_ARCH_CALL_IFUNC)
283 void
284 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
285 {
286 }
287 #endif
288
289 #ifdef RTLD_COMMON_CALL_IFUNC_RELA
290 # ifdef __sparc__
291 # include <machine/elf_support.h>
292 # endif
293
294 void
295 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
296 {
297 const Elf_Rela *rela;
298 Elf_Addr *where;
299 #ifdef __sparc__
300 Elf_Word *where2;
301 #endif
302 Elf_Addr target;
303
304 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
305 rela = obj->pltrelalim - obj->ifunc_remaining--;
306 #ifdef __sparc__
307 #define PLT_IRELATIVE R_TYPE(JMP_IREL)
308 #else
309 #define PLT_IRELATIVE R_TYPE(IRELATIVE)
310 #endif
311 if (ELF_R_TYPE(rela->r_info) != PLT_IRELATIVE)
312 continue;
313 #ifdef __sparc__
314 where2 = (Elf_Word *)(obj->relocbase + rela->r_offset);
315 #else
316 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
317 #endif
318 target = (Elf_Addr)(obj->relocbase + rela->r_addend);
319 _rtld_exclusive_exit(mask);
320 target = _rtld_resolve_ifunc2(obj, target);
321 _rtld_exclusive_enter(mask);
322 #ifdef __sparc__
323 sparc_write_branch(where2 + 1, (void *)target);
324 #else
325 if (*where != target)
326 *where = target;
327 #endif
328 }
329
330 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
331 rela = obj->relalim - obj->ifunc_remaining_nonplt--;
332 if (ELF_R_TYPE(rela->r_info) != R_TYPE(IRELATIVE))
333 continue;
334 where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
335 target = (Elf_Addr)(obj->relocbase + rela->r_addend);
336 _rtld_exclusive_exit(mask);
337 target = _rtld_resolve_ifunc2(obj, target);
338 _rtld_exclusive_enter(mask);
339 if (*where != target)
340 *where = target;
341 }
342 }
343 #endif
344
345 #ifdef RTLD_COMMON_CALL_IFUNC_REL
346 void
347 _rtld_call_ifunc(Obj_Entry *obj, sigset_t *mask, u_int cur_objgen)
348 {
349 const Elf_Rel *rel;
350 Elf_Addr *where, target;
351
352 while (obj->ifunc_remaining > 0 && _rtld_objgen == cur_objgen) {
353 rel = obj->pltrellim - obj->ifunc_remaining;
354 --obj->ifunc_remaining;
355 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
356 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
357 _rtld_exclusive_exit(mask);
358 target = _rtld_resolve_ifunc2(obj, *where);
359 _rtld_exclusive_enter(mask);
360 if (*where != target)
361 *where = target;
362 }
363 }
364
365 while (obj->ifunc_remaining_nonplt > 0 && _rtld_objgen == cur_objgen) {
366 rel = obj->rellim - obj->ifunc_remaining_nonplt--;
367 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE)) {
368 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
369 _rtld_exclusive_exit(mask);
370 target = _rtld_resolve_ifunc2(obj, *where);
371 _rtld_exclusive_enter(mask);
372 if (*where != target)
373 *where = target;
374 }
375 }
376 }
377 #endif
378