Skip to content

Commit

Permalink
x86, vdso: Move the 32-bit vdso special pages after the text
Browse files Browse the repository at this point in the history
This unifies the vdso mapping code and teaches it how to map special
pages at addresses corresponding to symbols in the vdso image.  The
new code is used for all vdso variants, but so far only the 32-bit
variants use the new vvar page position.

Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/b6d7858ad7b5ac3fd3c29cab6d6d769bc45d195e.1399317206.git.luto@amacapital.net
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
  • Loading branch information
Andy Lutomirski authored and H. Peter Anvin committed May 5, 2014
1 parent 6f121e5 commit 18d0a6f
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 166 deletions.
8 changes: 3 additions & 5 deletions arch/x86/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,9 @@ struct linux_binprm;
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
extern int x32_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);

extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
#define compat_arch_setup_additional_pages syscall32_setup_pages
extern int compat_arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
#define compat_arch_setup_additional_pages compat_arch_setup_additional_pages

extern unsigned long arch_randomize_brk(struct mm_struct *mm);
#define arch_randomize_brk arch_randomize_brk
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/include/asm/vdso.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ struct vdso_image {

unsigned long alt, alt_len;

unsigned long sym_end_mapping; /* Total size of the mapping */

unsigned long sym_vvar_page;
unsigned long sym_hpet_page;
unsigned long sym_VDSO32_NOTE_MASK;
unsigned long sym___kernel_sigreturn;
unsigned long sym___kernel_rt_sigreturn;
Expand Down
11 changes: 0 additions & 11 deletions arch/x86/include/asm/vdso32.h

This file was deleted.

42 changes: 26 additions & 16 deletions arch/x86/vdso/vdso-layout.lds.S
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <asm/vdso.h>

/*
* Linker script for vDSO. This is an ELF shared object prelinked to
* its virtual address, and with only one read-only segment.
Expand All @@ -6,20 +8,6 @@

SECTIONS
{
#ifdef BUILD_VDSO32
#include <asm/vdso32.h>

hpet_page = . - VDSO_OFFSET(VDSO_HPET_PAGE);

vvar = . - VDSO_OFFSET(VDSO_VVAR_PAGE);

/* Place all vvars at the offsets in asm/vvar.h. */
#define EMIT_VVAR(name, offset) vvar_ ## name = vvar + offset;
#define __VVAR_KERNEL_LDS
#include <asm/vvar.h>
#undef __VVAR_KERNEL_LDS
#undef EMIT_VVAR
#endif
. = SIZEOF_HEADERS;

.hash : { *(.hash) } :text
Expand Down Expand Up @@ -59,11 +47,33 @@ SECTIONS

.text : { *(.text*) } :text =0x90909090,

#ifdef BUILD_VDSO32
/*
* The comma above works around a bug in gold:
* https://sourceware.org/bugzilla/show_bug.cgi?id=16804
* The remainder of the vDSO consists of special pages that are
* shared between the kernel and userspace. It needs to be at the
* end so that it doesn't overlap the mapping of the actual
* vDSO image.
*/

. = ALIGN(PAGE_SIZE);
vvar_page = .;

/* Place all vvars at the offsets in asm/vvar.h. */
#define EMIT_VVAR(name, offset) vvar_ ## name = vvar_page + offset;
#define __VVAR_KERNEL_LDS
#include <asm/vvar.h>
#undef __VVAR_KERNEL_LDS
#undef EMIT_VVAR

. = vvar_page + PAGE_SIZE;

hpet_page = .;
. = . + PAGE_SIZE;
#endif

. = ALIGN(PAGE_SIZE);
end_mapping = .;

/DISCARD/ : {
*(.discard)
*(.discard.*)
Expand Down
14 changes: 14 additions & 0 deletions arch/x86/vdso/vdso2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,21 @@
#include <linux/types.h>

/* Symbols that we need in vdso2c. */
enum {
sym_vvar_page,
sym_hpet_page,
sym_end_mapping,
};

const int special_pages[] = {
sym_vvar_page,
sym_hpet_page,
};

char const * const required_syms[] = {
[sym_vvar_page] = "vvar_page",
[sym_hpet_page] = "hpet_page",
[sym_end_mapping] = "end_mapping",
"VDSO32_NOTE_MASK",
"VDSO32_SYSENTER_RETURN",
"__kernel_vsyscall",
Expand Down
17 changes: 17 additions & 0 deletions arch/x86/vdso/vdso2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ static int GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
}
}

/* Validate mapping addresses. */
for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) {
if (!syms[i])
continue; /* The mapping isn't used; ignore it. */

if (syms[i] % 4096)
fail("%s must be a multiple of 4096\n",
required_syms[i]);
if (syms[i] < data_size)
fail("%s must be after the text mapping\n",
required_syms[i]);
if (syms[sym_end_mapping] < syms[i] + 4096)
fail("%s overruns end_mapping\n", required_syms[i]);
}
if (syms[sym_end_mapping] % 4096)
fail("end_mapping must be a multiple of 4096\n");

