It turns out that parsing the vDSO is nontrivial if you don't already have an ELF dynamic loader around. So document it in Documentation/ABI and add a reference CC0-licenced parser. This code is dedicated to Go issue 1933: http://code.google.com/p/go/issues/detail?id=1933 Signed-off-by: Andy Lutomirski <luto@mit.edu> Link: http://lkml.kernel.org/r/a315a9514cd71bcf29436cc31e35aada21a5ff21.1310563276.git.luto@mit.edu Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
		
			
				
	
	
		
			112 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * vdso_test.c: Sample code to test parse_vdso.c on x86_64
 | |
|  * Copyright (c) 2011 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
 | |
|  *      vdso_test.c parse_vdso.c -o vdso_test
 | |
|  * to generate a small binary with no dependencies at all.
 | |
|  */
 | |
| 
 | |
| #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_64-specific. */
 | |
| static inline long linux_write(int fd, const void *data, size_t len)
 | |
| {
 | |
| 
 | |
| 	long ret;
 | |
| 	asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write),
 | |
| 		      "D" (fd), "S" (data), "d" (len) :
 | |
| 		      "cc", "memory", "rcx",
 | |
| 		      "r8", "r9", "r10", "r11" );
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static inline void linux_exit(int code)
 | |
| {
 | |
| 	asm volatile ("syscall" : : "a" (__NR_exit), "D" (code));
 | |
| }
 | |
| 
 | |
| 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"
 | |
|         "mov %rsp,%rdi\n\t"
 | |
|         "jmp c_main"
 | |
| 	);
 |