diff options
author | H. J. Lu <hjl.tools@gmail.com> | 2012-02-19 14:38:06 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2012-02-20 15:52:06 -0500 |
commit | 1a21d4e095ef720abf81299000afc038206d571b (patch) | |
tree | 8d64feea71f41f785521f66fecbd29c16b0cf63a | |
parent | 5fd92e65a68b813667bc8739f5fa463e5bfcd66d (diff) |
x32: Add x32 VDSO support
Add support for the x32 VDSO. The x32 VDSO takes advantage of the
similarity between the x86-64 and the x32 ABIs to contain the same
content, only the container is different, as the x32 VDSO obviously is
an x32 shared object.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | arch/x86/vdso/.gitignore | 2 | ||||
-rw-r--r-- | arch/x86/vdso/Makefile | 46 | ||||
-rw-r--r-- | arch/x86/vdso/vdso32-setup.c | 6 | ||||
-rw-r--r-- | arch/x86/vdso/vdsox32.S | 22 | ||||
-rw-r--r-- | arch/x86/vdso/vdsox32.lds.S | 32 | ||||
-rw-r--r-- | arch/x86/vdso/vma.c | 78 |
6 files changed, 177 insertions, 9 deletions
diff --git a/arch/x86/vdso/.gitignore b/arch/x86/vdso/.gitignore index 60274d5746e1..3282874bc61d 100644 --- a/arch/x86/vdso/.gitignore +++ b/arch/x86/vdso/.gitignore | |||
@@ -1,5 +1,7 @@ | |||
1 | vdso.lds | 1 | vdso.lds |
2 | vdso-syms.lds | 2 | vdso-syms.lds |
3 | vdsox32.lds | ||
4 | vdsox32-syms.lds | ||
3 | vdso32-syms.lds | 5 | vdso32-syms.lds |
4 | vdso32-syscall-syms.lds | 6 | vdso32-syscall-syms.lds |
5 | vdso32-sysenter-syms.lds | 7 | vdso32-sysenter-syms.lds |
diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile index 5d179502a52c..fd14be1d1472 100644 --- a/arch/x86/vdso/Makefile +++ b/arch/x86/vdso/Makefile | |||
@@ -3,21 +3,29 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | VDSO64-$(CONFIG_X86_64) := y | 5 | VDSO64-$(CONFIG_X86_64) := y |
6 | VDSOX32-$(CONFIG_X86_X32_ABI) := y | ||
6 | VDSO32-$(CONFIG_X86_32) := y | 7 | VDSO32-$(CONFIG_X86_32) := y |
7 | VDSO32-$(CONFIG_COMPAT) := y | 8 | VDSO32-$(CONFIG_COMPAT) := y |
8 | 9 | ||
9 | vdso-install-$(VDSO64-y) += vdso.so | 10 | vdso-install-$(VDSO64-y) += vdso.so |
11 | vdso-install-$(VDSOX32-y) += vdsox32.so | ||
10 | vdso-install-$(VDSO32-y) += $(vdso32-images) | 12 | vdso-install-$(VDSO32-y) += $(vdso32-images) |
11 | 13 | ||
12 | 14 | ||
13 | # files to link into the vdso | 15 | # files to link into the vdso |
14 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o | 16 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o |
15 | 17 | ||
18 | vobjs-$(VDSOX32-y) += $(vobjx32s-compat) | ||
19 | |||
20 | # Filter out x32 objects. | ||
21 | vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y)) | ||
22 | |||
16 | # files to link into kernel | 23 | # files to link into kernel |
17 | obj-$(VDSO64-y) += vma.o vdso.o | 24 | obj-$(VDSO64-y) += vma.o vdso.o |
25 | obj-$(VDSOX32-y) += vdsox32.o | ||
18 | obj-$(VDSO32-y) += vdso32.o vdso32-setup.o | 26 | obj-$(VDSO32-y) += vdso32.o vdso32-setup.o |
19 | 27 | ||
20 | vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) | 28 | vobjs := $(foreach F,$(vobj64s),$(obj)/$F) |
21 | 29 | ||
22 | $(obj)/vdso.o: $(obj)/vdso.so | 30 | $(obj)/vdso.o: $(obj)/vdso.so |
23 | 31 | ||
@@ -73,6 +81,42 @@ $(obj)/%-syms.lds: $(obj)/%.so.dbg FORCE | |||
73 | $(call if_changed,vdsosym) | 81 | $(call if_changed,vdsosym) |
74 | 82 | ||
75 | # | 83 | # |
84 | # X32 processes use x32 vDSO to access 64bit kernel data. | ||
85 | # | ||
86 | # Build x32 vDSO image: | ||
87 | # 1. Compile x32 vDSO as 64bit. | ||
88 | # 2. Convert object files to x32. | ||
89 | # 3. Build x32 VDSO image with x32 objects, which contains 64bit codes | ||
90 | # so that it can reach 64bit address space with 64bit pointers. | ||
91 | # | ||
92 | |||
93 | targets += vdsox32-syms.lds | ||
94 | obj-$(VDSOX32-y) += vdsox32-syms.lds | ||
95 | |||
96 | CPPFLAGS_vdsox32.lds = $(CPPFLAGS_vdso.lds) | ||
97 | VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \ | ||
98 | -Wl,-soname=linux-vdso.so.1 \ | ||
99 | -Wl,-z,max-page-size=4096 \ | ||
100 | -Wl,-z,common-page-size=4096 | ||
101 | |||
102 | vobjx32s-y := $(vobj64s:.o=-x32.o) | ||
103 | vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) | ||
104 | |||
105 | # Convert 64bit object file to x32 for x32 vDSO. | ||
106 | quiet_cmd_x32 = X32 $@ | ||
107 | cmd_x32 = $(OBJCOPY) -O elf32-x86-64 $< $@ | ||
108 | |||
109 | $(obj)/%-x32.o: $(obj)/%.o FORCE | ||
110 | $(call if_changed,x32) | ||
111 | |||
112 | targets += vdsox32.so vdsox32.so.dbg vdsox32.lds $(vobjx32s-y) | ||
113 | |||
114 | $(obj)/vdsox32.o: $(src)/vdsox32.S $(obj)/vdsox32.so | ||
115 | |||
116 | $(obj)/vdsox32.so.dbg: $(src)/vdsox32.lds $(vobjx32s) FORCE | ||
117 | $(call if_changed,vdso) | ||
118 | |||
119 | # | ||
76 | # Build multiple 32-bit vDSO images to choose from at boot time. | 120 | # Build multiple 32-bit vDSO images to choose from at boot time. |
77 | # | 121 | # |
78 | obj-$(VDSO32-y) += vdso32-syms.lds | 122 | obj-$(VDSO32-y) += vdso32-syms.lds |
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c index 468d591dde31..01b8a0df5e0e 100644 --- a/arch/x86/vdso/vdso32-setup.c +++ b/arch/x86/vdso/vdso32-setup.c | |||
@@ -317,6 +317,12 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
317 | int ret = 0; | 317 | int ret = 0; |
318 | bool compat; | 318 | bool compat; |
319 | 319 | ||
320 | #ifdef CONFIG_X86_X32_ABI | ||
321 | extern int x32_setup_additional_pages(struct linux_binprm *, int); | ||
322 | if (test_thread_flag(TIF_X32)) | ||
323 | return x32_setup_additional_pages (bprm, uses_interp); | ||
324 | #endif | ||
325 | |||
320 | if (vdso_enabled == VDSO_DISABLED) | 326 | if (vdso_enabled == VDSO_DISABLED) |
321 | return 0; | 327 | return 0; |
322 | 328 | ||
diff --git a/arch/x86/vdso/vdsox32.S b/arch/x86/vdso/vdsox32.S new file mode 100644 index 000000000000..d6b9a7f42a8a --- /dev/null +++ b/arch/x86/vdso/vdsox32.S | |||
@@ -0,0 +1,22 @@ | |||
1 | #include <asm/page_types.h> | ||
2 | #include <linux/linkage.h> | ||
3 | #include <linux/init.h> | ||
4 | |||
5 | __PAGE_ALIGNED_DATA | ||
6 | |||
7 | .globl vdsox32_start, vdsox32_end | ||
8 | .align PAGE_SIZE | ||
9 | vdsox32_start: | ||
10 | .incbin "arch/x86/vdso/vdsox32.so" | ||
11 | vdsox32_end: | ||
12 | .align PAGE_SIZE /* extra data here leaks to userspace. */ | ||
13 | |||
14 | .previous | ||
15 | |||
16 | .globl vdsox32_pages | ||
17 | .bss | ||
18 | .align 8 | ||
19 | .type vdsox32_pages, @object | ||
20 | vdsox32_pages: | ||
21 | .zero (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE * 8 | ||
22 | .size vdsox32_pages, .-vdsox32_pages | ||
diff --git a/arch/x86/vdso/vdsox32.lds.S b/arch/x86/vdso/vdsox32.lds.S new file mode 100644 index 000000000000..373ca9a02a53 --- /dev/null +++ b/arch/x86/vdso/vdsox32.lds.S | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Linker script for x32 vDSO. | ||
3 | * We #include the file to define the layout details. | ||
4 | * Here we only choose the prelinked virtual address. | ||
5 | * | ||
6 | * This file defines the version script giving the user-exported symbols in | ||
7 | * the DSO. We can define local symbols here called VDSO* to make their | ||
8 | * values visible using the asm-x86/vdso.h macros from the kernel proper. | ||
9 | */ | ||
10 | |||
11 | #define VDSO_PRELINK 0 | ||
12 | #include "vdso-layout.lds.S" | ||
13 | |||
14 | /* | ||
15 | * This controls what userland symbols we export from the vDSO. | ||
16 | */ | ||
17 | VERSION { | ||
18 | LINUX_2.6 { | ||
19 | global: | ||
20 | clock_gettime; | ||
21 | __vdso_clock_gettime; | ||
22 | gettimeofday; | ||
23 | __vdso_gettimeofday; | ||
24 | getcpu; | ||
25 | __vdso_getcpu; | ||
26 | time; | ||
27 | __vdso_time; | ||
28 | local: *; | ||
29 | }; | ||
30 | } | ||
31 | |||
32 | VDSOX32_PRELINK = VDSO_PRELINK; | ||
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c index 153407c35b75..1bbcc6205ace 100644 --- a/arch/x86/vdso/vma.c +++ b/arch/x86/vdso/vma.c | |||
@@ -24,7 +24,44 @@ extern unsigned short vdso_sync_cpuid; | |||
24 | extern struct page *vdso_pages[]; | 24 | extern struct page *vdso_pages[]; |
25 | static unsigned vdso_size; | 25 | static unsigned vdso_size; |
26 | 26 | ||
27 | static void __init patch_vdso(void *vdso, size_t len) | 27 | #ifdef CONFIG_X86_X32_ABI |
28 | extern char vdsox32_start[], vdsox32_end[]; | ||
29 | extern struct page *vdsox32_pages[]; | ||
30 | static unsigned vdsox32_size; | ||
31 | |||
32 | static void __init patch_vdsox32(void *vdso, size_t len) | ||
33 | { | ||
34 | Elf32_Ehdr *hdr = vdso; | ||
35 | Elf32_Shdr *sechdrs, *alt_sec = 0; | ||
36 | char *secstrings; | ||
37 | void *alt_data; | ||
38 | int i; | ||
39 | |||
40 | BUG_ON(len < sizeof(Elf32_Ehdr)); | ||
41 | BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0); | ||
42 | |||
43 | sechdrs = (void *)hdr + hdr->e_shoff; | ||
44 | secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
45 | |||
46 | for (i = 1; i < hdr->e_shnum; i++) { | ||
47 | Elf32_Shdr *shdr = &sechdrs[i]; | ||
48 | if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) { | ||
49 | alt_sec = shdr; | ||
50 | goto found; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /* If we get here, it's probably a bug. */ | ||
55 | pr_warning("patch_vdsox32: .altinstructions not found\n"); | ||
56 | return; /* nothing to patch */ | ||
57 | |||
58 | found: | ||
59 | alt_data = (void *)hdr + alt_sec->sh_offset; | ||
60 | apply_alternatives(alt_data, alt_data + alt_sec->sh_size); | ||
61 | } | ||
62 | #endif | ||
63 | |||
64 | static void __init patch_vdso64(void *vdso, size_t len) | ||
28 | { | 65 | { |
29 | Elf64_Ehdr *hdr = vdso; | 66 | Elf64_Ehdr *hdr = vdso; |
30 | Elf64_Shdr *sechdrs, *alt_sec = 0; | 67 | Elf64_Shdr *sechdrs, *alt_sec = 0; |
@@ -47,7 +84,7 @@ static void __init patch_vdso(void *vdso, size_t len) | |||
47 | } | 84 | } |
48 | 85 | ||
49 | /* If we get here, it's probably a bug. */ | 86 | /* If we get here, it's probably a bug. */ |
50 | pr_warning("patch_vdso: .altinstructions not found\n"); | 87 | pr_warning("patch_vdso64: .altinstructions not found\n"); |
51 | return; /* nothing to patch */ | 88 | return; /* nothing to patch */ |
52 | 89 | ||
53 | found: | 90 | found: |
@@ -60,12 +97,20 @@ static int __init init_vdso(void) | |||
60 | int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE; | 97 | int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE; |
61 | int i; | 98 | int i; |
62 | 99 | ||
63 | patch_vdso(vdso_start, vdso_end - vdso_start); | 100 | patch_vdso64(vdso_start, vdso_end - vdso_start); |
64 | 101 | ||
65 | vdso_size = npages << PAGE_SHIFT; | 102 | vdso_size = npages << PAGE_SHIFT; |
66 | for (i = 0; i < npages; i++) | 103 | for (i = 0; i < npages; i++) |
67 | vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE); | 104 | vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE); |
68 | 105 | ||
106 | #ifdef CONFIG_X86_X32_ABI | ||
107 | patch_vdsox32(vdsox32_start, vdsox32_end - vdsox32_start); | ||
108 | npages = (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE; | ||
109 | vdsox32_size = npages << PAGE_SHIFT; | ||
110 | for (i = 0; i < npages; i++) | ||
111 | vdsox32_pages[i] = virt_to_page(vdsox32_start + i*PAGE_SIZE); | ||
112 | #endif | ||
113 | |||
69 | return 0; | 114 | return 0; |
70 | } | 115 | } |
71 | subsys_initcall(init_vdso); | 116 | subsys_initcall(init_vdso); |
@@ -103,7 +148,10 @@ static unsigned long vdso_addr(unsigned long start, unsigned len) | |||
103 | 148 | ||
104 | /* Setup a VMA at program startup for the vsyscall page. | 149 | /* Setup a VMA at program startup for the vsyscall page. |
105 | Not called for compat tasks */ | 150 | Not called for compat tasks */ |
106 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | 151 | static int setup_additional_pages(struct linux_binprm *bprm, |
152 | int uses_interp, | ||
153 | struct page **pages, | ||
154 | unsigned size) | ||
107 | { | 155 | { |
108 | struct mm_struct *mm = current->mm; | 156 | struct mm_struct *mm = current->mm; |
109 | unsigned long addr; | 157 | unsigned long addr; |
@@ -113,8 +161,8 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
113 | return 0; | 161 | return 0; |
114 | 162 | ||
115 | down_write(&mm->mmap_sem); | 163 | down_write(&mm->mmap_sem); |
116 | addr = vdso_addr(mm->start_stack, vdso_size); | 164 | addr = vdso_addr(mm->start_stack, size); |
117 | addr = get_unmapped_area(NULL, addr, vdso_size, 0, 0); | 165 | addr = get_unmapped_area(NULL, addr, size, 0, 0); |
118 | if (IS_ERR_VALUE(addr)) { | 166 | if (IS_ERR_VALUE(addr)) { |
119 | ret = addr; | 167 | ret = addr; |
120 | goto up_fail; | 168 | goto up_fail; |
@@ -122,11 +170,11 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |||
122 | 170 | ||
123 | current->mm->context.vdso = (void *)addr; | 171 | current->mm->context.vdso = (void *)addr; |
124 | 172 | ||
125 | ret = install_special_mapping(mm, addr, vdso_size, | 173 | ret = install_special_mapping(mm, addr, size, |
126 | VM_READ|VM_EXEC| | 174 | VM_READ|VM_EXEC| |
127 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| | 175 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| |
128 | VM_ALWAYSDUMP, | 176 | VM_ALWAYSDUMP, |
129 | vdso_pages); | 177 | pages); |
130 | if (ret) { | 178 | if (ret) { |
131 | current->mm->context.vdso = NULL; | 179 | current->mm->context.vdso = NULL; |
132 | goto up_fail; | 180 | goto up_fail; |
@@ -137,6 +185,20 @@ up_fail: | |||
137 | return ret; | 185 | return ret; |
138 | } | 186 | } |
139 | 187 | ||
188 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
189 | { | ||
190 | return setup_additional_pages (bprm, uses_interp, vdso_pages, | ||
191 | vdso_size); | ||
192 | } | ||
193 | |||
194 | #ifdef CONFIG_X86_X32_ABI | ||
195 | int x32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
196 | { | ||
197 | return setup_additional_pages (bprm, uses_interp, vdsox32_pages, | ||
198 | vdsox32_size); | ||
199 | } | ||
200 | #endif | ||
201 | |||
140 | static __init int vdso_setup(char *s) | 202 | static __init int vdso_setup(char *s) |
141 | { | 203 | { |
142 | vdso_enabled = simple_strtoul(s, NULL, 0); | 204 | vdso_enabled = simple_strtoul(s, NULL, 0); |