diff options
author | Andy Lutomirski <luto@amacapital.net> | 2014-03-20 18:01:21 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2014-03-20 18:19:14 -0400 |
commit | b67e612cef1e5964efc6fa99fb7ad3d31c4db01a (patch) | |
tree | c037de472a7e20c143dc3ca05e3f9327dc917d09 | |
parent | 4e40112c4ff6a577dd06d92b2a54cdf06265bf74 (diff) |
x86: Load the 32-bit vdso in place, just like the 64-bit vdsos
This replaces a decent amount of incomprehensible and buggy code
with much more straightforward code. It also brings the 32-bit vdso
more in line with the 64-bit vdsos, so maybe someday they can share
even more code.
This wastes a small amount of kernel .data and .text space, but it
avoids a couple of allocations on startup, so it should be more or
less a wash memory-wise.
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Cc: Stefani Seibold <stefani@seibold.net>
Link: http://lkml.kernel.org/r/b8093933fad09ce181edb08a61dcd5d2592e9814.1395352498.git.luto@amacapital.net
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | arch/x86/include/asm/vdso.h | 8 | ||||
-rw-r--r-- | arch/x86/vdso/vdso.S | 22 | ||||
-rw-r--r-- | arch/x86/vdso/vdso32-setup.c | 50 | ||||
-rw-r--r-- | arch/x86/vdso/vdso32.S | 21 | ||||
-rw-r--r-- | arch/x86/vdso/vdso_image.h | 30 | ||||
-rw-r--r-- | arch/x86/vdso/vdsox32.S | 22 | ||||
-rw-r--r-- | arch/x86/vdso/vma.c | 8 |
7 files changed, 70 insertions, 91 deletions
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index bde435998f3a..0301d78bb910 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h | |||
@@ -25,14 +25,6 @@ extern const char VDSO32_PRELINK[]; | |||
25 | extern void __user __kernel_sigreturn; | 25 | extern void __user __kernel_sigreturn; |
26 | extern void __user __kernel_rt_sigreturn; | 26 | extern void __user __kernel_rt_sigreturn; |
27 | 27 | ||
28 | /* | ||
29 | * These symbols are defined by vdso32.S to mark the bounds | ||
30 | * of the ELF DSO images included therein. | ||
31 | */ | ||
32 | extern const char vdso32_int80_start, vdso32_int80_end; | ||
33 | extern const char vdso32_syscall_start, vdso32_syscall_end; | ||
34 | extern const char vdso32_sysenter_start, vdso32_sysenter_end; | ||
35 | |||
36 | void __init patch_vdso32(void *vdso, size_t len); | 28 | void __init patch_vdso32(void *vdso, size_t len); |
37 | 29 | ||
38 | #endif /* _ASM_X86_VDSO_H */ | 30 | #endif /* _ASM_X86_VDSO_H */ |
diff --git a/arch/x86/vdso/vdso.S b/arch/x86/vdso/vdso.S index 1e13eb8c9656..c749d15da2e9 100644 --- a/arch/x86/vdso/vdso.S +++ b/arch/x86/vdso/vdso.S | |||
@@ -1,21 +1,3 @@ | |||
1 | #include <asm/page_types.h> | 1 | #include "vdso_image.h" |
2 | #include <linux/linkage.h> | ||
3 | 2 | ||
4 | __PAGE_ALIGNED_DATA | 3 | DEFINE_VDSO_IMAGE(vdso, "arch/x86/vdso/vdso.so") |
5 | |||
6 | .globl vdso_start, vdso_end | ||
7 | .align PAGE_SIZE | ||
8 | vdso_start: | ||
9 | .incbin "arch/x86/vdso/vdso.so" | ||
10 | vdso_end: | ||
11 | .align PAGE_SIZE /* extra data here leaks to userspace. */ | ||
12 | |||
13 | .previous | ||
14 | |||
15 | .globl vdso_pages | ||
16 | .bss | ||
17 | .align 8 | ||
18 | .type vdso_pages, @object | ||
19 | vdso_pages: | ||
20 | .zero (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE * 8 | ||
21 | .size vdso_pages, .-vdso_pages | ||
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index 5b4aaefb6b42..b45528ee8e19 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/fixmap.h> | 29 | #include <asm/fixmap.h> |
30 | #include <asm/hpet.h> | 30 | #include <asm/hpet.h> |
31 | #include <asm/vvar.h> | 31 | #include <asm/vvar.h> |
32 | #include "vdso_image.h" | ||
32 | 33 | ||
33 | #ifdef CONFIG_COMPAT_VDSO | 34 | #ifdef CONFIG_COMPAT_VDSO |
34 | #define VDSO_DEFAULT 0 | 35 | #define VDSO_DEFAULT 0 |
@@ -41,6 +42,12 @@ | |||
41 | #define arch_setup_additional_pages syscall32_setup_pages | 42 | #define arch_setup_additional_pages syscall32_setup_pages |
42 | #endif | 43 | #endif |
43 | 44 | ||
45 | DECLARE_VDSO_IMAGE(vdso32_int80); | ||
46 | #ifdef CONFIG_COMPAT | ||
47 | DECLARE_VDSO_IMAGE(vdso32_syscall); | ||
48 | #endif | ||
49 | DECLARE_VDSO_IMAGE(vdso32_sysenter); | ||
50 | |||
44 | /* | 51 | /* |
45 | * Should the kernel map a VDSO page into processes and pass its | 52 | * Should the kernel map a VDSO page into processes and pass its |
46 | * address down to glibc upon exec()? | 53 | * address down to glibc upon exec()? |
@@ -71,7 +78,7 @@ EXPORT_SYMBOL_GPL(vdso_enabled); | |||
71 | #endif | 78 | #endif |
72 | 79 | ||
73 | static struct page **vdso32_pages; | 80 | static struct page **vdso32_pages; |
74 | static unsigned int vdso32_size; | 81 | static unsigned vdso32_size; |
75 | 82 | ||
76 | #ifdef CONFIG_X86_64 | 83 | #ifdef CONFIG_X86_64 |
77 | 84 | ||
@@ -117,31 +124,32 @@ void enable_sep_cpu(void) | |||
117 | 124 | ||
118 | int __init sysenter_setup(void) | 125 | int __init sysenter_setup(void) |
119 | { | 126 | { |
120 | void *vdso_pages; | 127 | char *vdso32_start, *vdso32_end; |
121 | const void *vdso; | 128 | int npages, i; |
122 | size_t vdso_len; | ||
123 | unsigned int i; | ||
124 | 129 | ||
130 | #ifdef CONFIG_COMPAT | ||
125 | if (vdso32_syscall()) { | 131 | if (vdso32_syscall()) { |
126 | vdso = &vdso32_syscall_start; | 132 | vdso32_start = vdso32_syscall_start; |
127 | vdso_len = &vdso32_syscall_end - &vdso32_syscall_start; | 133 | vdso32_end = vdso32_syscall_end; |
128 | } else if (vdso32_sysenter()){ | 134 | vdso32_pages = vdso32_syscall_pages; |
129 | vdso = &vdso32_sysenter_start; | 135 | } else |
130 | vdso_len = &vdso32_sysenter_end - &vdso32_sysenter_start; | 136 | #endif |
137 | if (vdso32_sysenter()) { | ||
138 | vdso32_start = vdso32_sysenter_start; | ||
139 | vdso32_end = vdso32_sysenter_end; | ||
140 | vdso32_pages = vdso32_sysenter_pages; | ||
131 | } else { | 141 | } else { |
132 | vdso = &vdso32_int80_start; | 142 | vdso32_start = vdso32_int80_start; |
133 | vdso_len = &vdso32_int80_end - &vdso32_int80_start; | 143 | vdso32_end = vdso32_int80_end; |
144 | vdso32_pages = vdso32_int80_pages; | ||
134 | } | 145 | } |
135 | 146 | ||
136 | vdso32_size = (vdso_len + PAGE_SIZE - 1) / PAGE_SIZE; | 147 | npages = ((vdso32_end - vdso32_start) + PAGE_SIZE - 1) / PAGE_SIZE; |
137 | vdso32_pages = kmalloc(sizeof(*vdso32_pages) * vdso32_size, GFP_ATOMIC); | 148 | vdso32_size = npages << PAGE_SHIFT; |
138 | vdso_pages = kmalloc(VDSO_OFFSET(vdso32_size), GFP_ATOMIC); | 149 | for (i = 0; i < npages; i++) |
139 | 150 | vdso32_pages[i] = virt_to_page(vdso32_start + i*PAGE_SIZE); | |
140 | for(i = 0; i != vdso32_size; ++i) | ||
141 | vdso32_pages[i] = virt_to_page(vdso_pages + VDSO_OFFSET(i)); | ||
142 | 151 | ||
143 | memcpy(vdso_pages, vdso, vdso_len); | 152 | patch_vdso32(vdso32_start, vdso32_size); |
144 | patch_vdso32(vdso_pages, vdso_len); | ||
145 | 153 | ||
146 | return 0; | 154 | return 0; |
147 | } | 155 | } |
@@ -177,7 +185,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
177 | */ | 185 | */ |
178 | ret = install_special_mapping(mm, | 186 | ret = install_special_mapping(mm, |
179 | addr, | 187 | addr, |
180 | VDSO_OFFSET(vdso32_size), | 188 | vdso32_size, |
181 | VM_READ|VM_EXEC| | 189 | VM_READ|VM_EXEC| |
182 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | 190 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, |
183 | vdso32_pages); | 191 | vdso32_pages); |
diff --git a/arch/x86/vdso/vdso32.S b/arch/x86/vdso/vdso32.S index 2ce5f82c333b..cfa6adda110b 100644 --- a/arch/x86/vdso/vdso32.S +++ b/arch/x86/vdso/vdso32.S | |||
@@ -1,22 +1,9 @@ | |||
1 | #include <linux/init.h> | 1 | #include "vdso_image.h" |
2 | 2 | ||
3 | __INITDATA | 3 | DEFINE_VDSO_IMAGE(vdso32_int80, "arch/x86/vdso/vdso32-int80.so") |
4 | 4 | ||
5 | .globl vdso32_int80_start, vdso32_int80_end | ||
6 | vdso32_int80_start: | ||
7 | .incbin "arch/x86/vdso/vdso32-int80.so" | ||
8 | vdso32_int80_end: | ||
9 | |||
10 | .globl vdso32_syscall_start, vdso32_syscall_end | ||
11 | vdso32_syscall_start: | ||
12 | #ifdef CONFIG_COMPAT | 5 | #ifdef CONFIG_COMPAT |
13 | .incbin "arch/x86/vdso/vdso32-syscall.so" | 6 | DEFINE_VDSO_IMAGE(vdso32_syscall, "arch/x86/vdso/vdso32-syscall.so") |
14 | #endif | 7 | #endif |
15 | vdso32_syscall_end: | ||
16 | |||
17 | .globl vdso32_sysenter_start, vdso32_sysenter_end | ||
18 | vdso32_sysenter_start: | ||
19 | .incbin "arch/x86/vdso/vdso32-sysenter.so" | ||
20 | vdso32_sysenter_end: | ||
21 | 8 | ||
22 | __FINIT | 9 | DEFINE_VDSO_IMAGE(vdso32_sysenter, "arch/x86/vdso/vdso32-sysenter.so") |
diff --git a/arch/x86/vdso/vdso_image.h b/arch/x86/vdso/vdso_image.h new file mode 100644 index 000000000000..1baa6bc517bf --- /dev/null +++ b/arch/x86/vdso/vdso_image.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef _VDSO_IMAGE_H | ||
2 | #define _VDSO_IMAGE_H | ||
3 | |||
4 | #include <asm/page_types.h> | ||
5 | #include <linux/linkage.h> | ||
6 | |||
7 | #define DEFINE_VDSO_IMAGE(symname, filename) \ | ||
8 | __PAGE_ALIGNED_DATA ; \ | ||
9 | .globl symname##_start, symname##_end ; \ | ||
10 | .align PAGE_SIZE ; \ | ||
11 | symname##_start: ; \ | ||
12 | .incbin filename ; \ | ||
13 | symname##_end: ; \ | ||
14 | .align PAGE_SIZE /* extra data here leaks to userspace. */ ; \ | ||
15 | \ | ||
16 | .previous ; \ | ||
17 | \ | ||
18 | .globl symname##_pages ; \ | ||
19 | .bss ; \ | ||
20 | .align 8 ; \ | ||
21 | .type symname##_pages, @object ; \ | ||
22 | symname##_pages: ; \ | ||
23 | .zero (symname##_end - symname##_start + PAGE_SIZE - 1) / PAGE_SIZE * (BITS_PER_LONG / 8) ; \ | ||
24 | .size symname##_pages, .-symname##_pages | ||
25 | |||
26 | #define DECLARE_VDSO_IMAGE(symname) \ | ||
27 | extern char symname##_start[], symname##_end[]; \ | ||
28 | extern struct page *symname##_pages[] | ||
29 | |||
30 | #endif /* _VDSO_IMAGE_H */ | ||
diff --git a/arch/x86/vdso/vdsox32.S b/arch/x86/vdso/vdsox32.S index 295f1c7543d8..19a692778650 100644 --- a/arch/x86/vdso/vdsox32.S +++ b/arch/x86/vdso/vdsox32.S | |||
@@ -1,21 +1,3 @@ | |||
1 | #include <asm/page_types.h> | 1 | #include "vdso_image.h" |
2 | #include <linux/linkage.h> | ||
3 | 2 | ||
4 | __PAGE_ALIGNED_DATA | 3 | DEFINE_VDSO_IMAGE(vdsox32, "arch/x86/vdso/vdsox32.so") |
5 | |||
6 | .globl vdsox32_start, vdsox32_end | ||
7 | .align PAGE_SIZE | ||
8 | vdsox32_start: | ||
9 | .incbin "arch/x86/vdso/vdsox32.so" | ||
10 | vdsox32_end: | ||
11 | .align PAGE_SIZE /* extra data here leaks to userspace. */ | ||
12 | |||
13 | .previous | ||
14 | |||
15 | .globl vdsox32_pages | ||
16 | .bss | ||
17 | .align 8 | ||
18 | .type vdsox32_pages, @object | ||
19 | vdsox32_pages: | ||
20 | .zero (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE * 8 | ||
21 | .size vdsox32_pages, .-vdsox32_pages | ||
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 7345bc9a1af6..6db0bbd876fd 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c | |||
@@ -15,19 +15,17 @@ | |||
15 | #include <asm/proto.h> | 15 | #include <asm/proto.h> |
16 | #include <asm/vdso.h> | 16 | #include <asm/vdso.h> |
17 | #include <asm/page.h> | 17 | #include <asm/page.h> |
18 | #include "vdso_image.h" | ||
18 | 19 | ||
19 | #if defined(CONFIG_X86_64) | 20 | #if defined(CONFIG_X86_64) |
20 | unsigned int __read_mostly vdso_enabled = 1; | 21 | unsigned int __read_mostly vdso_enabled = 1; |
21 | 22 | ||
22 | extern char vdso_start[], vdso_end[]; | 23 | DECLARE_VDSO_IMAGE(vdso); |
23 | extern unsigned short vdso_sync_cpuid; | 24 | extern unsigned short vdso_sync_cpuid; |
24 | |||
25 | extern struct page *vdso_pages[]; | ||
26 | static unsigned vdso_size; | 25 | static unsigned vdso_size; |
27 | 26 | ||
28 | #ifdef CONFIG_X86_X32_ABI | 27 | #ifdef CONFIG_X86_X32_ABI |
29 | extern char vdsox32_start[], vdsox32_end[]; | 28 | DECLARE_VDSO_IMAGE(vdsox32); |
30 | extern struct page *vdsox32_pages[]; | ||
31 | static unsigned vdsox32_size; | 29 | static unsigned vdsox32_size; |
32 | #endif | 30 | #endif |
33 | #endif | 31 | #endif |