aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/vDSO/parse_vdso.c67
-rw-r--r--Documentation/vDSO/vdso_standalone_test_x86.c128
-rw-r--r--Documentation/vDSO/vdso_test.c107
-rw-r--r--arch/x86/vdso/Makefile40
-rw-r--r--arch/x86/vdso/vdso-fakesections.c32
-rw-r--r--arch/x86/vdso/vdso2c.c19
-rw-r--r--arch/x86/vdso/vdso2c.h23
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
53static struct vdso_info 61static 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
171static bool vdso_match_version(Elf64_Versym ver, 184static 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
244void vdso_init_from_auxv(void *auxv) 257void 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
19extern void *vdso_sym(const char *version, const char *name);
20extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
21extern void vdso_init_from_auxv(void *auxv);
22
23/* We need a libc functions... */
24int 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. */
40static 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
56static 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
61static inline void linux_exit(int code)
62{
63 x86_syscall3(__NR_exit, code, 0, 0);
64}
65
66void 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 */
115asm (
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
18extern void *vdso_sym(const char *version, const char *name); 18extern void *vdso_sym(const char *version, const char *name);
19extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); 19extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
20extern void vdso_init_from_auxv(void *auxv); 20extern void vdso_init_from_auxv(void *auxv);
21 21
22/* We need a libc functions... */ 22int main(int argc, char **argv)
23int 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. */
39static 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
50static inline void linux_exit(int code)
51{
52 asm volatile ("syscall" : : "a" (__NR_exit), "D" (code));
53}
54
55void 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 */
104asm (
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
9VDSO32-$(CONFIG_X86_32) := y 9VDSO32-$(CONFIG_X86_32) := y
10VDSO32-$(CONFIG_COMPAT) := y 10VDSO32-$(CONFIG_COMPAT) := y
11 11
12vdso-install-$(VDSO64-y) += vdso.so
13vdso-install-$(VDSOX32-y) += vdsox32.so
14vdso-install-$(VDSO32-y) += $(vdso32-images)
15
16
17# files to link into the vdso 12# files to link into the vdso
18vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o 13vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
19 14vobjs-nox32 := vdso-fakesections.o
20vobjs-$(VDSOX32-y) += $(vobjx32s-compat)
21
22# Filter out x32 objects.
23vobj64s := $(filter-out $(vobjx32s-compat),$(vobjs-y))
24 15
25# files to link into kernel 16# files to link into kernel
26obj-y += vma.o 17obj-y += vma.o
@@ -34,7 +25,7 @@ vdso_img-$(VDSO32-y) += 32-sysenter
34 25
35obj-$(VDSO32-y) += vdso32-setup.o 26obj-$(VDSO32-y) += vdso32-setup.o
36 27
37vobjs := $(foreach F,$(vobj64s),$(obj)/$F) 28vobjs := $(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
107vobjx32s-y := $(vobj64s:.o=-x32.o) 98# 64-bit objects to re-brand as x32
99vobjs64-for-x32 := $(filter-out $(vobjs-nox32),$(vobjs-y))
100
101# x32-rebranded versions
102vobjx32s-y := $(vobjs64-for-x32:.o=-x32.o)
103
104# same thing, but in the output directory
108vobjx32s := $(foreach F,$(vobjx32s-y),$(obj)/$F) 105vobjx32s := $(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) \
176GCOV_PROFILE := n 173GCOV_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#
181quiet_cmd_vdso_install = INSTALL $@ 178quiet_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
181vdso_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
187PHONY += vdso_install $(vdso-install-y) 189PHONY += vdso_install $(vdso_img_insttargets)
188vdso_install: $(vdso-install-y) 190vdso_install: $(vdso_img_insttargets) FORCE
189 191
190clean-files := vdso32-syscall* vdso32-sysenter* vdso32-int80* 192clean-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
26extern const __visible struct elf64_shdr vdso_fake_sections[];
27const __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
64extern void bad_get_le(void); 64extern 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
76extern 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);