mdreloc.c revision 1.48 1 /* $NetBSD: mdreloc.c,v 1.48 2024/08/03 21:59:58 riastradh Exp $ */
2
3 #include <sys/cdefs.h>
4 #ifndef lint
5 __RCSID("$NetBSD: mdreloc.c,v 1.48 2024/08/03 21:59:58 riastradh Exp $");
6 #endif /* not lint */
7
8 /*
9 * Arm (32-bit) ELF relocations.
10 *
11 * Reference:
12 *
13 * [AAELF32] ELF for the Arm Architecture, 2022Q3. Arm Ltd.
14 * https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aaelf32/aaelf32.rst
15 */
16
17 #include <sys/types.h>
18 #include <string.h>
19
20 #include "debug.h"
21 #include "rtld.h"
22
23 void _rtld_bind_start(void);
24 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
25 caddr_t _rtld_bind(const Obj_Entry *, Elf_Word);
26
27 void
28 _rtld_setup_pltgot(const Obj_Entry *obj)
29 {
30 obj->pltgot[1] = (Elf_Addr) obj;
31 obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
32 }
33
34 void
35 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
36 {
37 const Elf_Rel *rel = 0, *rellim;
38 Elf_Addr relsz = 0;
39 Elf_Addr *where;
40
41 for (; dynp->d_tag != DT_NULL; dynp++) {
42 switch (dynp->d_tag) {
43 case DT_REL:
44 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
45 break;
46 case DT_RELSZ:
47 relsz = dynp->d_un.d_val;
48 break;
49 }
50 }
51 rellim = (const Elf_Rel *)((const uint8_t *)rel + relsz);
52 for (; rel < rellim; rel++) {
53 where = (Elf_Addr *)(relocbase + rel->r_offset);
54 *where += (Elf_Addr)relocbase;
55 }
56 }
57
58 /*
59 * It is possible for the compiler to emit relocations for unaligned data.
60 * We handle this situation with these inlines.
61 */
62 #define RELOC_ALIGNED_P(x) \
63 (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
64
65 static inline Elf_Addr
66 load_ptr(void *where)
67 {
68 Elf_Addr res;
69
70 memcpy(&res, where, sizeof(res));
71
72 return (res);
73 }
74
75 static inline void
76 store_ptr(void *where, Elf_Addr val)
77 {
78
79 memcpy(where, &val, sizeof(val));
80 }
81
82 int
83 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
84 {
85 const Elf_Rel *rel;
86 const Elf_Sym *def = NULL;
87 const Obj_Entry *defobj = NULL;
88 unsigned long last_symnum = ULONG_MAX;
89
90 for (rel = obj->rel; rel < obj->rellim; rel++) {
91 Elf_Addr *where;
92 Elf_Addr tmp;
93 unsigned long symnum;
94
95 where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
96
97 switch (ELF_R_TYPE(rel->r_info)) {
98 case R_TYPE(PC24): /* word32 S - P + A */
99 case R_TYPE(ABS32): /* word32 B + S + A */
100 case R_TYPE(GLOB_DAT): /* word32 B + S */
101 case R_TYPE(TLS_DTPOFF32):
102 case R_TYPE(TLS_DTPMOD32):
103 case R_TYPE(TLS_TPOFF32):
104 symnum = ELF_R_SYM(rel->r_info);
105 if (last_symnum != symnum) {
106 last_symnum = symnum;
107 def = _rtld_find_symdef(symnum, obj, &defobj,
108 false);
109 if (def == NULL)
110 return -1;
111 }
112 break;
113
114 default:
115 break;
116 }
117
118 switch (ELF_R_TYPE(rel->r_info)) {
119 case R_TYPE(NONE):
120 break;
121
122 #if 1 /* XXX should not occur */
123 case R_TYPE(PC24): { /* word32 S - P + A */
124 Elf32_Sword addend;
125
126 /*
127 * Extract addend and sign-extend if needed.
128 */
129 addend = *where;
130 if (addend & 0x00800000)
131 addend |= 0xff000000;
132 tmp = (Elf_Addr)obj->relocbase + def->st_value
133 - (Elf_Addr)where + (addend << 2);
134 if ((tmp & 0xfe000000) != 0xfe000000 &&
135 (tmp & 0xfe000000) != 0) {
136 _rtld_error(
137 "%s: R_ARM_PC24 relocation @ %p to %s failed "
138 "(displacement %ld (%#lx) out of range)",
139 obj->path, where,
140 obj->strtab + obj->symtab[
141 ELF_R_SYM(rel->r_info)].st_name,
142 (long) tmp, (long) tmp);
143 return -1;
144 }
145 tmp >>= 2;
146 *where = (*where & 0xff000000) | (tmp & 0x00ffffff);
147 rdbg(("PC24 %s in %s --> %p @ %p in %s",
148 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)]
149 .st_name, obj->path, (void *)*where, where,
150 defobj->path));
151 break;
152 }
153 #endif
154
155 case R_TYPE(ABS32): /* word32 B + S + A */
156 case R_TYPE(GLOB_DAT): /* word32 B + S */
157 if (__predict_true(RELOC_ALIGNED_P(where))) {
158 tmp = *where + (Elf_Addr)defobj->relocbase +
159 def->st_value;
160 /* Set the Thumb bit, if needed. */
161 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC)
162 tmp |= 1;
163 *where = tmp;
164 } else {
165 tmp = load_ptr(where) +
166 (Elf_Addr)defobj->relocbase +
167 def->st_value;
168 /* Set the Thumb bit, if needed. */
169 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC)
170 tmp |= 1;
171 store_ptr(where, tmp);
172 }
173 rdbg(("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
174 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)]
175 .st_name, obj->path, (void *)tmp, where,
176 defobj->path));
177 break;
178
179 case R_TYPE(IRELATIVE):
180 /* IFUNC relocations are handled in _rtld_call_ifunc */
181 if (obj->ifunc_remaining_nonplt == 0)
182 obj->ifunc_remaining_nonplt = obj->rellim - rel;
183 /* FALL-THROUGH */
184
185 case R_TYPE(RELATIVE): /* word32 B + A */
186 if (__predict_true(RELOC_ALIGNED_P(where))) {
187 tmp = *where + (Elf_Addr)obj->relocbase;
188 *where = tmp;
189 } else {
190 tmp = load_ptr(where) +
191 (Elf_Addr)obj->relocbase;
192 store_ptr(where, tmp);
193 }
194 rdbg(("RELATIVE in %s --> %p", obj->path,
195 (void *)tmp));
196 break;
197
198 case R_TYPE(COPY):
199 /*
200 * These are deferred until all other relocations have
201 * been done. All we do here is make sure that the
202 * COPY relocation is not in a shared library. They
203 * are allowed only in executable files.
204 */
205 if (obj->isdynamic) {
206 _rtld_error(
207 "%s: Unexpected R_COPY relocation in shared library",
208 obj->path);
209 return -1;
210 }
211 rdbg(("COPY (avoid in main)"));
212 break;
213
214 case R_TYPE(TLS_DTPOFF32):
215 tmp = (Elf_Addr)(def->st_value);
216 if (__predict_true(RELOC_ALIGNED_P(where)))
217 *where = tmp;
218 else
219 store_ptr(where, tmp);
220
221 rdbg(("TLS_DTPOFF32 %s in %s --> %p",
222 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)]
223 .st_name, obj->path, (void *)tmp));
224
225 break;
226 case R_TYPE(TLS_DTPMOD32):
227 tmp = (Elf_Addr)(defobj->tlsindex);
228 if (__predict_true(RELOC_ALIGNED_P(where)))
229 *where = tmp;
230 else
231 store_ptr(where, tmp);
232
233 rdbg(("TLS_DTPMOD32 %s in %s --> %p",
234 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)]
235 .st_name, obj->path, (void *)tmp));
236
237 break;
238
239 case R_TYPE(TLS_TPOFF32):
240 if (!defobj->tls_static &&
241 _rtld_tls_offset_allocate(__UNCONST(defobj)))
242 return -1;
243
244 if (__predict_true(RELOC_ALIGNED_P(where)))
245 tmp = *where;
246 else
247 tmp = load_ptr(where);
248 tmp += (Elf_Addr)def->st_value + defobj->tlsoffset + sizeof(struct tls_tcb);
249 if (__predict_true(RELOC_ALIGNED_P(where)))
250 *where = tmp;
251 else
252 store_ptr(where, tmp);
253 rdbg(("TLS_TPOFF32 %s in %s --> %p",
254 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)]
255 .st_name, obj->path, (void *)tmp));
256 break;
257
258 default:
259 rdbg(("sym = %lu, type = %lu, offset = %p, "
260 "contents = %p",
261 (u_long)ELF_R_SYM(rel->r_info),
262 (u_long)ELF_R_TYPE(rel->r_info),
263 (void *)rel->r_offset, (void *)load_ptr(where)));
264 _rtld_error("%s: Unsupported relocation type %ld "
265 "in non-PLT relocations",
266 obj->path, (u_long) ELF_R_TYPE(rel->r_info));
267 return -1;
268 }
269 }
270 return 0;
271 }
272
273 int
274 _rtld_relocate_plt_lazy(Obj_Entry *obj)
275 {
276 const Elf_Rel *rel;
277
278 for (rel = obj->pltrellim; rel-- > obj->pltrel; ) {
279 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
280
281 assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT) ||
282 ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE));
283
284 if (ELF_R_TYPE(rel->r_info) == R_TYPE(IRELATIVE))
285 obj->ifunc_remaining = obj->pltrellim - rel;
286
287 /* Just relocate the GOT slots pointing into the PLT */
288 *where += (Elf_Addr)obj->relocbase;
289 rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where));
290 }
291
292 return 0;
293 }
294
295 static int
296 _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel,
297 Elf_Addr *tp)
298 {
299 Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
300 Elf_Addr new_value;
301 const Elf_Sym *def;
302 const Obj_Entry *defobj;
303 unsigned long info = rel->r_info;
304
305 assert(ELF_R_TYPE(info) == R_TYPE(JUMP_SLOT));
306
307 def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL);
308 if (__predict_false(def == NULL))
309 return -1;
310 if (__predict_false(def == &_rtld_sym_zero))
311 return 0;
312
313 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
314 if (tp == NULL)
315 return 0;
316 new_value = _rtld_resolve_ifunc(defobj, def);
317 } else {
318 new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
319 }
320 /* Set the Thumb bit, if needed. */
321 if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC)
322 new_value |= 1;
323 rdbg(("bind now/fixup in %s --> old=%p new=%p",
324 defobj->strtab + def->st_name, (void *)*where, (void *)new_value));
325 if (*where != new_value)
326 *where = new_value;
327 if (tp)
328 *tp = new_value;
329
330 return 0;
331 }
332
333 caddr_t
334 _rtld_bind(const Obj_Entry *obj, Elf_Word reloff)
335 {
336 const Elf_Rel *rel = (const Elf_Rel *)((const uint8_t *)obj->pltrel + reloff);
337 Elf_Addr new_value = 0; /* XXX gcc */
338 int err;
339
340 _rtld_shared_enter();
341 err = _rtld_relocate_plt_object(obj, rel, &new_value);
342 if (err)
343 _rtld_die();
344 _rtld_shared_exit();
345
346 return (caddr_t)new_value;
347 }
348 int
349 _rtld_relocate_plt_objects(const Obj_Entry *obj)
350 {
351 const Elf_Rel *rel;
352 int err = 0;
353
354 for (rel = obj->pltrel; rel < obj->pltrellim; rel++) {
355 err = _rtld_relocate_plt_object(obj, rel, NULL);
356 if (err)
357 break;
358 }
359
360 return err;
361 }
362