xref: /openbsd-src/libexec/ld.so/powerpc/rtld_machine.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: rtld_machine.c,v 1.70 2019/12/07 22:57:48 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/mman.h>
33 #include <sys/syscall.h>
34 #include <sys/unistd.h>
35 
36 #include <nlist.h>
37 #include <link.h>
38 
39 #include "syscall.h"
40 #include "archdep.h"
41 #include "resolve.h"
42 
43 #define	DT_PROC(n)	((n) - DT_LOPROC + DT_NUM)
44 
45 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
46 
47 /* relocation bits */
48 #define B24_VALID_RANGE(x) \
49     ((((x) & 0xfe000000) == 0x00000000) || (((x) &  0xfe000000) == 0xfe000000))
50 
51 void _dl_bind_start(void); /* XXX */
52 Elf_Addr _dl_bind(elf_object_t *object, int reloff);
53 
54 int
55 _dl_md_reloc(elf_object_t *object, int rel, int relasz)
56 {
57 	int	i;
58 	int	numrela;
59 	long	relrel;
60 	int	fails = 0;
61 	Elf_Addr loff;
62 	Elf_RelA  *relas;
63 	/* for jmp table relocations */
64 	Elf_Addr prev_value = 0, prev_ooff = 0;
65 	const Elf_Sym *prev_sym = NULL;
66 
67 	loff = object->obj_base;
68 	numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
69 	relrel = rel == DT_RELA ? object->relacount : 0;
70 	relas = (Elf_RelA *)(object->Dyn.info[rel]);
71 
72 	if (relas == NULL)
73 		return 0;
74 
75 	if (relrel > numrela)
76 		_dl_die("relcount > numrel: %ld > %d", relrel, numrela);
77 
78 	if (object->Dyn.info[DT_PROC(DT_PPC_GOT)] == 0)
79 		_dl_die("unsupported insecure BSS PLT object");
80 
81 	/* tight loop for leading RELATIVE relocs */
82 	for (i = 0; i < relrel; i++, relas++) {
83 		Elf_Addr *r_addr;
84 
85 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
86 		*r_addr = loff + relas->r_addend;
87 	}
88 	for (; i < numrela; i++, relas++) {
89 		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
90 		const Elf_Sym *sym;
91 		const char *symn;
92 		int type;
93 
94 		if (ELF_R_SYM(relas->r_info) == 0xffffff)
95 			continue;
96 
97 		type = ELF_R_TYPE(relas->r_info);
98 
99 		if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
100 			continue;
101 
102 		sym = object->dyn.symtab;
103 		sym += ELF_R_SYM(relas->r_info);
104 		symn = object->dyn.strtab + sym->st_name;
105 
106 		if (ELF_R_SYM(relas->r_info) &&
107 		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
108 		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
109 		    sym != prev_sym) {
110 			struct sym_res sr;
111 
112 			sr = _dl_find_symbol(symn,
113 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
114 			    ((type == RELOC_JMP_SLOT) ?
115 			    SYM_PLT:SYM_NOTPLT), sym, object);
116 
117 			if (sr.sym == NULL) {
118 				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
119 					fails++;
120 				continue;
121 			}
122 			prev_sym = sym;
123 			prev_value = sr.sym->st_value;
124 			prev_ooff = sr.obj->obj_base;
125 		}
126 
127 		switch (type) {
128 		case RELOC_32:
129 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
130 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
131 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
132 				*r_addr = prev_ooff + relas->r_addend;
133 			} else {
134 				*r_addr = prev_ooff + prev_value +
135 				    relas->r_addend;
136 			}
137 			break;
138 		case RELOC_RELATIVE:
139 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
140 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
141 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
142 				*r_addr = loff + relas->r_addend;
143 			} else {
144 				*r_addr = loff + prev_value +
145 				    relas->r_addend;
146 			}
147 			break;
148 		/*
149 		 * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
150 		 * slots similarly to how RELOC_GLOB_DAT updates GOT
151 		 * slots.
152 		 */
153 		case RELOC_JMP_SLOT:
154 		case RELOC_GLOB_DAT:
155 			*r_addr = prev_ooff + prev_value + relas->r_addend;
156 			break;
157 #if 1
158 		/* should not be supported ??? */
159 		case RELOC_REL24:
160 		    {
161 			Elf_Addr val = prev_ooff + prev_value +
162 			    relas->r_addend - (Elf_Addr)r_addr;
163 			if (!B24_VALID_RANGE(val)) {
164 				/* invalid offset */
165 				_dl_die("%s: invalid %s offset %x at %p",
166 				    object->load_name, "REL24", val,
167 				    (void *)r_addr);
168 			}
169 			val &= ~0xfc000003;
170 			val |= (*r_addr & 0xfc000003);
171 			*r_addr = val;
172 
173 			_dl_dcbf(r_addr);
174 		    }
175 		break;
176 #endif
177 #if 1
178 		case RELOC_16_LO:
179 		    {
180 			Elf_Addr val;
181 
182 			val = loff + relas->r_addend;
183 			*(Elf_Half *)r_addr = val;
184 
185 			_dl_dcbf(r_addr);
186 		    }
187 		break;
188 #endif
189 #if 1
190 		case RELOC_16_HI:
191 		    {
192 			Elf_Addr val;
193 
194 			val = loff + relas->r_addend;
195 			*(Elf_Half *)r_addr = (val >> 16);
196 
197 			_dl_dcbf(r_addr);
198 		    }
199 		break;
200 #endif
201 #if 1
202 		case RELOC_16_HA:
203 		    {
204 			Elf_Addr val;
205 
206 			val = loff + relas->r_addend;
207 			*(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
208 
209 			_dl_dcbf(r_addr);
210 		    }
211 		break;
212 #endif
213 		case RELOC_REL14_TAKEN:
214 			/* val |= 1 << (31-10) XXX? */
215 		case RELOC_REL14:
216 		case RELOC_REL14_NTAKEN:
217 		    {
218 			Elf_Addr val = prev_ooff + prev_value +
219 			    relas->r_addend - (Elf_Addr)r_addr;
220 			if (((val & 0xffff8000) != 0) &&
221 			    ((val & 0xffff8000) != 0xffff8000)) {
222 				/* invalid offset */
223 				_dl_die("%s: invalid %s offset %x at %p",
224 				    object->load_name, "REL14", val,
225 				    (void *)r_addr);
226 			}
227 			val &= ~0xffff0003;
228 			val |= (*r_addr & 0xffff0003);
229 			*r_addr = val;
230 			_dl_dcbf(r_addr);
231 		    }
232 			break;
233 		case RELOC_COPY:
234 		{
235 			struct sym_res sr;
236 			/*
237 			 * we need to find a symbol, that is not in the current
238 			 * object, start looking at the beginning of the list,
239 			 * searching all objects but _not_ the current object,
240 			 * first one found wins.
241 			 */
242 			sr = _dl_find_symbol(symn,
243 			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
244 			    sym, object);
245 			if (sr.sym != NULL) {
246 				_dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value),
247 				    r_addr, sym->st_size);
248 			} else
249 				fails++;
250 		}
251 			break;
252 		case RELOC_NONE:
253 			break;
254 
255 		default:
256 			_dl_die("%s: unsupported relocation '%s' %d at %p\n",
257 			    object->load_name, symn,
258 			    ELF_R_TYPE(relas->r_info), (void *)r_addr );
259 		}
260 	}
261 
262 	return fails;
263 }
264 
265 /*
266  *	Relocate the Global Offset Table (GOT).
267  *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
268  *	otherwise the lazy binding plt initialization is performed.
269  */
270 int
271 _dl_md_reloc_got(elf_object_t *object, int lazy)
272 {
273 	int fails = 0;
274 
275 	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
276 		return 0;
277 
278 	if (!lazy) {
279 		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
280 	} else {
281 		Elf_Addr *got;
282 		Elf_Addr *plt;
283 		int numplt, i;
284 
285 		/* Relocate processor-specific tags. */
286 		object->Dyn.info[DT_PROC(DT_PPC_GOT)] += object->obj_base;
287 
288 		got = (Elf_Addr *)
289 		    (Elf_RelA *)(object->Dyn.info[DT_PROC(DT_PPC_GOT)]);
290 		got[1] = (Elf_Addr)_dl_bind_start;
291 		got[2] = (Elf_Addr)object;
292 
293 		plt = (Elf_Addr *)
294 		   (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
295 		numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
296 		for (i = 0; i < numplt; i++)
297 			plt[i] += object->obj_base;
298 	}
299 
300 	return fails;
301 }
302 
303 Elf_Addr
304 _dl_bind(elf_object_t *object, int reloff)
305 {
306 	const Elf_Sym *sym;
307 	struct sym_res sr;
308 	const char *symn;
309 	Elf_RelA *relas;
310 	Elf_Addr *plttable;
311 	int64_t cookie = pcookie;
312 	struct {
313 		struct __kbind param;
314 		Elf_Addr newval;
315 	} buf;
316 
317 	relas = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
318 
319 	sym = object->dyn.symtab;
320 	sym += ELF_R_SYM(relas->r_info);
321 	symn = object->dyn.strtab + sym->st_name;
322 
323 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
324 	    sym, object);
325 	if (sr.sym == NULL)
326 		_dl_die("lazy binding failed!");
327 
328 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
329 
330 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
331 		return buf.newval;
332 
333 	plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
334 	buf.param.kb_addr = &plttable[ reloff / sizeof(Elf_RelA) ];
335 	buf.param.kb_size = sizeof(Elf_Addr);
336 
337 	{
338 		register long syscall_num __asm("r0") = SYS_kbind;
339 		register void *arg1 __asm("r3") = &buf.param;
340 		register long  arg2 __asm("r4") = sizeof(struct __kbind) +
341 		    sizeof(Elf_Addr);
342 		register long  arg3 __asm("r5") = 0xffffffff & (cookie >> 32);
343 		register long  arg4 __asm("r6") = 0xffffffff &  cookie;
344 
345 		__asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
346 		    "+r" (arg2) : "r" (arg3), "r" (arg4) : "cc", "memory");
347 	}
348 
349 	return buf.newval;
350 }
351