mips_reloc.c revision 1.77 1 /* $NetBSD: mips_reloc.c,v 1.77 2024/08/06 20:26:45 riastradh Exp $ */
2
3 /*
4 * Copyright 1997 Michael L. Hitch <mhitch (at) montana.edu>
5 * Portions copyright 2002 Charles M. Hannum <root (at) ihack.net>
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. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32 * MIPS ELF relocations, for all ABIs: o32, n32, n64.
33 *
34 * References:
35 *
36 * [SYSVMIPS32ABI] System V Application Binary Interface: MIPS
37 * RISC Processor Supplement, 3rd ed., 1996.
38 * https://www.sco.com/developers/devspecs/mipsabi.pdf
39 * https://web.archive.org/web/20221026055746/http://www.sco.com/developers/devspecs/mipsabi.pdf
40 *
41 * [SGIMIPS64ELF] 64-bit ELF Object File Specification, Draft
42 * Version 2.5, MIPS Technologies / Silicon Graphics Computer
43 * Systems. Archived 2015-08-18.
44 * https://web.archive.org/web/20150818210955/http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
45 */
46
47 #include <sys/cdefs.h>
48 #ifndef lint
49 __RCSID("$NetBSD: mips_reloc.c,v 1.77 2024/08/06 20:26:45 riastradh Exp $");
50 #endif /* not lint */
51
52 #include <sys/types.h>
53 #include <sys/endian.h>
54 #include <sys/tls.h>
55
56 #include <stdlib.h>
57 #include <string.h>
58
59 #include "debug.h"
60 #include "rtld.h"
61
62 #ifdef __mips_o32
63 #define SUPPORT_OLD_BROKEN_LD
64 #endif
65
66 void _rtld_bind_start(void);
67 void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
68 caddr_t _rtld_bind(Elf_Word, Elf_Addr, Elf_Addr, Elf_Addr);
69
70 /*
71 * It is possible for the compiler to emit relocations for unaligned data.
72 * We handle this situation with these inlines.
73 */
74
75 #if ELFSIZE == 64
76 /*
77 * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain
78 * the symbol index. The top 32-bits contain three relocation types encoded
79 * in big-endian integer with first relocation in LSB. This means for little
80 * endian we have to byte swap that integer (r_type).
81 */
82 #define Elf_Sxword Elf64_Sxword
83 #define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64))
84 #if BYTE_ORDER == LITTLE_ENDIAN
85 #undef ELF_R_SYM
86 #undef ELF_R_TYPE
87 #define ELF_R_SYM(r_info) ((r_info) & 0xffffffff)
88 #define ELF_R_TYPE(r_info) bswap32((r_info) >> 32)
89 #endif
90 #else
91 #define ELF_R_NXTTYPE_64_P(r_type) (0)
92 #define Elf_Sxword Elf32_Sword
93 #endif
94 #define GOT1_MASK (~(Elf_Addr)0 >> 1)
95
96 static inline Elf_Sxword
97 load_ptr(void *where, size_t len)
98 {
99 Elf_Sxword val;
100
101 if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) {
102 #if ELFSIZE == 64
103 if (len == sizeof(Elf_Sxword))
104 return *(Elf_Sxword *)where;
105 #endif
106 return *(Elf_Sword *)where;
107 }
108
109 val = 0;
110 #if BYTE_ORDER == LITTLE_ENDIAN
111 (void)memcpy(&val, where, len);
112 #endif
113 #if BYTE_ORDER == BIG_ENDIAN
114 uint8_t *valp = (void *)&val;
115 (void)memcpy(valp + sizeof(val) - len, where, len);
116 #endif
117 return (len == sizeof(Elf_Sxword)) ? val : (Elf_Sword)val;
118 }
119
120 static inline void
121 store_ptr(void *where, Elf_Sxword val, size_t len)
122 {
123 if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) {
124 #if ELFSIZE == 64
125 if (len == sizeof(Elf_Sxword)) {
126 *(Elf_Sxword *)where = val;
127 return;
128 }
129 #endif
130 *(Elf_Sword *)where = val;
131 return;
132 }
133 #if BYTE_ORDER == LITTLE_ENDIAN
134 (void)memcpy(where, &val, len);
135 #endif
136 #if BYTE_ORDER == BIG_ENDIAN
137 const uint8_t *valp = (const void *)&val;
138 (void)memcpy(where, valp + sizeof(val) - len, len);
139 #endif
140 }
141
142
143 void
144 _rtld_setup_pltgot(const Obj_Entry *obj)
145 {
146 obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
147 /* XXX only if obj->pltgot[1] & 0x80000000 ?? */
148 obj->pltgot[1] = (Elf_Addr) obj;
149 }
150
151 void
152 _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
153 {
154 const Elf_Rel *rel = 0, *rellim;
155 Elf_Addr relsz = 0;
156 void *where;
157 const Elf_Sym *symtab = NULL, *sym;
158 Elf_Addr *got = NULL;
159 Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0;
160 size_t i;
161
162 for (; dynp->d_tag != DT_NULL; dynp++) {
163 switch (dynp->d_tag) {
164 case DT_REL:
165 rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
166 break;
167 case DT_RELSZ:
168 relsz = dynp->d_un.d_val;
169 break;
170 case DT_SYMTAB:
171 symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr);
172 break;
173 case DT_PLTGOT:
174 got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr);
175 break;
176 case DT_MIPS_LOCAL_GOTNO:
177 local_gotno = dynp->d_un.d_val;
178 break;
179 case DT_MIPS_SYMTABNO:
180 symtabno = dynp->d_un.d_val;
181 break;
182 case DT_MIPS_GOTSYM:
183 gotsym = dynp->d_un.d_val;
184 break;
185 }
186 }
187
188 i = (got[1] & 0x80000000) ? 2 : 1;
189 /* Relocate the local GOT entries */
190 got += i;
191 for (; i < local_gotno; i++)
192 *got++ += relocbase;
193 sym = symtab + gotsym;
194 /* Now do the global GOT entries */
195 for (i = gotsym; i < symtabno; i++) {
196 *got = sym->st_value + relocbase;
197 ++sym;
198 ++got;
199 }
200
201 rellim = (const Elf_Rel *)((uintptr_t)rel + relsz);
202 for (; rel < rellim; rel++) {
203 Elf_Word r_symndx, r_type;
204
205 where = (void *)(relocbase + rel->r_offset);
206
207 r_symndx = ELF_R_SYM(rel->r_info);
208 r_type = ELF_R_TYPE(rel->r_info);
209
210 switch (r_type & 0xff) {
211 case R_TYPE(REL32): {
212 const size_t rlen =
213 ELF_R_NXTTYPE_64_P(r_type)
214 ? sizeof(Elf_Sxword)
215 : sizeof(Elf_Sword);
216 Elf_Sxword old = load_ptr(where, rlen);
217 Elf_Sxword val = old;
218 #if ELFSIZE == 64
219 assert(r_type == R_TYPE(REL32)
220 || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8)));
221 #endif
222 assert(r_symndx < gotsym);
223 sym = symtab + r_symndx;
224 assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL);
225 val += relocbase;
226 store_ptr(where, val, sizeof(Elf_Sword));
227 rdbg(("REL32/L(%p) %p -> %p in <self>",
228 where, (void *)old, (void *)val));
229 store_ptr(where, val, rlen);
230 break;
231 }
232
233 case R_TYPE(GPREL32):
234 case R_TYPE(NONE):
235 break;
236
237
238 default:
239 abort();
240 }
241 }
242 }
243
244 int
245 _rtld_relocate_nonplt_objects(Obj_Entry *obj)
246 {
247 const Elf_Rel *rel;
248 Elf_Addr *got = obj->pltgot;
249 const Elf_Sym *sym, *def = NULL;
250 const Obj_Entry *defobj = NULL;
251 unsigned long last_symnum = ULONG_MAX;
252 Elf_Word i;
253 #ifdef SUPPORT_OLD_BROKEN_LD
254 int broken;
255 #endif
256
257 #ifdef SUPPORT_OLD_BROKEN_LD
258 broken = 0;
259 sym = obj->symtab;
260 for (i = 1; i < 12; i++)
261 if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE))
262 broken = 1;
263 dbg(("%s: broken=%d", obj->path, broken));
264 #endif
265
266 i = (got[1] & 0x80000000) ? 2 : 1;
267 /* Relocate the local GOT entries */
268 got += i;
269 for (; i < obj->local_gotno; i++)
270 *got++ += (Elf_Addr)obj->relocbase;
271 sym = obj->symtab + obj->gotsym;
272 /* Now do the global GOT entries */
273 for (i = obj->gotsym; i < obj->symtabno; i++) {
274 rdbg((" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym,
275 sym->st_name + obj->strtab, (u_long) *got));
276
277 #ifdef SUPPORT_OLD_BROKEN_LD
278 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
279 broken && sym->st_shndx == SHN_UNDEF) {
280 /*
281 * XXX DANGER WILL ROBINSON!
282 * You might think this is stupid, as it intentionally
283 * defeats lazy binding -- and you'd be right.
284 * Unfortunately, for lazy binding to work right, we
285 * need to a way to force the GOT slots used for
286 * function pointers to be resolved immediately. This
287 * is supposed to be done automatically by the linker,
288 * by not outputting a PLT slot and setting st_value
289 * to 0 if there are non-PLT references, but older
290 * versions of GNU ld do not do this.
291 */
292 def = _rtld_find_symdef(i, obj, &defobj, false);
293 if (def == NULL)
294 return -1;
295 *got = def->st_value + (Elf_Addr)defobj->relocbase;
296 } else
297 #endif
298 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
299 sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) {
300 /*
301 * If there are non-PLT references to the function,
302 * st_value should be 0, forcing us to resolve the
303 * address immediately.
304 *
305 * XXX DANGER WILL ROBINSON!
306 * The linker is not outputting PLT slots for calls to
307 * functions that are defined in the same shared
308 * library. This is a bug, because it can screw up
309 * link ordering rules if the symbol is defined in
310 * more than one module. For now, if there is a
311 * definition, we fail the test above and force a full
312 * symbol lookup. This means that all intra-module
313 * calls are bound immediately. - mycroft, 2003/09/24
314 */
315 *got = sym->st_value + (Elf_Addr)obj->relocbase;
316 } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) {
317 /* Symbols with index SHN_ABS are not relocated. */
318 if (sym->st_shndx != SHN_ABS)
319 *got = sym->st_value +
320 (Elf_Addr)obj->relocbase;
321 } else {
322 def = _rtld_find_symdef(i, obj, &defobj, false);
323 if (def == NULL)
324 return -1;
325 *got = def->st_value + (Elf_Addr)defobj->relocbase;
326 }
327
328 rdbg((" --> now %lx", (u_long) *got));
329 ++sym;
330 ++got;
331 }
332
333 got = obj->pltgot;
334 for (rel = obj->rel; rel < obj->rellim; rel++) {
335 unsigned long symnum;
336 void *where;
337
338 where = obj->relocbase + rel->r_offset;
339
340 switch (ELF_R_TYPE(rel->r_info) & 0xff) {
341 #if ELFSIZE == 64
342 case R_TYPE(TLS_DTPMOD64):
343 case R_TYPE(TLS_DTPREL64):
344 case R_TYPE(TLS_TPREL64):
345 #else
346 case R_TYPE(TLS_DTPMOD32):
347 case R_TYPE(TLS_DTPREL32):
348 case R_TYPE(TLS_TPREL32):
349 #endif
350 symnum = ELF_R_SYM(rel->r_info);
351 if (last_symnum != symnum) {
352 last_symnum = symnum;
353 def = _rtld_find_symdef(symnum, obj, &defobj,
354 false);
355 if (def == NULL)
356 return -1;
357 }
358 break;
359 default:
360 break;
361 }
362
363 switch (ELF_R_TYPE(rel->r_info) & 0xff) {
364 case R_TYPE(NONE):
365 break;
366
367 case R_TYPE(REL32): {
368 /* 32-bit PC-relative reference */
369 const Elf_Sym *def2;
370 const size_t rlen =
371 ELF_R_NXTTYPE_64_P(ELF_R_TYPE(rel->r_info))
372 ? sizeof(Elf_Sxword)
373 : sizeof(Elf_Sword);
374 Elf_Sxword old = load_ptr(where, rlen);
375 Elf_Sxword val = old;
376
377 def2 = obj->symtab + ELF_R_SYM(rel->r_info);
378
379 if (ELF_R_SYM(rel->r_info) >= obj->gotsym) {
380 val += got[obj->local_gotno +
381 ELF_R_SYM(rel->r_info) - obj->gotsym];
382 rdbg(("REL32/G(%p) %p --> %p (%s) in %s",
383 where, (void *)old, (void *)val,
384 obj->strtab + def2->st_name,
385 obj->path));
386 } else {
387 /*
388 * XXX: ABI DIFFERENCE!
389 *
390 * Old NetBSD binutils would generate shared
391 * libs with section-relative relocations being
392 * already adjusted for the start address of
393 * the section.
394 *
395 * New binutils, OTOH, generate shared libs
396 * with the same relocations being based at
397 * zero, so we need to add in the start address
398 * of the section.
399 *
400 * --rkb, Oct 6, 2001
401 */
402
403 if (def2->st_info ==
404 ELF_ST_INFO(STB_LOCAL, STT_SECTION)
405 #ifdef SUPPORT_OLD_BROKEN_LD
406 && !broken
407 #endif
408 )
409 val += (Elf_Addr)def->st_value;
410
411 val += (Elf_Addr)obj->relocbase;
412
413 rdbg(("REL32/L(%p) %p -> %p (%s) in %s",
414 where, (void *)old, (void *)val,
415 obj->strtab + def2->st_name, obj->path));
416 }
417 store_ptr(where, val, rlen);
418 break;
419 }
420
421 #if ELFSIZE == 64
422 case R_TYPE(TLS_DTPMOD64):
423 #else
424 case R_TYPE(TLS_DTPMOD32):
425 #endif
426 {
427 Elf_Addr old = load_ptr(where, ELFSIZE / 8);
428 Elf_Addr val = old;
429
430 val += (Elf_Addr)defobj->tlsindex;
431
432 store_ptr(where, val, ELFSIZE / 8);
433 rdbg(("DTPMOD %s in %s --> %p in %s",
434 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)].st_name,
435 obj->path, (void *)old, defobj->path));
436 break;
437 }
438
439 #if ELFSIZE == 64
440 case R_TYPE(TLS_DTPREL64):
441 #else
442 case R_TYPE(TLS_DTPREL32):
443 #endif
444 {
445 Elf_Addr old = load_ptr(where, ELFSIZE / 8);
446 Elf_Addr val = old;
447
448 val += (Elf_Addr)def->st_value - TLS_DTV_OFFSET;
449 store_ptr(where, val, ELFSIZE / 8);
450
451 rdbg(("DTPREL %s in %s --> %p in %s",
452 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)].st_name,
453 obj->path, (void *)old, defobj->path));
454 break;
455 }
456
457 #if ELFSIZE == 64
458 case R_TYPE(TLS_TPREL64):
459 #else
460 case R_TYPE(TLS_TPREL32):
461 #endif
462 {
463 Elf_Addr old = load_ptr(where, ELFSIZE / 8);
464 Elf_Addr val = old;
465
466 if (!defobj->tls_static &&
467 _rtld_tls_offset_allocate(__UNCONST(defobj)))
468 return -1;
469
470 val += (Elf_Addr)(def->st_value + defobj->tlsoffset
471 - TLS_TP_OFFSET);
472 store_ptr(where, val, ELFSIZE / 8);
473
474 rdbg(("TPREL %s in %s --> %p in %s",
475 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)].st_name,
476 obj->path, where, defobj->path));
477 break;
478 }
479
480 default:
481 rdbg(("sym = %lu, type = %lu, offset = %p, "
482 "contents = %p, symbol = %s",
483 (u_long)ELF_R_SYM(rel->r_info),
484 (u_long)ELF_R_TYPE(rel->r_info),
485 (void *)rel->r_offset,
486 (void *)load_ptr(where, sizeof(Elf_Sword)),
487 obj->strtab + obj->symtab[ELF_R_SYM(rel->r_info)].st_name));
488 _rtld_error("%s: Unsupported relocation type %ld "
489 "in non-PLT relocations",
490 obj->path, (u_long) ELF_R_TYPE(rel->r_info));
491 return -1;
492 }
493 }
494
495 return 0;
496 }
497
498 int
499 _rtld_relocate_plt_lazy(Obj_Entry *obj)
500 {
501 /* PLT fixups were done above in the GOT relocation. */
502 return 0;
503 }
504
505 static inline int
506 _rtld_relocate_plt_object(const Obj_Entry *obj, Elf_Word sym, Elf_Addr *tp)
507 {
508 Elf_Addr *got = obj->pltgot;
509 const Elf_Sym *def;
510 const Obj_Entry *defobj;
511 Elf_Addr new_value;
512
513 def = _rtld_find_plt_symdef(sym, obj, &defobj, tp != NULL);
514 if (__predict_false(def == NULL))
515 return -1;
516 if (__predict_false(def == &_rtld_sym_zero))
517 return 0;
518
519 if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
520 if (tp == NULL)
521 return 0;
522 new_value = _rtld_resolve_ifunc(defobj, def);
523 } else {
524 new_value = (Elf_Addr)(defobj->relocbase + def->st_value);
525 }
526 rdbg(("bind now/fixup in %s --> new=%p",
527 defobj->strtab + def->st_name, (void *)new_value));
528 got[obj->local_gotno + sym - obj->gotsym] = new_value;
529
530 if (tp)
531 *tp = new_value;
532 return 0;
533 }
534
535 caddr_t
536 _rtld_bind(Elf_Word a0, Elf_Addr a1, Elf_Addr a2, Elf_Addr a3)
537 {
538 Elf_Addr *got = (Elf_Addr *)(a2 - 0x7ff0);
539 const Obj_Entry *obj = (Obj_Entry *)(got[1] & GOT1_MASK);
540 Elf_Addr new_value = 0; /* XXX gcc */
541 int err;
542
543 _rtld_shared_enter();
544 err = _rtld_relocate_plt_object(obj, a0, &new_value);
545 if (err)
546 _rtld_die();
547 _rtld_shared_exit();
548
549 return (caddr_t)new_value;
550 }
551
552 int
553 _rtld_relocate_plt_objects(const Obj_Entry *obj)
554 {
555 const Elf_Sym *sym = obj->symtab + obj->gotsym;
556 Elf_Word i;
557
558 for (i = obj->gotsym; i < obj->symtabno; i++, sym++) {
559 if (ELF_ST_TYPE(sym->st_info) == STT_FUNC)
560 if (_rtld_relocate_plt_object(obj, i, NULL) < 0)
561 return -1;
562 }
563
564 return 0;
565 }
566