1 /*	$OpenBSD: rtld_machine.c,v 1.2 2020/06/28 20:52:05 drahn 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 	/* tight loop for leading RELATIVE relocs */
79 	for (i = 0; i < relrel; i++, relas++) {
80 		Elf_Addr *r_addr;
81 
82 		r_addr = (Elf_Addr *)(relas->r_offset + loff);
83 		*r_addr = loff + relas->r_addend;
84 	}
85 	for (; i < numrela; i++, relas++) {
86 		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
87 		const Elf_Sym *sym;
88 		const char *symn;
89 		int type;
90 
91 		if (ELF_R_SYM(relas->r_info) == 0xffffff)
92 			continue;
93 
94 		type = ELF_R_TYPE(relas->r_info);
95 
96 		if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
97 			continue;
98 
99 		sym = object->dyn.symtab;
100 		sym += ELF_R_SYM(relas->r_info);
101 		symn = object->dyn.strtab + sym->st_name;
102 
103 		if (ELF_R_SYM(relas->r_info) &&
104 		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
105 		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
106 		    sym != prev_sym) {
107 			struct sym_res sr;
108 
109 			sr = _dl_find_symbol(symn,
110 			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
111 			    ((type == RELOC_JMP_SLOT) ?
112 			    SYM_PLT:SYM_NOTPLT), sym, object);
113 
114 			if (sr.sym == NULL) {
115 				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
116 					fails++;
117 				continue;
118 			}
119 			prev_sym = sym;
120 			prev_value = sr.sym->st_value;
121 			prev_ooff = sr.obj->obj_base;
122 		}
123 
124 		switch (type) {
125 		case RELOC_ADDR64: //RELOC_64:
126 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
127 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
128 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
129 				*r_addr = prev_ooff + relas->r_addend;
130 			} else {
131 				*r_addr = prev_ooff + prev_value +
132 				    relas->r_addend;
133 			}
134 			break;
135 		case RELOC_RELATIVE:
136 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
137 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
138 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
139 				*r_addr = loff + relas->r_addend;
140 			} else {
141 				*r_addr = loff + prev_value +
142 				    relas->r_addend;
143 			}
144 			break;
145 		/*
146 		 * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
147 		 * slots similarly to how RELOC_GLOB_DAT updates GOT
148 		 * slots.
149 		 */
150 		case RELOC_JMP_SLOT:
151 		case RELOC_GLOB_DAT:
152 			*r_addr = prev_ooff + prev_value + relas->r_addend;
153 			break;
154 #if 0
155 		/* should not be supported ??? */
156 		case RELOC_REL24:
157 		    {
158 			Elf_Addr val = prev_ooff + prev_value +
159 			    relas->r_addend - (Elf_Addr)r_addr;
160 			if (!B24_VALID_RANGE(val)) {
161 				/* invalid offset */
162 				_dl_die("%s: invalid %s offset %llx at %p",
163 				    object->load_name, "REL24", val,
164 				    (void *)r_addr);
165 			}
166 			val &= ~0xfc000003;
167 			val |= (*r_addr & 0xfc000003);
168 			*r_addr = val;
169 
170 			_dl_dcbf(r_addr);
171 		    }
172 		break;
173 #endif
174 #if 0
175 		case RELOC_16_LO:
176 		    {
177 			Elf_Addr val;
178 
179 			val = loff + relas->r_addend;
180 			*(Elf_Half *)r_addr = val;
181 
182 			_dl_dcbf(r_addr);
183 		    }
184 		break;
185 #endif
186 #if 0
187 		case RELOC_16_HI:
188 		    {
189 			Elf_Addr val;
190 
191 			val = loff + relas->r_addend;
192 			*(Elf_Half *)r_addr = (val >> 16);
193 
194 			_dl_dcbf(r_addr);
195 		    }
196 		break;
197 #endif
198 #if 0
199 		case RELOC_16_HA:
200 		    {
201 			Elf_Addr val;
202 
203 			val = loff + relas->r_addend;
204 			*(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
205 
206 			_dl_dcbf(r_addr);
207 		    }
208 		break;
209 #endif
210 		case RELOC_REL14_TAKEN:
211 			/* val |= 1 << (31-10) XXX? */
212 		case RELOC_REL14:
213 		case RELOC_REL14_NTAKEN:
214 		    {
215 			Elf_Addr val = prev_ooff + prev_value +
216 			    relas->r_addend - (Elf_Addr)r_addr;
217 			if (((val & 0xffff8000) != 0) &&
218 			    ((val & 0xffff8000) != 0xffff8000)) {
219 				/* invalid offset */
220 				_dl_die("%s: invalid %s offset %llx at %p",
221 				    object->load_name, "REL14", val,
222 				    (void *)r_addr);
223 			}
224 			val &= ~0xffff0003;
225 			val |= (*r_addr & 0xffff0003);
226 			*r_addr = val;
227 			_dl_dcbf(r_addr);
228 		    }
229 			break;
230 		case RELOC_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 RELOC_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
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 	fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
276 
277 	return fails;
278 }
279 
280 Elf_Addr
281 _dl_bind(elf_object_t *object, int reloff)
282 {
283 	const Elf_Sym *sym;
284 	struct sym_res sr;
285 	const char *symn;
286 	Elf_RelA *relas;
287 	Elf_Addr *plttable;
288 	int64_t cookie = pcookie;
289 	struct {
290 		struct __kbind param;
291 		Elf_Addr newval;
292 	} buf;
293 
294 	relas = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
295 
296 	sym = object->dyn.symtab;
297 	sym += ELF_R_SYM(relas->r_info);
298 	symn = object->dyn.strtab + sym->st_name;
299 
300 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
301 	    sym, object);
302 	if (sr.sym == NULL)
303 		_dl_die("lazy binding failed!");
304 
305 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
306 
307 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
308 		return buf.newval;
309 
310 	plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
311 	buf.param.kb_addr = &plttable[ reloff / sizeof(Elf_RelA) ];
312 	buf.param.kb_size = sizeof(Elf_Addr);
313 
314 	{
315 		register long syscall_num __asm("r0") = SYS_kbind;
316 		register void *arg1 __asm("r3") = &buf.param;
317 		register long  arg2 __asm("r4") = sizeof(struct __kbind) +
318 		    sizeof(Elf_Addr);
319 		register long  arg3 __asm("r5") = cookie;
320 
321 		__asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
322 		    "+r" (arg2) : "r" (arg3) : "cc", "memory");
323 	}
324 
325 	return buf.newval;
326 }
327