Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/linux/linux_sysvec.c
Show All 35 Lines | |||||
#include <sys/exec.h> | #include <sys/exec.h> | ||||
#include <sys/imgact.h> | #include <sys/imgact.h> | ||||
#include <sys/imgact_elf.h> | #include <sys/imgact_elf.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/stddef.h> | |||||
#include <sys/signalvar.h> | #include <sys/signalvar.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <vm/vm.h> | |||||
#include <vm/pmap.h> | |||||
#include <vm/vm_map.h> | |||||
#include <vm/vm_extern.h> | |||||
#include <vm/vm_object.h> | |||||
#include <vm/vm_page.h> | |||||
#include <vm/vm_param.h> | #include <vm/vm_param.h> | ||||
#include <arm64/linux/linux.h> | #include <arm64/linux/linux.h> | ||||
#include <arm64/linux/linux_proto.h> | #include <arm64/linux/linux_proto.h> | ||||
#include <compat/linux/linux_dtrace.h> | #include <compat/linux/linux_dtrace.h> | ||||
#include <compat/linux/linux_emul.h> | #include <compat/linux/linux_emul.h> | ||||
#include <compat/linux/linux_ioctl.h> | #include <compat/linux/linux_ioctl.h> | ||||
#include <compat/linux/linux_mib.h> | #include <compat/linux/linux_mib.h> | ||||
#include <compat/linux/linux_misc.h> | #include <compat/linux/linux_misc.h> | ||||
#include <compat/linux/linux_util.h> | #include <compat/linux/linux_util.h> | ||||
#include <compat/linux/linux_vdso.h> | #include <compat/linux/linux_vdso.h> | ||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#ifdef VFP | #ifdef VFP | ||||
#include <machine/vfp.h> | #include <machine/vfp.h> | ||||
#endif | #endif | ||||
MODULE_VERSION(linux64elf, 1); | MODULE_VERSION(linux64elf, 1); | ||||
#define LINUX_VDSOPAGE_SIZE PAGE_SIZE * 2 | |||||
#define LINUX_VDSOPAGE (VM_MAXUSER_ADDRESS - \ | |||||
LINUX_VDSOPAGE_SIZE) | |||||
#define LINUX_SHAREDPAGE (LINUX_VDSOPAGE - PAGE_SIZE) | |||||
/* | |||||
* PAGE_SIZE - the size | |||||
* of the native SHAREDPAGE | |||||
*/ | |||||
#define LINUX_USRSTACK LINUX_SHAREDPAGE | |||||
#define LINUX_PS_STRINGS (LINUX_USRSTACK - \ | |||||
sizeof(struct ps_strings)) | |||||
static int linux_szsigcode; | static int linux_szsigcode; | ||||
static vm_object_t linux_shared_page_obj; | static vm_object_t linux_vdso_obj; | ||||
static char *linux_shared_page_mapping; | static char *linux_vdso_mapping; | ||||
extern char _binary_linux_locore_o_start; | extern char _binary_linux_vdso_so_o_start; | ||||
extern char _binary_linux_locore_o_end; | extern char _binary_linux_vdso_so_o_end; | ||||
static vm_offset_t linux_vdso_base; | |||||
extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL]; | extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL]; | ||||
SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); | SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); | ||||
static int linux_copyout_strings(struct image_params *imgp, | static int linux_copyout_strings(struct image_params *imgp, | ||||
uintptr_t *stack_base); | uintptr_t *stack_base); | ||||
static int linux_elf_fixup(uintptr_t *stack_base, | static int linux_elf_fixup(uintptr_t *stack_base, | ||||
struct image_params *iparams); | struct image_params *iparams); | ||||
static bool linux_trans_osrel(const Elf_Note *note, int32_t *osrel); | static bool linux_trans_osrel(const Elf_Note *note, int32_t *osrel); | ||||
static void linux_vdso_install(const void *param); | static void linux_vdso_install(const void *param); | ||||
static void linux_vdso_deinstall(const void *param); | static void linux_vdso_deinstall(const void *param); | ||||
static void linux_vdso_reloc(char *mapping, Elf_Addr offset); | |||||
static void linux_set_syscall_retval(struct thread *td, int error); | static void linux_set_syscall_retval(struct thread *td, int error); | ||||
static int linux_fetch_syscall_args(struct thread *td); | static int linux_fetch_syscall_args(struct thread *td); | ||||
static void linux_exec_setregs(struct thread *td, struct image_params *imgp, | static void linux_exec_setregs(struct thread *td, struct image_params *imgp, | ||||
uintptr_t stack); | uintptr_t stack); | ||||
static void linux_exec_sysvec_init(void *param); | |||||
static int linux_on_exec_vmspace(struct proc *p, struct image_params *imgp); | |||||
static int linux_vsyscall(struct thread *td); | static int linux_vsyscall(struct thread *td); | ||||
/* DTrace init */ | /* DTrace init */ | ||||
LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); | LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); | ||||
/* DTrace probes */ | /* DTrace probes */ | ||||
LIN_SDT_PROBE_DEFINE2(sysvec, linux_translate_traps, todo, "int", "int"); | LIN_SDT_PROBE_DEFINE2(sysvec, linux_translate_traps, todo, "int", "int"); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_exec_setregs, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_exec_setregs, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_copyout_auxargs, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_copyout_auxargs, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_elf_fixup, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_elf_fixup, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_rt_sigreturn, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_rt_sigreturn, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_rt_sendsig, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_rt_sendsig, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_vsyscall, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_vsyscall, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_vdso_install, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_vdso_install, todo); | ||||
LIN_SDT_PROBE_DEFINE0(sysvec, linux_vdso_deinstall, todo); | LIN_SDT_PROBE_DEFINE0(sysvec, linux_vdso_deinstall, todo); | ||||
LINUX_VDSO_SYM_CHAR(linux_platform); | |||||
LINUX_VDSO_SYM_INTPTR(kern_timekeep_base); | |||||
LINUX_VDSO_SYM_INTPTR(__kernel_rt_sigreturn); | |||||
/* LINUXTODO: do we have traps to translate? */ | /* LINUXTODO: do we have traps to translate? */ | ||||
static int | static int | ||||
linux_translate_traps(int signal, int trap_code) | linux_translate_traps(int signal, int trap_code) | ||||
{ | { | ||||
LIN_SDT_PROBE2(sysvec, linux_translate_traps, todo, signal, trap_code); | LIN_SDT_PROBE2(sysvec, linux_translate_traps, todo, signal, trap_code); | ||||
return (signal); | return (signal); | ||||
} | } | ||||
LINUX_VDSO_SYM_CHAR(linux_platform); | |||||
static int | static int | ||||
linux_fetch_syscall_args(struct thread *td) | linux_fetch_syscall_args(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
struct syscall_args *sa; | struct syscall_args *sa; | ||||
register_t *ap; | register_t *ap; | ||||
p = td->td_proc; | p = td->td_proc; | ||||
Show All 39 Lines | linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) | ||||
LIN_SDT_PROBE0(sysvec, linux_copyout_auxargs, todo); | LIN_SDT_PROBE0(sysvec, linux_copyout_auxargs, todo); | ||||
p = imgp->proc; | p = imgp->proc; | ||||
args = (Elf64_Auxargs *)imgp->auxargs; | args = (Elf64_Auxargs *)imgp->auxargs; | ||||
argarray = pos = malloc(LINUX_AT_COUNT * sizeof(*pos), M_TEMP, | argarray = pos = malloc(LINUX_AT_COUNT * sizeof(*pos), M_TEMP, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
issetugid = p->p_flag & P_SUGID ? 1 : 0; | issetugid = p->p_flag & P_SUGID ? 1 : 0; | ||||
AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR, | AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR, linux_vdso_base); | ||||
imgp->proc->p_sysent->sv_shared_page_base); | |||||
AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, *imgp->sysent->sv_hwcap); | AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, *imgp->sysent->sv_hwcap); | ||||
AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); | AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); | ||||
AUXARGS_ENTRY(pos, LINUX_AT_CLKTCK, stclohz); | AUXARGS_ENTRY(pos, LINUX_AT_CLKTCK, stclohz); | ||||
AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); | AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); | ||||
AUXARGS_ENTRY(pos, AT_PHENT, args->phent); | AUXARGS_ENTRY(pos, AT_PHENT, args->phent); | ||||
AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); | AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); | ||||
AUXARGS_ENTRY(pos, AT_BASE, args->base); | AUXARGS_ENTRY(pos, AT_BASE, args->base); | ||||
AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); | AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); | ||||
▲ Show 20 Lines • Show All 226 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
struct sysentvec elf_linux_sysvec = { | struct sysentvec elf_linux_sysvec = { | ||||
.sv_size = LINUX_SYS_MAXSYSCALL, | .sv_size = LINUX_SYS_MAXSYSCALL, | ||||
.sv_table = linux_sysent, | .sv_table = linux_sysent, | ||||
.sv_transtrap = linux_translate_traps, | .sv_transtrap = linux_translate_traps, | ||||
.sv_fixup = linux_elf_fixup, | .sv_fixup = linux_elf_fixup, | ||||
.sv_sendsig = linux_rt_sendsig, | .sv_sendsig = linux_rt_sendsig, | ||||
.sv_sigcode = &_binary_linux_locore_o_start, | .sv_sigcode = &_binary_linux_vdso_so_o_start, | ||||
.sv_szsigcode = &linux_szsigcode, | .sv_szsigcode = &linux_szsigcode, | ||||
.sv_name = "Linux ELF64", | .sv_name = "Linux ELF64", | ||||
.sv_coredump = elf64_coredump, | .sv_coredump = elf64_coredump, | ||||
.sv_imgact_try = linux_exec_imgact_try, | .sv_imgact_try = linux_exec_imgact_try, | ||||
.sv_minsigstksz = LINUX_MINSIGSTKSZ, | .sv_minsigstksz = LINUX_MINSIGSTKSZ, | ||||
.sv_minuser = VM_MIN_ADDRESS, | .sv_minuser = VM_MIN_ADDRESS, | ||||
.sv_maxuser = VM_MAXUSER_ADDRESS, | .sv_maxuser = VM_MAXUSER_ADDRESS, | ||||
.sv_usrstack = USRSTACK, | .sv_usrstack = LINUX_USRSTACK, | ||||
.sv_psstrings = PS_STRINGS, /* XXX */ | .sv_psstrings = LINUX_PS_STRINGS, | ||||
.sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, | .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, | ||||
.sv_copyout_auxargs = linux_copyout_auxargs, | .sv_copyout_auxargs = linux_copyout_auxargs, | ||||
.sv_copyout_strings = linux_copyout_strings, | .sv_copyout_strings = linux_copyout_strings, | ||||
.sv_setregs = linux_exec_setregs, | .sv_setregs = linux_exec_setregs, | ||||
.sv_fixlimit = NULL, | .sv_fixlimit = NULL, | ||||
.sv_maxssiz = NULL, | .sv_maxssiz = NULL, | ||||
.sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN | | .sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN | | ||||
SV_SIG_WAITNDQ, | SV_SIG_WAITNDQ | SV_TIMEKEEP, | ||||
.sv_set_syscall_retval = linux_set_syscall_retval, | .sv_set_syscall_retval = linux_set_syscall_retval, | ||||
.sv_fetch_syscall_args = linux_fetch_syscall_args, | .sv_fetch_syscall_args = linux_fetch_syscall_args, | ||||
.sv_syscallnames = NULL, | .sv_syscallnames = NULL, | ||||
.sv_shared_page_base = SHAREDPAGE, | .sv_shared_page_base = LINUX_SHAREDPAGE, | ||||
.sv_shared_page_len = PAGE_SIZE, | .sv_shared_page_len = PAGE_SIZE, | ||||
.sv_schedtail = linux_schedtail, | .sv_schedtail = linux_schedtail, | ||||
.sv_thread_detach = linux_thread_detach, | .sv_thread_detach = linux_thread_detach, | ||||
.sv_trap = linux_vsyscall, | .sv_trap = linux_vsyscall, | ||||
.sv_hwcap = &elf_hwcap, | .sv_hwcap = &elf_hwcap, | ||||
.sv_hwcap2 = &elf_hwcap2, | .sv_hwcap2 = &elf_hwcap2, | ||||
.sv_onexec = linux_on_exec, | .sv_onexec = linux_on_exec_vmspace, | ||||
.sv_onexit = linux_on_exit, | .sv_onexit = linux_on_exit, | ||||
.sv_ontdexit = linux_thread_dtor, | .sv_ontdexit = linux_thread_dtor, | ||||
.sv_setid_allowed = &linux_setid_allowed_query, | .sv_setid_allowed = &linux_setid_allowed_query, | ||||
}; | }; | ||||
static int | |||||
linux_on_exec_vmspace(struct proc *p, struct image_params *imgp) | |||||
{ | |||||
int error; | |||||
error = linux_map_vdso(p, linux_vdso_obj, linux_vdso_base, | |||||
LINUX_VDSOPAGE_SIZE, imgp); | |||||
if (error == 0) | |||||
linux_on_exec(p, imgp); | |||||
return (error); | |||||
} | |||||
static void | static void | ||||
linux_vdso_install(const void *param) | linux_exec_sysvec_init(void *param) | ||||
{ | { | ||||
l_uintptr_t *ktimekeep_base; | |||||
struct sysentvec *sv; | |||||
ptrdiff_t tkoff; | |||||
linux_szsigcode = (&_binary_linux_locore_o_end - | sv = param; | ||||
&_binary_linux_locore_o_start); | /* Fill timekeep_base */ | ||||
exec_sysvec_init_abi(sv); | |||||
if (linux_szsigcode > elf_linux_sysvec.sv_shared_page_len) | tkoff = kern_timekeep_base - linux_vdso_base; | ||||
panic("invalid Linux VDSO size\n"); | ktimekeep_base = (l_uintptr_t *)(linux_vdso_mapping + tkoff); | ||||
*ktimekeep_base = sv->sv_timekeep_base; | |||||
} | |||||
SYSINIT(elf_linux_exec_sysvec_init, SI_SUB_EXEC, SI_ORDER_ANY, | |||||
linux_exec_sysvec_init, &elf_linux_sysvec); | |||||
__elfN(linux_vdso_fixup)(&elf_linux_sysvec); | static void | ||||
linux_vdso_install(const void *param) | |||||
{ | |||||
char *vdso_start = &_binary_linux_vdso_so_o_start; | |||||
char *vdso_end = &_binary_linux_vdso_so_o_end; | |||||
linux_shared_page_obj = __elfN(linux_shared_page_init) | linux_szsigcode = vdso_end - vdso_start; | ||||
(&linux_shared_page_mapping); | MPASS(linux_szsigcode <= LINUX_VDSOPAGE_SIZE); | ||||
__elfN(linux_vdso_reloc)(&elf_linux_sysvec); | linux_vdso_base = LINUX_VDSOPAGE; | ||||
memcpy(linux_shared_page_mapping, elf_linux_sysvec.sv_sigcode, | __elfN(linux_vdso_fixup)(vdso_start, linux_vdso_base); | ||||
linux_szsigcode); | |||||
elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj; | linux_vdso_obj = __elfN(linux_shared_page_init) | ||||
(&linux_vdso_mapping, LINUX_VDSOPAGE_SIZE); | |||||
bcopy(vdso_start, linux_vdso_mapping, linux_szsigcode); | |||||
linux_vdso_reloc(linux_vdso_mapping, linux_vdso_base); | |||||
} | } | ||||
SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_ANY, | SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_FIRST, | ||||
linux_vdso_install, NULL); | linux_vdso_install, NULL); | ||||
static void | static void | ||||
linux_vdso_deinstall(const void *param) | linux_vdso_deinstall(const void *param) | ||||
{ | { | ||||
LIN_SDT_PROBE0(sysvec, linux_vdso_deinstall, todo); | __elfN(linux_shared_page_fini)(linux_vdso_obj, | ||||
__elfN(linux_shared_page_fini)(linux_shared_page_obj, | linux_vdso_mapping, LINUX_VDSOPAGE_SIZE); | ||||
linux_shared_page_mapping); | |||||
} | } | ||||
SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST, | SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST, | ||||
linux_vdso_deinstall, NULL); | linux_vdso_deinstall, NULL); | ||||
static void | |||||
linux_vdso_reloc(char *mapping, Elf_Addr offset) | |||||
{ | |||||
Elf_Size rtype, symidx; | |||||
const Elf_Rela *rela; | |||||
const Elf_Shdr *shdr; | |||||
const Elf_Ehdr *ehdr; | |||||
Elf_Addr *where; | |||||
Elf_Addr addr, addend; | |||||
int i, relacnt; | |||||
MPASS(offset != 0); | |||||
relacnt = 0; | |||||
ehdr = (const Elf_Ehdr *)mapping; | |||||
shdr = (const Elf_Shdr *)(mapping + ehdr->e_shoff); | |||||
for (i = 0; i < ehdr->e_shnum; i++) | |||||
{ | |||||
switch (shdr[i].sh_type) { | |||||
case SHT_REL: | |||||
printf("Linux Aarch64 vDSO: unexpected Rel section\n"); | |||||
break; | |||||
case SHT_RELA: | |||||
rela = (const Elf_Rela *)(mapping + shdr[i].sh_offset); | |||||
relacnt = shdr[i].sh_size / sizeof(*rela); | |||||
} | |||||
} | |||||
for (i = 0; i < relacnt; i++, rela++) { | |||||
where = (Elf_Addr *)(mapping + rela->r_offset); | |||||
addend = rela->r_addend; | |||||
rtype = ELF_R_TYPE(rela->r_info); | |||||
symidx = ELF_R_SYM(rela->r_info); | |||||
switch (rtype) { | |||||
case R_AARCH64_NONE: /* none */ | |||||
break; | |||||
case R_AARCH64_RELATIVE: /* B + A */ | |||||
addr = (Elf_Addr)(mapping + addend); | |||||
if (*where != addr) | |||||
*where = addr; | |||||
break; | |||||
default: | |||||
printf("Linux Aarch64 vDSO: unexpected relocation type %ld, " | |||||
"symbol index %ld\n", rtype, symidx); | |||||
} | |||||
} | |||||
} | |||||
static char GNU_ABI_VENDOR[] = "GNU"; | static char GNU_ABI_VENDOR[] = "GNU"; | ||||
static int GNU_ABI_LINUX = 0; | static int GNU_ABI_LINUX = 0; | ||||
/* LINUXTODO: deduplicate */ | /* LINUXTODO: deduplicate */ | ||||
static bool | static bool | ||||
linux_trans_osrel(const Elf_Note *note, int32_t *osrel) | linux_trans_osrel(const Elf_Note *note, int32_t *osrel) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 96 Lines • Show Last 20 Lines |