diff options
Diffstat (limited to 'Documentation/vDSO/vdso_standalone_test_x86.c')
| -rw-r--r-- | Documentation/vDSO/vdso_standalone_test_x86.c | 128 | 
1 files changed, 128 insertions, 0 deletions
diff --git a/Documentation/vDSO/vdso_standalone_test_x86.c b/Documentation/vDSO/vdso_standalone_test_x86.c new file mode 100644 index 00000000000..d46240265c5 --- /dev/null +++ b/Documentation/vDSO/vdso_standalone_test_x86.c @@ -0,0 +1,128 @@ +/* + * vdso_test.c: Sample code to test parse_vdso.c on x86 + * Copyright (c) 2011-2014 Andy Lutomirski + * Subject to the GNU General Public License, version 2 + * + * You can amuse yourself by compiling with: + * gcc -std=gnu99 -nostdlib + *     -Os -fno-asynchronous-unwind-tables -flto -lgcc_s + *      vdso_standalone_test_x86.c parse_vdso.c + * to generate a small binary.  On x86_64, you can omit -lgcc_s + * if you want the binary to be completely standalone. + */ + +#include <sys/syscall.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdint.h> + +extern void *vdso_sym(const char *version, const char *name); +extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); +extern void vdso_init_from_auxv(void *auxv); + +/* We need a libc functions... */ +int strcmp(const char *a, const char *b) +{ +	/* This implementation is buggy: it never returns -1. */ +	while (*a || *b) { +		if (*a != *b) +			return 1; +		if (*a == 0 || *b == 0) +			return 1; +		a++; +		b++; +	} + +	return 0; +} + +/* ...and two syscalls.  This is x86-specific. */ +static inline long x86_syscall3(long nr, long a0, long a1, long a2) +{ +	long ret; +#ifdef __x86_64__ +	asm volatile ("syscall" : "=a" (ret) : "a" (nr), +		      "D" (a0), "S" (a1), "d" (a2) : +		      "cc", "memory", "rcx", +		      "r8", "r9", "r10", "r11" ); +#else +	asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), +		      "b" (a0), "c" (a1), "d" (a2) : +		      "cc", "memory" ); +#endif +	return ret; +} + +static inline long linux_write(int fd, const void *data, size_t len) +{ +	return x86_syscall3(__NR_write, fd, (long)data, (long)len); +} + +static inline void linux_exit(int code) +{ +	x86_syscall3(__NR_exit, code, 0, 0); +} + +void to_base10(char *lastdig, uint64_t n) +{ +	while (n) { +		*lastdig = (n % 10) + '0'; +		n /= 10; +		lastdig--; +	} +} + +__attribute__((externally_visible)) void c_main(void **stack) +{ +	/* Parse the stack */ +	long argc = (long)*stack; +	stack += argc + 2; + +	/* Now we're pointing at the environment.  Skip it. */ +	while(*stack) +		stack++; +	stack++; + +	/* Now we're pointing at auxv.  Initialize the vDSO parser. */ +	vdso_init_from_auxv((void *)stack); + +	/* Find gettimeofday. */ +	typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); +	gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); + +	if (!gtod) +		linux_exit(1); + +	struct timeval tv; +	long ret = gtod(&tv, 0); + +	if (ret == 0) { +		char buf[] = "The time is                     .000000\n"; +		to_base10(buf + 31, tv.tv_sec); +		to_base10(buf + 38, tv.tv_usec); +		linux_write(1, buf, sizeof(buf) - 1); +	} else { +		linux_exit(ret); +	} + +	linux_exit(0); +} + +/* + * This is the real entry point.  It passes the initial stack into + * the C entry point. + */ +asm ( +	".text\n" +	".global _start\n" +	".type _start,@function\n" +	"_start:\n\t" +#ifdef __x86_64__ +	"mov %rsp,%rdi\n\t" +	"jmp c_main" +#else +	"push %esp\n\t" +	"call c_main\n\t" +	"int $3" +#endif +	);  | 
