aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@amacapital.net>2014-07-10 21:13:15 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2014-07-11 19:57:51 -0400
commite6577a7ce99a506b587bcd1d2cd803cb45119557 (patch)
tree71e985803dce2a087d98f98537efd37d1ba630e3
parentd093601be5e97d2729614419d0d256ed3b6a56b0 (diff)
x86, vdso: Move the vvar area before the vdso text
Putting the vvar area after the vdso text is rather complicated: it only works of the total length of the vdso text mapping is known at vdso link time, and the linker doesn't allow symbol addresses to depend on the sizes of non-allocatable data after the PT_LOAD segment. Moving the vvar area before the vdso text will allow is to safely map non-allocatable data after the vdso text, which is a nice simplification. Signed-off-by: Andy Lutomirski <luto@amacapital.net> Link: http://lkml.kernel.org/r/156c78c0d93144ff1055a66493783b9e56813983.1405040914.git.luto@amacapital.net Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r--arch/x86/include/asm/vdso.h18
-rw-r--r--arch/x86/vdso/vdso-layout.lds.S44
-rw-r--r--arch/x86/vdso/vdso2c.c12
-rw-r--r--arch/x86/vdso/vdso2c.h25
-rw-r--r--arch/x86/vdso/vma.c20
5 files changed, 62 insertions, 57 deletions
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 30be253dd283..8021bd28c0f1 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -18,15 +18,15 @@ struct vdso_image {
18 18
19 unsigned long alt, alt_len; 19 unsigned long alt, alt_len;
20 20
21 unsigned long sym_end_mapping; /* Total size of the mapping */ 21 long sym_vvar_start; /* Negative offset to the vvar area */
22 22
23 unsigned long sym_vvar_page; 23 long sym_vvar_page;
24 unsigned long sym_hpet_page; 24 long sym_hpet_page;
25 unsigned long sym_VDSO32_NOTE_MASK; 25 long sym_VDSO32_NOTE_MASK;
26 unsigned long sym___kernel_sigreturn; 26 long sym___kernel_sigreturn;
27 unsigned long sym___kernel_rt_sigreturn; 27 long sym___kernel_rt_sigreturn;
28 unsigned long sym___kernel_vsyscall; 28 long sym___kernel_vsyscall;
29 unsigned long sym_VDSO32_SYSENTER_RETURN; 29 long sym_VDSO32_SYSENTER_RETURN;
30}; 30};
31 31
32#ifdef CONFIG_X86_64 32#ifdef CONFIG_X86_64
diff --git a/arch/x86/vdso/vdso-layout.lds.S b/arch/x86/vdso/vdso-layout.lds.S
index 9197544eea9a..de2c921025f5 100644
--- a/arch/x86/vdso/vdso-layout.lds.S
+++ b/arch/x86/vdso/vdso-layout.lds.S
@@ -18,6 +18,25 @@
18 18
19SECTIONS 19SECTIONS
20{ 20{
21 /*
22 * User/kernel shared data is before the vDSO. This may be a little
23 * uglier than putting it after the vDSO, but it avoids issues with
24 * non-allocatable things that dangle past the end of the PT_LOAD
25 * segment.
26 */
27
28 vvar_start = . - 2 * PAGE_SIZE;
29 vvar_page = vvar_start;
30
31 /* Place all vvars at the offsets in asm/vvar.h. */
32#define EMIT_VVAR(name, offset) vvar_ ## name = vvar_page + offset;
33#define __VVAR_KERNEL_LDS
34#include <asm/vvar.h>
35#undef __VVAR_KERNEL_LDS
36#undef EMIT_VVAR
37
38 hpet_page = vvar_start + PAGE_SIZE;
39
21 . = SIZEOF_HEADERS; 40 . = SIZEOF_HEADERS;
22 41
23 .hash : { *(.hash) } :text 42 .hash : { *(.hash) } :text
@@ -74,31 +93,6 @@ SECTIONS
74 .altinstructions : { *(.altinstructions) } :text 93 .altinstructions : { *(.altinstructions) } :text
75 .altinstr_replacement : { *(.altinstr_replacement) } :text 94 .altinstr_replacement : { *(.altinstr_replacement) } :text
76 95
77 /*
78 * The remainder of the vDSO consists of special pages that are
79 * shared between the kernel and userspace. It needs to be at the
80 * end so that it doesn't overlap the mapping of the actual
81 * vDSO image.
82 */
83
84 . = ALIGN(PAGE_SIZE);
85 vvar_page = .;
86
87 /* Place all vvars at the offsets in asm/vvar.h. */
88#define EMIT_VVAR(name, offset) vvar_ ## name = vvar_page + offset;
89#define __VVAR_KERNEL_LDS
90#include <asm/vvar.h>
91#undef __VVAR_KERNEL_LDS
92#undef EMIT_VVAR
93
94 . = vvar_page + PAGE_SIZE;
95
96 hpet_page = .;
97 . = . + PAGE_SIZE;
98
99 . = ALIGN(PAGE_SIZE);
100 end_mapping = .;
101
102 /DISCARD/ : { 96 /DISCARD/ : {
103 *(.discard) 97 *(.discard)
104 *(.discard.*) 98 *(.discard.*)
diff --git a/arch/x86/vdso/vdso2c.c b/arch/x86/vdso/vdso2c.c
index 238dbe82776e..22c54d04bced 100644
--- a/arch/x86/vdso/vdso2c.c
+++ b/arch/x86/vdso/vdso2c.c
@@ -20,9 +20,9 @@ const char *outfilename;
20 20
21/* Symbols that we need in vdso2c. */ 21/* Symbols that we need in vdso2c. */
22enum { 22enum {
23 sym_vvar_start,
23 sym_vvar_page, 24 sym_vvar_page,
24 sym_hpet_page, 25 sym_hpet_page,
25 sym_end_mapping,
26 sym_VDSO_FAKE_SECTION_TABLE_START, 26 sym_VDSO_FAKE_SECTION_TABLE_START,
27 sym_VDSO_FAKE_SECTION_TABLE_END, 27 sym_VDSO_FAKE_SECTION_TABLE_END,
28}; 28};
@@ -38,9 +38,9 @@ struct vdso_sym {
38}; 38};
39 39
40struct vdso_sym required_syms[] = { 40struct vdso_sym required_syms[] = {
41 [sym_vvar_start] = {"vvar_start", true},
41 [sym_vvar_page] = {"vvar_page", true}, 42 [sym_vvar_page] = {"vvar_page", true},
42 [sym_hpet_page] = {"hpet_page", true}, 43 [sym_hpet_page] = {"hpet_page", true},
43 [sym_end_mapping] = {"end_mapping", true},
44 [sym_VDSO_FAKE_SECTION_TABLE_START] = { 44 [sym_VDSO_FAKE_SECTION_TABLE_START] = {
45 "VDSO_FAKE_SECTION_TABLE_START", false 45 "VDSO_FAKE_SECTION_TABLE_START", false
46 }, 46 },
@@ -96,9 +96,11 @@ extern void bad_put_le(void);
96 96
97#define NSYMS (sizeof(required_syms) / sizeof(required_syms[0])) 97#define NSYMS (sizeof(required_syms) / sizeof(required_syms[0]))
98 98
99#define BITSFUNC3(name, bits) name##bits 99#define BITSFUNC3(name, bits, suffix) name##bits##suffix
100#define BITSFUNC2(name, bits) BITSFUNC3(name, bits) 100#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix)
101#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS) 101#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, )
102
103#define INT_BITS BITSFUNC2(int, ELF_BITS, _t)
102 104
103#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 105#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
104#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 106#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h
index 11b65d4f9414..2da32fbc46da 100644
--- a/arch/x86/vdso/vdso2c.h
+++ b/arch/x86/vdso/vdso2c.h
@@ -132,7 +132,7 @@ static void BITSFUNC(go)(void *addr, size_t len,
132 *alt_sec = NULL; 132 *alt_sec = NULL;
133 ELF(Dyn) *dyn = 0, *dyn_end = 0; 133 ELF(Dyn) *dyn = 0, *dyn_end = 0;
134 const char *secstrings; 134 const char *secstrings;
135 uint64_t syms[NSYMS] = {}; 135 INT_BITS syms[NSYMS] = {};
136 136
137 struct BITSFUNC(fake_sections) fake_sections = {}; 137 struct BITSFUNC(fake_sections) fake_sections = {};
138 138
@@ -209,6 +209,13 @@ static void BITSFUNC(go)(void *addr, size_t len,
209 fail("duplicate symbol %s\n", 209 fail("duplicate symbol %s\n",
210 required_syms[k].name); 210 required_syms[k].name);
211 } 211 }
212
213 /*
214 * Careful: we use negative addresses, but
215 * st_value is unsigned, so we rely
216 * on syms[k] being a signed type of the
217 * correct width.
218 */
212 syms[k] = GET_LE(&sym->st_value); 219 syms[k] = GET_LE(&sym->st_value);
213 } 220 }
214 } 221 }
@@ -263,15 +270,15 @@ static void BITSFUNC(go)(void *addr, size_t len,
263 if (syms[i] % 4096) 270 if (syms[i] % 4096)
264 fail("%s must be a multiple of 4096\n", 271 fail("%s must be a multiple of 4096\n",
265 required_syms[i].name); 272 required_syms[i].name);
266 if (syms[i] < data_size) 273 if (syms[sym_vvar_start] > syms[i] + 4096)
267 fail("%s must be after the text mapping\n", 274 fail("%s underruns begin_vvar\n",
268 required_syms[i].name); 275 required_syms[i].name);
269 if (syms[sym_end_mapping] < syms[i] + 4096) 276 if (syms[i] + 4096 > 0)
270 fail("%s overruns end_mapping\n", 277 fail("%s is on the wrong side of the vdso text\n",
271 required_syms[i].name); 278 required_syms[i].name);
272 } 279 }
273 if (syms[sym_end_mapping] % 4096) 280 if (syms[sym_vvar_start] % 4096)
274 fail("end_mapping must be a multiple of 4096\n"); 281 fail("vvar_begin must be a multiple of 4096\n");
275 282
276 if (!name) { 283 if (!name) {
277 fwrite(addr, load_size, 1, outfile); 284 fwrite(addr, load_size, 1, outfile);
@@ -311,8 +318,8 @@ static void BITSFUNC(go)(void *addr, size_t len,
311 } 318 }
312 for (i = 0; i < NSYMS; i++) { 319 for (i = 0; i < NSYMS; i++) {
313 if (required_syms[i].export && syms[i]) 320 if (required_syms[i].export && syms[i])
314 fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n", 321 fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
315 required_syms[i].name, syms[i]); 322 required_syms[i].name, (int64_t)syms[i]);
316 } 323 }
317 fprintf(outfile, "};\n"); 324 fprintf(outfile, "};\n");
318} 325}
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 5a5176de8d0a..dbef622bb5af 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -93,7 +93,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
93{ 93{
94 struct mm_struct *mm = current->mm; 94 struct mm_struct *mm = current->mm;
95 struct vm_area_struct *vma; 95 struct vm_area_struct *vma;
96 unsigned long addr; 96 unsigned long addr, text_start;
97 int ret = 0; 97 int ret = 0;
98 static struct page *no_pages[] = {NULL}; 98 static struct page *no_pages[] = {NULL};
99 static struct vm_special_mapping vvar_mapping = { 99 static struct vm_special_mapping vvar_mapping = {
@@ -103,26 +103,28 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
103 103
104 if (calculate_addr) { 104 if (calculate_addr) {
105 addr = vdso_addr(current->mm->start_stack, 105 addr = vdso_addr(current->mm->start_stack,
106 image->sym_end_mapping); 106 image->size - image->sym_vvar_start);
107 } else { 107 } else {
108 addr = 0; 108 addr = 0;
109 } 109 }
110 110
111 down_write(&mm->mmap_sem); 111 down_write(&mm->mmap_sem);
112 112
113 addr = get_unmapped_area(NULL, addr, image->sym_end_mapping, 0, 0); 113 addr = get_unmapped_area(NULL, addr,
114 image->size - image->sym_vvar_start, 0, 0);
114 if (IS_ERR_VALUE(addr)) { 115 if (IS_ERR_VALUE(addr)) {
115 ret = addr; 116 ret = addr;
116 goto up_fail; 117 goto up_fail;
117 } 118 }
118 119
119 current->mm->context.vdso = (void __user *)addr; 120 text_start = addr - image->sym_vvar_start;
121 current->mm->context.vdso = (void __user *)text_start;
120 122
121 /* 123 /*
122 * MAYWRITE to allow gdb to COW and set breakpoints 124 * MAYWRITE to allow gdb to COW and set breakpoints
123 */ 125 */
124 vma = _install_special_mapping(mm, 126 vma = _install_special_mapping(mm,
125 addr, 127 text_start,
126 image->size, 128 image->size,
127 VM_READ|VM_EXEC| 129 VM_READ|VM_EXEC|
128 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 130 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
@@ -134,8 +136,8 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
134 } 136 }
135 137
136 vma = _install_special_mapping(mm, 138 vma = _install_special_mapping(mm,
137 addr + image->size, 139 addr,
138 image->sym_end_mapping - image->size, 140 -image->sym_vvar_start,
139 VM_READ, 141 VM_READ,
140 &vvar_mapping); 142 &vvar_mapping);
141 143
@@ -146,7 +148,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
146 148
147 if (image->sym_vvar_page) 149 if (image->sym_vvar_page)
148 ret = remap_pfn_range(vma, 150 ret = remap_pfn_range(vma,
149 addr + image->sym_vvar_page, 151 text_start + image->sym_vvar_page,
150 __pa_symbol(&__vvar_page) >> PAGE_SHIFT, 152 __pa_symbol(&__vvar_page) >> PAGE_SHIFT,
151 PAGE_SIZE, 153 PAGE_SIZE,
152 PAGE_READONLY); 154 PAGE_READONLY);
@@ -157,7 +159,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
157#ifdef CONFIG_HPET_TIMER 159#ifdef CONFIG_HPET_TIMER
158 if (hpet_address && image->sym_hpet_page) { 160 if (hpet_address && image->sym_hpet_page) {
159 ret = io_remap_pfn_range(vma, 161 ret = io_remap_pfn_range(vma,
160 addr + image->sym_hpet_page, 162 text_start + image->sym_hpet_page,
161 hpet_address >> PAGE_SHIFT, 163 hpet_address >> PAGE_SHIFT,
162 PAGE_SIZE, 164 PAGE_SIZE,
163 pgprot_noncached(PAGE_READONLY)); 165 pgprot_noncached(PAGE_READONLY));