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