Changeset View
Changeset View
Standalone View
Standalone View
sys/i386/linux/linux_sysvec.c
Show All 36 Lines | |||||
#include <sys/imgact_aout.h> | #include <sys/imgact_aout.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/malloc.h> | #include <sys/malloc.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/syscallsubr.h> | #include <sys/syscallsubr.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/vnode.h> | #include <sys/vnode.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.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_param.h> | #include <vm/vm_param.h> | ||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||
#include <machine/cputypes.h> | #include <machine/cputypes.h> | ||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/pcb.h> | #include <machine/pcb.h> | ||||
#include <machine/trap.h> | #include <machine/trap.h> | ||||
#include <x86/linux/linux_x86.h> | |||||
#include <i386/linux/linux.h> | #include <i386/linux/linux.h> | ||||
#include <i386/linux/linux_proto.h> | #include <i386/linux/linux_proto.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_signal.h> | #include <compat/linux/linux_signal.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> | ||||
MODULE_VERSION(linux, 1); | MODULE_VERSION(linux, 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)) | #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_fixup(uintptr_t *stack_base, | static int linux_fixup(uintptr_t *stack_base, | ||||
struct image_params *iparams); | struct image_params *iparams); | ||||
static int linux_fixup_elf(uintptr_t *stack_base, | static int linux_fixup_elf(uintptr_t *stack_base, | ||||
struct image_params *iparams); | struct image_params *iparams); | ||||
static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask); | static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask); | ||||
static void linux_exec_setregs(struct thread *td, | static void linux_exec_setregs(struct thread *td, | ||||
struct image_params *imgp, uintptr_t stack); | struct image_params *imgp, 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_copyout_strings(struct image_params *imgp, | static int linux_copyout_strings(struct image_params *imgp, | ||||
uintptr_t *stack_base); | uintptr_t *stack_base); | ||||
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(void *param); | static void linux_vdso_install(void *param); | ||||
static void linux_vdso_deinstall(void *param); | static void linux_vdso_deinstall(void *param); | ||||
static void linux_vdso_reloc(char *mapping, Elf_Addr offset); | |||||
#define LINUX_T_UNKNOWN 255 | #define LINUX_T_UNKNOWN 255 | ||||
static int _bsd_to_linux_trapcode[] = { | static int _bsd_to_linux_trapcode[] = { | ||||
LINUX_T_UNKNOWN, /* 0 */ | LINUX_T_UNKNOWN, /* 0 */ | ||||
6, /* 1 T_PRIVINFLT */ | 6, /* 1 T_PRIVINFLT */ | ||||
LINUX_T_UNKNOWN, /* 2 */ | LINUX_T_UNKNOWN, /* 2 */ | ||||
3, /* 3 T_BPTFLT */ | 3, /* 3 T_BPTFLT */ | ||||
LINUX_T_UNKNOWN, /* 4 */ | LINUX_T_UNKNOWN, /* 4 */ | ||||
Show All 25 Lines | static int _bsd_to_linux_trapcode[] = { | ||||
15 /* 30 T_RESERVED */ | 15 /* 30 T_RESERVED */ | ||||
}; | }; | ||||
#define bsd_to_linux_trapcode(code) \ | #define bsd_to_linux_trapcode(code) \ | ||||
((code)<nitems(_bsd_to_linux_trapcode)? \ | ((code)<nitems(_bsd_to_linux_trapcode)? \ | ||||
_bsd_to_linux_trapcode[(code)]: \ | _bsd_to_linux_trapcode[(code)]: \ | ||||
LINUX_T_UNKNOWN) | LINUX_T_UNKNOWN) | ||||
LINUX_VDSO_SYM_CHAR(linux_platform); | LINUX_VDSO_SYM_CHAR(linux_platform); | ||||
LINUX_VDSO_SYM_INTPTR(linux_sigcode); | LINUX_VDSO_SYM_INTPTR(__kernel_vsyscall); | ||||
LINUX_VDSO_SYM_INTPTR(linux_rt_sigcode); | LINUX_VDSO_SYM_INTPTR(__kernel_sigreturn); | ||||
LINUX_VDSO_SYM_INTPTR(linux_vsyscall); | LINUX_VDSO_SYM_INTPTR(__kernel_rt_sigreturn); | ||||
LINUX_VDSO_SYM_INTPTR(kern_timekeep_base); | |||||
LINUX_VDSO_SYM_INTPTR(kern_tsc_selector); | |||||
/* | /* | ||||
* If FreeBSD & Linux have a difference of opinion about what a trap | * If FreeBSD & Linux have a difference of opinion about what a trap | ||||
* means, deal with it here. | * means, deal with it here. | ||||
* | * | ||||
* MPSAFE | * MPSAFE | ||||
*/ | */ | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | linux_copyout_auxargs(struct image_params *imgp, uintptr_t base) | ||||
p = imgp->proc; | p = imgp->proc; | ||||
issetugid = imgp->proc->p_flag & P_SUGID ? 1 : 0; | issetugid = imgp->proc->p_flag & P_SUGID ? 1 : 0; | ||||
arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; | arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; | ||||
args = (Elf32_Auxargs *)imgp->auxargs; | args = (Elf32_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); | ||||
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_SYSINFO, __kernel_vsyscall); | ||||
AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO, linux_vsyscall); | |||||
AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, cpu_feature); | AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, cpu_feature); | ||||
/* | /* | ||||
* Do not export AT_CLKTCK when emulating Linux kernel prior to 2.4.0, | * Do not export AT_CLKTCK when emulating Linux kernel prior to 2.4.0, | ||||
* as it has appeared in the 2.4.0-rc7 first time. | * as it has appeared in the 2.4.0-rc7 first time. | ||||
* Being exported, AT_CLKTCK is returned by sysconf(_SC_CLK_TCK), | * Being exported, AT_CLKTCK is returned by sysconf(_SC_CLK_TCK), | ||||
* glibc falls back to the hard-coded CLK_TCK value when aux entry | * glibc falls back to the hard-coded CLK_TCK value when aux entry | ||||
* is not present. | * is not present. | ||||
▲ Show 20 Lines • Show All 247 Lines • ▼ Show 20 Lines | if (copyout(&frame, fp, sizeof(frame)) != 0) { | ||||
* instruction to halt it in its tracks. | * instruction to halt it in its tracks. | ||||
*/ | */ | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
sigexit(td, SIGILL); | sigexit(td, SIGILL); | ||||
} | } | ||||
/* Build context to run handler in. */ | /* Build context to run handler in. */ | ||||
regs->tf_esp = (int)fp; | regs->tf_esp = (int)fp; | ||||
regs->tf_eip = linux_rt_sigcode; | regs->tf_eip = __kernel_rt_sigreturn; | ||||
regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D); | regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D); | ||||
regs->tf_cs = _ucodesel; | regs->tf_cs = _ucodesel; | ||||
regs->tf_ds = _udatasel; | regs->tf_ds = _udatasel; | ||||
regs->tf_es = _udatasel; | regs->tf_es = _udatasel; | ||||
regs->tf_fs = _udatasel; | regs->tf_fs = _udatasel; | ||||
regs->tf_ss = _udatasel; | regs->tf_ss = _udatasel; | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
mtx_lock(&psp->ps_mtx); | mtx_lock(&psp->ps_mtx); | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | if (copyout(&frame, fp, sizeof(frame)) != 0) { | ||||
* instruction to halt it in its tracks. | * instruction to halt it in its tracks. | ||||
*/ | */ | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
sigexit(td, SIGILL); | sigexit(td, SIGILL); | ||||
} | } | ||||
/* Build context to run handler in. */ | /* Build context to run handler in. */ | ||||
regs->tf_esp = (int)fp; | regs->tf_esp = (int)fp; | ||||
regs->tf_eip = linux_sigcode; | regs->tf_eip = __kernel_sigreturn; | ||||
regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D); | regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D); | ||||
regs->tf_cs = _ucodesel; | regs->tf_cs = _ucodesel; | ||||
regs->tf_ds = _udatasel; | regs->tf_ds = _udatasel; | ||||
regs->tf_es = _udatasel; | regs->tf_es = _udatasel; | ||||
regs->tf_fs = _udatasel; | regs->tf_fs = _udatasel; | ||||
regs->tf_ss = _udatasel; | regs->tf_ss = _udatasel; | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
mtx_lock(&psp->ps_mtx); | mtx_lock(&psp->ps_mtx); | ||||
▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
struct sysentvec linux_sysvec = { | struct sysentvec 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_fixup, | .sv_fixup = linux_fixup, | ||||
.sv_sendsig = linux_sendsig, | .sv_sendsig = linux_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 a.out", | .sv_name = "Linux a.out", | ||||
.sv_coredump = NULL, | .sv_coredump = NULL, | ||||
.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 = LINUX_USRSTACK, | .sv_usrstack = LINUX_USRSTACK, | ||||
.sv_psstrings = PS_STRINGS, | .sv_psstrings = PS_STRINGS, | ||||
.sv_stackprot = VM_PROT_ALL, | .sv_stackprot = VM_PROT_ALL, | ||||
.sv_copyout_strings = exec_copyout_strings, | .sv_copyout_strings = exec_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_AOUT | SV_IA32 | SV_ILP32 | | .sv_flags = SV_ABI_LINUX | SV_AOUT | SV_IA32 | SV_ILP32 | | ||||
SV_SIG_DISCIGN | SV_SIG_WAITNDQ, | SV_SIG_DISCIGN | SV_SIG_WAITNDQ, | ||||
.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_schedtail = linux_schedtail, | .sv_schedtail = linux_schedtail, | ||||
.sv_thread_detach = linux_thread_detach, | .sv_thread_detach = linux_thread_detach, | ||||
.sv_trap = NULL, | .sv_trap = NULL, | ||||
.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, | ||||
}; | }; | ||||
INIT_SYSENTVEC(aout_sysvec, &linux_sysvec); | INIT_SYSENTVEC(aout_sysvec, &linux_sysvec); | ||||
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_fixup_elf, | .sv_fixup = linux_fixup_elf, | ||||
.sv_sendsig = linux_sendsig, | .sv_sendsig = linux_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 ELF32", | .sv_name = "Linux ELF32", | ||||
.sv_coredump = elf32_coredump, | .sv_coredump = elf32_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 = LINUX_USRSTACK, | .sv_usrstack = LINUX_USRSTACK, | ||||
.sv_psstrings = LINUX_PS_STRINGS, | .sv_psstrings = LINUX_PS_STRINGS, | ||||
.sv_stackprot = VM_PROT_ALL, | .sv_stackprot = VM_PROT_ALL, | ||||
.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_IA32 | SV_ILP32 | SV_SHP | | .sv_flags = SV_ABI_LINUX | SV_IA32 | SV_ILP32 | SV_SHP | | ||||
SV_SIG_DISCIGN | SV_SIG_WAITNDQ, | SV_SIG_DISCIGN | 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 = LINUX_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 = NULL, | .sv_trap = NULL, | ||||
.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 = 0; | |||||
if (SV_PROC_FLAG(p, SV_SHP) != 0) | |||||
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(void *param) | linux_exec_sysvec_init(void *param) | ||||
{ | { | ||||
l_uintptr_t *ktimekeep_base, *ktsc_selector; | |||||
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("Linux invalid vdso size\n"); | ktimekeep_base = (l_uintptr_t *)(linux_vdso_mapping + tkoff); | ||||
*ktimekeep_base = sv->sv_timekeep_base; | |||||
__elfN(linux_vdso_fixup)(&elf_linux_sysvec); | tkoff = kern_tsc_selector - linux_vdso_base; | ||||
ktsc_selector = (l_uintptr_t *)(linux_vdso_mapping + tkoff); | |||||
*ktsc_selector = linux_vdso_tsc_selector_idx(); | |||||
if (bootverbose) | |||||
printf("Linux i386 vDSO tsc_selector: %u\n", *ktsc_selector); | |||||
} | |||||
SYSINIT(elf_linux_exec_sysvec_init, SI_SUB_EXEC, SI_ORDER_ANY, | |||||
linux_exec_sysvec_init, &elf_linux_sysvec); | |||||
linux_shared_page_obj = __elfN(linux_shared_page_init) | static void | ||||
(&linux_shared_page_mapping); | linux_vdso_install(void *param) | ||||
{ | |||||
char *vdso_start = &_binary_linux_vdso_so_o_start; | |||||
char *vdso_end = &_binary_linux_vdso_so_o_end; | |||||
__elfN(linux_vdso_reloc)(&elf_linux_sysvec); | linux_szsigcode = vdso_end - vdso_start; | ||||
MPASS(linux_szsigcode <= LINUX_VDSOPAGE_SIZE); | |||||
bcopy(elf_linux_sysvec.sv_sigcode, linux_shared_page_mapping, | linux_vdso_base = LINUX_VDSOPAGE; | ||||
linux_szsigcode); | |||||
elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj; | __elfN(linux_vdso_fixup)(vdso_start, linux_vdso_base); | ||||
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(void *param) | linux_vdso_deinstall(void *param) | ||||
{ | { | ||||
__elfN(linux_shared_page_fini)(linux_shared_page_obj, | __elfN(linux_shared_page_fini)(linux_vdso_obj, | ||||
linux_shared_page_mapping); | linux_vdso_mapping, LINUX_VDSOPAGE_SIZE); | ||||
} | } | ||||
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) | |||||
{ | |||||
const Elf_Shdr *shdr; | |||||
const Elf_Rel *rel; | |||||
const Elf_Ehdr *ehdr; | |||||
Elf_Addr *where; | |||||
Elf_Size rtype, symidx; | |||||
Elf_Addr addr, addend; | |||||
int i, relcnt; | |||||
MPASS(offset != 0); | |||||
relcnt = 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: | |||||
rel = (const Elf_Rel *)(mapping + shdr[i].sh_offset); | |||||
relcnt = shdr[i].sh_size / sizeof(*rel); | |||||
break; | |||||
case SHT_RELA: | |||||
printf("Linux i386 vDSO: unexpected Rela section\n"); | |||||
break; | |||||
} | |||||
} | |||||
for (i = 0; i < relcnt; i++, rel++) { | |||||
where = (Elf_Addr *)(mapping + rel->r_offset); | |||||
addend = *where; | |||||
rtype = ELF_R_TYPE(rel->r_info); | |||||
symidx = ELF_R_SYM(rel->r_info); | |||||
switch (rtype) { | |||||
case R_386_NONE: /* none */ | |||||
break; | |||||
case R_386_RELATIVE: /* B + A */ | |||||
addr = (Elf_Addr)PTROUT(offset + addend); | |||||
if (*where != addr) | |||||
*where = addr; | |||||
break; | |||||
case R_386_IRELATIVE: | |||||
printf("Linux i386 vDSO: unexpected ifunc relocation, " | |||||
"symbol index %d\n", symidx); | |||||
break; | |||||
default: | |||||
printf("Linux i386 vDSO: unexpected relocation type %d, " | |||||
"symbol index %d\n", rtype, symidx); | |||||
} | |||||
} | |||||
} | |||||
static char GNU_ABI_VENDOR[] = "GNU"; | static char GNU_ABI_VENDOR[] = "GNU"; | ||||
static int GNULINUX_ABI_DESC = 0; | static int GNULINUX_ABI_DESC = 0; | ||||
static bool | static bool | ||||
linux_trans_osrel(const Elf_Note *note, int32_t *osrel) | linux_trans_osrel(const Elf_Note *note, int32_t *osrel) | ||||
{ | { | ||||
const Elf32_Word *desc; | const Elf32_Word *desc; | ||||
▲ Show 20 Lines • Show All 135 Lines • Show Last 20 Lines |