| Andy Lutomirski | 98eedc3 | 2011-07-13 09:24:16 -0400 | [diff] [blame] | 1 | /* | 
 | 2 |  * vdso_test.c: Sample code to test parse_vdso.c on x86_64 | 
 | 3 |  * Copyright (c) 2011 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 | 
 | 9 |  *      vdso_test.c parse_vdso.c -o vdso_test | 
 | 10 |  * to generate a small binary with no dependencies at all. | 
 | 11 |  */ | 
 | 12 |  | 
 | 13 | #include <sys/syscall.h> | 
 | 14 | #include <sys/time.h> | 
 | 15 | #include <unistd.h> | 
 | 16 | #include <stdint.h> | 
 | 17 |  | 
 | 18 | extern void *vdso_sym(const char *version, const char *name); | 
 | 19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | 
 | 20 | extern void vdso_init_from_auxv(void *auxv); | 
 | 21 |  | 
 | 22 | /* We need a libc functions... */ | 
 | 23 | int strcmp(const char *a, const char *b) | 
 | 24 | { | 
 | 25 | 	/* This implementation is buggy: it never returns -1. */ | 
 | 26 | 	while (*a || *b) { | 
 | 27 | 		if (*a != *b) | 
 | 28 | 			return 1; | 
 | 29 | 		if (*a == 0 || *b == 0) | 
 | 30 | 			return 1; | 
 | 31 | 		a++; | 
 | 32 | 		b++; | 
 | 33 | 	} | 
 | 34 |  | 
 | 35 | 	return 0; | 
 | 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 |  | 
 | 78 | 	/* Find gettimeofday. */ | 
 | 79 | 	typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | 
 | 80 | 	gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | 
 | 81 |  | 
 | 82 | 	if (!gtod) | 
 | 83 | 		linux_exit(1); | 
 | 84 |  | 
 | 85 | 	struct timeval tv; | 
 | 86 | 	long ret = gtod(&tv, 0); | 
 | 87 |  | 
 | 88 | 	if (ret == 0) { | 
 | 89 | 		char buf[] = "The time is                     .000000\n"; | 
 | 90 | 		to_base10(buf + 31, tv.tv_sec); | 
 | 91 | 		to_base10(buf + 38, tv.tv_usec); | 
 | 92 | 		linux_write(1, buf, sizeof(buf) - 1); | 
 | 93 | 	} else { | 
 | 94 | 		linux_exit(ret); | 
 | 95 | 	} | 
 | 96 |  | 
 | 97 | 	linux_exit(0); | 
 | 98 | } | 
 | 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 | 	); |