Changeset View
Changeset View
Standalone View
Standalone View
sys/compat/linux/linux_vdso.c
Show All 32 Lines | |||||
#define __ELF_WORD_SIZE 32 | #define __ELF_WORD_SIZE 32 | ||||
#else | #else | ||||
#define __ELF_WORD_SIZE 64 | #define __ELF_WORD_SIZE 64 | ||||
#endif | #endif | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/elf.h> | #include <sys/elf.h> | ||||
#include <sys/imgact.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/proc.h> | ||||
#include <sys/rwlock.h> | #include <sys/rwlock.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <vm/vm.h> | |||||
#include <vm/vm_param.h> | #include <vm/vm_param.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.h> | ||||
#include <vm/vm_kern.h> | |||||
#include <vm/vm_map.h> | #include <vm/vm_map.h> | ||||
#include <vm/vm_object.h> | #include <vm/vm_object.h> | ||||
#include <vm/vm_page.h> | #include <vm/vm_page.h> | ||||
#include <vm/vm_pager.h> | #include <vm/vm_pager.h> | ||||
#include <compat/linux/linux_vdso.h> | #include <compat/linux/linux_vdso.h> | ||||
SLIST_HEAD(, linux_vdso_sym) __elfN(linux_vdso_syms) = | SLIST_HEAD(, linux_vdso_sym) __elfN(linux_vdso_syms) = | ||||
SLIST_HEAD_INITIALIZER(__elfN(linux_vdso_syms)); | SLIST_HEAD_INITIALIZER(__elfN(linux_vdso_syms)); | ||||
static int __elfN(symtabindex); | |||||
static int __elfN(symstrindex); | |||||
static void | |||||
__elfN(linux_vdso_lookup)(Elf_Ehdr *, struct linux_vdso_sym *); | |||||
void | void | ||||
__elfN(linux_vdso_sym_init)(struct linux_vdso_sym *s) | __elfN(linux_vdso_sym_init)(struct linux_vdso_sym *s) | ||||
{ | { | ||||
SLIST_INSERT_HEAD(&__elfN(linux_vdso_syms), s, sym); | SLIST_INSERT_HEAD(&__elfN(linux_vdso_syms), s, sym); | ||||
} | } | ||||
vm_object_t | vm_object_t | ||||
__elfN(linux_shared_page_init)(char **mapping) | __elfN(linux_shared_page_init)(char **mapping, vm_size_t size) | ||||
{ | { | ||||
vm_page_t m; | vm_page_t m; | ||||
vm_object_t obj; | vm_object_t obj; | ||||
vm_offset_t addr; | vm_offset_t addr; | ||||
size_t n, pages; | |||||
obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE, | pages = size / PAGE_SIZE; | ||||
addr = kva_alloc(size); | |||||
obj = vm_pager_allocate(OBJT_PHYS, 0, size, | |||||
VM_PROT_DEFAULT, 0, NULL); | VM_PROT_DEFAULT, 0, NULL); | ||||
VM_OBJECT_WLOCK(obj); | VM_OBJECT_WLOCK(obj); | ||||
m = vm_page_grab(obj, 0, VM_ALLOC_ZERO); | for (n = 0; n < pages; n++) { | ||||
VM_OBJECT_WUNLOCK(obj); | m = vm_page_grab(obj, n, | ||||
VM_ALLOC_ZERO); | |||||
vm_page_valid(m); | vm_page_valid(m); | ||||
vm_page_xunbusy(m); | vm_page_xunbusy(m); | ||||
addr = kva_alloc(PAGE_SIZE); | pmap_qenter(addr + n * PAGE_SIZE, &m, 1); | ||||
pmap_qenter(addr, &m, 1); | } | ||||
VM_OBJECT_WUNLOCK(obj); | |||||
*mapping = (char *)addr; | *mapping = (char *)addr; | ||||
return (obj); | return (obj); | ||||
} | } | ||||
void | void | ||||
__elfN(linux_shared_page_fini)(vm_object_t obj, void *mapping) | __elfN(linux_shared_page_fini)(vm_object_t obj, void *mapping, | ||||
vm_size_t size) | |||||
{ | { | ||||
vm_offset_t va; | vm_offset_t va; | ||||
va = (vm_offset_t)mapping; | va = (vm_offset_t)mapping; | ||||
pmap_qremove(va, 1); | pmap_qremove(va, size / PAGE_SIZE); | ||||
kva_free(va, PAGE_SIZE); | kva_free(va, size); | ||||
vm_object_deallocate(obj); | vm_object_deallocate(obj); | ||||
} | } | ||||
void | void | ||||
__elfN(linux_vdso_fixup)(struct sysentvec *sv) | __elfN(linux_vdso_fixup)(char *base, vm_offset_t offset) | ||||
{ | { | ||||
struct linux_vdso_sym *lsym; | |||||
const Elf_Shdr *shdr; | |||||
Elf_Ehdr *ehdr; | Elf_Ehdr *ehdr; | ||||
Elf_Shdr *shdr; | Elf_Sym *dsym, *sym; | ||||
int i; | char *strtab, *symname; | ||||
int i, symcnt; | |||||
ehdr = (Elf_Ehdr *) sv->sv_sigcode; | ehdr = (Elf_Ehdr *)base; | ||||
if (!IS_ELF(*ehdr) || | MPASS(IS_ELF(*ehdr)); | ||||
ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || | MPASS(ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS); | ||||
ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || | MPASS(ehdr->e_ident[EI_DATA] == ELF_TARG_DATA); | ||||
ehdr->e_ident[EI_VERSION] != EV_CURRENT || | MPASS(ehdr->e_ident[EI_VERSION] == EV_CURRENT); | ||||
ehdr->e_shoff == 0 || | MPASS(ehdr->e_shentsize == sizeof(Elf_Shdr)); | ||||
ehdr->e_shentsize != sizeof(Elf_Shdr)) | MPASS(ehdr->e_shoff != 0); | ||||
panic("Linux invalid vdso header.\n"); | MPASS(ehdr->e_type == ET_DYN); | ||||
if (ehdr->e_type != ET_DYN) | shdr = (const Elf_Shdr *)(base + ehdr->e_shoff); | ||||
panic("Linux invalid vdso header.\n"); | |||||
shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff); | dsym = NULL; | ||||
__elfN(symtabindex) = -1; | |||||
__elfN(symstrindex) = -1; | |||||
for (i = 0; i < ehdr->e_shnum; i++) { | for (i = 0; i < ehdr->e_shnum; i++) { | ||||
if (shdr[i].sh_size == 0) | if (shdr[i].sh_size == 0) | ||||
continue; | continue; | ||||
if (shdr[i].sh_type == SHT_DYNSYM) { | if (shdr[i].sh_type == SHT_DYNSYM) { | ||||
__elfN(symtabindex) = i; | dsym = (Elf_Sym *)(base + shdr[i].sh_offset); | ||||
__elfN(symstrindex) = shdr[i].sh_link; | strtab = base + shdr[shdr[i].sh_link].sh_offset; | ||||
symcnt = shdr[i].sh_size / sizeof(*dsym); | |||||
break; | |||||
} | } | ||||
} | } | ||||
MPASS(dsym != NULL); | |||||
if (__elfN(symtabindex) == -1 || __elfN(symstrindex) == -1) | |||||
panic("Linux invalid vdso header.\n"); | |||||
ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX; | ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX; | ||||
} | |||||
void | /* | ||||
__elfN(linux_vdso_reloc)(struct sysentvec *sv) | * VDSO is readonly mapped to the process VA and | ||||
{ | * can't be relocated by rtld. | ||||
struct linux_vdso_sym *lsym; | */ | ||||
Elf_Ehdr *ehdr; | SLIST_FOREACH(lsym, &__elfN(linux_vdso_syms), sym) { | ||||
Elf_Phdr *phdr; | for (i = 0, sym = dsym; i < symcnt; i++, sym++) { | ||||
Elf_Shdr *shdr; | symname = strtab + sym->st_name; | ||||
Elf_Dyn *dyn; | if (strncmp(lsym->symname, symname, lsym->size) == 0) { | ||||
Elf_Sym *sym; | sym->st_value += offset; | ||||
int i, j, symcnt; | *lsym->ptr = sym->st_value; | ||||
ehdr = (Elf_Ehdr *) sv->sv_sigcode; | |||||
/* Adjust our so relative to the sigcode_base */ | |||||
if (sv->sv_shared_page_base != 0) { | |||||
ehdr->e_entry += sv->sv_shared_page_base; | |||||
phdr = (Elf_Phdr *)((caddr_t)ehdr + ehdr->e_phoff); | |||||
/* phdrs */ | |||||
for (i = 0; i < ehdr->e_phnum; i++) { | |||||
phdr[i].p_vaddr += sv->sv_shared_page_base; | |||||
if (phdr[i].p_type != PT_DYNAMIC) | |||||
continue; | |||||
dyn = (Elf_Dyn *)((caddr_t)ehdr + phdr[i].p_offset); | |||||
for(; dyn->d_tag != DT_NULL; dyn++) { | |||||
switch (dyn->d_tag) { | |||||
case DT_PLTGOT: | |||||
case DT_HASH: | |||||
case DT_STRTAB: | |||||
case DT_SYMTAB: | |||||
case DT_RELA: | |||||
case DT_INIT: | |||||
case DT_FINI: | |||||
case DT_REL: | |||||
case DT_DEBUG: | |||||
case DT_JMPREL: | |||||
case DT_VERSYM: | |||||
case DT_VERDEF: | |||||
case DT_VERNEED: | |||||
case DT_ADDRRNGLO ... DT_ADDRRNGHI: | |||||
dyn->d_un.d_ptr += sv->sv_shared_page_base; | |||||
break; | break; | ||||
case DT_ENCODING ... DT_LOOS-1: | |||||
case DT_LOOS ... DT_HIOS: | |||||
if (dyn->d_tag >= DT_ENCODING && | |||||
(dyn->d_tag & 1) == 0) | |||||
dyn->d_un.d_ptr += sv->sv_shared_page_base; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* sections */ | |||||
shdr = (Elf_Shdr *)((caddr_t)ehdr + ehdr->e_shoff); | |||||
for(i = 0; i < ehdr->e_shnum; i++) { | |||||
if (!(shdr[i].sh_flags & SHF_ALLOC)) | |||||
continue; | |||||
shdr[i].sh_addr += sv->sv_shared_page_base; | |||||
if (shdr[i].sh_type != SHT_SYMTAB && | |||||
shdr[i].sh_type != SHT_DYNSYM) | |||||
continue; | |||||
sym = (Elf_Sym *)((caddr_t)ehdr + shdr[i].sh_offset); | |||||
symcnt = shdr[i].sh_size / sizeof(*sym); | |||||
for(j = 0; j < symcnt; j++, sym++) { | |||||
if (sym->st_shndx == SHN_UNDEF || | |||||
sym->st_shndx == SHN_ABS) | |||||
continue; | |||||
sym->st_value += sv->sv_shared_page_base; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
SLIST_FOREACH(lsym, &__elfN(linux_vdso_syms), sym) | |||||
__elfN(linux_vdso_lookup)(ehdr, lsym); | |||||
} | } | ||||
static void | int | ||||
__elfN(linux_vdso_lookup)(Elf_Ehdr *ehdr, struct linux_vdso_sym *vsym) | linux_map_vdso(struct proc *p, vm_object_t obj, vm_offset_t base, | ||||
vm_offset_t size, struct image_params *imgp) | |||||
{ | { | ||||
vm_offset_t strtab, symname; | struct vmspace *vmspace; | ||||
uint32_t symcnt; | vm_map_t map; | ||||
Elf_Shdr *shdr; | int error; | ||||
int i; | |||||
shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff); | MPASS((imgp->sysent->sv_flags & SV_ABI_MASK) == SV_ABI_LINUX); | ||||
MPASS(obj != NULL); | |||||
strtab = (vm_offset_t)((caddr_t)ehdr + | vmspace = p->p_vmspace; | ||||
shdr[__elfN(symstrindex)].sh_offset); | map = &vmspace->vm_map; | ||||
Elf_Sym *sym = (Elf_Sym *)((caddr_t)ehdr + | |||||
shdr[__elfN(symtabindex)].sh_offset); | |||||
symcnt = shdr[__elfN(symtabindex)].sh_size / sizeof(*sym); | |||||
for (i = 0; i < symcnt; ++i, ++sym) { | vm_object_reference(obj); | ||||
symname = strtab + sym->st_name; | error = vm_map_fixed(map, obj, 0, base, size, | ||||
if (strncmp(vsym->symname, (char *)symname, vsym->size) == 0) { | VM_PROT_READ | VM_PROT_EXECUTE, | ||||
*vsym->ptr = (uintptr_t)sym->st_value; | VM_PROT_READ | VM_PROT_EXECUTE, | ||||
break; | MAP_INHERIT_SHARE | MAP_ACC_NO_CHARGE); | ||||
if (error != KERN_SUCCESS) { | |||||
vm_object_deallocate(obj); | |||||
return (vm_mmap_to_errno(error)); | |||||
} | } | ||||
} | return (0); | ||||
} | } |