rtld_machine.c revision 1.14
1/*	$OpenBSD: rtld_machine.c,v 1.14 2019/11/26 23:38:52 guenther Exp $ */
2
3/*
4 * Copyright (c) 2004 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
43int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
44
45void _dl_bind_start(void) __dso_hidden;
46
47#define R_TYPE(x) R_AARCH64_ ## x
48
49int
50_dl_md_reloc(elf_object_t *object, int rel, int relsz)
51{
52	long	i;
53	long	numrel;
54	long	relrel;
55	Elf_Addr loff;
56	Elf_Addr prev_value = 0;
57	const Elf_Sym *prev_sym = NULL;
58	Elf_RelA *rels;
59
60	loff = object->obj_base;
61	numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA);
62	relrel = object->relcount;
63	rels = (Elf_RelA *)(object->Dyn.info[rel]);
64
65	if (rels == NULL)
66		return 0;
67
68	if (relrel > numrel)
69		_dl_die("relcount > numrel: %ld > %ld", relrel, numrel);
70
71	/* tight loop for leading RELATIVE relocs */
72	for (i = 0; i < relrel; i++, rels++) {
73		Elf_Addr *where;
74
75		where = (Elf_Addr *)(rels->r_offset + loff);
76		*where += loff;
77	}
78	for (; i < numrel; i++, rels++) {
79		Elf_Addr *where, value;
80		Elf_Word type;
81		const Elf_Sym *sym;
82		const char *symn;
83
84		where = (Elf_Addr *)(rels->r_offset + loff);
85
86		sym = object->dyn.symtab;
87		sym += ELF_R_SYM(rels->r_info);
88		symn = object->dyn.strtab + sym->st_name;
89
90		type = ELF_R_TYPE(rels->r_info);
91		switch (type) {
92		case R_TYPE(NONE):
93		case R_TYPE(JUMP_SLOT):		/* shouldn't happen */
94			continue;
95
96		case R_TYPE(RELATIVE):
97			*where = loff + rels->r_addend;
98			continue;
99
100		case R_TYPE(ABS64):
101			value = rels->r_addend;
102			break;
103
104		case R_TYPE(GLOB_DAT):
105			value = 0;
106			break;
107
108		case R_TYPE(COPY):
109		{
110			struct sym_res sr;
111
112			sr = _dl_find_symbol(symn,
113			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
114			    sym, object);
115			if (sr.sym == NULL)
116				return 1;
117
118			value = sr.obj->obj_base + sr.sym->st_value;
119			_dl_bcopy((void *)value, where, sym->st_size);
120			continue;
121		}
122
123		default:
124			_dl_die("bad relocation %d", type);
125		}
126
127		if (sym->st_shndx != SHN_UNDEF &&
128		    ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
129			value += loff;
130		} else if (sym == prev_sym) {
131			value += prev_value;
132		} else {
133			struct sym_res sr;
134
135			sr = _dl_find_symbol(symn,
136			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
137			    sym, object);
138			if (sr.sym == NULL) {
139				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
140					return 1;
141				continue;
142			}
143			prev_sym = sym;
144			prev_value = sr.obj->obj_base + sr.sym->st_value;
145			value += prev_value;
146		}
147
148		*where = value;
149	}
150
151	return 0;
152}
153
154static int
155_dl_md_reloc_all_plt(elf_object_t *object, const Elf_RelA *reloc,
156    const Elf_RelA *rend)
157{
158	for (; reloc < rend; reloc++) {
159		const Elf_Sym *sym;
160		const char *symn;
161		Elf_Addr *where;
162		struct sym_res sr;
163
164		sym = object->dyn.symtab;
165		sym += ELF_R_SYM(reloc->r_info);
166		symn = object->dyn.strtab + sym->st_name;
167
168		sr = _dl_find_symbol(symn,
169		    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object);
170		if (sr.sym == NULL) {
171			if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
172				return 1;
173			continue;
174		}
175
176		where = (Elf_Addr *)(reloc->r_offset + object->obj_base);
177		*where = sr.obj->obj_base + sr.sym->st_value;
178	}
179
180	return 0;
181}
182
183/*
184 *	Relocate the Global Offset Table (GOT).
185 *	This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
186 *	otherwise the lazy binding plt initialization is performed.
187 */
188int
189_dl_md_reloc_got(elf_object_t *object, int lazy)
190{
191	Elf_Addr	*pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
192	const Elf_RelA	*reloc, *rend;
193
194	if (pltgot == NULL)
195		return 0; /* it is possible to have no PLT/GOT relocations */
196
197	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
198		return 0;
199
200	if (object->traced)
201		lazy = 1;
202
203	reloc = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
204	rend = (Elf_RelA *)((char *)reloc + object->Dyn.info[DT_PLTRELSZ]);
205
206	if (!lazy)
207		return _dl_md_reloc_all_plt(object, reloc, rend);
208
209	/* Lazy */
210	pltgot[1] = (Elf_Addr)object;
211	pltgot[2] = (Elf_Addr)_dl_bind_start;
212
213	for (; reloc < rend; reloc++) {
214		Elf_Addr *where;
215		where = (Elf_Addr *)(reloc->r_offset + object->obj_base);
216		*where += object->obj_base;
217	}
218
219	return 0;
220}
221
222Elf_Addr
223_dl_bind(elf_object_t *object, int relidx)
224{
225	Elf_RelA *rel;
226	const Elf_Sym *sym;
227	const char *symn;
228	struct sym_res sr;
229	int64_t cookie = pcookie;
230	struct {
231		struct __kbind param;
232		Elf_Addr newval;
233	} buf;
234
235	rel = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (relidx);
236
237	sym = object->dyn.symtab;
238	sym += ELF_R_SYM(rel->r_info);
239	symn = object->dyn.strtab + sym->st_name;
240
241	sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
242	    sym, object);
243	if (sr.sym == NULL)
244		_dl_die("lazy binding failed!");
245
246	buf.newval = sr.obj->obj_base + sr.sym->st_value;
247
248	if (sr.obj->traced && _dl_trace_plt(sr.obj, symn))
249		return buf.newval;
250
251	buf.param.kb_addr = (Elf_Word *)(object->obj_base + rel->r_offset);
252	buf.param.kb_size = sizeof(Elf_Addr);
253
254	/* directly code the syscall, so that it's actually inline here */
255	{
256		register long syscall_num __asm("x8") = SYS_kbind;
257		register void *arg1 __asm("x0") = &buf;
258		register long  arg2 __asm("x1") = sizeof(buf);
259		register long  arg3 __asm("x2") = cookie;
260
261		__asm volatile("svc 0" : "+r" (arg1), "+r" (arg2)
262		    : "r" (syscall_num), "r" (arg3)
263		    : "cc", "memory");
264	}
265
266	return buf.newval;
267}
268