1 /* $OpenBSD: rtld_machine.c,v 1.31 2022/01/08 06:49:42 guenther Exp $ */
2
3 /*
4 * Copyright (c) 2013 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 /*
19 * Copyright (c) 1999 Dale Rahn
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
31 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
34 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44 #define _DYN_LOADER
45
46 #include <sys/types.h>
47 #include <sys/exec_elf.h>
48 #include <sys/syscall.h>
49 #include <sys/unistd.h>
50
51 #include <machine/reloc.h>
52
53 #include "util.h"
54 #include "resolve.h"
55
56 int _dl_cacheflush(unsigned long, size_t);
57 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
58 void _dl_md_reloc_gotp_ent(Elf_Addr, Elf_Addr, Elf_Addr);
59
60 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
61
62 int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)63 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
64 {
65 int i;
66 int numrela;
67 int relrela;
68 int fails = 0;
69 Elf_Addr loff;
70 Elf_RelA *relas;
71 Elf_Addr prev_value = 0, prev_ooff = 0;
72 const Elf_Sym *prev_sym = NULL;
73
74 loff = object->obj_base;
75 numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
76 relrela = rel == DT_RELA ? object->relacount : 0;
77
78 relas = (Elf_RelA *)(object->Dyn.info[rel]);
79
80 if (relas == NULL)
81 return 0;
82
83 if (relrela > numrela)
84 _dl_die("relacount > numrel: %d > %d", relrela, numrela);
85
86 /* tight loop for leading RELATIVE relocs */
87 for (i = 0; i < relrela; i++, relas++) {
88 Elf_Addr *r_addr;
89
90 r_addr = (Elf_Addr *)(relas->r_offset + loff);
91 *r_addr = relas->r_addend + loff;
92 }
93 for (; i < numrela; i++, relas++) {
94 Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
95 Elf_Addr addend, newval;
96 const Elf_Sym *sym;
97 const char *symn;
98 int type;
99
100 type = ELF_R_TYPE(relas->r_info);
101
102 if (type == RELOC_GOTP_ENT && rel != DT_JMPREL)
103 continue;
104
105 if (type == RELOC_NONE)
106 continue;
107
108 sym = object->dyn.symtab;
109 sym += ELF_R_SYM(relas->r_info);
110 symn = object->dyn.strtab + sym->st_name;
111
112 if (type == RELOC_COPY) {
113 /*
114 * we need to find a symbol, that is not in the current
115 * object, start looking at the beginning of the list,
116 * searching all objects but _not_ the current object,
117 * first one found wins.
118 */
119 struct sym_res sr;
120
121 sr = _dl_find_symbol(symn,
122 SYM_SEARCH_OTHER | SYM_WARNNOTFOUND | SYM_NOTPLT,
123 sym, object);
124 if (sr.sym != NULL) {
125 _dl_bcopy((void *)(sr.obj->obj_base +
126 sr.sym->st_value), r_addr, sym->st_size);
127 } else
128 fails++;
129
130 continue;
131 }
132
133 if (ELF_R_SYM(relas->r_info) &&
134 !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
135 ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
136 sym != prev_sym) {
137 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
138 ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
139 prev_sym = sym;
140 prev_value = 0;
141 prev_ooff = object->obj_base;
142 } else {
143 struct sym_res sr;
144
145 sr = _dl_find_symbol(symn,
146 SYM_SEARCH_ALL | SYM_WARNNOTFOUND |
147 ((type == RELOC_GOTP_ENT) ?
148 SYM_PLT : SYM_NOTPLT), sym, object);
149
150 if (sr.sym == NULL) {
151 if (ELF_ST_BIND(sym->st_info) !=
152 STB_WEAK)
153 fails++;
154 continue;
155 }
156 prev_sym = sym;
157 prev_value = sr.sym->st_value;
158 prev_ooff = sr.obj->obj_base;
159 }
160 }
161
162 if (type == RELOC_GOTP_ENT) {
163 _dl_md_reloc_gotp_ent((Elf_Addr)r_addr,
164 relas->r_addend + loff,
165 prev_ooff + prev_value);
166 continue;
167 }
168
169 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
170 (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
171 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE))
172 addend = relas->r_addend;
173 else
174 addend = prev_value + relas->r_addend;
175
176 switch (type) {
177 case RELOC_16L:
178 newval = prev_ooff + addend;
179 *(unsigned short *)r_addr = newval & 0xffff;
180 _dl_cacheflush((unsigned long)r_addr, 2);
181 break;
182 case RELOC_16H:
183 newval = prev_ooff + addend;
184 *(unsigned short *)r_addr = newval >> 16;
185 _dl_cacheflush((unsigned long)r_addr, 2);
186 break;
187 case RELOC_DISP26:
188 newval = prev_ooff + addend;
189 newval -= (Elf_Addr)r_addr;
190 if ((newval >> 28) != 0 && (newval >> 28) != 0x0f)
191 _dl_die("%s: out of range DISP26"
192 " relocation to '%s' at %p\n",
193 object->load_name, symn, (void *)r_addr);
194 *r_addr = (*r_addr & 0xfc000000) |
195 (((int32_t)newval >> 2) & 0x03ffffff);
196 _dl_cacheflush((unsigned long)r_addr, 4);
197 break;
198 case RELOC_32:
199 newval = prev_ooff + addend;
200 *r_addr = newval;
201 break;
202 case RELOC_BBASED_32:
203 newval = loff + addend;
204 *r_addr = newval;
205 break;
206 default:
207 _dl_die("%s: unsupported relocation '%s' %d at %p\n",
208 object->load_name, symn, type, (void *)r_addr);
209 }
210 }
211
212 return fails;
213 }
214
215 /*
216 * GOTP_ENT relocations are special in that they define both a .got and a
217 * .plt relocation.
218 */
219 void
_dl_md_reloc_gotp_ent(Elf_Addr got_addr,Elf_Addr plt_addr,Elf_Addr val)220 _dl_md_reloc_gotp_ent(Elf_Addr got_addr, Elf_Addr plt_addr, Elf_Addr val)
221 {
222 uint16_t *plt_entry = (uint16_t *)plt_addr;
223
224 /* .got update */
225 *(Elf_Addr *)got_addr = val;
226 /* .plt update */
227 plt_entry[1] = got_addr >> 16;
228 plt_entry[3] = got_addr & 0xffff;
229 }
230
231 /*
232 * Relocate the Global Offset Table (GOT).
233 * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
234 * otherwise the lazy binding plt operation is preserved.
235 */
236 int
_dl_md_reloc_got(elf_object_t * object,int lazy)237 _dl_md_reloc_got(elf_object_t *object, int lazy)
238 {
239 extern void _dl_bind_start(void); /* XXX */
240 int fails = 0;
241 Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
242 Elf_Addr plt_start, plt_end;
243
244 if (pltgot == NULL)
245 return 0;
246
247 pltgot[1] = (Elf_Addr)object;
248 pltgot[2] = (Elf_Addr)_dl_bind_start;
249
250 if (object->Dyn.info[DT_PLTREL] != DT_RELA)
251 return 0;
252
253 if (!lazy) {
254 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
255 } else {
256 if (object->obj_base != 0) {
257 int cnt;
258 Elf_Addr *addr;
259 Elf_RelA *rela;
260
261 cnt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
262 rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL];
263
264 for (; cnt != 0; cnt--, rela++) {
265 addr = (Elf_Addr *)(object->obj_base +
266 rela->r_offset);
267 _dl_md_reloc_gotp_ent((Elf_Addr)addr,
268 object->obj_base + rela->r_addend,
269 *addr + object->obj_base);
270 }
271 }
272 }
273
274 /*
275 * Force a cache sync here on the whole PLT if we updated it
276 * (and have the DT entries to find what we need to flush),
277 * otherwise I$ might have stale information.
278 */
279 plt_start = object->Dyn.info[DT_88K_PLTSTART - DT_LOPROC + DT_NUM];
280 plt_end = object->Dyn.info[DT_88K_PLTEND - DT_LOPROC + DT_NUM];
281 if ((!lazy || object->obj_base != 0) && plt_start != 0 &&
282 plt_end != 0) {
283 size_t plt_size = plt_end - plt_start;
284 if (plt_size != 0)
285 _dl_cacheflush(plt_start + object->obj_base, plt_size);
286 }
287
288 return fails;
289 }
290
291 Elf_Addr
_dl_bind(elf_object_t * object,int reloff)292 _dl_bind(elf_object_t *object, int reloff)
293 {
294 Elf_RelA *rel;
295 struct sym_res sr;
296 const Elf_Sym *sym;
297 const char *symn;
298 uint64_t cookie = pcookie;
299 struct {
300 struct __kbind param;
301 Elf_Addr newval;
302 } buf;
303
304 rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
305
306 sym = object->dyn.symtab;
307 sym += ELF_R_SYM(rel->r_info);
308 symn = object->dyn.strtab + sym->st_name;
309
310 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
311 sym, object);
312 if (sr.sym == NULL)
313 _dl_die("lazy binding failed!");
314
315 buf.newval = sr.obj->obj_base + sr.sym->st_value;
316
317 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
318 return buf.newval;
319
320 buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
321 buf.param.kb_size = sizeof(Elf_Addr);
322
323 /* directly code the syscall, so that it's actually inline here */
324 {
325 register long syscall_num __asm("r13") = SYS_kbind;
326 register void *arg1 __asm("r2") = &buf;
327 register long arg2 __asm("r3") = sizeof(buf);
328 register long arg3 __asm("r4") = 0xffffffff & (cookie >> 32);
329 register long arg4 __asm("r5") = 0xffffffff & cookie;
330
331 __asm volatile("tb0 0, %%r0, 450; or %%r0, %%r0, %%r0"
332 : "+r" (arg1), "+r" (arg2) : "r" (syscall_num),
333 "r" (arg3), "r" (arg4) : "memory");
334 }
335
336 return buf.newval;
337 }
338