Skip to content

Commit c728762

Browse files
committed
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()
2 parents 503698e + a934fb5 commit c728762

File tree

7 files changed

+279
-137
lines changed

7 files changed

+279
-137
lines changed

Documentation/vDSO/parse_vdso.c

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* parse_vdso.c: Linux reference vDSO parser
3-
* Written by Andrew Lutomirski, 2011.
3+
* Written by Andrew Lutomirski, 2011-2014.
44
*
55
* This code is meant to be linked in to various programs that run on Linux.
66
* As such, it is available with as few restrictions as possible. This file
@@ -11,13 +11,14 @@
1111
* it starts a program. It works equally well in statically and dynamically
1212
* linked binaries.
1313
*
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
1515
* architecture that has a vDSO.
1616
*/
1717

1818
#include <stdbool.h>
1919
#include <stdint.h>
2020
#include <string.h>
21+
#include <limits.h>
2122
#include <elf.h>
2223

2324
/*
@@ -45,11 +46,18 @@ extern void *vdso_sym(const char *version, const char *name);
4546

4647

4748
/* And here's the code. */
48-
49-
#ifndef __x86_64__
50-
# error Not yet ported to non-x86_64 architectures
49+
#ifndef ELF_BITS
50+
# if ULONG_MAX > 0xffffffffUL
51+
# define ELF_BITS 64
52+
# else
53+
# define ELF_BITS 32
54+
# endif
5155
#endif
5256

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+
5361
static struct vdso_info
5462
{
5563
bool valid;
@@ -59,14 +67,14 @@ static struct vdso_info
5967
uintptr_t load_offset; /* load_addr - recorded vaddr */
6068

6169
/* Symbol table */
62-
Elf64_Sym *symtab;
70+
ELF(Sym) *symtab;
6371
const char *symstrings;
64-
Elf64_Word *bucket, *chain;
65-
Elf64_Word nbucket, nchain;
72+
ELF(Word) *bucket, *chain;
73+
ELF(Word) nbucket, nchain;
6674

6775
/* Version table */
68-
Elf64_Versym *versym;
69-
Elf64_Verdef *verdef;
76+
ELF(Versym) *versym;
77+
ELF(Verdef) *verdef;
7078
} vdso_info;
7179

7280
/* Straight from the ELF specification. */
@@ -92,9 +100,14 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
92100

93101
vdso_info.load_addr = base;
94102

95-
Elf64_Ehdr *hdr = (Elf64_Ehdr*)base;
96-
Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info.load_addr + hdr->e_phoff);
97-
Elf64_Dyn *dyn = 0;
103+
ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;
104+
if (hdr->e_ident[EI_CLASS] !=
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;
98111

99112
/*
100113
* We need two things from the segment table: the load offset
@@ -108,7 +121,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
108121
+ (uintptr_t)pt[i].p_offset
109122
- (uintptr_t)pt[i].p_vaddr;
110123
} 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);
112125
}
113126
}
114127

@@ -118,7 +131,7 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
118131
/*
119132
* Fish out the useful bits of the dynamic table.
120133
*/
121-
Elf64_Word *hash = 0;
134+
ELF(Word) *hash = 0;
122135
vdso_info.symstrings = 0;
123136
vdso_info.symtab = 0;
124137
vdso_info.versym = 0;
@@ -131,22 +144,22 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
131144
+ vdso_info.load_offset);
132145
break;
133146
case DT_SYMTAB:
134-
vdso_info.symtab = (Elf64_Sym *)
147+
vdso_info.symtab = (ELF(Sym) *)
135148
((uintptr_t)dyn[i].d_un.d_ptr
136149
+ vdso_info.load_offset);
137150
break;
138151
case DT_HASH:
139-
hash = (Elf64_Word *)
152+
hash = (ELF(Word) *)
140153
((uintptr_t)dyn[i].d_un.d_ptr
141154
+ vdso_info.load_offset);
142155
break;
143156
case DT_VERSYM:
144-
vdso_info.versym = (Elf64_Versym *)
157+
vdso_info.versym = (ELF(Versym) *)
145158
((uintptr_t)dyn[i].d_un.d_ptr
146159
+ vdso_info.load_offset);
147160
break;
148161
case DT_VERDEF:
149-
vdso_info.verdef = (Elf64_Verdef *)
162+
vdso_info.verdef = (ELF(Verdef) *)
150163
((uintptr_t)dyn[i].d_un.d_ptr
151164
+ vdso_info.load_offset);
152165
break;
@@ -168,8 +181,8 @@ void vdso_init_from_sysinfo_ehdr(uintptr_t base)
168181
vdso_info.valid = true;
169182
}
170183

171-
static bool vdso_match_version(Elf64_Versym ver,
172-
const char *name, Elf64_Word hash)
184+
static bool vdso_match_version(ELF(Versym) ver,
185+
const char *name, ELF(Word) hash)
173186
{
174187
/*
175188
* This is a helper function to check if the version indexed by
@@ -188,7 +201,7 @@ static bool vdso_match_version(Elf64_Versym ver,
188201

189202
/* First step: find the version definition */
190203
ver &= 0x7fff; /* Apparently bit 15 means "hidden" */
191-
Elf64_Verdef *def = vdso_info.verdef;
204+
ELF(Verdef) *def = vdso_info.verdef;
192205
while(true) {
193206
if ((def->vd_flags & VER_FLG_BASE) == 0
194207
&& (def->vd_ndx & 0x7fff) == ver)
@@ -197,11 +210,11 @@ static bool vdso_match_version(Elf64_Versym ver,
197210
if (def->vd_next == 0)
198211
return false; /* No definition. */
199212

200-
def = (Elf64_Verdef *)((char *)def + def->vd_next);
213+
def = (ELF(Verdef) *)((char *)def + def->vd_next);
201214
}
202215

203216
/* 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);
205218
return def->vd_hash == hash
206219
&& !strcmp(name, vdso_info.symstrings + aux->vda_name);
207220
}
@@ -213,10 +226,10 @@ void *vdso_sym(const char *version, const char *name)
213226
return 0;
214227

215228
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];
217230

218231
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];
220233

221234
/* Check for a defined global or weak function w/ right name. */
222235
if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
@@ -243,7 +256,7 @@ void *vdso_sym(const char *version, const char *name)
243256

244257
void vdso_init_from_auxv(void *auxv)
245258
{
246-
Elf64_auxv_t *elf_auxv = auxv;
259+
ELF(auxv_t) *elf_auxv = auxv;
247260
for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)
248261
{
249262
if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
);

0 commit comments

Comments
 (0)