Skip to content

Commit e6577a7

Browse files
amlutoH. Peter Anvin
authored andcommitted
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>
1 parent d093601 commit e6577a7

File tree

5 files changed

+62
-57
lines changed

5 files changed

+62
-57
lines changed

arch/x86/include/asm/vdso.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ struct vdso_image {
1818

1919
unsigned long alt, alt_len;
2020

21-
unsigned long sym_end_mapping; /* Total size of the mapping */
22-
23-
unsigned long sym_vvar_page;
24-
unsigned long sym_hpet_page;
25-
unsigned long sym_VDSO32_NOTE_MASK;
26-
unsigned long sym___kernel_sigreturn;
27-
unsigned long sym___kernel_rt_sigreturn;
28-
unsigned long sym___kernel_vsyscall;
29-
unsigned long sym_VDSO32_SYSENTER_RETURN;
21+
long sym_vvar_start; /* Negative offset to the vvar area */
22+
23+
long sym_vvar_page;
24+
long sym_hpet_page;
25+
long sym_VDSO32_NOTE_MASK;
26+
long sym___kernel_sigreturn;
27+
long sym___kernel_rt_sigreturn;
28+
long sym___kernel_vsyscall;
29+
long sym_VDSO32_SYSENTER_RETURN;
3030
};
3131

3232
#ifdef CONFIG_X86_64

arch/x86/vdso/vdso-layout.lds.S

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@
1818