/* Remove sections. */
hdr->e_shoff = 0;
hdr->e_shentsize = 0;
Expand Down
115 changes: 3 additions & 112 deletions arch/x86/vdso/vdso32-setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,19 @@

#include <linux/init.h>
#include <linux/smp.h>
#include <linux/thread_info.h>
#include <linux/sched.h>
#include <linux/gfp.h>
#include <linux/string.h>
#include <linux/elf.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/mm_types.h>

#include <asm/cpufeature.h>
#include <asm/msr.h>
#include <asm/pgtable.h>
#include <asm/unistd.h>
#include <asm/elf.h>
#include <asm/tlbflush.h>
#include <asm/processor.h>
#include <asm/vdso.h>
#include <asm/proto.h>
#include <asm/fixmap.h>
#include <asm/hpet.h>
#include <asm/vvar.h>
#include <asm/vdso32.h>

#ifdef CONFIG_COMPAT_VDSO
#define VDSO_DEFAULT 0
#else
#define VDSO_DEFAULT 1
#endif

#ifdef CONFIG_X86_64
#define arch_setup_additional_pages syscall32_setup_pages
#endif

/*
* Should the kernel map a VDSO page into processes and pass its
* address down to glibc upon exec()?
Expand Down Expand Up @@ -101,95 +81,6 @@ int __init sysenter_setup(void)
return 0;
}

/* Setup a VMA at program startup for the vsyscall page */
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
struct mm_struct *mm = current->mm;
unsigned long addr;
int ret = 0;
struct vm_area_struct *vma;
unsigned long vdso32_size = selected_vdso32->size;

#ifdef CONFIG_X86_X32_ABI
if (test_thread_flag(TIF_X32))
return x32_setup_additional_pages(bprm, uses_interp);
#endif

if (vdso32_enabled != 1) /* Other values all mean "disabled" */
return 0;

down_write(&mm->mmap_sem);

addr = get_unmapped_area(NULL, 0, vdso32_size + VDSO_OFFSET(VDSO_PREV_PAGES), 0, 0);
if (IS_ERR_VALUE(addr)) {
ret = addr;
goto up_fail;
}

addr += VDSO_OFFSET(VDSO_PREV_PAGES);

current->mm->context.vdso = (void __user *)addr;

/*
* MAYWRITE to allow gdb to COW and set breakpoints
*/
ret = install_special_mapping(mm,
addr,
vdso32_size,
VM_READ|VM_EXEC|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
selected_vdso32->pages);

if (ret)
goto up_fail;

vma = _install_special_mapping(mm,
addr - VDSO_OFFSET(VDSO_PREV_PAGES),
VDSO_OFFSET(VDSO_PREV_PAGES),
VM_READ,
NULL);

if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto up_fail;
}

ret = remap_pfn_range(vma,
addr - VDSO_OFFSET(VDSO_VVAR_PAGE),
__pa_symbol(&__vvar_page) >> PAGE_SHIFT,
PAGE_SIZE,
PAGE_READONLY);

if (ret)
goto up_fail;

#ifdef CONFIG_HPET_TIMER
if (hpet_address) {
ret = io_remap_pfn_range(vma,
addr - VDSO_OFFSET(VDSO_HPET_PAGE),
hpet_address >> PAGE_SHIFT,
PAGE_SIZE,
pgprot_noncached(PAGE_READONLY));

if (ret)
goto up_fail;
}
#endif

if (selected_vdso32->sym_VDSO32_SYSENTER_RETURN)
current_thread_info()->sysenter_return =
current->mm->context.vdso +
selected_vdso32->sym_VDSO32_SYSENTER_RETURN;

up_fail:
if (ret)
current->mm->context.vdso = NULL;

up_write(&mm->mmap_sem);

return ret;
}

#ifdef CONFIG_X86_64

subsys_initcall(sysenter_setup);
Expand Down
Loading

0 comments on commit 18d0a6f

Please sign in to comment.