1 /* $OpenBSD: rtld_machine.c,v 1.7 2022/01/08 06:49:42 guenther Exp $ */
2
3 /*
4 * Copyright (c) 1999 Dale Rahn
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #define _DYN_LOADER
30
31 #include <sys/types.h>
32 #include <sys/exec_elf.h>
33 #include <sys/syscall.h>
34 #include <sys/unistd.h>
35
36 #include <machine/reloc.h>
37
38 #include "util.h"
39 #include "resolve.h"
40
41 #define DT_PROC(n) ((n) - DT_LOPROC + DT_NUM)
42
43 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
44
45 /* relocation bits */
46 #define B24_VALID_RANGE(x) \
47 ((((x) & 0xfe000000) == 0x00000000) || (((x) & 0xfe000000) == 0xfe000000))
48
49 void _dl_bind_start(void); /* XXX */
50 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
51
52 int
_dl_md_reloc(elf_object_t * object,int rel,int relasz)53 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
54 {
55 int i;
56 int numrela;
57 long relrel;
58 int fails = 0;
59 Elf_Addr loff;
60 Elf_RelA *relas;
61 /* for jmp table relocations */
62 Elf_Addr prev_value = 0, prev_ooff = 0;
63 const Elf_Sym *prev_sym = NULL;
64
65 loff = object->obj_base;
66 numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
67 relrel = rel == DT_RELA ? object->relacount : 0;
68 relas = (Elf_RelA *)(object->Dyn.info[rel]);
69
70 if (relas == NULL)
71 return 0;
72
73 if (relrel > numrela)
74 _dl_die("relcount > numrel: %ld > %d", relrel, numrela);
75
76 /* tight loop for leading RELATIVE relocs */
77 for (i = 0; i < relrel; i++, relas++) {
78 Elf_Addr *r_addr;
79
80 r_addr = (Elf_Addr *)(relas->r_offset + loff);
81 *r_addr = loff + relas->r_addend;
82 }
83 for (; i < numrela; i++, relas++) {
84 Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
85 const Elf_Sym *sym;
86 const char *symn;
87 int type;
88
89 if (ELF_R_SYM(relas->r_info) == 0xffffff)
90 continue;
91
92 type = ELF_R_TYPE(relas->r_info);
93
94 if (type == R_PPC64_JMP_SLOT && rel != DT_JMPREL)
95 continue;
96
97 sym = object->dyn.symtab;
98 sym += ELF_R_SYM(relas->r_info);
99 symn = object->dyn.strtab + sym->st_name;
100
101 if (ELF_R_SYM(relas->r_info) &&
102 !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
103 ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
104 sym != prev_sym) {
105 struct sym_res sr;
106
107 sr = _dl_find_symbol(symn,
108 SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
109 ((type == R_PPC64_JMP_SLOT) ?
110 SYM_PLT:SYM_NOTPLT), sym, object);
111
112 if (sr.sym == NULL) {
113 if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
114 fails++;
115 continue;
116 }
117 prev_sym = sym;
118 prev_value = sr.sym->st_value;
119 prev_ooff = sr.obj->obj_base;
120 }
121
122 switch (type) {
123 case R_PPC64_ADDR64:
124 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
125 (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
126 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
127 *r_addr = prev_ooff + relas->r_addend;
128 } else {
129 *r_addr = prev_ooff + prev_value +
130 relas->r_addend;
131 }
132 break;
133 case R_PPC64_RELATIVE:
134 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
135 (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
136 ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
137 *r_addr = loff + relas->r_addend;
138 } else {
139 *r_addr = loff + prev_value +
140 relas->r_addend;
141 }
142 break;
143 /*
144 * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
145 * slots similarly to how RELOC_GLOB_DAT updates GOT
146 * slots.
147 */
148 case R_PPC64_JMP_SLOT:
149 case R_PPC64_GLOB_DAT:
150 *r_addr = prev_ooff + prev_value + relas->r_addend;
151 break;
152 #if 0
153 /* should not be supported ??? */
154 case RELOC_REL24:
155 {
156 Elf_Addr val = prev_ooff + prev_value +
157 relas->r_addend - (Elf_Addr)r_addr;
158 if (!B24_VALID_RANGE(val)) {
159 /* invalid offset */
160 _dl_die("%s: invalid %s offset %llx at %p",
161 object->load_name, "REL24", val,
162 (void *)r_addr);
163 }
164 val &= ~0xfc000003;
165 val |= (*r_addr & 0xfc000003);
166 *r_addr = val;
167
168 _dl_dcbf(r_addr);
169 }
170 break;
171 #endif
172 #if 0
173 case RELOC_16_LO:
174 {
175 Elf_Addr val;
176
177 val = loff + relas->r_addend;
178 *(Elf_Half *)r_addr = val;
179
180 _dl_dcbf(r_addr);
181 }
182 break;
183 #endif
184 #if 0
185 case RELOC_16_HI:
186 {
187 Elf_Addr val;
188
189 val = loff + relas->r_addend;
190 *(Elf_Half *)r_addr = (val >> 16);
191
192 _dl_dcbf(r_addr);
193 }
194 break;
195 #endif
196 #if 0
197 case RELOC_16_HA:
198 {
199 Elf_Addr val;
200
201 val = loff + relas->r_addend;
202 *(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
203
204 _dl_dcbf(r_addr);
205 }
206 break;
207 #endif
208 #if 0
209 case RELOC_REL14_TAKEN:
210 /* val |= 1 << (31-10) XXX? */
211 case RELOC_REL14:
212 case RELOC_REL14_NTAKEN:
213 {
214 Elf_Addr val = prev_ooff + prev_value +
215 relas->r_addend - (Elf_Addr)r_addr;
216 if (((val & 0xffff8000) != 0) &&
217 ((val & 0xffff8000) != 0xffff8000)) {
218 /* invalid offset */
219 _dl_die("%s: invalid %s offset %llx at %p",
220 object->load_name, "REL14", val,
221 (void *)r_addr);
222 }
223 val &= ~0xffff0003;
224 val |= (*r_addr & 0xffff0003);
225 *r_addr = val;
226 _dl_dcbf(r_addr);
227 }
228 break;
229 #endif
230 case R_PPC64_COPY:
231 {
232 struct sym_res sr;
233 /*
234 * we need to find a symbol, that is not in the current
235 * object, start looking at the beginning of the list,
236 * searching all objects but _not_ the current object,
237 * first one found wins.
238 */
239 sr = _dl_find_symbol(symn,
240 SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
241 sym, object);
242 if (sr.sym != NULL) {
243 _dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value),
244 r_addr, sym->st_size);
245 } else
246 fails++;
247 }
248 break;
249 case R_PPC64_NONE:
250 break;
251
252 default:
253 _dl_die("%s: unsupported relocation '%s' %lld at %p\n",
254 object->load_name, symn,
255 ELF_R_TYPE(relas->r_info), (void *)r_addr );
256 }
257 }
258
259 return fails;
260 }
261
262 /*
263 * Relocate the Global Offset Table (GOT).
264 * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
265 * otherwise the lazy binding plt initialization is performed.
266 */
267 int
_dl_md_reloc_got(elf_object_t * object,int lazy)268 _dl_md_reloc_got(elf_object_t *object, int lazy)
269 {
270 int fails = 0;
271
272 if (object->Dyn.info[DT_PLTREL] != DT_RELA)
273 return 0;
274
275 if (!lazy) {
276 fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
277 } else {
278 Elf_Addr *plt;
279 int numplt, n;
280
281 /* Relocate processor-specific tags. */
282 object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] += object->obj_base;
283
284 plt = (Elf_Addr *)
285 (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
286 numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
287 plt[0] = (uint64_t)_dl_bind_start;
288 plt[1] = (uint64_t)object;
289 for (n = 0; n < numplt; n++) {
290 plt[n + 2] = object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] +
291 n * 4 + 32;
292 }
293 }
294
295 return fails;
296 }
297
298 Elf_Addr
_dl_bind(elf_object_t * object,int relidx)299 _dl_bind(elf_object_t *object, int relidx)
300 {
301 const Elf_Sym *sym;
302 struct sym_res sr;
303 const char *symn;
304 Elf_RelA *relas;
305 Elf_Addr *plttable;
306 int64_t cookie = pcookie;
307 struct {
308 struct __kbind param;
309 Elf_Addr newval;
310 } buf;
311
312 relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + relidx;
313
314 sym = object->dyn.symtab;
315 sym += ELF_R_SYM(relas->r_info);
316 symn = object->dyn.strtab + sym->st_name;
317
318 sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
319 sym, object);
320 if (sr.sym == NULL)
321 _dl_die("lazy binding failed!");
322
323 buf.newval = sr.obj->obj_base + sr.sym->st_value;
324
325 if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
326 return buf.newval;
327
328 plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
329 buf.param.kb_addr = &plttable[relidx + 2];
330 buf.param.kb_size = sizeof(Elf_Addr);
331
332 {
333 register long syscall_num __asm("r0") = SYS_kbind;
334 register void *arg1 __asm("r3") = &buf.param;
335 register long arg2 __asm("r4") = sizeof(struct __kbind) +
336 sizeof(Elf_Addr);
337 register long arg3 __asm("r5") = cookie;
338
339 __asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
340 "+r" (arg2) : "r" (arg3) : "cc", "memory");
341 }
342
343 return buf.newval;
344 }
345