diff options
-rw-r--r-- | arch/x86/include/asm/vdso.h | 18 | ||||
-rw-r--r-- | arch/x86/vdso/vdso-layout.lds.S | 44 | ||||
-rw-r--r-- | arch/x86/vdso/vdso2c.c | 12 | ||||
-rw-r--r-- | arch/x86/vdso/vdso2c.h | 25 | ||||
-rw-r--r-- | arch/x86/vdso/vma.c | 20 |
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 | ||
19 | SECTIONS | 19 | SECTIONS |
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. */ |
22 | enum { | 22 | enum { |
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 | ||
40 | struct vdso_sym required_syms[] = { | 40 | struct 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)); |