Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/vdso/doc: Make vDSO examples more portable

This adds a new vdso_test.c that's written entirely in C. It also
makes all of the vDSO examples work on 32-bit x86.

Cc: Stefani Seibold <stefani@seibold.net>
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/62b701fc44b79f118ac2b2d64d19965fc5c291fb.1402620737.git.luto@amacapital.net
Signed-off-by: H. Peter Anvin <hpa@zytor.com>

authored by

Andy Lutomirski and committed by
H. Peter Anvin
4ebbefd6 6e8f2158

+123 -41
+40 -27
Documentation/vDSO/parse_vdso.c
··· 1 1 /* 2 2 * parse_vdso.c: Linux reference vDSO parser 3 - * Written by Andrew Lutomirski, 2011. 3 + * Written by Andrew Lutomirski, 2011-2014. 4 4 * 5 5 * This code is meant to be linked in to various programs that run on Linux. 6 6 * As such, it is available with as few restrictions as possible. This file ··· 11 11 * it starts a program. It works equally well in statically and dynamically 12 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 15 * architecture that has a vDSO. 16 16 */ 17 17 18 18 #include <stdbool.h> 19 19 #include <stdint.h> 20 20 #include <string.h> 21 + #include <limits.h> 21 22 #include <elf.h> 22 23 23 24 /* ··· 46 45 47 46 48 47 /* And here's the code. */ 49 - 50 - #ifndef __x86_64__ 51 - # error Not yet ported to non-x86_64 architectures 48 + #ifndef ELF_BITS 49 + # if ULONG_MAX > 0xffffffffUL 50 + # define ELF_BITS 64 51 + # else 52 + # define ELF_BITS 32 53 + # endif 52 54 #endif 55 + 56 + #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x 57 + #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) 58 + #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) 53 59 54 60 static struct vdso_info 55 61 { ··· 67 59 uintptr_t load_offset; /* load_addr - recorded vaddr */ 68 60 69 61 /* Symbol table */ 70 - Elf64_Sym *symtab; 62 + ELF(Sym) *symtab; 71 63 const char *symstrings; 72 - Elf64_Word *bucket, *chain; 73 - Elf64_Word nbucket, nchain; 64 + ELF(Word) *bucket, *chain; 65 + ELF(Word) nbucket, nchain; 74 66 75 67 /* Version table */ 76 - Elf64_Versym *versym; 77 - Elf64_Verdef *verdef; 68 + ELF(Versym) *versym; 69 + ELF(Verdef) *verdef; 78 70 } vdso_info; 79 71 80 72 /* Straight from the ELF specification. */ ··· 100 92 101 93 vdso_info.load_addr = base; 102 94 103 - Elf64_Ehdr *hdr = (Elf64_Ehdr*)base; 104 - Elf64_Phdr *pt = (Elf64_Phdr*)(vdso_info.load_addr + hdr->e_phoff); 105 - Elf64_Dyn *dyn = 0; 95 + ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; 96 + if (hdr->e_ident[EI_CLASS] != 97 + (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { 98 + return; /* Wrong ELF class -- check ELF_BITS */ 99 + } 100 + 101 + ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); 102 + ELF(Dyn) *dyn = 0; 106 103 107 104 /* 108 105 * We need two things from the segment table: the load offset ··· 121 108 + (uintptr_t)pt[i].p_offset 122 109 - (uintptr_t)pt[i].p_vaddr; 123 110 } else if (pt[i].p_type == PT_DYNAMIC) { 124 - dyn = (Elf64_Dyn*)(base + pt[i].p_offset); 111 + dyn = (ELF(Dyn)*)(base + pt[i].p_offset); 125 112 } 126 113 } 127 114 ··· 131 118 /* 132 119 * Fish out the useful bits of the dynamic table. 133 120 */ 134 - Elf64_Word *hash = 0; 121 + ELF(Word) *hash = 0; 135 122 vdso_info.symstrings = 0; 136 123 vdso_info.symtab = 0; 137 124 vdso_info.versym = 0; ··· 144 131 + vdso_info.load_offset); 145 132 break; 146 133 case DT_SYMTAB: 147 - vdso_info.symtab = (Elf64_Sym *) 134 + vdso_info.symtab = (ELF(Sym) *) 148 135 ((uintptr_t)dyn[i].d_un.d_ptr 149 136 + vdso_info.load_offset); 150 137 break; 151 138 case DT_HASH: 152 - hash = (Elf64_Word *) 139 + hash = (ELF(Word) *) 153 140 ((uintptr_t)dyn[i].d_un.d_ptr 154 141 + vdso_info.load_offset); 155 142 break; 156 143 case DT_VERSYM: 157 - vdso_info.versym = (Elf64_Versym *) 144 + vdso_info.versym = (ELF(Versym) *) 158 145 ((uintptr_t)dyn[i].d_un.d_ptr 159 146 + vdso_info.load_offset); 160 147 break; 161 148 case DT_VERDEF: 162 - vdso_info.verdef = (Elf64_Verdef *) 149 + vdso_info.verdef = (ELF(Verdef) *) 163 150 ((uintptr_t)dyn[i].d_un.d_ptr 164 151 + vdso_info.load_offset); 165 152 break; ··· 181 168 vdso_info.valid = true; 182 169 } 183 170 184 - static bool vdso_match_version(Elf64_Versym ver, 185 - const char *name, Elf64_Word hash) 171 + static bool vdso_match_version(ELF(Versym) ver, 172 + const char *name, ELF(Word) hash) 186 173 { 187 174 /* 188 175 * This is a helper function to check if the version indexed by ··· 201 188 202 189 /* First step: find the version definition */ 203 190 ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ 204 - Elf64_Verdef *def = vdso_info.verdef; 191 + ELF(Verdef) *def = vdso_info.verdef; 205 192 while(true) { 206 193 if ((def->vd_flags & VER_FLG_BASE) == 0 207 194 && (def->vd_ndx & 0x7fff) == ver) ··· 210 197 if (def->vd_next == 0) 211 198 return false; /* No definition. */ 212 199 213 - def = (Elf64_Verdef *)((char *)def + def->vd_next); 200 + def = (ELF(Verdef) *)((char *)def + def->vd_next); 214 201 } 215 202 216 203 /* Now figure out whether it matches. */ 217 - Elf64_Verdaux *aux = (Elf64_Verdaux*)((char *)def + def->vd_aux); 204 + ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); 218 205 return def->vd_hash == hash 219 206 && !strcmp(name, vdso_info.symstrings + aux->vda_name); 220 207 } ··· 226 213 return 0; 227 214 228 215 ver_hash = elf_hash(version); 229 - Elf64_Word chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; 216 + ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; 230 217 231 218 for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { 232 - Elf64_Sym *sym = &vdso_info.symtab[chain]; 219 + ELF(Sym) *sym = &vdso_info.symtab[chain]; 233 220 234 221 /* Check for a defined global or weak function w/ right name. */ 235 222 if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) ··· 256 243 257 244 void vdso_init_from_auxv(void *auxv) 258 245 { 259 - Elf64_auxv_t *elf_auxv = auxv; 246 + ELF(auxv_t) *elf_auxv = auxv; 260 247 for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) 261 248 { 262 249 if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
+31 -14
Documentation/vDSO/vdso_standalone_test_x86.c
··· 1 1 /* 2 - * vdso_test.c: Sample code to test parse_vdso.c on x86_64 3 - * Copyright (c) 2011 Andy Lutomirski 2 + * vdso_test.c: Sample code to test parse_vdso.c on x86 3 + * Copyright (c) 2011-2014 Andy Lutomirski 4 4 * Subject to the GNU General Public License, version 2 5 5 * 6 6 * You can amuse yourself by compiling with: 7 7 * gcc -std=gnu99 -nostdlib 8 - * -Os -fno-asynchronous-unwind-tables -flto 8 + * -Os -fno-asynchronous-unwind-tables -flto -lgcc_s 9 9 * vdso_standalone_test_x86.c parse_vdso.c 10 - * to generate a small binary with no dependencies at all. 10 + * to generate a small binary. On x86_64, you can omit -lgcc_s 11 + * if you want the binary to be completely standalone. 11 12 */ 12 13 13 14 #include <sys/syscall.h> ··· 36 35 return 0; 37 36 } 38 37 39 - /* ...and two syscalls. This is x86_64-specific. */ 40 - static inline long linux_write(int fd, const void *data, size_t len) 38 + /* ...and two syscalls. This is x86-specific. */ 39 + static inline long x86_syscall3(long nr, long a0, long a1, long a2) 41 40 { 42 - 43 41 long ret; 44 - asm volatile ("syscall" : "=a" (ret) : "a" (__NR_write), 45 - "D" (fd), "S" (data), "d" (len) : 42 + #ifdef __x86_64__ 43 + asm volatile ("syscall" : "=a" (ret) : "a" (nr), 44 + "D" (a0), "S" (a1), "d" (a2) : 46 45 "cc", "memory", "rcx", 47 46 "r8", "r9", "r10", "r11" ); 47 + #else 48 + asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), 49 + "b" (a0), "c" (a1), "d" (a2) : 50 + "cc", "memory" ); 51 + #endif 48 52 return ret; 53 + } 54 + 55 + static inline long linux_write(int fd, const void *data, size_t len) 56 + { 57 + return x86_syscall3(__NR_write, fd, (long)data, (long)len); 49 58 } 50 59 51 60 static inline void linux_exit(int code) 52 61 { 53 - asm volatile ("syscall" : : "a" (__NR_exit), "D" (code)); 62 + x86_syscall3(__NR_exit, code, 0, 0); 54 63 } 55 64 56 65 void to_base10(char *lastdig, uint64_t n) ··· 115 104 asm ( 116 105 ".text\n" 117 106 ".global _start\n" 118 - ".type _start,@function\n" 119 - "_start:\n\t" 120 - "mov %rsp,%rdi\n\t" 121 - "jmp c_main" 107 + ".type _start,@function\n" 108 + "_start:\n\t" 109 + #ifdef __x86_64__ 110 + "mov %rsp,%rdi\n\t" 111 + "jmp c_main" 112 + #else 113 + "push %esp\n\t" 114 + "call c_main\n\t" 115 + "int $3" 116 + #endif 122 117 );
+52
Documentation/vDSO/vdso_test.c
··· 1 + /* 2 + * vdso_test.c: Sample code to test parse_vdso.c 3 + * Copyright (c) 2014 Andy Lutomirski 4 + * Subject to the GNU General Public License, version 2 5 + * 6 + * Compile with: 7 + * gcc -std=gnu99 vdso_test.c parse_vdso.c 8 + * 9 + * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. 10 + */ 11 + 12 + #include <stdint.h> 13 + #include <elf.h> 14 + #include <stdio.h> 15 + #include <sys/auxv.h> 16 + #include <sys/time.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 + int main(int argc, char **argv) 23 + { 24 + unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); 25 + if (!sysinfo_ehdr) { 26 + printf("AT_SYSINFO_EHDR is not present!\n"); 27 + return 0; 28 + } 29 + 30 + vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); 31 + 32 + /* Find gettimeofday. */ 33 + typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); 34 + gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); 35 + 36 + if (!gtod) { 37 + printf("Could not find __vdso_gettimeofday\n"); 38 + return 1; 39 + } 40 + 41 + struct timeval tv; 42 + long ret = gtod(&tv, 0); 43 + 44 + if (ret == 0) { 45 + printf("The time is %lld.%06lld\n", 46 + (long long)tv.tv_sec, (long long)tv.tv_usec); 47 + } else { 48 + printf("__vdso_gettimeofday failed\n"); 49 + } 50 + 51 + return 0; 52 + }