1283407Sdchagin/*-
2283407Sdchagin * Copyright (c) 2013 Dmitry Chagin
3283407Sdchagin * All rights reserved.
4283407Sdchagin *
5283407Sdchagin * Redistribution and use in source and binary forms, with or without
6283407Sdchagin * modification, are permitted provided that the following conditions
7283407Sdchagin * are met:
8283407Sdchagin * 1. Redistributions of source code must retain the above copyright
9283407Sdchagin *    notice, this list of conditions and the following disclaimer
10283407Sdchagin *    in this position and unchanged.
11283407Sdchagin * 2. Redistributions in binary form must reproduce the above copyright
12283407Sdchagin *    notice, this list of conditions and the following disclaimer in the
13283407Sdchagin *    documentation and/or other materials provided with the distribution.
14283407Sdchagin *
15283407Sdchagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16283407Sdchagin * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17283407Sdchagin * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18283407Sdchagin * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19283407Sdchagin * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20283407Sdchagin * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21283407Sdchagin * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22283407Sdchagin * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23283407Sdchagin * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24283407Sdchagin * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25283407Sdchagin */
26283407Sdchagin
27283407Sdchagin#include <sys/cdefs.h>
28283407Sdchagin__FBSDID("$FreeBSD$");
29283407Sdchagin
30283407Sdchagin#include "opt_compat.h"
31283407Sdchagin
32293523Sdchagin#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
33283407Sdchagin#define	__ELF_WORD_SIZE	32
34293523Sdchagin#else
35293523Sdchagin#define	__ELF_WORD_SIZE	64
36293523Sdchagin#endif
37283407Sdchagin
38283407Sdchagin#include <sys/param.h>
39283407Sdchagin#include <sys/systm.h>
40283407Sdchagin#include <sys/elf.h>
41283407Sdchagin#include <sys/kernel.h>
42283407Sdchagin#include <sys/lock.h>
43283407Sdchagin#include <sys/rwlock.h>
44283407Sdchagin#include <sys/queue.h>
45283407Sdchagin#include <sys/sysent.h>
46283407Sdchagin
47283407Sdchagin#include <vm/vm.h>
48283407Sdchagin#include <vm/vm_param.h>
49283407Sdchagin#include <vm/pmap.h>
50283407Sdchagin#include <vm/vm_extern.h>
51283407Sdchagin#include <vm/vm_kern.h>
52283407Sdchagin#include <vm/vm_map.h>
53283407Sdchagin#include <vm/vm_object.h>
54283407Sdchagin#include <vm/vm_page.h>
55283407Sdchagin#include <vm/vm_pager.h>
56283407Sdchagin
57283407Sdchagin#include <compat/linux/linux_vdso.h>
58283407Sdchagin
59283407SdchaginSLIST_HEAD(, linux_vdso_sym) __elfN(linux_vdso_syms) =
60283407Sdchagin    SLIST_HEAD_INITIALIZER(__elfN(linux_vdso_syms));
61283407Sdchagin
62283407Sdchaginstatic int __elfN(symtabindex);
63283407Sdchaginstatic int __elfN(symstrindex);
64283407Sdchagin
65283407Sdchaginstatic void
66283407Sdchagin__elfN(linux_vdso_lookup)(Elf_Ehdr *, struct linux_vdso_sym *);
67283407Sdchagin
68283407Sdchagin
69283407Sdchaginvoid
70283407Sdchagin__elfN(linux_vdso_sym_init)(struct linux_vdso_sym *s)
71283407Sdchagin{
72283407Sdchagin
73283407Sdchagin	SLIST_INSERT_HEAD(&__elfN(linux_vdso_syms), s, sym);
74283407Sdchagin}
75283407Sdchagin
76283407Sdchaginvm_object_t
77283407Sdchagin__elfN(linux_shared_page_init)(char **mapping)
78283407Sdchagin{
79283407Sdchagin	vm_page_t m;
80283407Sdchagin	vm_object_t obj;
81283407Sdchagin	vm_offset_t addr;
82283407Sdchagin
83283407Sdchagin	obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE,
84283407Sdchagin	    VM_PROT_DEFAULT, 0, NULL);
85283407Sdchagin	VM_OBJECT_WLOCK(obj);
86283407Sdchagin	m = vm_page_grab(obj, 0, VM_ALLOC_NOBUSY | VM_ALLOC_ZERO);
87283407Sdchagin	m->valid = VM_PAGE_BITS_ALL;
88283407Sdchagin	VM_OBJECT_WUNLOCK(obj);
89283407Sdchagin	addr = kva_alloc(PAGE_SIZE);
90283407Sdchagin	pmap_qenter(addr, &m, 1);
91283407Sdchagin	*mapping = (char *)addr;
92283407Sdchagin	return (obj);
93283407Sdchagin}
94283407Sdchagin
95283407Sdchaginvoid
96283407Sdchagin__elfN(linux_shared_page_fini)(vm_object_t obj)
97283407Sdchagin{
98283407Sdchagin
99283407Sdchagin	vm_object_deallocate(obj);
100283407Sdchagin}
101283407Sdchagin
102283407Sdchaginvoid
103283407Sdchagin__elfN(linux_vdso_fixup)(struct sysentvec *sv)
104283407Sdchagin{
105283407Sdchagin	Elf_Ehdr *ehdr;
106283407Sdchagin	Elf_Shdr *shdr;
107283407Sdchagin	int i;
108283407Sdchagin
109283407Sdchagin	ehdr = (Elf_Ehdr *) sv->sv_sigcode;
110283407Sdchagin
111283407Sdchagin	if (!IS_ELF(*ehdr) ||
112283407Sdchagin	    ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
113283407Sdchagin	    ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
114283407Sdchagin	    ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
115283407Sdchagin	    ehdr->e_shoff == 0 ||
116283407Sdchagin	    ehdr->e_shentsize != sizeof(Elf_Shdr))
117283407Sdchagin		panic("Linux invalid vdso header.\n");
118283407Sdchagin
119283407Sdchagin	if (ehdr->e_type != ET_DYN)
120283407Sdchagin		panic("Linux invalid vdso header.\n");
121283407Sdchagin
122283407Sdchagin	shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff);
123283407Sdchagin
124283407Sdchagin	__elfN(symtabindex) = -1;
125283407Sdchagin	__elfN(symstrindex) = -1;
126283407Sdchagin	for (i = 0; i < ehdr->e_shnum; i++) {
127283407Sdchagin		if (shdr[i].sh_size == 0)
128283407Sdchagin			continue;
129283407Sdchagin		if (shdr[i].sh_type == SHT_DYNSYM) {
130283407Sdchagin			__elfN(symtabindex) = i;
131283407Sdchagin			__elfN(symstrindex) = shdr[i].sh_link;
132283407Sdchagin		}
133283407Sdchagin	}
134283407Sdchagin
135283407Sdchagin	if (__elfN(symtabindex) == -1 || __elfN(symstrindex) == -1)
136283407Sdchagin		panic("Linux invalid vdso header.\n");
137283407Sdchagin
138283407Sdchagin	ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX;
139283407Sdchagin}
140283407Sdchagin
141283407Sdchaginvoid
142293523Sdchagin__elfN(linux_vdso_reloc)(struct sysentvec *sv, long vdso_adjust)
143283407Sdchagin{
144283407Sdchagin	struct linux_vdso_sym *lsym;
145283407Sdchagin	Elf_Ehdr *ehdr;
146283407Sdchagin	Elf_Phdr *phdr;
147283407Sdchagin	Elf_Shdr *shdr;
148283407Sdchagin	Elf_Dyn *dyn;
149283407Sdchagin	Elf_Sym *sym;
150283407Sdchagin	int i, symcnt;
151283407Sdchagin
152283407Sdchagin	ehdr = (Elf_Ehdr *) sv->sv_sigcode;
153283407Sdchagin
154283407Sdchagin	/* Adjust our so relative to the sigcode_base */
155283407Sdchagin	if (vdso_adjust != 0) {
156283407Sdchagin		ehdr->e_entry += vdso_adjust;
157283407Sdchagin		phdr = (Elf_Phdr *)((caddr_t)ehdr + ehdr->e_phoff);
158283407Sdchagin
159283407Sdchagin		/* phdrs */
160283407Sdchagin		for (i = 0; i < ehdr->e_phnum; i++) {
161283407Sdchagin			phdr[i].p_vaddr += vdso_adjust;
162283407Sdchagin			if (phdr[i].p_type != PT_DYNAMIC)
163283407Sdchagin				continue;
164283407Sdchagin			dyn = (Elf_Dyn *)((caddr_t)ehdr + phdr[i].p_offset);
165283407Sdchagin			for(; dyn->d_tag != DT_NULL; dyn++) {
166283407Sdchagin				switch (dyn->d_tag) {
167283407Sdchagin				case DT_PLTGOT:
168283407Sdchagin				case DT_HASH:
169283407Sdchagin				case DT_STRTAB:
170283407Sdchagin				case DT_SYMTAB:
171283407Sdchagin				case DT_RELA:
172283407Sdchagin				case DT_INIT:
173283407Sdchagin				case DT_FINI:
174283407Sdchagin				case DT_REL:
175283407Sdchagin				case DT_DEBUG:
176283407Sdchagin				case DT_JMPREL:
177283407Sdchagin				case DT_VERSYM:
178283407Sdchagin				case DT_VERDEF:
179283407Sdchagin				case DT_VERNEED:
180283407Sdchagin				case DT_ADDRRNGLO ... DT_ADDRRNGHI:
181283407Sdchagin					dyn->d_un.d_ptr += vdso_adjust;
182283407Sdchagin					break;
183283407Sdchagin				case DT_ENCODING ... DT_LOOS-1:
184283407Sdchagin				case DT_LOOS ... DT_HIOS:
185283407Sdchagin					if (dyn->d_tag >= DT_ENCODING &&
186283407Sdchagin					    (dyn->d_tag & 1) == 0)
187283407Sdchagin						dyn->d_un.d_ptr += vdso_adjust;
188283407Sdchagin					break;
189283407Sdchagin				default:
190283407Sdchagin					break;
191283407Sdchagin				}
192283407Sdchagin			}
193283407Sdchagin		}
194283407Sdchagin
195283407Sdchagin		/* sections */
196283407Sdchagin		shdr = (Elf_Shdr *)((caddr_t)ehdr + ehdr->e_shoff);
197283407Sdchagin		for(i = 0; i < ehdr->e_shnum; i++) {
198283407Sdchagin			if (!(shdr[i].sh_flags & SHF_ALLOC))
199283407Sdchagin				continue;
200283407Sdchagin			shdr[i].sh_addr += vdso_adjust;
201283407Sdchagin			if (shdr[i].sh_type != SHT_SYMTAB &&
202283407Sdchagin			    shdr[i].sh_type != SHT_DYNSYM)
203283407Sdchagin				continue;
204283407Sdchagin
205283407Sdchagin			sym = (Elf_Sym *)((caddr_t)ehdr + shdr[i].sh_offset);
206283407Sdchagin			symcnt = shdr[i].sh_size / sizeof(*sym);
207283407Sdchagin
208283407Sdchagin			for(i = 0; i < symcnt; i++, sym++) {
209283407Sdchagin				if (sym->st_shndx == SHN_UNDEF ||
210283407Sdchagin				    sym->st_shndx == SHN_ABS)
211283407Sdchagin					continue;
212283407Sdchagin				sym->st_value += vdso_adjust;
213283407Sdchagin			}
214283407Sdchagin		}
215283407Sdchagin	}
216283407Sdchagin
217283407Sdchagin	SLIST_FOREACH(lsym, &__elfN(linux_vdso_syms), sym)
218283407Sdchagin		__elfN(linux_vdso_lookup)(ehdr, lsym);
219283407Sdchagin}
220283407Sdchagin
221283407Sdchaginstatic void
222283407Sdchagin__elfN(linux_vdso_lookup)(Elf_Ehdr *ehdr, struct linux_vdso_sym *vsym)
223283407Sdchagin{
224283407Sdchagin	vm_offset_t strtab, symname;
225283407Sdchagin	uint32_t symcnt;
226283407Sdchagin	Elf_Shdr *shdr;
227283407Sdchagin	int i;
228283407Sdchagin
229283407Sdchagin	shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff);
230283407Sdchagin
231283407Sdchagin	strtab = (vm_offset_t)((caddr_t)ehdr +
232283407Sdchagin	    shdr[__elfN(symstrindex)].sh_offset);
233283407Sdchagin	Elf_Sym *sym = (Elf_Sym *)((caddr_t)ehdr +
234283407Sdchagin	    shdr[__elfN(symtabindex)].sh_offset);
235283407Sdchagin	symcnt = shdr[__elfN(symtabindex)].sh_size / sizeof(*sym);
236283407Sdchagin
237283407Sdchagin	for (i = 0; i < symcnt; ++i, ++sym) {
238283407Sdchagin		symname = strtab + sym->st_name;
239283407Sdchagin		if (strncmp(vsym->symname, (char *)symname, vsym->size) == 0) {
240283407Sdchagin			*vsym->ptr = (uintptr_t)sym->st_value;
241283407Sdchagin			break;
242283407Sdchagin		}
243283407Sdchagin	}
244283407Sdchagin}
245