1919
SECTIONS
2020
{
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+
2140
. = SIZEOF_HEADERS;
2241

2342
.hash : { *(.hash) } :text
@@ -74,31 +93,6 @@ SECTIONS
7493
.altinstructions : { *(.altinstructions) } :text
7594
.altinstr_replacement : { *(.altinstr_replacement) } :text
7695

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-
10296
/DISCARD/ : {
10397
*(.discard)
10498
*(.discard.*)

arch/x86/vdso/vdso2c.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ const char *outfilename;
2020

2121
/* Symbols that we need in vdso2c. */
2222
enum {
23+
sym_vvar_start,
2324
sym_vvar_page,
2425
sym_hpet_page,
25-
sym_end_mapping,
2626
sym_VDSO_FAKE_SECTION_TABLE_START,
2727
sym_VDSO_FAKE_SECTION_TABLE_END,
2828
};
@@ -38,9 +38,9 @@ struct vdso_sym {
3838
};
3939

4040
struct vdso_sym required_syms[] = {
41+
[sym_vvar_start] = {"vvar_start", true},
4142
[sym_vvar_page] = {"vvar_page", true},
4243
[sym_hpet_page] = {"hpet_page", true},
43-
[sym_end_mapping] = {"end_mapping", true},
4444
[sym_VDSO_FAKE_SECTION_TABLE_START] = {
4545
"VDSO_FAKE_SECTION_TABLE_START", false
4646
},
@@ -96,9 +96,11 @@ extern void bad_put_le(void);
9696

9797
#define NSYMS (sizeof(required_syms) / sizeof(required_syms[0]))
9898

99-
#define BITSFUNC3(name, bits) name##bits
100-
#define BITSFUNC2(name, bits) BITSFUNC3(name, bits)
101-
#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS)
99+
#define BITSFUNC3(name, bits, suffix) name##bits##suffix
100+
#define BITSFUNC2(name, bits, suffix) BITSFUNC3(name, bits, suffix)
101+
#define BITSFUNC(name) BITSFUNC2(name, ELF_BITS, )
102+
103+
#define INT_BITS BITSFUNC2(int, ELF_BITS, _t)
102104

103105
#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
104106
#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)

arch/x86/vdso/vdso2c.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ static void BITSFUNC(go)(void *addr, size_t len,
132132
*alt_sec = NULL;
133133
ELF(Dyn) *dyn = 0, *dyn_end = 0;
134134
const char *secstrings;
135-
uint64_t syms[NSYMS] = {};
135+
INT_BITS syms[NSYMS] = {};
136136

137137
struct BITSFUNC(fake_sections) fake_sections = {};
138138

@@ -209,6 +209,13 @@ static void BITSFUNC(go)(void *addr, size_t len,
209209
fail("duplicate symbol %s\n",
210210
required_syms[k].name);
211211
}
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+
*/
212219
syms[k] = GET_LE(&sym->st_value);
213220
}
214221
}
@@ -263,15 +270,15 @@ static void BITSFUNC(go)(void *addr, size_t len,
263270
if (syms[i] % 4096)
264271
fail("%s must be a multiple of 4096\n",
265272
required_syms[i].name);
266-
if (syms[i] < data_size)
267-
fail("%s must be after the text mapping\n",
273+
if (syms[sym_vvar_start] > syms[i] + 4096)
274+
fail("%s underruns begin_vvar\n",
268275
required_syms[i].name);
269-
if (syms[sym_end_mapping] < syms[i] + 4096)
270-
fail("%s overruns end_mapping\n",
276+
if (syms[i] + 4096 > 0)
277+
fail("%s is on the wrong side of the vdso text\n",
271278
required_syms[i].name);
272279
}
273-
if (syms[sym_end_mapping] % 4096)
274-
fail("end_mapping must be a multiple of 4096\n");
280+
if (syms[sym_vvar_start] % 4096)
281+
fail("vvar_begin must be a multiple of 4096\n");
275282

276283
if (!name) {
277284
fwrite(addr, load_size, 1, outfile);
@@ -311,8 +318,8 @@ static void BITSFUNC(go)(void *addr, size_t len,
311318
}
312319
for (i = 0; i < NSYMS; i++) {
313320
if (required_syms[i].export && syms[i])
314-
fprintf(outfile, "\t.sym_%s = 0x%" PRIx64 ",\n",
315-
required_syms[i].name, syms[i]);
321+
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
322+
required_syms[i].name, (int64_t)syms[i]);
316323
}
317324
fprintf(outfile, "};\n");
318325
}

arch/x86/vdso/vma.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
9393
{
9494
struct mm_struct *mm = current->mm;
9595
struct vm_area_struct *vma;
96-
unsigned long addr;
96+
unsigned long addr, text_start;
9797
int ret = 0;
9898
static struct page *no_pages[] = {NULL};
9999
static struct vm_special_mapping vvar_mapping = {
@@ -103,26 +103,28 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
103103

104104
if (calculate_addr) {
105105
addr = vdso_addr(current->mm->start_stack,
106-
image->sym_end_mapping);
106+
image->size - image->sym_vvar_start);
107107
} else {
108108
addr = 0;
109109
}
110110

111111
down_write(&mm->mmap_sem);
112112

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);
114115
if (IS_ERR_VALUE(addr)) {
115116
ret = addr;
116117
goto up_fail;
117118
}
118119

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;
120122

121123
/*
122124
* MAYWRITE to allow gdb to COW and set breakpoints
123125
*/
124126
vma = _install_special_mapping(mm,
125-
addr,
127+
text_start,
126128
image->size,
127129
VM_READ|VM_EXEC|
128130
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
@@ -134,8 +136,8 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
134136
}
135137

136138
vma = _install_special_mapping(mm,
137-
addr + image->size,
138-
image->sym_end_mapping - image->size,
139+
addr,
140+
-image->sym_vvar_start,
139141
VM_READ,
140142
&vvar_mapping);
141143

@@ -146,7 +148,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
146148

147149
if (image->sym_vvar_page)
148150
ret = remap_pfn_range(vma,
149-
addr + image->sym_vvar_page,
151+
text_start + image->sym_vvar_page,
150152
__pa_symbol(&__vvar_page) >> PAGE_SHIFT,
151153
PAGE_SIZE,
152154
PAGE_READONLY);
@@ -157,7 +159,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr)
157159
#ifdef CONFIG_HPET_TIMER
158160
if (hpet_address && image->sym_hpet_page) {
159161
ret = io_remap_pfn_range(vma,
160-
addr + image->sym_hpet_page,
162+
text_start + image->sym_hpet_page,
161163
hpet_address >> PAGE_SHIFT,
162164
PAGE_SIZE,
163165
pgprot_noncached(PAGE_READONLY));

0 commit comments

Comments
 (0)