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