mdreloc.c revision 1.48 1 1.48 riastrad /* $NetBSD: mdreloc.c,v 1.48 2025/04/16 17:37:48 riastradh Exp $ */
2 1.48 riastrad
3 1.48 riastrad /*
4 1.48 riastrad * Copyright 1996 John D. Polstra.
5 1.48 riastrad * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
6 1.48 riastrad * All rights reserved.
7 1.48 riastrad *
8 1.48 riastrad * Redistribution and use in source and binary forms, with or without
9 1.48 riastrad * modification, are permitted provided that the following conditions
10 1.48 riastrad * are met:
11 1.48 riastrad * 1. Redistributions of source code must retain the above copyright
12 1.48 riastrad * notice, this list of conditions and the following disclaimer.
13 1.48 riastrad * 2. Redistributions in binary form must reproduce the above copyright
14 1.48 riastrad * notice, this list of conditions and the following disclaimer in the
15 1.48 riastrad * documentation and/or other materials provided with the distribution.
16 1.48 riastrad * 3. All advertising materials mentioning features or use of this software
17 1.48 riastrad * must display the following acknowledgement:
18 1.48 riastrad * This product includes software developed by John Polstra.
19 1.48 riastrad * 4. The name of the author may not be used to endorse or promote products
20 1.48 riastrad * derived from this software without specific prior written permission.
21 1.48 riastrad *
22 1.48 riastrad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 1.48 riastrad * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 1.48 riastrad * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 1.48 riastrad * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 1.48 riastrad * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 1.48 riastrad * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 1.48 riastrad * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 1.48 riastrad * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 1.48 riastrad * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 1.48 riastrad * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 1.48 riastrad */
33 1.48 riastrad
34 1.48 riastrad /*
35 1.48 riastrad * i386 ELF relocations.
36 1.48 riastrad *
37 1.48 riastrad * References:
38 1.48 riastrad *
39 1.48 riastrad * [ABI386-4] System V Application Binary Interface: Intel386
40 1.48 riastrad * Architecture Processor Supplement, Fourth Edition, 1997-03-19,
41 1.48 riastrad * The Santa Cruz Operation, Inc.
42 1.48 riastrad * https://www.sco.com/developers/devspecs/abi386-4.pdf
43 1.48 riastrad * https://web.archive.org/web/20250329184450/https://www.sco.com/developers/devspecs/abi386-4.pdf
44 1.48 riastrad *
45 1.48 riastrad * Note: Intel and SuSE have published an update to the i386 ELF
46 1.48 riastrad * supplement, but it is not entirely compatible (e.g., it requires
47 1.48 riastrad * 16-byte alignment for the stack pointer, not just 4-byte alignment),
48 1.48 riastrad * so it is not reliable as a normative reference:
49 1.48 riastrad *
50 1.48 riastrad * [ABI386-2015] System V Application Binary Interface: Intel386
51 1.48 riastrad * Architecture Processor Supplement, Version 1.0, 2015-02-03.
52 1.48 riastrad * https://uclibc.org/docs/psABI-i386.pdf
53 1.48 riastrad * https://web.archive.org/web/20250118211449/https://uclibc.org/docs/psABI-i386.pdf
54 1.48 riastrad * https://gitlab.com/x86-psABIs/i386-ABI
55 1.48 riastrad */
56 1.21 skrll
57 1.21 skrll #include <sys/cdefs.h>
58 1.21 skrll #ifndef lint
59 1.48 riastrad __RCSID("$NetBSD: mdreloc.c,v 1.48 2025/04/16 17:37:48 riastradh Exp $");
60 1.21 skrll #endif /* not lint */
61 1.11 junyoung
62 1.1 mycroft #include <sys/types.h>
63 1.47 christos #include <machine/lwp_private.h>
64 1.1 mycroft
65 1.1 mycroft #include "debug.h"
66 1.1 mycroft #include "rtld.h"
67 1.1 mycroft
68 1.13 mycroft void _rtld_bind_start(void);
69 1.12 mycroft void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
70 1.19 skrll caddr_t _rtld_bind(const Obj_Entry *, Elf_Word);
71 1.12 mycroft
72 1.43 martin #define rdbg_symname(obj, rela) \
73 1.43 martin ((obj)->strtab + (obj)->symtab[ELF_R_SYM((rela)->r_info)].st_name)
74 1.43 martin
75 1.1 mycroft void
76 1.1 mycroft _rtld_setup_pltgot(const Obj_Entry *obj)
77 1.1 mycroft {
78 1.1 mycroft obj->pltgot[1] = (Elf_Addr) obj;
79 1.1 mycroft obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
80 1.1 mycroft }
81 1.2 mycroft
82 1.12 mycroft void
83 1.19 skrll _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
84 1.12 mycroft {
85 1.12 mycroft const Elf_Rel *rel = 0, *rellim;
86 1.12 mycroft Elf_Addr relsz = 0;
87 1.12 mycroft Elf_Addr *where;
88 1.12 mycroft
89 1.12 mycroft for (; dynp->d_tag != DT_NULL; dynp++) {
90 1.12 mycroft switch (dynp->d_tag) {
91 1.12 mycroft case DT_REL:
92 1.12 mycroft rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
93 1.12 mycroft break;
94 1.12 mycroft case DT_RELSZ:
95 1.12 mycroft relsz = dynp->d_un.d_val;
96 1.12 mycroft break;
97 1.12 mycroft }
98 1.12 mycroft }
99 1.22 christos if (rel == 0 || relsz == 0)
100 1.22 christos return;
101 1.27 lukem rellim = (const Elf_Rel *)((const uint8_t *)rel + relsz);
102 1.12 mycroft for (; rel < rellim; rel++) {
103 1.12 mycroft where = (Elf_Addr *)(relocbase + rel->r_offset);
104 1.12 mycroft *where += (Elf_Addr)relocbase;
105 1.12 mycroft }
106 1.12 mycroft }
107 1.12 mycroft
108 1.2 mycroft int
109 1.32 joerg _rtld_relocate_nonplt_objects(Obj_Entry *obj)
110 1.2 mycroft {
111 1.3 mycroft const Elf_Rel *rel;
112 1.20 lukem Elf_Addr target = 0;
113 1.38 joerg const Elf_Sym *def = NULL;
114 1.38 joerg const Obj_Entry *defobj = NULL;
115 1.38 joerg unsigned long last_symnum = ULONG_MAX;
116 1.12 mycroft
117 1.3 mycroft for (rel = obj->rel; rel < obj->rellim; rel++) {
118 1.3 mycroft Elf_Addr *where;
119 1.3 mycroft Elf_Addr tmp;
120 1.4 mycroft unsigned long symnum;
121 1.3 mycroft
122 1.3 mycroft where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
123 1.38 joerg
124 1.38 joerg switch (ELF_R_TYPE(rel->r_info)) {
125 1.38 joerg case R_TYPE(PC32):
126 1.38 joerg case R_TYPE(GOT32):
127 1.38 joerg case R_TYPE(32):
128 1.38 joerg case R_TYPE(GLOB_DAT):
129 1.38 joerg case R_TYPE(TLS_TPOFF):
130 1.38 joerg case R_TYPE(TLS_TPOFF32):
131 1.38 joerg case R_TYPE(TLS_DTPMOD32):
132 1.38 joerg case R_TYPE(TLS_DTPOFF32):
133 1.38 joerg symnum = ELF_R_SYM(rel->r_info);
134 1.38 joerg if (symnum != last_symnum) {
135 1.38 joerg last_symnum = symnum;
136 1.38 joerg def = _rtld_find_symdef(symnum, obj, &defobj,
137 1.38 joerg false);
138 1.38 joerg if (def == NULL)
139 1.38 joerg return -1;
140 1.38 joerg }
141 1.38 joerg break;
142 1.38 joerg default:
143 1.38 joerg break;
144 1.38 joerg }
145 1.38 joerg
146 1.3 mycroft
147 1.3 mycroft switch (ELF_R_TYPE(rel->r_info)) {
148 1.3 mycroft case R_TYPE(NONE):
149 1.3 mycroft break;
150 1.2 mycroft
151 1.2 mycroft #if 1 /* XXX should not occur */
152 1.3 mycroft case R_TYPE(PC32):
153 1.23 matt target = (Elf_Addr)(defobj->relocbase + def->st_value);
154 1.3 mycroft
155 1.4 mycroft *where += target - (Elf_Addr)where;
156 1.14 mycroft rdbg(("PC32 %s in %s --> %p in %s",
157 1.43 martin rdbg_symname(obj, rel),
158 1.5 mycroft obj->path, (void *)*where, defobj->path));
159 1.3 mycroft break;
160 1.2 mycroft
161 1.3 mycroft case R_TYPE(GOT32):
162 1.2 mycroft #endif
163 1.3 mycroft case R_TYPE(32):
164 1.3 mycroft case R_TYPE(GLOB_DAT):
165 1.23 matt target = (Elf_Addr)(defobj->relocbase + def->st_value);
166 1.3 mycroft
167 1.4 mycroft tmp = target + *where;
168 1.3 mycroft if (*where != tmp)
169 1.3 mycroft *where = tmp;
170 1.14 mycroft rdbg(("32/GLOB_DAT %s in %s --> %p in %s",
171 1.43 martin rdbg_symname(obj, rel),
172 1.5 mycroft obj->path, (void *)*where, defobj->path));
173 1.3 mycroft break;
174 1.3 mycroft
175 1.41 joerg
176 1.41 joerg case R_TYPE(IRELATIVE):
177 1.41 joerg /* IFUNC relocations are handled in _rtld_call_ifunc */
178 1.41 joerg if (obj->ifunc_remaining_nonplt == 0) {
179 1.41 joerg obj->ifunc_remaining_nonplt =
180 1.41 joerg obj->rellim - rel;
181 1.41 joerg }
182 1.41 joerg /* FALL-THROUGH */
183 1.41 joerg
184 1.3 mycroft case R_TYPE(RELATIVE):
185 1.12 mycroft *where += (Elf_Addr)obj->relocbase;
186 1.14 mycroft rdbg(("RELATIVE in %s --> %p", obj->path,
187 1.12 mycroft (void *)*where));
188 1.3 mycroft break;
189 1.3 mycroft
190 1.3 mycroft case R_TYPE(COPY):
191 1.3 mycroft /*
192 1.3 mycroft * These are deferred until all other relocations have
193 1.3 mycroft * been done. All we do here is make sure that the
194 1.3 mycroft * COPY relocation is not in a shared library. They
195 1.3 mycroft * are allowed only in executable files.
196 1.3 mycroft */
197 1.8 mycroft if (obj->isdynamic) {
198 1.3 mycroft _rtld_error(
199 1.2 mycroft "%s: Unexpected R_COPY relocation in shared library",
200 1.3 mycroft obj->path);
201 1.3 mycroft return -1;
202 1.3 mycroft }
203 1.14 mycroft rdbg(("COPY (avoid in main)"));
204 1.3 mycroft break;
205 1.3 mycroft
206 1.33 joerg case R_TYPE(TLS_TPOFF):
207 1.42 joerg if (!defobj->tls_static &&
208 1.42 joerg _rtld_tls_offset_allocate(__UNCONST(defobj)))
209 1.33 joerg return -1;
210 1.33 joerg
211 1.35 apb *where += (Elf_Addr)(def->st_value - defobj->tlsoffset);
212 1.33 joerg
213 1.33 joerg rdbg(("TLS_TPOFF %s in %s --> %p",
214 1.43 martin rdbg_symname(obj, rel),
215 1.33 joerg obj->path, (void *)*where));
216 1.33 joerg break;
217 1.33 joerg
218 1.35 apb case R_TYPE(TLS_TPOFF32):
219 1.42 joerg if (!defobj->tls_static &&
220 1.42 joerg _rtld_tls_offset_allocate(__UNCONST(defobj)))
221 1.35 apb return -1;
222 1.35 apb
223 1.35 apb *where += (Elf_Addr)(defobj->tlsoffset - def->st_value);
224 1.35 apb rdbg(("TLS_TPOFF32 %s in %s --> %p",
225 1.43 martin rdbg_symname(obj, rel),
226 1.35 apb obj->path, (void *)*where));
227 1.35 apb break;
228 1.35 apb
229 1.33 joerg case R_TYPE(TLS_DTPMOD32):
230 1.33 joerg *where = (Elf_Addr)(defobj->tlsindex);
231 1.33 joerg
232 1.33 joerg rdbg(("TLS_DTPMOD32 %s in %s --> %p",
233 1.43 martin rdbg_symname(obj, rel),
234 1.33 joerg obj->path, (void *)*where));
235 1.33 joerg break;
236 1.33 joerg
237 1.33 joerg case R_TYPE(TLS_DTPOFF32):
238 1.33 joerg *where = (Elf_Addr)(def->st_value);
239 1.33 joerg
240 1.33 joerg rdbg(("TLS_DTPOFF32 %s in %s --> %p",
241 1.43 martin rdbg_symname(obj, rel),
242 1.33 joerg obj->path, (void *)*where));
243 1.33 joerg
244 1.33 joerg break;
245 1.33 joerg
246 1.3 mycroft default:
247 1.14 mycroft rdbg(("sym = %lu, type = %lu, offset = %p, "
248 1.3 mycroft "contents = %p, symbol = %s",
249 1.38 joerg (u_long)ELF_R_SYM(rel->r_info),
250 1.38 joerg (u_long)ELF_R_TYPE(rel->r_info),
251 1.3 mycroft (void *)rel->r_offset, (void *)*where,
252 1.43 martin rdbg_symname(obj, rel)));
253 1.3 mycroft _rtld_error("%s: Unsupported relocation type %ld "
254 1.28 jmmv "in non-PLT relocations",
255 1.3 mycroft obj->path, (u_long) ELF_R_TYPE(rel->r_info));
256 1.2 mycroft return -1;
257 1.2 mycroft }
258 1.2 mycroft }
259 1.6 mycroft return 0;
260 1.6 mycroft }
261 1.6 mycroft
262 1.6 mycroft int
263 1.39 joerg _rtld_relocate_plt_lazy(Obj_Entry *obj)
264 1.6 mycroft {
265 1.6 mycroft const Elf_Rel *rel;
266 1.6 mycroft
267 1.39 joerg for (rel = obj->pltrellim; rel-- > obj->pltrel; ) {
268 1.6 mycroft Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
269 1.6 mycroft
270 1.39 joerg assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JMP_SLOT) ||
271 1.39 joerg ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE));
272 1.39 joerg
273 1.39 joerg if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE))
274 1.39 joerg obj->ifunc_remaining = obj->pltrellim - rel;
275 1.6 mycroft
276 1.6 mycroft /* Just relocate the GOT slots pointing into the PLT */
277 1.6 mycroft *where += (Elf_Addr)obj->relocbase;
278 1.14 mycroft rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where));
279 1.6 mycroft }
280 1.6 mycroft
281 1.6 mycroft return 0;
282 1.6 mycroft }
283 1.6 mycroft
284 1.26 matt static inline int
285 1.26 matt _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel,
286 1.26 matt Elf_Addr *tp)
287 1.6 mycroft {
288 1.16 mycroft Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
289 1.26 matt Elf_Addr target;
290 1.26 matt const Elf_Sym *def;
291 1.6 mycroft const Obj_Entry *defobj;
292 1.29 christos unsigned long info = rel->r_info;
293 1.6 mycroft
294 1.39 joerg if (ELF_R_TYPE(info) == R_TYPE(IRELATIVE))
295 1.39 joerg return 0;
296 1.39 joerg
297 1.29 christos assert(ELF_R_TYPE(info) == R_TYPE(JMP_SLOT));
298 1.6 mycroft
299 1.29 christos def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL);
300 1.29 christos if (__predict_false(def == NULL))
301 1.26 matt return -1;
302 1.29 christos if (__predict_false(def == &_rtld_sym_zero))
303 1.29 christos return 0;
304 1.29 christos
305 1.36 joerg if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
306 1.36 joerg if (tp == NULL)
307 1.36 joerg return 0;
308 1.36 joerg target = _rtld_resolve_ifunc(defobj, def);
309 1.36 joerg } else {
310 1.36 joerg target = (Elf_Addr)(defobj->relocbase + def->st_value);
311 1.36 joerg }
312 1.36 joerg
313 1.26 matt rdbg(("bind now/fixup in %s --> old=%p new=%p",
314 1.44 riastrad defobj->strtab + def->st_name, (void *)*where,
315 1.26 matt (void *)target));
316 1.26 matt if (*where != target)
317 1.26 matt *where = target;
318 1.26 matt if (tp)
319 1.26 matt *tp = target;
320 1.26 matt return 0;
321 1.26 matt }
322 1.26 matt
323 1.26 matt caddr_t
324 1.26 matt _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
325 1.26 matt {
326 1.31 skrll const Elf_Rel *rel = (const Elf_Rel *)((const uint8_t *)obj->pltrel
327 1.31 skrll + reloff);
328 1.26 matt Elf_Addr new_value;
329 1.26 matt int err;
330 1.26 matt
331 1.30 skrll new_value = 0; /* XXX gcc */
332 1.30 skrll
333 1.34 joerg _rtld_shared_enter();
334 1.26 matt err = _rtld_relocate_plt_object(obj, rel, &new_value);
335 1.29 christos if (err)
336 1.16 mycroft _rtld_die();
337 1.34 joerg _rtld_shared_exit();
338 1.6 mycroft
339 1.16 mycroft return (caddr_t)new_value;
340 1.15 junyoung }
341 1.15 junyoung
342 1.15 junyoung int
343 1.15 junyoung _rtld_relocate_plt_objects(const Obj_Entry *obj)
344 1.15 junyoung {
345 1.15 junyoung const Elf_Rel *rel;
346 1.26 matt int err = 0;
347 1.44 riastrad
348 1.15 junyoung for (rel = obj->pltrel; rel < obj->pltrellim; rel++) {
349 1.26 matt err = _rtld_relocate_plt_object(obj, rel, NULL);
350 1.26 matt if (err)
351 1.26 matt break;
352 1.15 junyoung }
353 1.26 matt return err;
354 1.2 mycroft }
355 1.33 joerg
356 1.33 joerg /*
357 1.33 joerg * i386 specific GNU variant of __tls_get_addr using register based
358 1.33 joerg * argument passing.
359 1.33 joerg */
360 1.33 joerg #define DTV_MAX_INDEX(dtv) ((size_t)((dtv)[-1]))
361 1.33 joerg
362 1.33 joerg __dso_public __attribute__((__regparm__(1))) void *
363 1.33 joerg ___tls_get_addr(void *arg_)
364 1.33 joerg {
365 1.33 joerg size_t *arg = (size_t *)arg_;
366 1.33 joerg void **dtv;
367 1.33 joerg struct tls_tcb *tcb = __lwp_getprivate_fast();
368 1.33 joerg size_t idx = arg[0], offset = arg[1];
369 1.33 joerg
370 1.33 joerg dtv = tcb->tcb_dtv;
371 1.33 joerg
372 1.33 joerg if (__predict_true(idx < DTV_MAX_INDEX(dtv) && dtv[idx] != NULL))
373 1.33 joerg return (uint8_t *)dtv[idx] + offset;
374 1.33 joerg
375 1.33 joerg return _rtld_tls_get_addr(tcb, idx, offset);
376 1.33 joerg }
377