xref: /openbsd-src/libexec/ld.so/mips64/rtld_machine.c (revision b722ba42570161220f25c5d789b5bec8a0166743)
1*b722ba42Sguenther /*	$OpenBSD: rtld_machine.c,v 1.35 2022/01/08 06:49:42 guenther Exp $ */
2d88a553eSpefo 
3d88a553eSpefo /*
4d88a553eSpefo  * Copyright (c) 1998-2004 Opsycon AB, Sweden.
5d88a553eSpefo  *
6d88a553eSpefo  * Redistribution and use in source and binary forms, with or without
7d88a553eSpefo  * modification, are permitted provided that the following conditions
8d88a553eSpefo  * are met:
9d88a553eSpefo  * 1. Redistributions of source code must retain the above copyright
10d88a553eSpefo  *    notice, this list of conditions and the following disclaimer.
11d88a553eSpefo  * 2. Redistributions in binary form must reproduce the above copyright
12d88a553eSpefo  *    notice, this list of conditions and the following disclaimer in the
13d88a553eSpefo  *    documentation and/or other materials provided with the distribution.
14d88a553eSpefo  *
15d88a553eSpefo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16d88a553eSpefo  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17d88a553eSpefo  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18d88a553eSpefo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19d88a553eSpefo  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20d88a553eSpefo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21d88a553eSpefo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22d88a553eSpefo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23d88a553eSpefo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24d88a553eSpefo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25d88a553eSpefo  * SUCH DAMAGE.
26d88a553eSpefo  *
27d88a553eSpefo  */
28d88a553eSpefo 
29d88a553eSpefo #define _DYN_LOADER
30d88a553eSpefo 
31d88a553eSpefo #include <sys/types.h>
32*b722ba42Sguenther #include <sys/exec_elf.h>
337ad95679Sguenther #include <sys/syscall.h>
347ad95679Sguenther #include <sys/unistd.h>
35d88a553eSpefo 
36*b722ba42Sguenther #include <machine/reloc.h>
373b9f2527Skettenis 
38*b722ba42Sguenther #include "util.h"
39d88a553eSpefo #include "resolve.h"
40d88a553eSpefo 
417ad95679Sguenther int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
427ad95679Sguenther 
43143e5accSguenther static inline void
_dl_reloc_ent(Elf_Addr r_addr,Elf_Addr value)44e3b0f1d9Sguenther _dl_reloc_ent(Elf_Addr r_addr, Elf_Addr value)
45143e5accSguenther {
46143e5accSguenther 	if ((r_addr & 7) == 0)
47143e5accSguenther 		*(u_int64_t *)r_addr += value;
48143e5accSguenther 	else {
49143e5accSguenther 		/*
50143e5accSguenther 		 * XXX Handle non aligned relocs. .eh_frame
51143e5accSguenther 		 * XXX in libstdc++ seems to have them...
52143e5accSguenther 		 */
53143e5accSguenther 		u_int64_t robj;
54143e5accSguenther 
55143e5accSguenther 		_dl_bcopy((char *)r_addr, &robj, sizeof(robj));
56143e5accSguenther 		robj += value;
57143e5accSguenther 		_dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
58143e5accSguenther 	}
59143e5accSguenther }
607ad95679Sguenther 
61d88a553eSpefo int
_dl_md_reloc(elf_object_t * object,int rel,int relsz)62d88a553eSpefo _dl_md_reloc(elf_object_t *object, int rel, int relsz)
63d88a553eSpefo {
64d88a553eSpefo 	int	i;
65d88a553eSpefo 	int	numrel;
66d88a553eSpefo 	int	fails = 0;
67e3b0f1d9Sguenther 	Elf_Addr loff;
68e3b0f1d9Sguenther 	Elf_Rel  *relocs;
69e3b0f1d9Sguenther 	const Elf_Sym *sym, *this;
70e3b0f1d9Sguenther 	Elf_Addr prev_value = 0;
71e3b0f1d9Sguenther 	const Elf_Sym *prev_sym = NULL;
72d88a553eSpefo 
73ce11e090Skurt 	loff = object->obj_base;
74e3b0f1d9Sguenther 	numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel);
75e3b0f1d9Sguenther 	relocs = (Elf_Rel *)(object->Dyn.info[rel]);
76d88a553eSpefo 
77d88a553eSpefo 	if (relocs == NULL)
78e3b0f1d9Sguenther 		return 0;
79d88a553eSpefo 
80dc594e96Spefo 	DL_DEB(("relocating %d\n", numrel));
81d88a553eSpefo 	for (i = 0; i < numrel; i++, relocs++) {
82e3b0f1d9Sguenther 		Elf_Addr r_addr = relocs->r_offset + loff;
83d88a553eSpefo 		const char *symn;
84d88a553eSpefo 
85e3b0f1d9Sguenther 		if (ELF_R_SYM(relocs->r_info) == 0xffffff)
86d88a553eSpefo 			continue;
87d88a553eSpefo 
88d88a553eSpefo 		sym = object->dyn.symtab;
89e3b0f1d9Sguenther 		sym += ELF_R_SYM(relocs->r_info);
90d88a553eSpefo 		symn = object->dyn.strtab + sym->st_name;
91d88a553eSpefo 
926718d15cSdrahn 		this = NULL;
93e3b0f1d9Sguenther 		if (ELF_R_SYM(relocs->r_info)) {
9488098a4dSguenther 			if (sym == prev_sym)
9588098a4dSguenther 				this = sym;	/* XXX non-NULL */
96e3b0f1d9Sguenther 			else if (!(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
97e3b0f1d9Sguenther 			    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
98143e5accSguenther 				struct sym_res sr;
996718d15cSdrahn 
100143e5accSguenther 				sr = _dl_find_symbol(symn,
101143e5accSguenther 				    SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
102143e5accSguenther 				    sym, object);
103143e5accSguenther 
104143e5accSguenther 				if (sr.sym == NULL) {
10588098a4dSguenther 					if (ELF_ST_BIND(sym->st_info) !=
10688098a4dSguenther 					    STB_WEAK)
107d88a553eSpefo 						fails++;
1086718d15cSdrahn 					continue;
1096718d15cSdrahn 				}
11088098a4dSguenther 				prev_sym = sym;
111143e5accSguenther 				prev_value = sr.obj->obj_base +
112143e5accSguenther 				    sr.sym->st_value;
113143e5accSguenther 				this = sym;	/* XXX non-NULL */
11488098a4dSguenther 			}
115d88a553eSpefo 		}
116d88a553eSpefo 
117e3b0f1d9Sguenther 		switch (ELF_R_TYPE(relocs->r_info)) {
118dc594e96Spefo 		case R_MIPS_REL32_64:
119e3b0f1d9Sguenther 			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
120e3b0f1d9Sguenther 			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
121e3b0f1d9Sguenther 			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) )
122143e5accSguenther 				_dl_reloc_ent(r_addr, loff + sym->st_value);
123143e5accSguenther 			else if (this)
124143e5accSguenther 				_dl_reloc_ent(r_addr, prev_value);
125d88a553eSpefo 			break;
126d88a553eSpefo 
127d88a553eSpefo 		case R_MIPS_NONE:
128d88a553eSpefo 			break;
129d88a553eSpefo 
130d88a553eSpefo 		default:
131a647091dSguenther 			_dl_die("unsupported relocation '%llu'",
132e3b0f1d9Sguenther 			    ELF_R_TYPE(relocs->r_info));
133d88a553eSpefo 		}
134d88a553eSpefo 	}
13590273bfbSguenther 
136dc594e96Spefo 	DL_DEB(("done %d fails\n", fails));
137e3b0f1d9Sguenther 	return fails;
138d88a553eSpefo }
139d88a553eSpefo 
1403b9f2527Skettenis extern void _dl_bind_start(void);
1413b9f2527Skettenis 
142d88a553eSpefo /*
143d88a553eSpefo  *	Relocate the Global Offset Table (GOT). Currently we don't
144d88a553eSpefo  *	do lazy evaluation here because the GNU linker doesn't
145d88a553eSpefo  *	follow the ABI spec which says that if an external symbol
146d88a553eSpefo  *	is referenced by other relocations than CALL16 and 26 it
147d88a553eSpefo  *	should not be given a stub and have a zero value in the
148d88a553eSpefo  *	symbol table. By not doing so, we can't use pointers to
149d88a553eSpefo  *	external functions and use them in comparisons...
150d88a553eSpefo  */
151e9cfe40cSmiod int
_dl_md_reloc_got(elf_object_t * object,int lazy)152d88a553eSpefo _dl_md_reloc_got(elf_object_t *object, int lazy)
153d88a553eSpefo {
154d88a553eSpefo 	int	i, n;
155e3b0f1d9Sguenther 	Elf_Addr loff;
156e3b0f1d9Sguenther 	Elf_Addr *gotp;
157e3b0f1d9Sguenther 	const Elf_Sym  *symp;
158d88a553eSpefo 	const char *strt;
159d88a553eSpefo 
160d88a553eSpefo 	if (object->status & STAT_GOT_DONE)
161e3b0f1d9Sguenther 		return 0;
162d88a553eSpefo 
163ce11e090Skurt 	loff = object->obj_base;
164d88a553eSpefo 	strt = object->dyn.strtab;
165d88a553eSpefo 	gotp = object->dyn.pltgot;
166d88a553eSpefo 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
167d88a553eSpefo 
168e5b55072Sguenther 	DL_DEB(("loff: 0x%lx\n", (unsigned long)loff));
169d88a553eSpefo 	/*
170d88a553eSpefo 	 *  Set up pointers for run time (lazy) resolving.
171d88a553eSpefo 	 */
1723b9f2527Skettenis 	gotp[0] = (long)_dl_bind_start;
1733b9f2527Skettenis 	gotp[1] = (long)object;
174d88a553eSpefo 
175d88a553eSpefo 	/*  First do all local references. */
1763b9f2527Skettenis 	for (i = 2; i < n; i++) {
177d88a553eSpefo 		gotp[i] += loff;
178d88a553eSpefo 	}
179d88a553eSpefo 
180d88a553eSpefo 	gotp += n;
181d88a553eSpefo 
182d88a553eSpefo 	symp =  object->dyn.symtab;
183d88a553eSpefo 	symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
184d88a553eSpefo 	n =  object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
185d88a553eSpefo 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
186d88a553eSpefo 
187d88a553eSpefo 	/*
188d88a553eSpefo 	 *  Then do all global references according to the ABI.
189d88a553eSpefo 	 *  Quickstart is not yet implemented.
190d88a553eSpefo 	 */
191d88a553eSpefo 	while (n--) {
192143e5accSguenther 		const char *symn = strt + symp->st_name;
193143e5accSguenther 		struct sym_res sr;
194143e5accSguenther 
195d88a553eSpefo 		if (symp->st_shndx == SHN_UNDEF &&
196e3b0f1d9Sguenther 		    ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
197d88a553eSpefo 			if (symp->st_value == 0 || !lazy) {
198143e5accSguenther 				sr = _dl_find_symbol(symn,
199d88a553eSpefo 				    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
200143e5accSguenther 				    symp, object);
201143e5accSguenther 				if (sr.sym)
202143e5accSguenther 					*gotp = sr.sym->st_value +
203143e5accSguenther 					    sr.obj->obj_base;
204d88a553eSpefo 			} else
2053b9f2527Skettenis 				*gotp = symp->st_value + loff;
206d88a553eSpefo 		} else if (symp->st_shndx == SHN_COMMON ||
207d88a553eSpefo 			symp->st_shndx == SHN_UNDEF) {
208143e5accSguenther 			sr = _dl_find_symbol(symn,
209d88a553eSpefo 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
210143e5accSguenther 			    symp, object);
211143e5accSguenther 			if (sr.sym)
212143e5accSguenther 				*gotp = sr.sym->st_value + sr.obj->obj_base;
213e3b0f1d9Sguenther 		} else if ((ELF_ST_TYPE(symp->st_info) == STT_FUNC &&
21404885a94Sguenther 			symp->st_value != *gotp) ||
21504885a94Sguenther 			ELF_ST_VISIBILITY(symp->st_other) == STV_PROTECTED) {
216d88a553eSpefo 			*gotp += loff;
217515da77eSkrw 		} else {	/* Resolve all others immediately */
218143e5accSguenther 			sr = _dl_find_symbol(symn,
21997f696bbSpefo 			    SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
220143e5accSguenther 			    symp, object);
221143e5accSguenther 			if (sr.sym)
222143e5accSguenther 				*gotp = sr.sym->st_value + sr.obj->obj_base;
22397f696bbSpefo 			else
224d88a553eSpefo 				*gotp = symp->st_value + loff;
225d88a553eSpefo 		}
226d88a553eSpefo 		gotp++;
227d88a553eSpefo 		symp++;
228d88a553eSpefo 	}
229d88a553eSpefo 	object->status |= STAT_GOT_DONE;
230d88a553eSpefo 
231e3b0f1d9Sguenther 	return 0;
232d88a553eSpefo }
2333b9f2527Skettenis 
2343b9f2527Skettenis Elf_Addr
_dl_bind(elf_object_t * object,int symidx)2353b9f2527Skettenis _dl_bind(elf_object_t *object, int symidx)
2363b9f2527Skettenis {
2373b9f2527Skettenis 	Elf_Addr *gotp = object->dyn.pltgot;
238143e5accSguenther 	struct sym_res sr;
239143e5accSguenther 	const Elf_Sym *sym;
2403b9f2527Skettenis 	const char *symn;
2417ad95679Sguenther 	int64_t cookie = pcookie;
2427ad95679Sguenther 	struct {
2437ad95679Sguenther 		struct __kbind param;
2447ad95679Sguenther 		Elf_Addr newval;
2457ad95679Sguenther 	} buf;
2463b9f2527Skettenis 	int n;
2473b9f2527Skettenis 
2483b9f2527Skettenis 	sym = object->dyn.symtab;
2493b9f2527Skettenis 	sym += symidx;
2503b9f2527Skettenis 	symn = object->dyn.strtab + sym->st_name;
2513b9f2527Skettenis 	n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
2523b9f2527Skettenis 	    object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
2533b9f2527Skettenis 
254143e5accSguenther 	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
255143e5accSguenther 	    sym, object);
256143e5accSguenther 	if (sr.sym == NULL)
2573b50b772Sguenther 		_dl_die("lazy binding failed!");
2583b9f2527Skettenis 
259143e5accSguenther 	buf.newval = sr.obj->obj_base + sr.sym->st_value;
260ae398163Smiod 
261143e5accSguenther 	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
262e3b0f1d9Sguenther 		return buf.newval;
2633b9f2527Skettenis 
2647ad95679Sguenther 	buf.param.kb_addr = &gotp[n + symidx];
2657ad95679Sguenther 	buf.param.kb_size = sizeof(Elf_Addr);
2667ad95679Sguenther 
2677ad95679Sguenther 	/* directly code the syscall, so that it's actually inline here */
2687ad95679Sguenther 	{
2697ad95679Sguenther 		register long syscall_num __asm("v0") = SYS_kbind;
2707ad95679Sguenther 		register void *arg1 __asm("a0") = &buf;
2717ad95679Sguenther 		register long  arg2 __asm("a1") = sizeof(buf);
2727ad95679Sguenther 		register long  arg3 __asm("a2") = cookie;
2737ad95679Sguenther 
2747ad95679Sguenther 		__asm volatile("syscall" : "+r" (syscall_num)
2757ad95679Sguenther 		    : "r" (arg1), "r" (arg2), "r" (arg3)
2767ad95679Sguenther 		    : "v1", "a3", "memory");
2773b9f2527Skettenis 	}
2783b9f2527Skettenis 
279e3b0f1d9Sguenther 	return buf.newval;
2803b9f2527Skettenis }
281