aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorDmitry Chagin <dchagin@FreeBSD.org>2015-05-24 15:28:17 +0000
committerDmitry Chagin <dchagin@FreeBSD.org>2015-05-24 15:28:17 +0000
commitbdc379344aee7b07ea84d4da61a4f228b72f8079 (patch)
treeefe2b80d07b6fbb60ad2454e5524bc385e7a2e33 /sys
parentec513841b3ac59ce581ffb5eb859926555f6834b (diff)
downloadsrc-bdc379344aee7b07ea84d4da61a4f228b72f8079.tar.gz
src-bdc379344aee7b07ea84d4da61a4f228b72f8079.zip
Implement vdso - virtual dynamic shared object. Through vdso Linux
exposes functions from kernel with proper DWARF CFI information so that it becomes easier to unwind through them. Using vdso is a mandatory for a thread cancelation && cleanup on a modern glibc. Differential Revision: https://reviews.freebsd.org/D1060
Notes
Notes: svn path=/head/; revision=283407
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/linux32/linux.h2
-rw-r--r--sys/amd64/linux32/linux32_genassym.c3
-rw-r--r--sys/amd64/linux32/linux32_locore.s152
-rw-r--r--sys/amd64/linux32/linux32_sysvec.c64
-rw-r--r--sys/amd64/linux32/linux32_vdso.lds.s65
-rw-r--r--sys/compat/linux/linux_misc.h2
-rw-r--r--sys/compat/linux/linux_vdso.c240
-rw-r--r--sys/compat/linux/linux_vdso.h65
-rw-r--r--sys/conf/files.amd6415
-rw-r--r--sys/conf/files.i38615
-rw-r--r--sys/i386/linux/linux.h2
-rw-r--r--sys/i386/linux/linux_genassym.c3
-rw-r--r--sys/i386/linux/linux_locore.s154
-rw-r--r--sys/i386/linux/linux_sysvec.c65
-rw-r--r--sys/i386/linux/linux_vdso.lds.s65
-rw-r--r--sys/modules/linux/Makefile29
16 files changed, 870 insertions, 71 deletions
diff --git a/sys/amd64/linux32/linux.h b/sys/amd64/linux32/linux.h
index 1c9cb0c624a6..eb0b0c999b10 100644
--- a/sys/amd64/linux32/linux.h
+++ b/sys/amd64/linux32/linux.h
@@ -114,7 +114,7 @@ typedef struct {
/*
* Miscellaneous
*/
-#define LINUX_AT_COUNT 16 /* Count of used aux entry types.
+#define LINUX_AT_COUNT 18 /* Count of used aux entry types.
* Keep this synchronized with
* elf_linux_fixup() code.
*/
diff --git a/sys/amd64/linux32/linux32_genassym.c b/sys/amd64/linux32/linux32_genassym.c
index bea846d0c2e2..bc94139c226d 100644
--- a/sys/amd64/linux32/linux32_genassym.c
+++ b/sys/amd64/linux32/linux32_genassym.c
@@ -7,9 +7,12 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <amd64/linux32/linux.h>
+#include <compat/linux/linux_mib.h>
ASSYM(LINUX_SIGF_HANDLER, offsetof(struct l_sigframe, sf_handler));
ASSYM(LINUX_SIGF_SC, offsetof(struct l_sigframe, sf_sc));
ASSYM(LINUX_RT_SIGF_HANDLER, offsetof(struct l_rt_sigframe, sf_handler));
ASSYM(LINUX_RT_SIGF_UC, offsetof(struct l_rt_sigframe, sf_sc));
ASSYM(LINUX_RT_SIGF_SC, offsetof(struct l_ucontext, uc_mcontext));
+ASSYM(LINUX_VERSION_CODE, LINUX_VERSION_CODE);
+ASSYM(LINUX_SC_ESP, offsetof(struct l_sigcontext, sc_esp));
diff --git a/sys/amd64/linux32/linux32_locore.s b/sys/amd64/linux32/linux32_locore.s
index 36e1abf87b83..8a3b83e9288f 100644
--- a/sys/amd64/linux32/linux32_locore.s
+++ b/sys/amd64/linux32/linux32_locore.s
@@ -8,31 +8,143 @@
.text
.code32
-NON_GPROF_ENTRY(linux_sigcode)
- call *LINUX_SIGF_HANDLER(%esp)
- leal LINUX_SIGF_SC(%esp),%ebx /* linux scp */
- movl %esp, %ebx /* pass sigframe */
- push %eax /* fake ret addr */
+/*
+ * To avoid excess stack frame the signal trampoline code emulates
+ * the 'call' instruction.
+ */
+NON_GPROF_ENTRY(linux32_sigcode)
+ movl %esp, %ebx /* preserve sigframe */
+ call .getip0
+.getip0:
+ popl %eax
+ add $.startsigcode-.getip0, %eax /* ret address */
+ push %eax
+ jmp *LINUX_SIGF_HANDLER(%ebx)
+.startsigcode:
+ popl %eax
movl $LINUX_SYS_linux_sigreturn,%eax /* linux_sigreturn() */
int $0x80 /* enter kernel with args */
+.endsigcode:
0: jmp 0b
- ALIGN_TEXT
-/* XXXXX */
-linux_rt_sigcode:
- call *LINUX_RT_SIGF_HANDLER(%esp)
+
+NON_GPROF_ENTRY(linux32_rt_sigcode)
leal LINUX_RT_SIGF_UC(%esp),%ebx /* linux ucp */
leal LINUX_RT_SIGF_SC(%ebx),%ecx /* linux sigcontext */
- push %eax /* fake ret addr */
+ movl %esp, %edi
+ call .getip1
+.getip1:
+ popl %eax
+ add $.startrtsigcode-.getip1, %eax /* ret address */
+ push %eax
+ jmp *LINUX_RT_SIGF_HANDLER(%edi)
+.startrtsigcode:
movl $LINUX_SYS_linux_rt_sigreturn,%eax /* linux_rt_sigreturn() */
int $0x80 /* enter kernel with args */
+.endrtsigcode:
0: jmp 0b
- ALIGN_TEXT
-/* XXXXX */
-linux_esigcode:
-
- .data
- .globl linux_szsigcode, linux_sznonrtsigcode
-linux_szsigcode:
- .long linux_esigcode-linux_sigcode
-linux_sznonrtsigcode:
- .long linux_rt_sigcode-linux_sigcode
+
+NON_GPROF_ENTRY(linux32_vsyscall)
+.startvsyscall:
+ int $0x80
+ ret
+.endvsyscall:
+
+
+ .section .note.Linux, "a",@note
+ .long 2f - 1f /* namesz */
+ .balign 4
+ .long 4f - 3f /* descsz */
+ .long 0
+1:
+ .asciz "Linux"
+2:
+ .balign 4
+3:
+ .long LINUX_VERSION_CODE
+4:
+ .balign 4
+ .previous
+
+
+#define do_cfa_expr(offset) \
+ .byte 0x0f; /* DW_CFA_def_cfa_expression */ \
+ .uleb128 11f-10f; /* length */ \
+10: .byte 0x74; /* DW_OP_breg4 */ \
+ .sleb128 offset; /* offset */ \
+ .byte 0x06; /* DW_OP_deref */ \
+11:
+
+
+ /* CIE */
+ .section .eh_frame,"a",@progbits
+.LSTARTFRAMEDLSI1:
+ .long .LENDCIEDLSI1-.LSTARTCIEDLSI1
+.LSTARTCIEDLSI1:
+ .long 0 /* CIE ID */
+ .byte 1 /* Version number */
+ .string "zRS" /* NULL-terminated
+ * augmentation string
+ */
+ .uleb128 1 /* Code alignment factor */
+ .sleb128 -4 /* Data alignment factor */
+ .byte 8 /* Return address
+ * register column
+ */
+ .uleb128 1 /* Augmentation value length */
+ .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
+ .byte 0 /* DW_CFA_nop */
+ .align 4
+.LENDCIEDLSI1:
+
+ /* FDE */
+ .long .LENDFDEDLSI1-.LSTARTFDEDLSI1 /* Length FDE */
+.LSTARTFDEDLSI1:
+ .long .LSTARTFDEDLSI1-.LSTARTFRAMEDLSI1 /* CIE pointer */
+ .long .startsigcode-. /* PC-relative start address */
+ .long .endsigcode-.startsigcode
+ .uleb128 0 /* Augmentation */
+ do_cfa_expr(LINUX_SIGF_SC-8)
+ .align 4
+.LENDFDEDLSI1:
+
+ .long .LENDFDEDLSI2-.LSTARTFDEDLSI2 /* Length FDE */
+.LSTARTFDEDLSI2:
+ .long .LSTARTFDEDLSI2-.LSTARTFRAMEDLSI1 /* CIE pointer */
+ .long .startrtsigcode-. /* PC-relative start address */
+ .long .endrtsigcode-.startrtsigcode
+ .uleb128 0 /* Augmentation */
+ do_cfa_expr(LINUX_RT_SIGF_SC-4+LINUX_SC_ESP)
+ .align 4
+.LENDFDEDLSI2:
+ .previous
+
+ .section .eh_frame,"a",@progbits
+.LSTARTFRAMEDLSI2:
+ .long .LENDCIEDLSI2-.LSTARTCIEDLSI2
+.LSTARTCIEDLSI2:
+ .long 0 /* CIE ID */
+ .byte 1 /* Version number */
+ .string "zR" /* NULL-terminated
+ * augmentation string
+ */
+ .uleb128 1 /* Code alignment factor */
+ .sleb128 -4 /* Data alignment factor */
+ .byte 8 /* Return address register column */
+ .uleb128 1 /* Augmentation value length */
+ .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
+ .byte 0x0c /* DW_CFA_def_cfa */
+ .uleb128 4
+ .uleb128 4
+ .byte 0x88 /* DW_CFA_offset, column 0x8 */
+ .uleb128 1
+ .align 4
+.LENDCIEDLSI2:
+ .long .LENDFDEDLSI3-.LSTARTFDEDLSI3 /* Length FDE */
+.LSTARTFDEDLSI3:
+ .long .LSTARTFDEDLSI3-.LSTARTFRAMEDLSI2 /* CIE pointer */
+ .long .startvsyscall-. /* PC-relative start address */
+ .long .endvsyscall-.startvsyscall
+ .uleb128 0
+ .align 4
+.LENDFDEDLSI3:
+ .previous
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index 9df5b0ee18fe..5ceae589afee 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -83,6 +83,7 @@ __FBSDID("$FreeBSD$");
#include <compat/linux/linux_misc.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_vdso.h>
MODULE_VERSION(linux, 1);
@@ -111,8 +112,11 @@ MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures");
const char *linux_platform = "i686";
static int linux_szplatform;
-extern char linux_sigcode[];
-extern int linux_szsigcode;
+static int linux_szsigcode;
+static vm_object_t linux_shared_page_obj;
+static char *linux_shared_page_mapping;
+extern char _binary_linux32_locore_o_start;
+extern char _binary_linux32_locore_o_end;
extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL];
@@ -127,6 +131,8 @@ static void exec_linux_setregs(struct thread *td,
struct image_params *imgp, u_long stack);
static void linux32_fixlimit(struct rlimit *rl, int which);
static boolean_t linux32_trans_osrel(const Elf_Note *note, int32_t *osrel);
+static void linux_vdso_install(void *param);
+static void linux_vdso_deinstall(void *param);
static eventhandler_tag linux_exit_tag;
static eventhandler_tag linux_exec_tag;
@@ -220,6 +226,10 @@ struct linux32_ps_strings {
u_int ps_nenvstr; /* the number of environment strings */
};
+LINUX_VDSO_SYM_INTPTR(linux32_sigcode);
+LINUX_VDSO_SYM_INTPTR(linux32_rt_sigcode);
+LINUX_VDSO_SYM_INTPTR(linux32_vsyscall);
+
/*
* If FreeBSD & Linux have a difference of opinion about what a trap
* means, deal with it here.
@@ -259,6 +269,9 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp)
args = (Elf32_Auxargs *)imgp->auxargs;
pos = base + (imgp->args->argc + imgp->args->envc + 2);
+ AUXARGS_ENTRY_32(pos, LINUX_AT_SYSINFO_EHDR,
+ imgp->proc->p_sysent->sv_shared_page_base);
+ AUXARGS_ENTRY_32(pos, LINUX_AT_SYSINFO, linux32_vsyscall);
AUXARGS_ENTRY_32(pos, LINUX_AT_HWCAP, cpu_feature);
/*
@@ -297,8 +310,6 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp)
return (0);
}
-extern unsigned long linux_sznonrtsigcode;
-
static void
linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
{
@@ -353,7 +364,8 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
ksiginfo_to_lsiginfo(ksi, &frame.sf_si, sig);
/*
- * Build the signal context to be used by sigreturn.
+ * Build the signal context to be used by sigreturn
+ * and libgcc unwind.
*/
frame.sf_sc.uc_flags = 0; /* XXX ??? */
frame.sf_sc.uc_link = 0; /* XXX ??? */
@@ -371,6 +383,7 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
frame.sf_sc.uc_mcontext.sc_esi = regs->tf_rsi;
frame.sf_sc.uc_mcontext.sc_ebp = regs->tf_rbp;
frame.sf_sc.uc_mcontext.sc_ebx = regs->tf_rbx;
+ frame.sf_sc.uc_mcontext.sc_esp = regs->tf_rsp;
frame.sf_sc.uc_mcontext.sc_edx = regs->tf_rdx;
frame.sf_sc.uc_mcontext.sc_ecx = regs->tf_rcx;
frame.sf_sc.uc_mcontext.sc_eax = regs->tf_rax;
@@ -412,7 +425,7 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
* Build context to run handler in.
*/
regs->tf_rsp = PTROUT(fp);
- regs->tf_rip = p->p_sysent->sv_sigcode_base + linux_sznonrtsigcode;
+ regs->tf_rip = linux32_rt_sigcode;
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucode32sel;
regs->tf_ss = _udatasel;
@@ -507,6 +520,7 @@ linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
frame.sf_sc.sc_esi = regs->tf_rsi;
frame.sf_sc.sc_ebp = regs->tf_rbp;
frame.sf_sc.sc_ebx = regs->tf_rbx;
+ frame.sf_sc.sc_esp = regs->tf_rsp;
frame.sf_sc.sc_edx = regs->tf_rdx;
frame.sf_sc.sc_ecx = regs->tf_rcx;
frame.sf_sc.sc_eax = regs->tf_rax;
@@ -535,7 +549,7 @@ linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
* Build context to run handler in.
*/
regs->tf_rsp = PTROUT(fp);
- regs->tf_rip = p->p_sysent->sv_sigcode_base;
+ regs->tf_rip = linux32_sigcode;
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucode32sel;
regs->tf_ss = _udatasel;
@@ -1014,7 +1028,7 @@ struct sysentvec elf_linux_sysvec = {
.sv_transtrap = translate_traps,
.sv_fixup = elf_linux_fixup,
.sv_sendsig = linux_sendsig,
- .sv_sigcode = linux_sigcode,
+ .sv_sigcode = &_binary_linux32_locore_o_start,
.sv_szsigcode = &linux_szsigcode,
.sv_prepsyscall = NULL,
.sv_name = "Linux ELF32",
@@ -1040,7 +1054,39 @@ struct sysentvec elf_linux_sysvec = {
.sv_schedtail = linux_schedtail,
.sv_thread_detach = linux_thread_detach,
};
-INIT_SYSENTVEC(elf_sysvec, &elf_linux_sysvec);
+
+static void
+linux_vdso_install(void *param)
+{
+
+ linux_szsigcode = (&_binary_linux32_locore_o_end -
+ &_binary_linux32_locore_o_start);
+
+ if (linux_szsigcode > elf_linux_sysvec.sv_shared_page_len)
+ panic("Linux invalid vdso size\n");
+
+ __elfN(linux_vdso_fixup)(&elf_linux_sysvec);
+
+ linux_shared_page_obj = __elfN(linux_shared_page_init)
+ (&linux_shared_page_mapping);
+
+ __elfN(linux_vdso_reloc)(&elf_linux_sysvec, LINUX32_SHAREDPAGE);
+
+ bcopy(elf_linux_sysvec.sv_sigcode, linux_shared_page_mapping,
+ linux_szsigcode);
+ elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj;
+}
+SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_ANY,
+ (sysinit_cfunc_t)linux_vdso_install, NULL);
+
+static void
+linux_vdso_deinstall(void *param)
+{
+
+ __elfN(linux_shared_page_fini)(linux_shared_page_obj);
+};
+SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST,
+ (sysinit_cfunc_t)linux_vdso_deinstall, NULL);
static char GNU_ABI_VENDOR[] = "GNU";
static int GNULINUX_ABI_DESC = 0;
diff --git a/sys/amd64/linux32/linux32_vdso.lds.s b/sys/amd64/linux32/linux32_vdso.lds.s
new file mode 100644
index 000000000000..beaaef574793
--- /dev/null
+++ b/sys/amd64/linux32/linux32_vdso.lds.s
@@ -0,0 +1,65 @@
+/*
+ * Linker script for 32-bit vDSO.
+ * Copied from Linux kernel arch/x86/vdso/vdso-layout.lds.S
+ * and arch/x86/vdso/vdso32/vdso32.lds.S
+ *
+ * $FreeBSD$
+ */
+
+SECTIONS
+{
+ . = . + SIZEOF_HEADERS;
+
+ .hash : { *(.hash) } :text
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+
+ .note : { *(.note.*) } :text :note
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
+ .eh_frame : { KEEP (*(.eh_frame)) } :text
+
+ .dynamic : { *(.dynamic) } :text :dynamic
+
+ .rodata : { *(.rodata*) } :text
+ .data : {
+ *(.data*)
+ *(.sdata*)
+ *(.got.plt) *(.got)
+ *(.gnu.linkonce.d.*)
+ *(.bss*)
+ *(.dynbss*)
+ *(.gnu.linkonce.b.*)
+ }
+
+ .altinstructions : { *(.altinstructions) }
+ .altinstr_replacement : { *(.altinstr_replacement) }
+
+ . = ALIGN(0x100);
+ .text : { *(.text*) } :text =0x90909090
+}
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+ dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
+ note PT_NOTE FLAGS(4); /* PF_R */
+ eh_frame_hdr PT_GNU_EH_FRAME;
+}
+
+ENTRY(linux32_vsyscall);
+
+VERSION
+{
+ LINUX_2.5 {
+ global:
+ linux32_vsyscall;
+ linux32_sigcode;
+ linux32_rt_sigcode;
+ local: *;
+ };
+}
diff --git a/sys/compat/linux/linux_misc.h b/sys/compat/linux/linux_misc.h
index d15c7e5437de..d8b92bc994ca 100644
--- a/sys/compat/linux/linux_misc.h
+++ b/sys/compat/linux/linux_misc.h
@@ -71,6 +71,8 @@ extern const char *linux_platform;
* differ from AT_PLATFORM.
*/
#define LINUX_AT_EXECFN 31 /* filename of program */
+#define LINUX_AT_SYSINFO 32 /* vsyscall */
+#define LINUX_AT_SYSINFO_EHDR 33 /* vdso header */
/* Linux sets the i387 to extended precision. */
#if defined(__i386__) || defined(__amd64__)
diff --git a/sys/compat/linux/linux_vdso.c b/sys/compat/linux/linux_vdso.c
new file mode 100644
index 000000000000..871a5daa5b84
--- /dev/null
+++ b/sys/compat/linux/linux_vdso.c
@@ -0,0 +1,240 @@
+/*-
+ * Copyright (c) 2013 Dmitry Chagin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_compat.h"
+
+#define __ELF_WORD_SIZE 32
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/elf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#include <sys/queue.h>
+#include <sys/sysent.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+#include <compat/linux/linux_vdso.h>
+
+SLIST_HEAD(, linux_vdso_sym) __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
+__elfN(linux_vdso_sym_init)(struct linux_vdso_sym *s)
+{
+
+ SLIST_INSERT_HEAD(&__elfN(linux_vdso_syms), s, sym);
+}
+
+vm_object_t
+__elfN(linux_shared_page_init)(char **mapping)
+{
+ vm_page_t m;
+ vm_object_t obj;
+ vm_offset_t addr;
+
+ obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE,
+ VM_PROT_DEFAULT, 0, NULL);
+ VM_OBJECT_WLOCK(obj);
+ m = vm_page_grab(obj, 0, VM_ALLOC_NOBUSY | VM_ALLOC_ZERO);
+ m->valid = VM_PAGE_BITS_ALL;
+ VM_OBJECT_WUNLOCK(obj);
+ addr = kva_alloc(PAGE_SIZE);
+ pmap_qenter(addr, &m, 1);
+ *mapping = (char *)addr;
+ return (obj);
+}
+
+void
+__elfN(linux_shared_page_fini)(vm_object_t obj)
+{
+
+ vm_object_deallocate(obj);
+}
+
+void
+__elfN(linux_vdso_fixup)(struct sysentvec *sv)
+{
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *shdr;
+ int i;
+
+ ehdr = (Elf_Ehdr *) sv->sv_sigcode;
+
+ if (!IS_ELF(*ehdr) ||
+ ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ ehdr->e_shoff == 0 ||
+ ehdr->e_shentsize != sizeof(Elf_Shdr))
+ panic("Linux invalid vdso header.\n");
+
+ if (ehdr->e_type != ET_DYN)
+ panic("Linux invalid vdso header.\n");
+
+ shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff);
+
+ __elfN(symtabindex) = -1;
+ __elfN(symstrindex) = -1;
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ if (shdr[i].sh_size == 0)
+ continue;
+ if (shdr[i].sh_type == SHT_DYNSYM) {
+ __elfN(symtabindex) = i;
+ __elfN(symstrindex) = shdr[i].sh_link;
+ }
+ }
+
+ if (__elfN(symtabindex) == -1 || __elfN(symstrindex) == -1)
+ panic("Linux invalid vdso header.\n");
+
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_LINUX;
+}
+
+void
+__elfN(linux_vdso_reloc)(struct sysentvec *sv, int vdso_adjust)
+{
+ struct linux_vdso_sym *lsym;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Shdr *shdr;
+ Elf_Dyn *dyn;
+ Elf_Sym *sym;
+ int i, symcnt;
+
+ ehdr = (Elf_Ehdr *) sv->sv_sigcode;
+
+ /* Adjust our so relative to the sigcode_base */
+ if (vdso_adjust != 0) {
+ ehdr->e_entry += vdso_adjust;
+ phdr = (Elf_Phdr *)((caddr_t)ehdr + ehdr->e_phoff);
+
+ /* phdrs */
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ phdr[i].p_vaddr += vdso_adjust;
+ 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 += vdso_adjust;
+ 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 += vdso_adjust;
+ 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 += vdso_adjust;
+ 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(i = 0; i < symcnt; i++, sym++) {
+ if (sym->st_shndx == SHN_UNDEF ||
+ sym->st_shndx == SHN_ABS)
+ continue;
+ sym->st_value += vdso_adjust;
+ }
+ }
+ }
+
+ SLIST_FOREACH(lsym, &__elfN(linux_vdso_syms), sym)
+ __elfN(linux_vdso_lookup)(ehdr, lsym);
+}
+
+static void
+__elfN(linux_vdso_lookup)(Elf_Ehdr *ehdr, struct linux_vdso_sym *vsym)
+{
+ vm_offset_t strtab, symname;
+ uint32_t symcnt;
+ Elf_Shdr *shdr;
+ int i;
+
+ shdr = (Elf_Shdr *) ((caddr_t)ehdr + ehdr->e_shoff);
+
+ strtab = (vm_offset_t)((caddr_t)ehdr +
+ shdr[__elfN(symstrindex)].sh_offset);
+ 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) {
+ symname = strtab + sym->st_name;
+ if (strncmp(vsym->symname, (char *)symname, vsym->size) == 0) {
+ *vsym->ptr = (uintptr_t)sym->st_value;
+ break;
+ }
+ }
+}
diff --git a/sys/compat/linux/linux_vdso.h b/sys/compat/linux/linux_vdso.h
new file mode 100644
index 000000000000..a3ee63de5ce4
--- /dev/null
+++ b/sys/compat/linux/linux_vdso.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2013 Dmitry Chagin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LINUX_VDSO_H_
+#define _LINUX_VDSO_H_
+
+#include <sys/types.h>
+
+struct linux_vdso_sym {
+ SLIST_ENTRY(linux_vdso_sym) sym;
+ uint32_t size;
+ uintptr_t * ptr;
+ char symname[];
+};
+
+vm_object_t __elfN(linux_shared_page_init)(char **);
+void __elfN(linux_shared_page_fini)(vm_object_t);
+void __elfN(linux_vdso_fixup)(struct sysentvec *);
+void __elfN(linux_vdso_reloc)(struct sysentvec *, int);
+void __elfN(linux_vdso_sym_init)(struct linux_vdso_sym *);
+
+#define LINUX_VDSO_SYM_INTPTR(name) \
+uintptr_t name; \
+LINUX_VDSO_SYM_DEFINE(name)
+
+#define LINUX_VDSO_SYM_CHAR(name) \
+const char * name; \
+LINUX_VDSO_SYM_DEFINE(name)
+
+#define LINUX_VDSO_SYM_DEFINE(name) \
+static struct linux_vdso_sym name ## sym = { \
+ .symname = #name, \
+ .size = sizeof(#name), \
+ .ptr = (uintptr_t *)&name \
+}; \
+SYSINIT(__elfN(name ## _sym_init), SI_SUB_EXEC, \
+ SI_ORDER_FIRST, __elfN(linux_vdso_sym_init), &name ## sym); \
+struct __hack
+
+#endif /* _LINUX_VDSO_H_ */
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
index 8aadcf5fba2f..a1e901f4775a 100644
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -20,6 +20,18 @@ linux32_assym.h optional compat_linux32 \
no-obj no-implicit-rule before-depend \
clean "linux32_assym.h"
#
+linux32_locore.o optional compat_linux32 \
+ dependency "linux32_assym.h $S/amd64/linux32/linux32_locore.s" \
+ compile-with "${CC} -x assembler-with-cpp -DLOCORE -m32 -shared -s -pipe -I. -I$S -Werror -Wall -fno-common -nostdinc -nostdlib -Wl,-T$S/amd64/linux32/linux32_vdso.lds.s -Wl,-soname=linux32_vdso.so,--eh-frame-hdr,-fPIC,-warn-common ${.IMPSRC} -o ${.TARGET}" \
+ no-obj no-implicit-rule \
+ clean "linux32_locore.o"
+#
+linux32_vdso.so optional compat_linux32 \
+ dependency "linux32_locore.o" \
+ compile-with "${OBJCOPY} --input binary --output elf64-x86-64-freebsd --binary-architecture i386 linux32_locore.o ${.TARGET}" \
+ no-implicit-rule \
+ clean "linux32_vdso.so"
+#
ia32_genassym.o standard \
dependency "$S/compat/ia32/ia32_genassym.c" \
compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \
@@ -473,8 +485,6 @@ compat/linsysfs/linsysfs.c optional linsysfs
# Linux/i386 binary support
#
amd64/linux32/linux32_dummy.c optional compat_linux32
-amd64/linux32/linux32_locore.s optional compat_linux32 \
- dependency "linux32_assym.h"
amd64/linux32/linux32_machdep.c optional compat_linux32
amd64/linux32/linux32_support.s optional compat_linux32 \
dependency "linux32_assym.h"
@@ -497,6 +507,7 @@ compat/linux/linux_time.c optional compat_linux32
compat/linux/linux_timer.c optional compat_linux32
compat/linux/linux_uid16.c optional compat_linux32
compat/linux/linux_util.c optional compat_linux32
+compat/linux/linux_vdso.c optional compat_linux32
dev/amr/amr_linux.c optional compat_linux32 amr
dev/mfi/mfi_linux.c optional compat_linux32 mfi
#
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 68dd6a9aea4e..9dffbdb6239e 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -19,6 +19,18 @@ linux_assym.h optional compat_linux \
no-obj no-implicit-rule before-depend \
clean "linux_assym.h"
#
+linux_locore.o optional compat_linux \
+ dependency "linux_assym.h $S/i386/linux/linux_locore.s" \
+ compile-with "${CC} -x assembler-with-cpp -DLOCORE -shared -s -pipe -I. -I$S -Werror -Wall -fno-common -nostdinc -nostdlib -Wl,-T$S/i386/linux/linux_vdso.lds.s -Wl,-soname=linux_vdso.so,--eh-frame-hdr,-fPIC,-warn-common ${.IMPSRC} -o ${.TARGET}" \
+ no-obj no-implicit-rule \
+ clean "linux_locore.o"
+#
+linux_vdso.so optional compat_linux \
+ dependency "linux_locore.o" \
+ compile-with "${OBJCOPY} --input binary --output elf32-i386-freebsd --binary-architecture i386 linux_locore.o ${.TARGET}" \
+ no-implicit-rule \
+ clean "linux_vdso.so"
+#
svr4_genassym.o optional compat_svr4 \
dependency "$S/i386/svr4/svr4_genassym.c" \
compile-with "${CC} ${CFLAGS:N-fno-common} -c ${.IMPSRC}" \
@@ -86,6 +98,7 @@ compat/linux/linux_time.c optional compat_linux
compat/linux/linux_timer.c optional compat_linux
compat/linux/linux_uid16.c optional compat_linux
compat/linux/linux_util.c optional compat_linux
+compat/linux/linux_vdso.c optional compat_linux
compat/ndis/kern_ndis.c optional ndisapi pci
compat/ndis/kern_windrv.c optional ndisapi pci
compat/ndis/subr_hal.c optional ndisapi pci
@@ -490,8 +503,6 @@ i386/isa/prof_machdep.c optional profiling-routine
i386/isa/spic.c optional spic
i386/linux/imgact_linux.c optional compat_linux
i386/linux/linux_dummy.c optional compat_linux
-i386/linux/linux_locore.s optional compat_linux \
- dependency "linux_assym.h"
i386/linux/linux_machdep.c optional compat_linux
i386/linux/linux_ptrace.c optional compat_linux
i386/linux/linux_support.s optional compat_linux \
diff --git a/sys/i386/linux/linux.h b/sys/i386/linux/linux.h
index 6a20a682d503..251c64e91de7 100644
--- a/sys/i386/linux/linux.h
+++ b/sys/i386/linux/linux.h
@@ -108,7 +108,7 @@ typedef struct {
/*
* Miscellaneous
*/
-#define LINUX_AT_COUNT 16 /* Count of used aux entry types.
+#define LINUX_AT_COUNT 18 /* Count of used aux entry types.
* Keep this synchronized with
* elf_linux_fixup() code.
*/
diff --git a/sys/i386/linux/linux_genassym.c b/sys/i386/linux/linux_genassym.c
index 1e845724cfdc..9735110b0472 100644
--- a/sys/i386/linux/linux_genassym.c
+++ b/sys/i386/linux/linux_genassym.c
@@ -6,6 +6,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <i386/linux/linux.h>
+#include <compat/linux/linux_mib.h>
ASSYM(LINUX_SIGF_HANDLER, offsetof(struct l_sigframe, sf_handler));
ASSYM(LINUX_SIGF_SC, offsetof(struct l_sigframe, sf_sc));
@@ -14,3 +15,5 @@ ASSYM(LINUX_SC_EFLAGS, offsetof(struct l_sigcontext, sc_eflags));
ASSYM(LINUX_RT_SIGF_HANDLER, offsetof(struct l_rt_sigframe, sf_handler));
ASSYM(LINUX_RT_SIGF_UC, offsetof(struct l_rt_sigframe, sf_sc));
ASSYM(LINUX_RT_SIGF_SC, offsetof(struct l_ucontext, uc_mcontext));
+ASSYM(LINUX_SC_ESP, offsetof(struct l_sigcontext, sc_esp));
+ASSYM(LINUX_VERSION_CODE, LINUX_VERSION_CODE);
diff --git a/sys/i386/linux/linux_locore.s b/sys/i386/linux/linux_locore.s
index a3e0e7dced07..54553b5292ec 100644
--- a/sys/i386/linux/linux_locore.s
+++ b/sys/i386/linux/linux_locore.s
@@ -5,33 +5,145 @@
#include <i386/linux/linux_syscall.h> /* system call numbers */
+#include "assym.s"
+
+/*
+ * To avoid excess stack frame the signal trampoline code emulates
+ * the 'call' instruction.
+ */
NON_GPROF_ENTRY(linux_sigcode)
- call *LINUX_SIGF_HANDLER(%esp)
- leal LINUX_SIGF_SC(%esp),%ebx /* linux scp */
- mov LINUX_SC_GS(%ebx),%gs
- movl %esp, %ebx /* pass sigframe */
- push %eax /* fake ret addr */
+ movl %esp, %ebx /* preserve sigframe */
+ call .getip0
+.getip0:
+ popl %eax
+ add $.startsigcode-.getip0, %eax /* ret address */
+ push %eax
+ jmp *LINUX_SIGF_HANDLER(%ebx)
+.startsigcode:
+ popl %eax /* gcc unwind code need this */
movl $LINUX_SYS_linux_sigreturn,%eax /* linux_sigreturn() */
int $0x80 /* enter kernel with args */
+.endsigcode:
0: jmp 0b
- ALIGN_TEXT
-/* XXXXX */
-linux_rt_sigcode:
- call *LINUX_RT_SIGF_HANDLER(%esp)
+
+NON_GPROF_ENTRY(linux_rt_sigcode)
leal LINUX_RT_SIGF_UC(%esp),%ebx /* linux ucp */
leal LINUX_RT_SIGF_SC(%ebx),%ecx /* linux sigcontext */
- mov LINUX_SC_GS(%ecx),%gs
- push %eax /* fake ret addr */
+ movl %esp, %edi
+ call .getip1
+.getip1:
+ popl %eax
+ add $.startrtsigcode-.getip1, %eax /* ret address */
+ push %eax
+ jmp *LINUX_RT_SIGF_HANDLER(%edi)
+.startrtsigcode:
movl $LINUX_SYS_linux_rt_sigreturn,%eax /* linux_rt_sigreturn() */
int $0x80 /* enter kernel with args */
+.endrtsigcode:
0: jmp 0b
- ALIGN_TEXT
-/* XXXXX */
-linux_esigcode:
-
- .data
- .globl linux_szsigcode, linux_sznonrtsigcode
-linux_szsigcode:
- .long linux_esigcode-linux_sigcode
-linux_sznonrtsigcode:
- .long linux_rt_sigcode-linux_sigcode
+
+NON_GPROF_ENTRY(linux_vsyscall)
+.startvsyscall:
+ int $0x80
+ ret
+.endvsyscall:
+
+
+ .section .note.Linux, "a",@note
+ .long 2f - 1f /* namesz */
+ .balign 4
+ .long 4f - 3f /* descsz */
+ .long 0
+1:
+ .asciz "Linux"
+2:
+ .balign 4
+3:
+ .long LINUX_VERSION_CODE
+4:
+ .balign 4
+ .previous
+
+
+#define do_cfa_expr(offset) \
+ .byte 0x0f; /* DW_CFA_def_cfa_expression */ \
+ .uleb128 11f-10f; /* length */ \
+10: .byte 0x74; /* DW_OP_breg4 */ \
+ .sleb128 offset; /* offset */ \
+ .byte 0x06; /* DW_OP_deref */ \
+11:
+
+
+ /* CIE */
+ .section .eh_frame,"a",@progbits
+.LSTARTFRAMEDLSI1:
+ .long .LENDCIEDLSI1-.LSTARTCIEDLSI1
+.LSTARTCIEDLSI1:
+ .long 0 /* CIE ID */
+ .byte 1 /* Version number */
+ .string "zRS" /* NULL-terminated
+ * augmentation string
+ */
+ .uleb128 1 /* Code alignment factor */
+ .sleb128 -4 /* Data alignment factor */
+ .byte 8 /* Return address
+ * register column
+ */
+ .uleb128 1 /* Augmentation value length */
+ .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
+ .byte 0 /* DW_CFA_nop */
+ .align 4
+.LENDCIEDLSI1:
+
+ /* FDE */
+ .long .LENDFDEDLSI1-.LSTARTFDEDLSI1 /* Length FDE */
+.LSTARTFDEDLSI1:
+ .long .LSTARTFDEDLSI1-.LSTARTFRAMEDLSI1 /* CIE pointer */
+ .long .startsigcode-. /* PC-relative start address */
+ .long .endsigcode-.startsigcode
+ .uleb128 0 /* Augmentation */
+ do_cfa_expr(LINUX_SIGF_SC-8)
+ .align 4
+.LENDFDEDLSI1:
+
+ .long .LENDFDEDLSI2-.LSTARTFDEDLSI2 /* Length FDE */
+.LSTARTFDEDLSI2:
+ .long .LSTARTFDEDLSI2-.LSTARTFRAMEDLSI1 /* CIE pointer */
+ .long .startrtsigcode-. /* PC-relative start address */
+ .long .endrtsigcode-.startrtsigcode
+ .uleb128 0 /* Augmentation */
+ do_cfa_expr(LINUX_RT_SIGF_SC-4+LINUX_SC_ESP)
+ .align 4
+.LENDFDEDLSI2:
+ .previous
+
+ .section .eh_frame,"a",@progbits
+.LSTARTFRAMEDLSI2:
+ .long .LENDCIEDLSI2-.LSTARTCIEDLSI2
+.LSTARTCIEDLSI2:
+ .long 0 /* CIE ID */
+ .byte 1 /* Version number */
+ .string "zR" /* NULL-terminated
+ * augmentation string
+ */
+ .uleb128 1 /* Code alignment factor */
+ .sleb128 -4 /* Data alignment factor */
+ .byte 8 /* Return address register column */
+ .uleb128 1 /* Augmentation value length */
+ .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */
+ .byte 0x0c /* DW_CFA_def_cfa */
+ .uleb128 4
+ .uleb128 4
+ .byte 0x88 /* DW_CFA_offset, column 0x8 */
+ .uleb128 1
+ .align 4
+.LENDCIEDLSI2:
+ .long .LENDFDEDLSI3-.LSTARTFDEDLSI3 /* Length FDE */
+.LSTARTFDEDLSI3:
+ .long .LSTARTFDEDLSI3-.LSTARTFRAMEDLSI2 /* CIE pointer */
+ .long .startvsyscall-. /* PC-relative start address */
+ .long .endvsyscall-.startvsyscall
+ .uleb128 0
+ .align 4
+.LENDFDEDLSI3:
+ .previous
diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c
index 970bd05120e6..730e7efc9572 100644
--- a/sys/i386/linux/linux_sysvec.c
+++ b/sys/i386/linux/linux_sysvec.c
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include <compat/linux/linux_misc.h>
#include <compat/linux/linux_signal.h>
#include <compat/linux/linux_util.h>
+#include <compat/linux/linux_vdso.h>
MODULE_VERSION(linux, 1);
@@ -93,8 +94,11 @@ MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures");
#define LINUX_PS_STRINGS (LINUX_USRSTACK - sizeof(struct ps_strings))
-extern char linux_sigcode[];
-extern int linux_szsigcode;
+static int linux_szsigcode;
+static vm_object_t linux_shared_page_obj;
+static char *linux_shared_page_mapping;
+extern char _binary_linux_locore_o_start;
+extern char _binary_linux_locore_o_end;
extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL];
@@ -110,6 +114,8 @@ static void exec_linux_setregs(struct thread *td,
struct image_params *imgp, u_long stack);
static register_t *linux_copyout_strings(struct image_params *imgp);
static boolean_t linux_trans_osrel(const Elf_Note *note, int32_t *osrel);
+static void linux_vdso_install(void *param);
+static void linux_vdso_deinstall(void *param);
static int linux_szplatform;
const char *linux_platform;
@@ -199,6 +205,10 @@ static int _bsd_to_linux_trapcode[] = {
_bsd_to_linux_trapcode[(code)]: \
LINUX_T_UNKNOWN)
+LINUX_VDSO_SYM_INTPTR(linux_sigcode);
+LINUX_VDSO_SYM_INTPTR(linux_rt_sigcode);
+LINUX_VDSO_SYM_INTPTR(linux_vsyscall);
+
/*
* If FreeBSD & Linux have a difference of opinion about what a trap
* means, deal with it here.
@@ -255,6 +265,9 @@ elf_linux_fixup(register_t **stack_base, struct image_params *imgp)
args = (Elf32_Auxargs *)imgp->auxargs;
pos = *stack_base + (imgp->args->argc + imgp->args->envc + 2);
+ AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR,
+ imgp->proc->p_sysent->sv_shared_page_base);
+ AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO, linux_vsyscall);
AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, cpu_feature);
/*
@@ -399,10 +412,6 @@ linux_copyout_strings(struct image_params *imgp)
return (stack_base);
}
-
-
-extern unsigned long linux_sznonrtsigcode;
-
static void
linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
{
@@ -478,6 +487,7 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
frame.sf_sc.uc_mcontext.sc_esi = regs->tf_esi;
frame.sf_sc.uc_mcontext.sc_ebp = regs->tf_ebp;
frame.sf_sc.uc_mcontext.sc_ebx = regs->tf_ebx;
+ frame.sf_sc.uc_mcontext.sc_esp = regs->tf_esp;
frame.sf_sc.uc_mcontext.sc_edx = regs->tf_edx;
frame.sf_sc.uc_mcontext.sc_ecx = regs->tf_ecx;
frame.sf_sc.uc_mcontext.sc_eax = regs->tf_eax;
@@ -515,7 +525,7 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
* Build context to run handler in.
*/
regs->tf_esp = (int)fp;
- regs->tf_eip = p->p_sysent->sv_sigcode_base + linux_sznonrtsigcode;
+ regs->tf_eip = linux_rt_sigcode;
regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D);
regs->tf_cs = _ucodesel;
regs->tf_ds = _udatasel;
@@ -606,6 +616,7 @@ linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
frame.sf_sc.sc_esi = regs->tf_esi;
frame.sf_sc.sc_ebp = regs->tf_ebp;
frame.sf_sc.sc_ebx = regs->tf_ebx;
+ frame.sf_sc.sc_esp = regs->tf_esp;
frame.sf_sc.sc_edx = regs->tf_edx;
frame.sf_sc.sc_ecx = regs->tf_ecx;
frame.sf_sc.sc_eax = regs->tf_eax;
@@ -634,7 +645,7 @@ linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
* Build context to run handler in.
*/
regs->tf_esp = (int)fp;
- regs->tf_eip = p->p_sysent->sv_sigcode_base;
+ regs->tf_eip = linux_sigcode;
regs->tf_eflags &= ~(PSL_T | PSL_VM | PSL_D);
regs->tf_cs = _ucodesel;
regs->tf_ds = _udatasel;
@@ -950,7 +961,7 @@ struct sysentvec linux_sysvec = {
.sv_transtrap = translate_traps,
.sv_fixup = linux_fixup,
.sv_sendsig = linux_sendsig,
- .sv_sigcode = linux_sigcode,
+ .sv_sigcode = &_binary_linux_locore_o_start,
.sv_szsigcode = &linux_szsigcode,
.sv_prepsyscall = NULL,
.sv_name = "Linux a.out",
@@ -989,7 +1000,7 @@ struct sysentvec elf_linux_sysvec = {
.sv_transtrap = translate_traps,
.sv_fixup = elf_linux_fixup,
.sv_sendsig = linux_sendsig,
- .sv_sigcode = linux_sigcode,
+ .sv_sigcode = &_binary_linux_locore_o_start,
.sv_szsigcode = &linux_szsigcode,
.sv_prepsyscall = NULL,
.sv_name = "Linux ELF",
@@ -1015,7 +1026,39 @@ struct sysentvec elf_linux_sysvec = {
.sv_schedtail = linux_schedtail,
.sv_thread_detach = linux_thread_detach,
};
-INIT_SYSENTVEC(elf_sysvec, &elf_linux_sysvec);
+
+static void
+linux_vdso_install(void *param)
+{
+
+ linux_szsigcode = (&_binary_linux_locore_o_end -
+ &_binary_linux_locore_o_start);
+
+ if (linux_szsigcode > elf_linux_sysvec.sv_shared_page_len)
+ panic("Linux invalid vdso size\n");
+
+ __elfN(linux_vdso_fixup)(&elf_linux_sysvec);
+
+ linux_shared_page_obj = __elfN(linux_shared_page_init)
+ (&linux_shared_page_mapping);
+
+ __elfN(linux_vdso_reloc)(&elf_linux_sysvec, LINUX_SHAREDPAGE);
+
+ bcopy(elf_linux_sysvec.sv_sigcode, linux_shared_page_mapping,
+ linux_szsigcode);
+ elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj;
+}
+SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_ANY,
+ (sysinit_cfunc_t)linux_vdso_install, NULL);
+
+static void
+linux_vdso_deinstall(void *param)
+{
+
+ __elfN(linux_shared_page_fini)(linux_shared_page_obj);
+};
+SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST,
+ (sysinit_cfunc_t)linux_vdso_deinstall, NULL);
static char GNU_ABI_VENDOR[] = "GNU";
static int GNULINUX_ABI_DESC = 0;
diff --git a/sys/i386/linux/linux_vdso.lds.s b/sys/i386/linux/linux_vdso.lds.s
new file mode 100644
index 000000000000..dcb61cf5e06f
--- /dev/null
+++ b/sys/i386/linux/linux_vdso.lds.s
@@ -0,0 +1,65 @@
+/*
+ * Linker script for 32-bit vDSO.
+ * Copied from Linux kernel arch/x86/vdso/vdso-layout.lds.S
+ * and arch/x86/vdso/vdso32/vdso32.lds.S
+ *
+ * $FreeBSD$
+ */
+
+SECTIONS
+{
+ . = . + SIZEOF_HEADERS;
+
+ .hash : { *(.hash) } :text
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+
+ .note : { *(.note.*) } :text :note
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
+ .eh_frame : { KEEP (*(.eh_frame)) } :text
+
+ .dynamic : { *(.dynamic) } :text :dynamic
+
+ .rodata : { *(.rodata*) } :text
+ .data : {
+ *(.data*)
+ *(.sdata*)
+ *(.got.plt) *(.got)
+ *(.gnu.linkonce.d.*)
+ *(.bss*)
+ *(.dynbss*)
+ *(.gnu.linkonce.b.*)
+ }
+
+ .altinstructions : { *(.altinstructions) }
+ .altinstr_replacement : { *(.altinstr_replacement) }
+
+ . = ALIGN(0x100);
+ .text : { *(.text*) } :text =0x90909090
+}
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+ dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
+ note PT_NOTE FLAGS(4); /* PF_R */
+ eh_frame_hdr PT_GNU_EH_FRAME;
+}
+
+ENTRY(linux_vsyscall);
+
+VERSION
+{
+ LINUX_2.5 {
+ global:
+ linux_vsyscall;
+ linux_sigcode;
+ linux_rt_sigcode;
+ local: *;
+ };
+}
diff --git a/sys/modules/linux/Makefile b/sys/modules/linux/Makefile
index a671a3377853..c50484d0d1dd 100644
--- a/sys/modules/linux/Makefile
+++ b/sys/modules/linux/Makefile
@@ -7,16 +7,18 @@ CFLAGS+=-DCOMPAT_FREEBSD32 -DCOMPAT_LINUX32
.PATH: ${.CURDIR}/../../compat/linux ${.CURDIR}/../../${MACHINE_CPUARCH}/linux${SFX}
+VDSO= linux${SFX}_vdso
+
KMOD= linux
SRCS= linux_fork.c linux${SFX}_dummy.c linux_emul.c linux_file.c \
linux_futex.c linux_getcwd.c linux_ioctl.c linux_ipc.c \
linux${SFX}_machdep.c linux_mib.c linux_misc.c linux_signal.c \
linux_socket.c linux_stats.c linux_sysctl.c linux${SFX}_sysent.c \
linux${SFX}_sysvec.c linux_uid16.c linux_util.c linux_time.c \
- linux_timer.c \
+ linux_timer.c linux_vdso.c \
opt_inet6.h opt_compat.h opt_posix.h opt_usb.h vnode_if.h \
device_if.h bus_if.h assym.s \
- linux${SFX}_locore.s linux${SFX}_support.s
+ linux${SFX}_support.s
DPSRCS= linux${SFX}_genassym.c
# XXX: for assym.s
@@ -25,6 +27,8 @@ SRCS+= opt_kstack_pages.h opt_nfs.h opt_compat.h opt_hwpmc_hooks.h
SRCS+= opt_apic.h
.endif
+OBJS= ${VDSO}.so
+
.if ${MACHINE_CPUARCH} == "i386"
SRCS+= linux_ptrace.c imgact_linux.c opt_cpu.h
.endif
@@ -37,19 +41,33 @@ EXPORT_SYMS+= linux_ifname
EXPORT_SYMS+= linux_ioctl_register_handler
EXPORT_SYMS+= linux_ioctl_unregister_handler
-CLEANFILES= linux${SFX}_assym.h linux${SFX}_genassym.o
+CLEANFILES= linux${SFX}_assym.h linux${SFX}_genassym.o linux${SFX}_locore.o
linux${SFX}_assym.h: linux${SFX}_genassym.o
sh ${SYSDIR}/kern/genassym.sh linux${SFX}_genassym.o > ${.TARGET}
linux${SFX}_locore.o: linux${SFX}_assym.h assym.s
- ${CC} -c -x assembler-with-cpp -DLOCORE ${CFLAGS} \
+ ${CC} -x assembler-with-cpp -DLOCORE -m32 -shared -s \
+ -pipe -I. -I${SYSDIR} -Werror -Wall -fno-common -nostdinc -nostdlib \
+ -fno-omit-frame-pointer \
+ -Wl,-T${.CURDIR}/../../${MACHINE_CPUARCH}/linux${SFX}/${VDSO}.lds.s \
+ -Wl,-soname=${VDSO}.so.1,--eh-frame-hdr,-fPIC,-warn-common \
${.IMPSRC} -o ${.TARGET}
linux${SFX}_support.o: linux${SFX}_assym.h assym.s
${CC} -c -x assembler-with-cpp -DLOCORE ${CFLAGS} \
${.IMPSRC} -o ${.TARGET}
+.if ${MACHINE_CPUARCH} == "amd64"
+${VDSO}.so: linux${SFX}_locore.o
+ ${OBJCOPY} --input binary --output elf64-x86-64-freebsd \
+ --binary-architecture i386 linux${SFX}_locore.o ${.TARGET}
+.else
+${VDSO}.so: linux${SFX}_locore.o
+ ${OBJCOPY} --input binary --output elf32-i386-freebsd \
+ --binary-architecture i386 linux${SFX}_locore.o ${.TARGET}
+.endif
+
linux${SFX}_genassym.o:
${CC} -c ${CFLAGS:N-fno-common} ${.IMPSRC}
@@ -57,6 +75,9 @@ linux${SFX}_genassym.o:
.if defined(KTR)
CFLAGS+= -DKTR
.endif
+.if defined(DEBUG)
+CFLAGS+= -DDEBUG
+.endif
.endif
.include <bsd.kmod.mk>