xref: /openbsd/libexec/ld.so/mips64/rtld_machine.c (revision 73471bf0)
1 /*	$OpenBSD: rtld_machine.c,v 1.34 2021/11/27 15:13:09 visa 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 static inline void
45 _dl_reloc_ent(Elf_Addr r_addr, Elf_Addr value)
46 {
47 	if ((r_addr & 7) == 0)
48 		*(u_int64_t *)r_addr += value;
49 	else {
50 		/*
51 		 * XXX Handle non aligned relocs. .eh_frame
52 		 * XXX in libstdc++ seems to have them...
53 		 */
54 		u_int64_t robj;
55 
56 		_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
57 		robj += value;
58 		_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
59 	}
60 }
61 
62 int
63 _dl_md_reloc(elf_object_t *object, int rel, int relsz)
64 {
65 	int	i;
66 	int	numrel;
67 	int	fails = 0;
68 	Elf_Addr loff;
69 	Elf_Rel  *relocs;
70 	const Elf_Sym *sym, *this;
71 	Elf_Addr prev_value = 0;
72 	const Elf_Sym *prev_sym = NULL;
73 
74 	loff = object->obj_base;
75 	numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel);
76 	relocs = (Elf_Rel *)(object->Dyn.info[rel]);
77 
78 	if (relocs == NULL)
79 		return 0;
80 
81 	DL_DEB(("relocating %d\n", numrel));
82 	for (i = 0; i < numrel; i++, relocs++) {
83 		Elf_Addr r_addr = relocs->r_offset + loff;
84 		const char *symn;
85 
86 		if (ELF_R_SYM(relocs->r_info) == 0xffffff)
87 			continue;
88 
89 		sym = object->dyn.symtab;
90 		sym += ELF_R_SYM(relocs->r_info);
91 		symn = object->dyn.strtab + sym->st_name;
92 
93 		this = NULL;
94 		if (ELF_R_SYM(relocs->r_info)) {
95 			if (sym == prev_sym)
96 				this = sym;	/* XXX non-NULL */
97 			else if (!(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
98 			    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
99 				struct sym_res sr;
100 
101 				sr = _dl_find_symbol(symn,
102 				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
103 				    sym, object);
104 
105 				if (sr.sym == NULL) {
106 					if (ELF_ST_BIND(sym->st_info) !=
107 					    STB_WEAK)
108 						fails++;
109 					continue;
110 				}
111 				prev_sym = sym;
112 				prev_value = sr.obj->obj_base +
113 				    sr.sym->st_value;
114 				this = sym;	/* XXX non-NULL */
115 			}
116 		}
117 
118 		switch (ELF_R_TYPE(relocs->r_info)) {
119 		case R_MIPS_REL32_64:
120 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
121 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
122 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) )
123 				_dl_reloc_ent(r_addr, loff + sym->st_value);
124 			else if (this)
125 				_dl_reloc_ent(r_addr, prev_value);
126 			break;
127 
128 		case R_MIPS_NONE:
129 			break;
130 
131 		default:
132 			_dl_die("unsupported relocation '%llu'",
133 			    ELF_R_TYPE(relocs->r_info));
134 		}
135 	}
136 
137 	DL_DEB(("done %d fails\n", fails));
138 	return fails;
139 }
140 
141 extern void _dl_bind_start(void);
142 
143 /*
144  *	Relocate the Global Offset Table (GOT). Currently we don't
145  *	do lazy evaluation here because the GNU linker doesn't
146  *	follow the ABI spec which says that if an external symbol
147  *	is referenced by other relocations than CALL16 and 26 it
148  *	should not be given a stub and have a zero value in the
149  *	symbol table. By not doing so, we can't use pointers to
150  *	external functions and use them in comparisons...
151  */
152 int
153 _dl_md_reloc_got(elf_object_t *object, int lazy)
154 {
155 	int	i, n;
156 	Elf_Addr loff;
157 	Elf_Addr *gotp;
158 	const Elf_Sym  *symp;
159 	const char *strt;
160 
161 	if (object->status & STAT_GOT_DONE)
162 		return 0;
163 
164 	loff = object->obj_base;
165 	strt = object->dyn.strtab;
166 	gotp = object->dyn.pltgot;
167 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
168 
169 	DL_DEB(("loff: 0x%lx\n", (unsigned long)loff));
170 	/*
171 	 *  Set up pointers for run time (lazy) resolving.
172 	 */
173 	gotp[0] = (long)_dl_bind_start;
174 	gotp[1] = (long)object;
175 
176 	/*  First do all local references. */
177 	for (i = 2; i < n; i++) {
178 		gotp[i] += loff;
179 	}
180 
181 	gotp += n;
182 
183 	symp =  object->dyn.symtab;
184 	symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
185 	n =  object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
186 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
187 
188 	/*
189 	 *  Then do all global references according to the ABI.
190 	 *  Quickstart is not yet implemented.
191 	 */
192 	while (n--) {
193 		const char *symn = strt + symp->st_name;
194 		struct sym_res sr;
195 
196 		if (symp->st_shndx == SHN_UNDEF &&
197 		    ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
198 			if (symp->st_value == 0 || !lazy) {
199 				sr = _dl_find_symbol(symn,
200 				    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
201 				    symp, object);
202 				if (sr.sym)
203 					*gotp = sr.sym->st_value +
204 					    sr.obj->obj_base;
205 			} else
206 				*gotp = symp->st_value + loff;
207 		} else if (symp->st_shndx == SHN_COMMON ||
208 			symp->st_shndx == SHN_UNDEF) {
209 			sr = _dl_find_symbol(symn,
210 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
211 			    symp, object);
212 			if (sr.sym)
213 				*gotp = sr.sym->st_value + sr.obj->obj_base;
214 		} else if ((ELF_ST_TYPE(symp->st_info) == STT_FUNC &&
215 			symp->st_value != *gotp) ||
216 			ELF_ST_VISIBILITY(symp->st_other) == STV_PROTECTED) {
217 			*gotp += loff;
218 		} else {	/* Resolve all others immediately */
219 			sr = _dl_find_symbol(symn,
220 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
221 			    symp, object);
222 			if (sr.sym)
223 				*gotp = sr.sym->st_value + sr.obj->obj_base;
224 			else
225 				*gotp = symp->st_value + loff;
226 		}
227 		gotp++;
228 		symp++;
229 	}
230 	object->status |= STAT_GOT_DONE;
231 
232 	return 0;
233 }
234 
235 Elf_Addr
236 _dl_bind(elf_object_t *object, int symidx)
237 {
238 	Elf_Addr *gotp = object->dyn.pltgot;
239 	struct sym_res sr;
240 	const Elf_Sym *sym;
241 	const char *symn;
242 	int64_t cookie = pcookie;
243 	struct {
244 		struct __kbind param;
245 		Elf_Addr newval;
246 	} buf;
247 	int n;
248 
249 	sym = object->dyn.symtab;
250 	sym += symidx;
251 	symn = object->dyn.strtab + sym->st_name;
252 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
253 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
254 
255 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
256 	    sym, object);
257 	if (sr.sym == NULL)
258 		_dl_die("lazy binding failed!");
259 
260 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
261 
262 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
263 		return buf.newval;
264 
265 	buf.param.kb_addr = &gotp[n + symidx];
266 	buf.param.kb_size = sizeof(Elf_Addr);
267 
268 	/* directly code the syscall, so that it's actually inline here */
269 	{
270 		register long syscall_num __asm("v0") = SYS_kbind;
271 		register void *arg1 __asm("a0") = &buf;
272 		register long  arg2 __asm("a1") = sizeof(buf);
273 		register long  arg3 __asm("a2") = cookie;
274 
275 		__asm volatile("syscall" : "+r" (syscall_num)
276 		    : "r" (arg1), "r" (arg2), "r" (arg3)
277 		    : "v1", "a3", "memory");
278 	}
279 
280 	return buf.newval;
281 }
282