diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 17:46:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-14 17:46:29 -0400 |
commit | c728762e064ad37bfd6689f2ac1f54d2355ac3ae (patch) | |
tree | 767b98c350a241b8d6fedb9f1f770516780f0918 | |
parent | 503698e12d68f6144b408b11156ad315943ffb53 (diff) | |
parent | a934fb5bc9cd1260be89272cfb7a6c9dc71974d7 (diff) |
Merge branch 'x86-vdso-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 vdso fixes from Peter Anvin:
"Fixes for x86/vdso.
One is a simple build fix for bigendian hosts, one is to make "make
vdso_install" work again, and the rest is about working around a bug
in Google's Go language -- two are documentation patches that improves
the sample code that the Go coders took, modified, and broke; the
other two implements a workaround that keeps existing Go binaries from
segfaulting at least"
* 'x86-vdso-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/vdso: Fix vdso_install
x86/vdso: Hack to keep 64-bit Go programs working
x86/vdso: Add PUT_LE to store little-endian values
x86/vdso/doc: Make vDSO examples more portable
x86/vdso/doc: Rename vdso_test.c to vdso_standalone_test_x86.c
x86, vdso: Remove one final use of htole16()
-rw-r--r-- | Documentation/vDSO/parse_vdso.c | 67 | ||||
-rw-r--r-- | Documentation/vDSO/vdso_standalone_test_x86.c | 128 | ||||
-rw-r--r-- | Documentation/vDSO/vdso_test.c | 107 | ||||
-rw-r--r-- | arch/x86/vdso/Makefile | 40 | ||||
-rw-r--r-- | arch/x86/vdso/vdso-fakesections.c | 32 | ||||
-rw-r--r-- | arch/x86/vdso/vdso2c.c | 19 | ||||
-rw-r--r-- | arch/x86/vdso/vdso2c.h | 23 |
7 files changed, 279 insertions, 137 deletions
diff --git a/Documentation/vDSO/parse_vdso.c b/Documentation/vDSO/parse_vdso.c index 85870208edcf..1dbb4b87268f 100644 --- a/Documentation/vDSO/parse_vdso.c +++ b/Documentation/vDSO/parse_vdso.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * parse_vdso.c: Linux reference vDSO parser | 2 | * parse_vdso.c: Linux reference vDSO parser |
3 | * Written by Andrew Lutomirski, 2011. | 3 | * Written by Andrew Lutomirski, 2011-2014. |
4 | * | 4 | * |
5 | * This code is meant to be linked in to various programs that run on Linux. | 5 | * This code is meant to be linked in to various programs that run on Linux. |
6 | * As such, it is available with as few restrictions as possible. This file | 6 | * As such, it is available with as few restrictions as possible. This file |
@@ -11,13 +11,14 @@ | |||
11 | * it starts a program. It works equally well in statically and dynamically | 11 | * it starts a program. It works equally well in statically and dynamically |
12 | * linked binaries. | 12 | * linked binaries. |
13 | * | 13 | * |
14 | * This code is tested on x86_64. In principle it should work on any 64-bit | 14 | * This code is tested on x86. In principle it should work on any |
15 | * architecture that has a vDSO. | 15 | * architecture that has a vDSO. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <stdbool.h> | 18 | #include <stdbool.h> |
19 | #include <stdint.h> | 19 | #include <stdint.h> |
20 | #include <string.h> | 20 | #include <string.h> |
21 | #include <limits.h> | ||
21 | #include <elf.h> | 22 | #include <elf.h> |
22 | 23 | ||
23 | /* | 24 | /* |
@@ -45,11 +46,18 @@ extern void *vdso_sym(const char *version, const char *name); | |||
45 | 46 | ||
46 | 47 | ||
47 | /* And here's the code. */ | 48 | /* And here's the code. */ |
48 | 49 | #ifndef ELF_BITS | |
49 | #ifndef __x86_64__ | 50 | # if ULONG_MAX > 0xffffffffUL |
50 | # error Not yet ported to non-x86_64 architectures | 51 | # define ELF_BITS 64 |
52 | # else | ||
53 | # define ELF_BITS 32 | ||
54 | # endif | ||
51 | #endif | 55 | #endif |
52 | 56 | ||
57 | #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x | ||
58 | #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) | ||
59 | #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) | ||
60 | |||
53 | static struct vdso_info | 61 | static struct vdso_info |
54 | { | 62 | { |
55 | bool valid; | 63 | bool valid; |
@@ -59,14 +67,14 @@ static struct vdso_info | |||
59 | uintptr_t load_offset; /* load_addr - recorded vaddr */ | 67 | uintptr_t load_offset; /* load_addr - recorded vaddr */ |
60 | 68 | ||
61 | /* Symbol table */ | 69 | /* Symbol table */ |
62 | Elf64_Sym *symtab; | 70 | ELF(Sym) *symtab; |
63 | const char *symstrings; | 71 | const char *symstrings; |
64 | Elf64_Word *bucket, *chain; | 72 | ELF(Word) *bucket, *chain; |
65 | Elf64_Word nbucket, nchain; | 73 | ELF(Word) nbucket, nchain; |
66 | 74 | ||
67 | /* Version table */ | 75 | /* Version table */ |
68 | Elf64_Versym *versym; | 76 | ELF(Versym) *versym; |
69 | Elf64_Verdef *verdef; | 77 | ELF(Verdef) *verdef; |
70 | } vdso_info; | 78 | } vdso_info; |
71 | 79 | ||
72 | /* Straight from the ELF specification. */ | 80 | /* Straight from the ELF specification. */ |
@@ -92,9 +100,14 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) | |||
92 | 100 | ||
93 | vdso_info.load_addr = base; | 101 | vdso_info.load_addr = base; |
94 | 102 | ||
95 | Elf64_Ehdr *hdr = (Elf64_Ehdr*)base; | 103 | ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; |
96 | Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info.load_addr + hdr->e_phoff); | 104 | if (hdr->e_ident[EI_CLASS] != |
97 | Elf64_Dyn *dyn = 0; | 105 | (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { |
106 | return; /* Wrong ELF class -- check ELF_BITS */ | ||
107 | } | ||
108 | |||
109 | ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); | ||
110 | ELF(Dyn) *dyn = 0; | ||
98 | 111 | ||
99 | /* | 112 | /* |
100 | * We need two things from the segment table: the load offset | 113 | * We need two things from the segment table: the load offset |
@@ -108,7 +121,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) | |||
108 | + (uintptr_t)pt[i].p_offset | 121 | + (uintptr_t)pt[i].p_offset |
109 | - (uintptr_t)pt[i].p_vaddr; | 122 | - (uintptr_t)pt[i].p_vaddr; |
110 | } else if (pt[i].p_type == PT_DYNAMIC) { | 123 | } else if (pt[i].p_type == PT_DYNAMIC) { |
111 | dyn = (Elf64_Dyn*)(base + pt[i].p_offset); | 124 | dyn = (ELF(Dyn)*)(base + pt[i].p_offset); |
112 | } | 125 | } |
113 | } | 126 | } |
114 | 127 | ||
@@ -118,7 +131,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) | |||
118 | /* | 131 | /* |
119 | * Fish out the useful bits of the dynamic table. | 132 | * Fish out the useful bits of the dynamic table. |
120 | */ | 133 | */ |
121 | Elf64_Word *hash = 0; | 134 | ELF(Word) *hash = 0; |
122 | vdso_info.symstrings = 0; | 135 | vdso_info.symstrings = 0; |
123 | vdso_info.symtab = 0; | 136 | vdso_info.symtab = 0; |
124 | vdso_info.versym = 0; | 137 | vdso_info.versym = 0; |
@@ -131,22 +144,22 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) | |||
131 | + vdso_info.load_offset); | 144 | + vdso_info.load_offset); |
132 | break; | 145 | break; |
133 | case DT_SYMTAB: | 146 | case DT_SYMTAB: |
134 | vdso_info.symtab = (Elf64_Sym *) | 147 | vdso_info.symtab = (ELF(Sym) *) |
135 | ((uintptr_t)dyn[i].d_un.d_ptr | 148 | ((uintptr_t)dyn[i].d_un.d_ptr |
136 | + vdso_info.load_offset); | 149 | + vdso_info.load_offset); |
137 | break; | 150 | break; |
138 | case DT_HASH: | 151 | case DT_HASH: |
139 | hash = (Elf64_Word *) | 152 | hash = (ELF(Word) *) |
140 | ((uintptr_t)dyn[i].d_un.d_ptr | 153 | ((uintptr_t)dyn[i].d_un.d_ptr |
141 | + vdso_info.load_offset); | 154 | + vdso_info.load_offset); |
142 | break; | 155 | break; |
143 | case DT_VERSYM: | 156 | case DT_VERSYM: |
144 | vdso_info.versym = (Elf64_Versym *) | 157 | vdso_info.versym = (ELF(Versym) *) |
145 | ((uintptr_t)dyn[i].d_un.d_ptr | 158 | ((uintptr_t)dyn[i].d_un.d_ptr |
146 | + vdso_info.load_offset); | 159 | + vdso_info.load_offset); |
147 | break; | 160 | break; |
148 | case DT_VERDEF: | 161 | case DT_VERDEF: |
149 | vdso_info.verdef = (Elf64_Verdef *) | 162 | vdso_info.verdef = (ELF(Verdef) *) |
150 | ((uintptr_t)dyn[i].d_un.d_ptr | 163 | ((uintptr_t)dyn[i].d_un.d_ptr |
151 | + vdso_info.load_offset); | 164 | + vdso_info.load_offset); |
152 | break; | 165 | break; |
@@ -168,8 +181,8 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base) | |||
168 | vdso_info.valid = true; | 181 | vdso_info.valid = true; |
169 | } | 182 | } |
170 | 183 | ||
171 | static bool vdso_match_version(Elf64_Versym ver, | 184 | static bool vdso_match_version(ELF(Versym) ver, |
172 | const char *name, Elf64_Word hash) | 185 | const char *name, ELF(Word) hash) |
173 | { | 186 | { |
174 | /* | 187 | /* |
175 | * This is a helper function to check if the version indexed by | 188 | * This is a helper function to check if the version indexed by |
@@ -188,7 +201,7 @@ static bool vdso_match_version(Elf64_Versym ver, | |||
188 | 201 | ||
189 | /* First step: find the version definition */ | 202 | /* First step: find the version definition */ |
190 | ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ | 203 | ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ |
191 | Elf64_Verdef *def = vdso_info.verdef; | 204 | ELF(Verdef) *def = vdso_info.verdef; |
192 | while(true) { | 205 | while(true) { |
193 | if ((def->vd_flags & VER_FLG_BASE) == 0 | 206 | if ((def->vd_flags & VER_FLG_BASE) == 0 |
194 | && (def->vd_ndx & 0x7fff) == ver) | 207 | && (def->vd_ndx & 0x7fff) == ver) |
@@ -197,11 +210,11 @@ static bool vdso_match_version(Elf64_Versym ver, | |||
197 | if (def->vd_next == 0) | 210 | if (def->vd_next == 0) |
198 | return false; /* No definition. */ | 211 | return false; /* No definition. */ |
199 | 212 | ||
200 | def = (Elf64_Verdef *)((char *)def + def->vd_next); | 213 | def = (ELF(Verdef) *)((char *)def + def->vd_next); |
201 | } | 214 | } |
202 | 215 | ||
203 | /* Now figure out whether it matches. */ | 216 | /* Now figure out whether it matches. */ |
204 | Elf64_Verdaux *aux = (Elf64_Verdaux*)((char *)def + def->vd_aux); | 217 | ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); |
205 | return def->vd_hash == hash | 218 | return def->vd_hash == hash |
206 | && !strcmp(name, vdso_info.symstrings + aux->vda_name); | 219 | && !strcmp(name, vdso_info.symstrings + aux->vda_name); |
207 | } | 220 | } |
@@ -213,10 +226,10 @@ void *vdso_sym(const char *version, const char *name) | |||
213 | return 0; | 226 | return 0; |
214 | 227 | ||
215 | ver_hash = elf_hash(version); | 228 | ver_hash = elf_hash(version); |
216 | Elf64_Word chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; | 229 | ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; |
217 | 230 | ||
218 | for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { | 231 | for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { |
219 | Elf64_Sym *sym = &vdso_info.symtab[chain]; | 232 | ELF(Sym) *sym = &vdso_info.symtab[chain]; |
220 | 233 | ||
221 | /* Check for a defined global or weak function w/ right name. */ | 234 | /* Check for a defined global or weak function w/ right name. */ |
222 | if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) | 235 | if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) |
@@ -243,7 +256,7 @@ void *vdso_sym(const char *version, const char *name) | |||
243 | 256 | ||
244 | void vdso_init_from_auxv(void *auxv) | 257 | void vdso_init_from_auxv(void *auxv) |
245 | { | 258 | { |
246 | Elf64_auxv_t *elf_auxv = auxv; | 259 | ELF(auxv_t) *elf_auxv = auxv; |
247 | for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) | 260 | for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) |
248 | { | 261 | { |
249 | if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { | 262 | if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { |
diff --git a/Documentation/vDSO/vdso_standalone_test_x86.c b/Documentation/vDSO/vdso_standalone_test_x86.c new file mode 100644 index 000000000000..d46240265c50 --- /dev/null +++ b/Documentation/vDSO/vdso_standalone_test_x86.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * vdso_test.c: Sample code to test parse_vdso.c on x86 | ||
3 | * Copyright (c) 2011-2014 Andy Lutomirski | ||
4 | * Subject to the GNU General Public License, version 2 | ||
5 | * | ||
6 | * You can amuse yourself by compiling with: | ||
7 | * gcc -std=gnu99 -nostdlib | ||
8 | * -Os -fno-asynchronous-unwind-tables -flto -lgcc_s | ||
9 | * vdso_standalone_test_x86.c parse_vdso.c | ||
10 | * to generate a small binary. On x86_64, you can omit -lgcc_s | ||
11 | * if you want the binary to be completely standalone. | ||
12 | */ | ||
13 | |||
14 | #include <sys/syscall.h> | ||
15 | #include <sys/time.h> | ||
16 | #include <unistd.h> | ||
17 | #include <stdint.h> | ||
18 | |||
19 | extern void *vdso_sym(const char *version, const char *name); | ||
20 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
21 | extern void vdso_init_from_auxv(void *auxv); | ||
22 | |||
23 | /* We need a libc functions... */ | ||
24 | int strcmp(const char *a, const char *b) | ||
25 | { | ||
26 | /* This implementation is buggy: it never returns -1. */ | ||
27 | while (*a || *b) { | ||
28 | if (*a != *b) | ||
29 | return 1; | ||
30 | if (*a == 0 || *b == 0) | ||
31 | return 1; | ||
32 | a++; | ||
33 | b++; | ||
34 | } | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | /* ...and two syscalls. This is x86-specific. */ | ||
40 | static inline long x86_syscall3(long nr, long a0, long a1, long a2) | ||
41 | { | ||
42 | long ret; | ||
43 | #ifdef __x86_64__ | ||
44 | asm volatile ("syscall" : "=a" (ret) : "a" (nr), | ||
45 | "D" (a0), "S" (a1), "d" (a2) : | ||
46 | "cc", "memory", "rcx", | ||
47 | "r8", "r9", "r10", "r11" ); | ||
48 | #else | ||
49 | asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), | ||
50 | "b" (a0), "c" (a1), "d" (a2) : | ||
51 | "cc", "memory" ); | ||
52 | #endif | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | static inline long linux_write(int fd, const void *data, size_t len) | ||
57 | { | ||
58 | return x86_syscall3(__NR_write, fd, (long)data, (long)len); | ||
59 | } | ||
60 | |||
61 | static inline void linux_exit(int code) | ||
62 | { | ||
63 | x86_syscall3(__NR_exit, code, 0, 0); | ||
64 | } | ||
65 | |||
66 | void to_base10(char *lastdig, uint64_t n) | ||
67 | { | ||
68 | while (n) { | ||
69 | *lastdig = (n % 10) + '0'; | ||
70 | n /= 10; | ||
71 | lastdig--; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | __attribute__((externally_visible)) void c_main(void **stack) | ||
76 | { | ||
77 | /* Parse the stack */ | ||
78 | long argc = (long)*stack; | ||
79 | stack += argc + 2; | ||
80 | |||
81 | /* Now we're pointing at the environment. Skip it. */ | ||
82 | while(*stack) | ||
83 | stack++; | ||
84 | stack++; | ||
85 | |||
86 | /* Now we're pointing at auxv. Initialize the vDSO parser. */ | ||
87 | vdso_init_from_auxv((void *)stack); | ||
88 | |||
89 | /* Find gettimeofday. */ | ||
90 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | ||
91 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
92 | |||
93 | if (!gtod) | ||
94 | linux_exit(1); | ||
95 | |||
96 | struct timeval tv; | ||
97 | long ret = gtod(&tv, 0); | ||
98 | |||
99 | if (ret == 0) { | ||
100 | char buf[] = "The time is .000000\n"; | ||
101 | to_base10(buf + 31, tv.tv_sec); | ||
102 | to_base10(buf + 38, tv.tv_usec); | ||
103 | linux_write(1, buf, sizeof(buf) - 1); | ||
104 | } else { | ||
105 | linux_exit(ret); | ||
106 | } | ||
107 | |||
108 | linux_exit(0); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * This is the real entry point. It passes the initial stack into | ||
113 | * the C entry point. | ||
114 | */ | ||
115 | asm ( | ||
116 | ".text\n" | ||
117 | ".global _start\n" | ||
118 | ".type _start,@function\n" | ||
119 | "_start:\n\t" | ||
120 | #ifdef __x86_64__ | ||
121 | "mov %rsp,%rdi\n\t" | ||
122 | "jmp c_main" | ||
123 | #else | ||
124 | "push %esp\n\t" | ||
125 | "call c_main\n\t" | ||
126 | "int $3" | ||
127 | #endif | ||
128 | ); | ||
diff --git a/Documentation/vDSO/vdso_test.c b/Documentation/vDSO/vdso_test.c index fff633432dff..8daeb7d7032c 100644 --- a/Documentation/vDSO/vdso_test.c +++ b/Documentation/vDSO/vdso_test.c | |||
@@ -1,111 +1,52 @@ | |||
1 | /* | 1 | /* |
2 | * vdso_test.c: Sample code to test parse_vdso.c on x86_64 | 2 | * vdso_test.c: Sample code to test parse_vdso.c |
3 | * Copyright (c) 2011 Andy Lutomirski | 3 | * Copyright (c) 2014 Andy Lutomirski |
4 | * Subject to the GNU General Public License, version 2 | 4 | * Subject to the GNU General Public License, version 2 |
5 | * | 5 | * |
6 | * You can amuse yourself by compiling with: | 6 | * Compile with: |
7 | * gcc -std=gnu99 -nostdlib | 7 | * gcc -std=gnu99 vdso_test.c parse_vdso.c |
8 | * -Os -fno-asynchronous-unwind-tables -flto | 8 | * |
9 | * vdso_test.c parse_vdso.c -o vdso_test | 9 | * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. |
10 | * to generate a small binary with no dependencies at all. | ||
11 | */ | 10 | */ |
12 | 11 | ||
13 | #include <sys/syscall.h> | ||
14 | #include <sys/time.h> | ||
15 | #include <unistd.h> | ||
16 | #include <stdint.h> | 12 | #include <stdint.h> |
13 | #include <elf.h> | ||
14 | #include <stdio.h> | ||
15 | #include <sys/auxv.h> | ||
16 | #include <sys/time.h> | ||
17 | 17 | ||
18 | extern void *vdso_sym(const char *version, const char *name); | 18 | extern void *vdso_sym(const char *version, const char *name); |
19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | 19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); |
20 | extern void vdso_init_from_auxv(void *auxv); | 20 | extern void vdso_init_from_auxv(void *auxv); |
21 | 21 | ||
22 | /* We need a libc functions... */ | 22 | int main(int argc, char **argv) |
23 | int strcmp(const char *a, const char *b) | ||
24 | { | 23 | { |
25 | /* This implementation is buggy: it never returns -1. */ | 24 | unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); |
26 | while (*a || *b) { | 25 | if (!sysinfo_ehdr) { |
27 | if (*a != *b) | 26 | printf("AT_SYSINFO_EHDR is not present!\n"); |
28 | return 1; | 27 | return 0; |
29 | if (*a == 0 || *b == 0) | ||
30 | return 1; | ||
31 | a++; | ||
32 | b++; | ||
33 | } | 28 | } |
34 | 29 | ||
35 | return 0; | 30 | vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); |
36 | } | ||
37 | |||
38 | /* ...and two syscalls. This is x86_64-specific. */ | ||
39 | static inline long linux_write(int fd, const void *data, size_t len) | ||
40 | { | ||
41 | |||
42 | long ret; | ||
43 | asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write), | ||
44 | "D" (fd), "S" (data), "d" (len) : | ||
45 | "cc", "memory", "rcx", | ||
46 | "r8", "r9", "r10", "r11" ); | ||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | static inline void linux_exit(int code) | ||
51 | { | ||
52 | asm volatile ("syscall" : : "a" (__NR_exit), "D" (code)); | ||
53 | } | ||
54 | |||
55 | void to_base10(char *lastdig, uint64_t n) | ||
56 | { | ||
57 | while (n) { | ||
58 | *lastdig = (n % 10) + '0'; | ||
59 | n /= 10; | ||
60 | lastdig--; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | __attribute__((externally_visible)) void c_main(void **stack) | ||
65 | { | ||
66 | /* Parse the stack */ | ||
67 | long argc = (long)*stack; | ||
68 | stack += argc + 2; | ||
69 | |||
70 | /* Now we're pointing at the environment. Skip it. */ | ||
71 | while(*stack) | ||
72 | stack++; | ||
73 | stack++; | ||
74 | |||
75 | /* Now we're pointing at auxv. Initialize the vDSO parser. */ | ||
76 | vdso_init_from_auxv((void *)stack); | ||
77 | 31 | ||
78 | /* Find gettimeofday. */ | 32 | /* Find gettimeofday. */ |
79 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | 33 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); |
80 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | 34 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); |
81 | 35 | ||
82 | if (!gtod) | 36 | if (!gtod) { |
83 | linux_exit(1); | 37 | printf("Could not find __vdso_gettimeofday\n"); |
38 | return 1; | ||
39 | } | ||
84 | 40 | ||
85 | struct timeval tv; | 41 | struct timeval tv; |
86 | long ret = gtod(&tv, 0); | 42 | long ret = gtod(&tv, 0); |
87 | 43 | ||
88 | if (ret == 0) { | 44 | if (ret == 0) { |
89 | char buf[] = "The time is .000000\n"; | 45 | printf("The time is %lld.%06lld\n", |
90 | to_base10(buf + 31, tv.tv_sec); | 46 | (long long)tv.tv_sec, (long long)tv.tv_usec); |
91 | to_base10(buf + 38, tv.tv_usec); | ||
92 | linux_write(1, buf, sizeof(buf) - 1); | ||
93 | } else { | 47 | } else { |
94 | linux_exit(ret); | 48 | printf("__vdso_gettimeofday failed\n"); |
95 | } | 49 | } |
96 | 50 | ||
97 | linux_exit(0); | 51 | return 0; |
98 | } | 52 | } |
99 | |||
100 | /* | ||
101 | * This is the real entry point. It passes the initial stack into | ||
102 | * the C entry point. | ||
103 | */ | ||
104 | asm ( | ||
105 | ".text\n" | ||
106 | ".global _start\n" | ||
107 | ".type _start,@function\n" | ||
108 | "_start:\n\t" | ||
109 | "mov %rsp,%rdi\n\t" | ||
110 | "jmp c_main" | ||
111 | ); | ||
diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile index 9769df094035..3c0809a0631f 100644 --- a/arch/x86/vdso/Makefile +++ b/arch/x86/vdso/Makefile | |||
@@ -9,18 +9,9 @@ VDSOX32-$(CONFIG_X86_X32_ABI) := y | |||
9 | VDSO32-$(CONFIG_X86_32) := y | 9 | VDSO32-$(CONFIG_X86_32) := y |
10 | VDSO32-$(CONFIG_COMPAT) := y | 10 | VDSO32-$(CONFIG_COMPAT) := y |
11 | 11 | ||
12 | vdso-install-$(VDSO64-y) += vdso.so | ||
13 | vdso-install-$(VDSOX32-y) += vdsox32.so | ||
14 | vdso-install-$(VDSO32-y) += $(vdso32-images) | ||
15 | |||
16 | |||
17 | # files to link into the vdso | 12 | # files to link into the vdso |
18 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o | 13 | vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o |
19 | 14 | vobjs-nox32 := vdso-fakesections.o | |
20 | vobjs-$(VDSOX32-y) += $(vobjx32s-compat) | ||
21 | |||
22 | # Filter out x32 objects. | ||
23 | vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y)) | ||
24 | 15 | ||
25 | # files to link into kernel | 16 | # files to link into kernel |
26 | obj-y += vma.o | 17 | obj-y += vma.o |
@@ -34,7 +25,7 @@ vdso_img-$(VDSO32-y) += 32-sysenter | |||
34 | 25 | ||
35 | obj-$(VDSO32-y) += vdso32-setup.o | 26 | obj-$(VDSO32-y) += vdso32-setup.o |
36 | 27 | ||
37 | vobjs := $(foreach F,$(vobj64s),$(obj)/$F) | 28 | vobjs := $(foreach F,$(vobjs-y),$(obj)/$F) |
38 | 29 | ||
39 | $(obj)/vdso.o: $(obj)/vdso.so | 30 | $(obj)/vdso.o: $(obj)/vdso.so |
40 | 31 | ||
@@ -104,7 +95,13 @@ VDSO_LDFLAGS_vdsox32.lds = -Wl,-m,elf32_x86_64 \ | |||
104 | -Wl,-z,max-page-size=4096 \ | 95 | -Wl,-z,max-page-size=4096 \ |
105 | -Wl,-z,common-page-size=4096 | 96 | -Wl,-z,common-page-size=4096 |
106 | 97 | ||
107 | vobjx32s-y := $(vobj64s:.o=-x32.o) | 98 | # 64-bit objects to re-brand as x32 |
99 | vobjs64-for-x32 := $(filter-out $(vobjs-nox32),$(vobjs-y)) | ||
100 | |||
101 | # x32-rebranded versions | ||
102 | vobjx32s-y := $(vobjs64-for-x32:.o=-x32.o) | ||
103 | |||
104 | # same thing, but in the output directory | ||
108 | vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) | 105 | vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) |
109 | 106 | ||
110 | # Convert 64bit object file to x32 for x32 vDSO. | 107 | # Convert 64bit object file to x32 for x32 vDSO. |
@@ -176,15 +173,20 @@ VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \ | |||
176 | GCOV_PROFILE := n | 173 | GCOV_PROFILE := n |
177 | 174 | ||
178 | # | 175 | # |
179 | # Install the unstripped copy of vdso*.so listed in $(vdso-install-y). | 176 | # Install the unstripped copies of vdso*.so. |
180 | # | 177 | # |
181 | quiet_cmd_vdso_install = INSTALL $@ | 178 | quiet_cmd_vdso_install = INSTALL $(@:install_%=%) |
182 | cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ | 179 | cmd_vdso_install = cp $< $(MODLIB)/vdso/$(@:install_%=%) |
183 | $(vdso-install-y): %.so: $(obj)/%.so.dbg FORCE | 180 | |
181 | vdso_img_insttargets := $(vdso_img_sodbg:%.dbg=install_%) | ||
182 | |||
183 | $(MODLIB)/vdso: FORCE | ||
184 | @mkdir -p $(MODLIB)/vdso | 184 | @mkdir -p $(MODLIB)/vdso |
185 | |||
186 | $(vdso_img_insttargets): install_%: $(obj)/%.dbg $(MODLIB)/vdso FORCE | ||
185 | $(call cmd,vdso_install) | 187 | $(call cmd,vdso_install) |
186 | 188 | ||
187 | PHONY += vdso_install $(vdso-install-y) | 189 | PHONY += vdso_install $(vdso_img_insttargets) |
188 | vdso_install: $(vdso-install-y) | 190 | vdso_install: $(vdso_img_insttargets) FORCE |
189 | 191 | ||
190 | clean-files := vdso32-syscall* vdso32-sysenter* vdso32-int80* | 192 | clean-files := vdso32-syscall* vdso32-sysenter* vdso32-int80* |
diff --git a/arch/x86/vdso/vdso-fakesections.c b/arch/x86/vdso/vdso-fakesections.c new file mode 100644 index 000000000000..cb8a8d72c24b --- /dev/null +++ b/arch/x86/vdso/vdso-fakesections.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Andy Lutomirski | ||
3 | * Subject to the GNU Public License, v.2 | ||
4 | * | ||
5 | * Hack to keep broken Go programs working. | ||
6 | * | ||
7 | * The Go runtime had a couple of bugs: it would read the section table to try | ||
8 | * to figure out how many dynamic symbols there were (it shouldn't have looked | ||
9 | * at the section table at all) and, if there were no SHT_SYNDYM section table | ||
10 | * entry, it would use an uninitialized value for the number of symbols. As a | ||
11 | * workaround, we supply a minimal section table. vdso2c will adjust the | ||
12 | * in-memory image so that "vdso_fake_sections" becomes the section table. | ||
13 | * | ||
14 | * The bug was introduced by: | ||
15 | * https://code.google.com/p/go/source/detail?r=56ea40aac72b (2012-08-31) | ||
16 | * and is being addressed in the Go runtime in this issue: | ||
17 | * https://code.google.com/p/go/issues/detail?id=8197 | ||
18 | */ | ||
19 | |||
20 | #ifndef __x86_64__ | ||
21 | #error This hack is specific to the 64-bit vDSO | ||
22 | #endif | ||
23 | |||
24 | #include <linux/elf.h> | ||
25 | |||
26 | extern const __visible struct elf64_shdr vdso_fake_sections[]; | ||
27 | const __visible struct elf64_shdr vdso_fake_sections[] = { | ||
28 | { | ||
29 | .sh_type = SHT_DYNSYM, | ||
30 | .sh_entsize = sizeof(Elf64_Sym), | ||
31 | } | ||
32 | }; | ||
diff --git a/arch/x86/vdso/vdso2c.c b/arch/x86/vdso/vdso2c.c index 450ac6eaf613..7a6bf50f9165 100644 --- a/arch/x86/vdso/vdso2c.c +++ b/arch/x86/vdso/vdso2c.c | |||
@@ -54,7 +54,7 @@ static void fail(const char *format, ...) | |||
54 | } | 54 | } |
55 | 55 | ||
56 | /* | 56 | /* |
57 | * Evil macros to do a little-endian read. | 57 | * Evil macros for little-endian reads and writes |
58 | */ | 58 | */ |
59 | #define GLE(x, bits, ifnot) \ | 59 | #define GLE(x, bits, ifnot) \ |
60 | __builtin_choose_expr( \ | 60 | __builtin_choose_expr( \ |
@@ -62,11 +62,24 @@ static void fail(const char *format, ...) | |||
62 | (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot) | 62 | (__typeof__(*(x)))get_unaligned_le##bits(x), ifnot) |
63 | 63 | ||
64 | extern void bad_get_le(void); | 64 | extern void bad_get_le(void); |
65 | #define LAST_LE(x) \ | 65 | #define LAST_GLE(x) \ |
66 | __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le()) | 66 | __builtin_choose_expr(sizeof(*(x)) == 1, *(x), bad_get_le()) |
67 | 67 | ||
68 | #define GET_LE(x) \ | 68 | #define GET_LE(x) \ |
69 | GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_LE(x)))) | 69 | GLE(x, 64, GLE(x, 32, GLE(x, 16, LAST_GLE(x)))) |
70 | |||
71 | #define PLE(x, val, bits, ifnot) \ | ||
72 | __builtin_choose_expr( \ | ||
73 | (sizeof(*(x)) == bits/8), \ | ||
74 | put_unaligned_le##bits((val), (x)), ifnot) | ||
75 | |||
76 | extern void bad_put_le(void); | ||
77 | #define LAST_PLE(x, val) \ | ||
78 | __builtin_choose_expr(sizeof(*(x)) == 1, *(x) = (val), bad_put_le()) | ||
79 | |||
80 | #define PUT_LE(x, val) \ | ||
81 | PLE(x, val, 64, PLE(x, val, 32, PLE(x, val, 16, LAST_PLE(x, val)))) | ||
82 | |||
70 | 83 | ||
71 | #define NSYMS (sizeof(required_syms) / sizeof(required_syms[0])) | 84 | #define NSYMS (sizeof(required_syms) / sizeof(required_syms[0])) |
72 | 85 | ||
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h index 8a074637a576..c6eefaf389b9 100644 --- a/arch/x86/vdso/vdso2c.h +++ b/arch/x86/vdso/vdso2c.h | |||
@@ -18,6 +18,8 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
18 | const char *secstrings; | 18 | const char *secstrings; |
19 | uint64_t syms[NSYMS] = {}; | 19 | uint64_t syms[NSYMS] = {}; |
20 | 20 | ||
21 | uint64_t fake_sections_value = 0, fake_sections_size = 0; | ||
22 | |||
21 | Elf_Phdr *pt = (Elf_Phdr *)(addr + GET_LE(&hdr->e_phoff)); | 23 | Elf_Phdr *pt = (Elf_Phdr *)(addr + GET_LE(&hdr->e_phoff)); |
22 | 24 | ||
23 | /* Walk the segment table. */ | 25 | /* Walk the segment table. */ |
@@ -84,6 +86,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
84 | GET_LE(&symtab_hdr->sh_entsize) * i; | 86 | GET_LE(&symtab_hdr->sh_entsize) * i; |
85 | const char *name = addr + GET_LE(&strtab_hdr->sh_offset) + | 87 | const char *name = addr + GET_LE(&strtab_hdr->sh_offset) + |
86 | GET_LE(&sym->st_name); | 88 | GET_LE(&sym->st_name); |
89 | |||
87 | for (k = 0; k < NSYMS; k++) { | 90 | for (k = 0; k < NSYMS; k++) { |
88 | if (!strcmp(name, required_syms[k])) { | 91 | if (!strcmp(name, required_syms[k])) { |
89 | if (syms[k]) { | 92 | if (syms[k]) { |
@@ -93,6 +96,13 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
93 | syms[k] = GET_LE(&sym->st_value); | 96 | syms[k] = GET_LE(&sym->st_value); |
94 | } | 97 | } |
95 | } | 98 | } |
99 | |||
100 | if (!strcmp(name, "vdso_fake_sections")) { | ||
101 | if (fake_sections_value) | ||
102 | fail("duplicate vdso_fake_sections\n"); | ||
103 | fake_sections_value = GET_LE(&sym->st_value); | ||
104 | fake_sections_size = GET_LE(&sym->st_size); | ||
105 | } | ||
96 | } | 106 | } |
97 | 107 | ||
98 | /* Validate mapping addresses. */ | 108 | /* Validate mapping addresses. */ |
@@ -112,11 +122,14 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name) | |||
112 | if (syms[sym_end_mapping] % 4096) | 122 | if (syms[sym_end_mapping] % 4096) |
113 | fail("end_mapping must be a multiple of 4096\n"); | 123 | fail("end_mapping must be a multiple of 4096\n"); |
114 | 124 | ||
115 | /* Remove sections. */ | 125 | /* Remove sections or use fakes */ |
116 | hdr->e_shoff = 0; | 126 | if (fake_sections_size % sizeof(Elf_Shdr)) |
117 | hdr->e_shentsize = 0; | 127 | fail("vdso_fake_sections size is not a multiple of %ld\n", |
118 | hdr->e_shnum = 0; | 128 | (long)sizeof(Elf_Shdr)); |
119 | hdr->e_shstrndx = htole16(SHN_UNDEF); | 129 | PUT_LE(&hdr->e_shoff, fake_sections_value); |
130 | PUT_LE(&hdr->e_shentsize, fake_sections_value ? sizeof(Elf_Shdr) : 0); | ||
131 | PUT_LE(&hdr->e_shnum, fake_sections_size / sizeof(Elf_Shdr)); | ||
132 | PUT_LE(&hdr->e_shstrndx, SHN_UNDEF); | ||
120 | 133 | ||
121 | if (!name) { | 134 | if (!name) { |
122 | fwrite(addr, load_size, 1, outfile); | 135 | fwrite(addr, load_size, 1, outfile); |