xref: /openbsd/libexec/ld.so/mips64/rtld_machine.c (revision 264ca280)
1 /*	$OpenBSD: rtld_machine.c,v 1.22 2016/06/18 02:40:46 guenther Exp $ */
2 
3 /*
4  * Copyright (c) 1998-2004 Opsycon AB, Sweden.
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 <link.h>
37 
38 #include "resolve.h"
39 #include "syscall.h"
40 #include "archdep.h"
41 
42 int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
43 
44 
45 int
46 _dl_md_reloc(elf_object_t *object, int rel, int relsz)
47 {
48 	int	i;
49 	int	numrel;
50 	int	fails = 0;
51 	struct load_list *load_list;
52 	Elf64_Addr loff;
53 	Elf64_Addr ooff;
54 	Elf64_Rel  *relocs;
55 	const Elf64_Sym *sym, *this;
56 	Elf64_Addr prev_value = 0;
57 	const Elf64_Sym *prev_sym = NULL;
58 
59 	loff = object->obj_base;
60 	numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel);
61 	relocs = (Elf64_Rel *)(object->Dyn.info[rel]);
62 
63 	if (relocs == NULL)
64 		return(0);
65 
66 	/*
67 	 * Change protection of all write protected segments in the
68 	 * object so we can do relocations in the .rodata section.
69 	 * After relocation restore protection.
70 	 */
71 	if (object->dyn.textrel) {
72 		for (load_list = object->load_list; load_list != NULL; load_list = load_list->next) {
73 			if ((load_list->prot & PROT_WRITE) == 0)
74 				_dl_mprotect(load_list->start, load_list->size,
75 				    PROT_READ | PROT_WRITE);
76 		}
77 	}
78 
79 	DL_DEB(("relocating %d\n", numrel));
80 	for (i = 0; i < numrel; i++, relocs++) {
81 		Elf64_Addr r_addr = relocs->r_offset + loff;
82 		const char *symn;
83 		int type;
84 
85 		if (ELF64_R_SYM(relocs->r_info) == 0xffffff)
86 			continue;
87 
88 		ooff = 0;
89 		sym = object->dyn.symtab;
90 		sym += ELF64_R_SYM(relocs->r_info);
91 		symn = object->dyn.strtab + sym->st_name;
92 		type = ELF64_R_TYPE(relocs->r_info);
93 
94 		this = NULL;
95 		if (ELF64_R_SYM(relocs->r_info)) {
96 			if (sym == prev_sym)
97 				this = sym;	/* XXX non-NULL */
98 			else if (!(ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
99 			    ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
100 				ooff = _dl_find_symbol(symn, &this,
101 				SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
102 				sym, object, NULL);
103 
104 				if (this == NULL) {
105 					if (ELF_ST_BIND(sym->st_info) !=
106 					    STB_WEAK)
107 						fails++;
108 					continue;
109 				}
110 				prev_sym = sym;
111 				prev_value = this->st_value + ooff;
112 			}
113 		}
114 
115 		switch (ELF64_R_TYPE(relocs->r_info)) {
116 			/* XXX Handle non aligned relocs. .eh_frame
117 			 * XXX in libstdc++ seems to have them... */
118 			u_int64_t robj;
119 
120 		case R_MIPS_REL32_64:
121 			if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
122 			    (ELF64_ST_TYPE(sym->st_info) == STT_SECTION ||
123 			    ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
124 				if ((long)r_addr & 7) {
125 					_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
126 					robj += loff + sym->st_value;
127 					_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
128 				} else {
129 					*(u_int64_t *)r_addr += loff + sym->st_value;
130 				}
131 			} else if (this && ((long)r_addr & 7)) {
132 				_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
133 				robj += prev_value;
134 				_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
135 			} else if (this) {
136 				*(u_int64_t *)r_addr += prev_value;
137 			}
138 			break;
139 
140 		case R_MIPS_NONE:
141 			break;
142 
143 		default:
144 			_dl_printf("%s: unsupported relocation '%d'\n",
145 			    __progname, ELF64_R_TYPE(relocs->r_info));
146 			_dl_exit(1);
147 		}
148 	}
149 	DL_DEB(("done %d fails\n", fails));
150 	if (object->dyn.textrel) {
151 		for (load_list = object->load_list; load_list != NULL; load_list = load_list->next) {
152 			if ((load_list->prot & PROT_WRITE) == 0)
153 				_dl_mprotect(load_list->start, load_list->size,
154 				    load_list->prot);
155 		}
156 	}
157 	return(fails);
158 }
159 
160 extern void _dl_bind_start(void);
161 
162 /*
163  *	Relocate the Global Offset Table (GOT). Currently we don't
164  *	do lazy evaluation here because the GNU linker doesn't
165  *	follow the ABI spec which says that if an external symbol
166  *	is referenced by other relocations than CALL16 and 26 it
167  *	should not be given a stub and have a zero value in the
168  *	symbol table. By not doing so, we can't use pointers to
169  *	external functions and use them in comparisons...
170  */
171 int
172 _dl_md_reloc_got(elf_object_t *object, int lazy)
173 {
174 	int	i, n;
175 	Elf64_Addr loff;
176 	Elf64_Addr ooff;
177 	Elf64_Addr *gotp;
178 	const Elf64_Sym  *symp;
179 	const Elf64_Sym  *this;
180 	const char *strt;
181 
182 	if (object->status & STAT_GOT_DONE)
183 		return (0);
184 
185 	loff = object->obj_base;
186 	strt = object->dyn.strtab;
187 	gotp = object->dyn.pltgot;
188 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
189 
190 	DL_DEB(("loff: '%p'\n", loff));
191 	/*
192 	 *  Set up pointers for run time (lazy) resolving.
193 	 */
194 	gotp[0] = (long)_dl_bind_start;
195 	gotp[1] = (long)object;
196 
197 	/*  First do all local references. */
198 	for (i = 2; i < n; i++) {
199 		gotp[i] += loff;
200 	}
201 
202 	gotp += n;
203 
204 	symp =  object->dyn.symtab;
205 	symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
206 	n =  object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
207 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
208 
209 	this = NULL;
210 
211 	if (object->traced)
212 		lazy = 1;
213 
214 	/*
215 	 *  Then do all global references according to the ABI.
216 	 *  Quickstart is not yet implemented.
217 	 */
218 	while (n--) {
219 		if (symp->st_shndx == SHN_UNDEF &&
220 		    ELF64_ST_TYPE(symp->st_info) == STT_FUNC) {
221 			if (symp->st_value == 0 || !lazy) {
222 				this = NULL;
223 				ooff = _dl_find_symbol(strt + symp->st_name,
224 				    &this,
225 				    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
226 				    symp, object, NULL);
227 				if (this)
228 					*gotp = this->st_value + ooff;
229 			} else
230 				*gotp = symp->st_value + loff;
231 		} else if (symp->st_shndx == SHN_COMMON ||
232 			symp->st_shndx == SHN_UNDEF) {
233 			this = NULL;
234 			ooff = _dl_find_symbol(strt + symp->st_name, &this,
235 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
236 			    symp, object, NULL);
237 			if (this)
238 				*gotp = this->st_value + ooff;
239 		} else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC &&
240 			symp->st_value != *gotp) {
241 			*gotp += loff;
242 		} else {	/* Resolve all others immediately */
243 			this = NULL;
244 			ooff = _dl_find_symbol(strt + symp->st_name, &this,
245 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
246 			    symp, object, NULL);
247 			if (this)
248 				*gotp = this->st_value + ooff;
249 			else
250 				*gotp = symp->st_value + loff;
251 		}
252 		gotp++;
253 		symp++;
254 	}
255 	object->status |= STAT_GOT_DONE;
256 
257 	/* mprotect the GOT */
258 	_dl_protect_segment(object, 0, "__got_start", "__got_end", PROT_READ);
259 
260 	return (0);
261 }
262 
263 Elf_Addr
264 _dl_bind(elf_object_t *object, int symidx)
265 {
266 	Elf_Addr *gotp = object->dyn.pltgot;
267 	Elf_Addr ooff;
268 	const Elf_Sym *sym, *this;
269 	const char *symn;
270 	const elf_object_t *sobj;
271 	int64_t cookie = pcookie;
272 	struct {
273 		struct __kbind param;
274 		Elf_Addr newval;
275 	} buf;
276 	int n;
277 
278 	sym = object->dyn.symtab;
279 	sym += symidx;
280 	symn = object->dyn.strtab + sym->st_name;
281 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
282 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
283 
284 	this = NULL;
285 	ooff = _dl_find_symbol(symn, &this,
286 	    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
287 	if (this == NULL) {
288 		_dl_printf("lazy binding failed\n");
289 		*(volatile int *)0 = 0;		/* XXX */
290 	}
291 
292 	buf.newval = ooff + this->st_value;
293 
294 	if (__predict_false(sobj->traced) && _dl_trace_plt(sobj, symn))
295 		return (buf.newval);
296 
297 	buf.param.kb_addr = &gotp[n + symidx];
298 	buf.param.kb_size = sizeof(Elf_Addr);
299 
300 	/* directly code the syscall, so that it's actually inline here */
301 	{
302 		register long syscall_num __asm("v0") = SYS_kbind;
303 		register void *arg1 __asm("a0") = &buf;
304 		register long  arg2 __asm("a1") = sizeof(buf);
305 		register long  arg3 __asm("a2") = cookie;
306 
307 		__asm volatile("syscall" : "+r" (syscall_num)
308 		    : "r" (arg1), "r" (arg2), "r" (arg3)
309 		    : "v1", "a3", "memory");
310 	}
311 
312 	return (buf.newval);
313 }
314