1 /* $OpenBSD: rtld_machine.c,v 1.44 2022/08/29 02:08:13 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2004 Michael Shalayeff
5 * Copyright (c) 2001 Niklas Hallqvist
6 * Copyright (c) 2001 Artur Grabowski
7 * Copyright (c) 1999 Dale Rahn
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
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 OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
28 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #define _DYN_LOADER
33
34 #include <sys/types.h>
35 #include <sys/exec_elf.h>
36 #include <sys/syscall.h>
37 #include <sys/tree.h>
38 #include <sys/unistd.h>
39
40 #include <machine/reloc.h>
41 #include <machine/vmparam.h> /* SYSCALLGATE */
42
43 #include "util.h"
44 #define _dl_bind XXX_dl_bind
45 #include "resolve.h"
46 #undef _dl_bind
47 uint64_t _dl_bind(elf_object_t *object, int reloff);
48
49 typedef
50 struct hppa_plabel {
51 Elf_Addr pc;
52 Elf_Addr *sl;
53 SPLAY_ENTRY(hppa_plabel) node;
54 } hppa_plabel_t;
55 SPLAY_HEAD(_dl_md_plabels, hppa_plabel) _dl_md_plabel_root;
56
57 void _hppa_dl_set_dp(Elf_Addr *dp); /* from ldasm.S */
58
59 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
60
61 static __inline int
_dl_md_plcmp(hppa_plabel_t * a,hppa_plabel_t * b)62 _dl_md_plcmp(hppa_plabel_t *a, hppa_plabel_t *b)
63 {
64 if (a->sl < b->sl)
65 return -1;
66 else if (a->sl > b->sl)
67 return 1;
68 else if (a->pc < b->pc)
69 return -1;
70 else if (a->pc > b->pc)
71 return 1;
72 else
73 return 0;
74 }
75
76 SPLAY_PROTOTYPE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
77 SPLAY_GENERATE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
78
79 Elf_Addr
_dl_md_plabel(Elf_Addr pc,Elf_Addr * sl)80 _dl_md_plabel(Elf_Addr pc, Elf_Addr *sl)
81 {
82 hppa_plabel_t key, *p;
83
84 key.pc = pc;
85 key.sl = sl;
86 p = SPLAY_FIND(_dl_md_plabels, &_dl_md_plabel_root, &key);
87 if (p == NULL) {
88 p = _dl_malloc(sizeof(*p));
89 if (p == NULL)
90 _dl_oom();
91 p->pc = pc;
92 p->sl = sl;
93 SPLAY_INSERT(_dl_md_plabels, &_dl_md_plabel_root, p);
94 }
95
96 return (Elf_Addr)p | 2;
97 }
98
99 int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)100 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
101 {
102 Elf_RelA *rela;
103 Elf_Addr loff;
104 int num_relative;
105 int i, numrela, fails = 0;
106
107 loff = object->obj_base;
108 numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
109 num_relative = rel == DT_RELA ? object->relacount : 0;
110 rela = (Elf_RelA *)(object->Dyn.info[rel]);
111
112 #ifdef DEBUG
113 DL_DEB(("object %s relasz %x, numrela %x loff %x\n",
114 object->load_name, object->Dyn.info[relasz], numrela, loff));
115 #endif
116
117 if (rela == NULL)
118 return 0;
119
120 /* either it's an ld bug or a wacky hpux abi */
121 if (!object->dyn.pltgot)
122 object->Dyn.info[DT_PLTGOT] += loff;
123
124 if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) {
125 Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init,
126 object->dyn.pltgot);
127 #ifdef DEBUG
128 DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n",
129 object->dyn.init, object->dyn.pltgot,
130 addr, object->load_name));
131 #endif
132 object->dyn.init = (void *)addr;
133 }
134
135 if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) {
136 Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini,
137 object->dyn.pltgot);
138 #ifdef DEBUG
139 DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n",
140 object->dyn.fini, object->dyn.pltgot,
141 addr, object->load_name));
142 #endif
143 object->dyn.fini = (void *)addr;
144 }
145
146 /*
147 * this is normally done by the crt0 code but we have to make
148 * sure it's set here to allow constructors to call functions
149 * that are overridden in the user binary (that are un-pic)
150 */
151 if (object->obj_type == OBJTYPE_EXE)
152 _hppa_dl_set_dp(object->dyn.pltgot);
153
154 /* tight loop for leading relative relocs */
155 for (i = 0; i < num_relative; i++, rela++) {
156 Elf_Addr *where = (Elf_Addr *)(rela->r_offset + loff);
157 *where = rela->r_addend + loff;
158 }
159 for (; i < numrela; i++, rela++) {
160 struct sym_res sr;
161 const Elf_Sym *sym;
162 Elf_Addr *pt;
163 const char *symn;
164 int type;
165
166 type = ELF_R_TYPE(rela->r_info);
167 if (type == RELOC_NONE)
168 continue;
169
170 sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
171 symn = object->dyn.strtab + sym->st_name;
172 pt = (Elf_Addr *)(rela->r_offset + loff);
173
174 if (ELF_R_SYM(rela->r_info) && sym->st_name) {
175 sr = _dl_find_symbol(symn,
176 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
177 sym, object);
178 if (sr.sym == NULL) {
179 if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
180 fails++;
181 continue;
182 }
183 } else {
184 sr.sym = NULL;
185 sr.obj = object;
186 }
187
188 #ifdef DEBUG
189 DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n",
190 *pt, rela->r_addend, ELF_R_SYM(rela->r_info)));
191 #endif
192
193 switch (type) {
194 case RELOC_DIR32:
195 if (ELF_R_SYM(rela->r_info) && sym->st_name) {
196 *pt = sr.obj->obj_base + sr.sym->st_value +
197 rela->r_addend;
198 #ifdef DEBUG
199 DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n",
200 i, symn, object->load_name,
201 *pt, sr.obj->load_name));
202 #endif
203 } else {
204 /*
205 * Either a relative relocation (symbol 0)
206 * or a relocation against a local section
207 */
208 *pt = loff + sym->st_value + rela->r_addend;
209 #ifdef DEBUG
210 DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i,
211 object->load_name, *pt));
212 #endif
213 }
214 break;
215
216 case RELOC_PLABEL32:
217 if (ELF_R_SYM(rela->r_info)) {
218 if (ELF_ST_TYPE(sr.sym->st_info) != STT_FUNC) {
219 DL_DEB(("[%x]PLABEL32: bad\n", i));
220 break;
221 }
222 *pt = _dl_md_plabel(sr.obj->obj_base +
223 sr.sym->st_value + rela->r_addend,
224 sr.obj->dyn.pltgot);
225 #ifdef DEBUG
226 DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n",
227 i, symn, object->load_name,
228 *pt, sr.obj->load_name));
229 #endif
230 } else {
231 *pt = loff + rela->r_addend;
232 #ifdef DEBUG
233 DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i,
234 object->load_name, *pt));
235 #endif
236 }
237 break;
238
239 case RELOC_IPLT:
240 if (ELF_R_SYM(rela->r_info)) {
241 pt[0] = sr.obj->obj_base + sr.sym->st_value +
242 rela->r_addend;
243 pt[1] = (Elf_Addr)sr.obj->dyn.pltgot;
244 #ifdef DEBUG
245 DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n",
246 i, symn, object->load_name,
247 pt[0], pt[1], sr.obj->load_name));
248 #endif
249 } else {
250 pt[0] = loff + rela->r_addend;
251 pt[1] = (Elf_Addr)object->dyn.pltgot;
252 #ifdef DEBUG
253 DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i,
254 object->load_name, pt[0], pt[1]));
255 #endif
256 }
257 break;
258
259 case RELOC_COPY:
260 {
261 sr = _dl_find_symbol(symn,
262 SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
263 sym, object);
264 if (sr.sym) {
265 _dl_bcopy((void *)(sr.obj->obj_base +
266 sr.sym->st_value), pt, sym->st_size);
267 #ifdef DEBUG
268 DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n",
269 i, symn, sr.obj->obj_base +
270 sr.sym->st_value, object->load_name,
271 pt, sym->st_size, sr.obj->load_name));
272 #endif
273 } else
274 DL_DEB(("[%x]COPY: no sym\n", i));
275 break;
276 }
277 default:
278 DL_DEB(("[%x]UNKNOWN(%d): type=%d off=0x%lx "
279 "addend=0x%lx rel=0x%x\n", i, type,
280 ELF_R_TYPE(rela->r_info), rela->r_offset,
281 rela->r_addend, *pt));
282 break;
283 }
284 }
285
286 return fails;
287 }
288
289 extern void _dl_bind_start(void);
290
291 #define PLT_STUB_SIZE (7 * 4)
292 #define PLT_ENTRY_SIZE (2 * 4)
293 #define PLT_STUB_GOTOFF (4 * 4)
294
295 #define PLT_STUB_MAGIC1 0x00c0ffee
296 #define PLT_STUB_MAGIC2 0xdeadbeef
297
298 #define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */
299 #define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */
300
301 int
_dl_md_reloc_got(elf_object_t * object,int lazy)302 _dl_md_reloc_got(elf_object_t *object, int lazy)
303 {
304 Elf_RelA *rela;
305 Elf_Addr ooff;
306 int i, numrela, fails = 0;
307
308 if (object->dyn.pltrel != DT_RELA)
309 return 0;
310
311 if (!lazy) {
312 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
313 } else {
314 register Elf_Addr ltp __asm ("%r19");
315 Elf_Addr *got = NULL;
316
317 rela = (Elf_RelA *)(object->dyn.jmprel);
318 numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
319 ooff = object->obj_base;
320
321 /*
322 * Find the PLT stub by looking at all the
323 * relocations. The PLT stub should be at the end of
324 * the .plt section so we start with the last
325 * relocation, since the linker should have emitted
326 * them in order.
327 */
328 for (i = numrela - 1; i >= 0; i--) {
329 got = (Elf_Addr *)(ooff + rela[i].r_offset +
330 PLT_ENTRY_SIZE + PLT_STUB_SIZE);
331 if (got[-2] == PLT_STUB_MAGIC1 ||
332 got[-1] == PLT_STUB_MAGIC2)
333 break;
334 got = NULL;
335 }
336 if (got == NULL)
337 return 1;
338
339 /*
340 * Patch up the PLT stub such that it doesn't clobber
341 * %r22, which is used to pass on the errno values
342 * from failed system calls to __cerrno() in libc.
343 */
344 got[-7] = PLT_STUB_INSN1;
345 got[-6] = PLT_STUB_INSN2;
346 __asm volatile("fdc 0(%0)" :: "r" (&got[-7]));
347 __asm volatile("fdc 0(%0)" :: "r" (&got[-6]));
348 __asm volatile("sync");
349 __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7]));
350 __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6]));
351 __asm volatile("sync");
352
353 /*
354 * Fill in the PLT stub such that it invokes the
355 * _dl_bind_start() trampoline to fix up the
356 * relocation.
357 */
358 got[1] = (Elf_Addr)object;
359 got[-2] = (Elf_Addr)&_dl_bind_start;
360 got[-1] = ltp;
361 /*
362 * We need the real address of the trampoline. Get it
363 * from the function descriptor if that's what we got.
364 */
365 if (got[-2] & 2) {
366 hppa_plabel_t *p = (hppa_plabel_t *)(got[-2] & ~2);
367 got[-2] = p->pc;
368 }
369 /*
370 * Even though we didn't modify any instructions it
371 * seems we still need to synchronize the caches.
372 * There may be instructions in the same cache line
373 * and they end up being corrupted otherwise.
374 */
375 __asm volatile("fdc 0(%0)" :: "r" (&got[-2]));
376 __asm volatile("fdc 0(%0)" :: "r" (&got[-1]));
377 __asm volatile("sync");
378 __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-2]));
379 __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-1]));
380 __asm volatile("sync");
381 for (i = 0; i < numrela; i++, rela++) {
382 Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);
383
384 if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
385 _dl_printf("unexpected reloc 0x%x\n",
386 ELF_R_TYPE(rela->r_info));
387 return 1;
388 }
389
390 if (ELF_R_SYM(rela->r_info)) {
391 r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
392 r_addr[1] = (Elf_Addr) (rela -
393 (Elf_RelA *)object->dyn.jmprel);
394 } else {
395 r_addr[0] = ooff + rela->r_addend;
396 r_addr[1] = (Elf_Addr)object->dyn.pltgot;
397 }
398 }
399 }
400
401 return fails;
402 }
403
404 /*
405 * Resolve a symbol at run-time.
406 */
407 uint64_t
_dl_bind(elf_object_t * object,int reloff)408 _dl_bind(elf_object_t *object, int reloff)
409 {
410 struct sym_res sr;
411 const Elf_Sym *sym;
412 const char *symn;
413 Elf_Addr value;
414 Elf_RelA *rela;
415 uint64_t cookie = pcookie;
416 struct {
417 struct __kbind param;
418 uint64_t newval;
419 } buf;
420
421 rela = (Elf_RelA *)object->dyn.jmprel + reloff;
422
423 sym = object->dyn.symtab;
424 sym += ELF_R_SYM(rela->r_info);
425 symn = object->dyn.strtab + sym->st_name;
426
427 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
428 sym, object);
429 if (sr.sym == NULL)
430 _dl_die("lazy binding failed!");
431
432 value = sr.obj->obj_base + sr.sym->st_value + rela->r_addend;
433
434 buf.newval = ((uint64_t)value << 32) | (Elf_Addr)sr.obj->dyn.pltgot;
435
436 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
437 return buf.newval;
438
439 buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
440 buf.param.kb_size = sizeof(uint64_t);
441
442 /* directly code the syscall, so that it's actually inline here */
443 {
444 register long r1 __asm__("r1") = SYSCALLGATE;
445 register void *arg0 __asm__("r26") = &buf;
446 register long arg1 __asm__("r25") = sizeof(buf);
447 register long arg2 __asm__("r24") = 0xffffffff & (cookie >> 32);
448 register long arg3 __asm__("r23") = 0xffffffff & cookie;
449 __asm__ volatile ("ble 4(%%sr7, %%r1) ! ldi %0, %%r22"
450 :
451 : "i" (SYS_kbind), "r" (r1), "r"(arg0), "r"(arg1),
452 "r"(arg2), "r"(arg3)
453 : "r22", "r28", "r29", "cc", "memory");
454 }
455
456 return buf.newval;
457 }
458