Difference between
modified post 11495b4 by Anonymous on Thu 29th Sep 2022 10:20
original post 0aef2778 by Anonymous on Wed 28th Sep 2022 16:49
Download diff
Show
old version |
new version |
both versions
| 1 | + | ||
| 1 | 2 | /* | |
| 2 | 3 | * linux_vdso_myclockgettime1.c - use Linux VDSO | |
| 3 | 4 | * shortcut for |clock_gettime()|, even if userland (g)libc | |
| 4 | 5 | * does not have support for VDSO | |
| 5 | 6 | * | |
| 6 | 7 | * Compile with: | |
| 7 | 8 | * $ clang -std=c99 -g -m32 -Wall -Wextra linux_vdso_myclockgettime1.c \ | |
| 8 | 9 | * -lpthread -o linux_vdso_myclockgettime1 | |
| 9 | 10 | * | |
| 10 | 11 | * Written by Roland Mainz <roland.mainz@nrubsig.org> | |
| 11 | 12 | * | |
| 12 | 13 | */ | |
| 13 | 14 | ||
| 14 | 15 | #define _XOPEN_SOURCE 600 | |
| 15 | 16 | ||
| 16 | 17 | #include <stdio.h> | |
| 17 | 18 | #include <stddef.h> | |
| 18 | 19 | #include <stdlib.h> | |
| 19 | 20 | #include <string.h> | |
| 20 | 21 | #include <elf.h> | |
| 21 | 22 | #include <time.h> | |
| 22 | 23 | #include <pthread.h> | |
| 23 | 24 | #include <sys/auxv.h> | |
| 24 | 25 | ||
| 26 | + | /* Catch cursed idiots playing around with XPG config */ | |
| 25 | 27 | #if (_XOPEN_SOURCE < 600) | |
| 26 | 28 | #error _XOPEN_SOURCE wrong version | |
| 27 | 29 | #endif | |
| 28 | 30 | ||
| 29 | - | #if (__PTRDIFF_WIDTH__ == 32) | |
| 31 | + | /* Build test code ? */ | |
| 30 | - | typedef Elf32_Ehdr ElfXX_Ehdr; | |
| 32 | + | #define MY_CLOCK_GETTIME_TESTS 1 | |
| 40 | 33 | ||
| 41 | 34 | /* debug */ | |
| 42 | 35 | #if 0 | |
| 43 | 36 | #define D(x) x | |
| 44 | 37 | #else | |
| 45 | 38 | #define D(x) | |
| 46 | 39 | #endif | |
| 47 | 40 | ||
| 48 | - | static | |
| 41 | + | ||
| 49 | - | void *get_linux_vdso_sym(const char *funcname) | |
| 42 | + | /* 32bit vs. 64bit */ | |
| 50 | - | { | |
| 43 | + | #if (__PTRDIFF_WIDTH__ == 32) | |
| 51 | - | const char *dynstr = NULL; | |
| 44 | + | typedef Elf32_Ehdr ElfXX_Ehdr; | |
| 52 | - | void *ret = NULL; | |
| 45 | + | typedef Elf32_Shdr ElfXX_Shdr; | |
| 53 | - | unsigned char *vdso_addr; | |
| 46 | + | typedef Elf32_Sym ElfXX_Sym; | |
| 54 | - | ssize_t i; | |
| 47 | + | #elif (__PTRDIFF_WIDTH__ == 64) | |
| 55 | - | size_t si; | |
| 48 | + | typedef Elf64_Ehdr ElfXX_Ehdr; | |
| 56 | - | const char *name; | |
| 49 | + | typedef Elf64_Shdr ElfXX_Shdr; | |
| 57 | - | ElfXX_Ehdr *elf_header; | |
| 50 | + | typedef Elf64_Sym ElfXX_Sym; | |
| 58 | - | ElfXX_Shdr *section_header; | |
| 51 | + | #else | |
| 59 | - | ElfXX_Shdr *s; | |
| 52 | + | #error Unsupported __PTRDIFF_WIDTH__ | |
| 60 | - | ElfXX_Shdr *ss_; | |
| 53 | + | #endif | |
| 62 | 54 | ||
| 55 | + | ||
| 63 | 56 | /* | |
| 64 | - | * valgrind can disable |AT_SYSINFO_EHDR| - see | |
| 57 | + | * get_linux_vdso_sym() - Look up symbol in AUX AT_SYSINFO_EHDR | |
| 66 | 58 | */ | |
| 67 | - | vdso_addr = (unsigned char *)getauxval(AT_SYSINFO_EHDR); | |
| 59 | + | static | |
| 68 | - | if (!vdso_addr) { | |
| 60 | + | void *get_linux_vdso_sym(const char *funcname) | |
| 69 | - | D((void)fprintf(stderr, "get_linux_vdso_sym: getauxval(AT_SYSINFO_EHDR) failed.\n")); | |
| 61 | + | { | |
| 70 | - | return NULL; | |
| 62 | + | const char *dynstr; | |
| 71 | - | } | |
| 63 | + | void *ret = NULL; | |
| 64 | + | unsigned char *vdso_addr; | |
| 72 | 73 | ||
| 73 | - | elf_header = (ElfXX_Ehdr *)vdso_addr; | |
| 74 | + | /* | |
| 74 | - | section_header = (ElfXX_Shdr *)(vdso_addr + elf_header->e_shoff); | |
| 75 | + | * valgrind can disable |AT_SYSINFO_EHDR| - see | |
| 76 | + | * https://www.mail-archive.com/kde-bugs-dist@kde.org/msg550927.html | |
| 75 | 83 | ||
| 76 | - | for (i=0L; i < elf_header->e_shnum; i++) { | |
| 84 | + | elf_header = (ElfXX_Ehdr *)vdso_addr; | |
| 77 | - | s = §ion_header[i]; | |
| 85 | + | section_header = (ElfXX_Shdr *)(vdso_addr + elf_header->e_shoff); | |
| 80 | 86 | ||
| 87 | + | for (dynstr = NULL, i=0L; i < elf_header->e_shnum; i++) { | |
| 81 | 92 | if (strcmp(name, ".dynstr") == 0) { | |
| 82 | 93 | dynstr = (const char *)(vdso_addr + s->sh_offset); | |
| 83 | 94 | break; | |
| 84 | 95 | } | |
| 85 | 96 | } | |
| 86 | 97 | ||
| 98 | + | if (!dynstr) | |
| 87 | 101 | for (i=0L; i < elf_header->e_shnum; i++) { | |
| 88 | - | s = §ion_header[i]; | |
| 102 | + | s = §ion_header[i]; | |
| 89 | - | ss_ = §ion_header[elf_header->e_shstrndx]; | |
| 103 | + | s2 = §ion_header[elf_header->e_shstrndx]; | |
| 90 | - | name = (const char*)(vdso_addr + ss_->sh_offset + s->sh_name); | |
| 104 | + | name = (const char*)(vdso_addr + s2->sh_offset + s->sh_name); | |
| 91 | 105 | ||
| 92 | 106 | /* Not .dynsym ? Then look at next section... */ | |
| 93 | 107 | if (strcmp(name, ".dynsym")) | |
| 94 | 108 | continue; | |
| 95 | 109 | ||
| 96 | 110 | for (si=0L; si < (s->sh_size/s->sh_entsize); si++) { | |
| 97 | 111 | sym = &(((ElfXX_Sym*)(vdso_addr + s->sh_offset))[si]); | |
| 98 | 112 | name = dynstr + sym->st_name; | |
| 99 | 113 | ||
| 100 | 114 | D((void)fprintf(stderr, "get_linux_vdso_sym: symbol=|%s| vs. |%s|\n", | |
| 101 | 115 | funcname, name)); | |
| 102 | 116 | if (!strcmp(name, funcname)) { | |
| 103 | 117 | ret = (void *)(vdso_addr + sym->st_value); | |
| 104 | 118 | break; | |
| 105 | 119 | } | |
| 106 | 120 | } | |
| 121 | + | ||
| 107 | 122 | if (ret) | |
| 108 | 123 | break; | |
| 109 | 124 | } | |
| 110 | 125 | ||
| 111 | 126 | return ret; | |
| 112 | 127 | } | |
| 113 | 128 | ||
| 114 | - | typedef int (clock_gettime_func_t)(clockid_t clk_id, struct timespec *tp); | |
| 115 | 129 | ||
| 130 | + | /* | |
| 116 | 135 | static pthread_once_t my_clock_gettime_func_is_initialized = PTHREAD_ONCE_INIT; | |
| 117 | - | clock_gettime_func_t *my_clock_gettime_func = NULL; | |
| 136 | + | static clock_gettime_func_t *my_clock_gettime_func = NULL; | |
| 118 | 137 | ||
| 138 | + | ||
| 119 | 140 | void my_clock_gettime_func_init(void) | |
| 120 | 141 | { | |
| 121 | 142 | my_clock_gettime_func = (clock_gettime_func_t *)get_linux_vdso_sym("clock_gettime"); | |
| 122 | 143 | if (!my_clock_gettime_func) | |
| 123 | 144 | my_clock_gettime_func = (clock_gettime_func_t *)get_linux_vdso_sym("__vdso_clock_gettime"); | |
| 124 | 145 | ||
| 125 | 146 | D((void)fprintf(stderr, "my_clock_gettime_func_init: " | |
| 126 | 147 | "my_clock_gettime_func = 0x%lx (%s)\n", | |
| 127 | 148 | (long)my_clock_gettime_func, | |
| 128 | 149 | (my_clock_gettime_func? | |
| 129 | 150 | "Using fast codepath": | |
| 130 | 151 | "Using slow codepath"))); | |
| 131 | 152 | if (!my_clock_gettime_func) { | |
| 132 | 153 | (void)fprintf(stderr, "my_clock_gettime_func_init: " | |
| 133 | 154 | "WARNING: aux symbol for clock_gettime not found" | |
| 134 | 155 | ", using slow libc codepath.\n"); | |
| 135 | 156 | } | |
| 136 | 157 | } | |
| 137 | 158 | ||
| 138 | 159 | ||
| 139 | 160 | /* | |
| 140 | - | * my_clock_gettime() - our "public" interface | |
| 161 | + | * |my_clock_gettime()| - our "public" interface | |
| 141 | 162 | * | |
| 142 | 163 | * Notes: | |
| 143 | 164 | * - The first call might be slower because the call to | |
| 144 | 165 | * |my_clock_gettime_func_init()| | |
| 145 | 166 | */ | |
| 146 | 167 | int my_clock_gettime(clockid_t clk_id, struct timespec *tp) | |
| 147 | 168 | { | |
| 148 | 169 | (void)pthread_once(&my_clock_gettime_func_is_initialized, | |
| 149 | 170 | my_clock_gettime_func_init); | |
| 150 | 171 | ||
| 151 | 172 | if (my_clock_gettime_func) { | |
| 152 | - | /* our own "fast" codepath */ | |
| 173 | + | /* our MY_CLOCK_GETTIME_TESTSown "fast" codepath */ | |
| 153 | 174 | return (*my_clock_gettime_func)(clk_id, tp); | |
| 154 | 175 | } | |
| 155 | 176 | else | |
| 156 | 177 | { | |
| 157 | 178 | /* libc codepath, whatever they use - might be VDSO, might be syscall */ | |
| 158 | 179 | return clock_gettime(clk_id, tp); | |
| 159 | 180 | } | |
| 160 | 181 | } | |
| 161 | 182 | ||
| 183 | + | ||
| 165 | 185 | /* | |
| 186 | + | * test code | |
| 168 | 187 | */ | |
| 188 | + | static | |
| 169 | 195 | struct timespec dummy; | |
| 170 | 196 | (void)my_clock_gettime(CLOCK_REALTIME, &dummy); | |
| 171 | 197 | ||
| 172 | 198 | #define PRINT_CLOCK(clock) \ | |
| 173 | 199 | { \ | |
| 174 | 200 | struct timespec tp1, tp2; \ | |
| 175 | 201 | /* \ | |
| 202 | + | * print timestamps: \ | |
| 176 | 203 | * first libc implementation, then our implementation, \ | |
| 177 | 204 | * so that the difference is the call time of our \ | |
| 178 | 205 | * codepath. Requires |my_clock_gettime_func_init()| \ | |
| 179 | 206 | * to run before this sequence!! \ | |
| 180 | 207 | */ \ | |
| 181 | 208 | (void)clock_gettime((clock), &tp1); \ | |
| 182 | 209 | (void)my_clock_gettime((clock), &tp2); \ | |
| 183 | 210 | (void)printf( \ | |
| 184 | 211 | "%-20s libc_impl = \t(%-10ld\t%-10ld), " \ | |
| 185 | 212 | "our_impl = \t(%-10ld\t%-10ld)\n", \ | |
| 186 | 213 | #clock ":", \ | |
| 187 | 214 | (long)tp1.tv_sec, (long)tp1.tv_nsec, \ | |
| 188 | 215 | (long)tp2.tv_sec, (long)tp2.tv_nsec); \ | |
| 189 | 216 | } | |
| 190 | 217 | ||
| 191 | 218 | PRINT_CLOCK(CLOCK_REALTIME); | |
| 192 | 219 | PRINT_CLOCK(CLOCK_MONOTONIC); | |
| 193 | 220 | } | |
| 194 | 221 | ||
| 222 | + | ||
| 195 | 223 | int main(int ac, char *av[]) | |
| 196 | 224 | { | |
| 197 | 225 | (void)ac; | |
| 198 | 226 | (void)av; | |
| 199 | 227 | ||
| 200 | 228 | test_my_clock_gettime1(); | |
| 201 | 229 | ||
| 202 | 230 | return EXIT_SUCCESS; | |
| 203 | 231 | } | |
| 204 | 232 | ||
| 205 | - | ||
| 233 | + | #endif /* MY_CLOCK_GETTIME_TESTS */ | |
| 206 | 234 | ||
| 207 | 235 | ||
| 208 | 236 |
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
rovema.kpaste.net RSS