Difference between
modified post 0c6 by Anonymous on Tue 28th Mar 2023 14:49
original post 06f04 by Anonymous on Wed 30th Nov 2022 14:50
Download diff
Show
old version |
new version |
both versions
| 1 | 1 | ||
| 2 | 2 | /* | |
| 3 | 3 | * linux_vdso_my_sched_getcpu.c - use Linux VDSO | |
| 4 | 4 | * shortcut for |sched_getcpu()|, even if userland (g)libc | |
| 5 | 5 | * does not have support for VDSO | |
| 6 | 6 | * | |
| 7 | 7 | * Compile with: | |
| 8 | 8 | * $ clang -std=c99 -g -m32 -Wall \ | |
| 9 | 9 | * -Wextra linux_vdso_my_sched_getcpu.c -lpthread \ | |
| 10 | 10 | * -o linux_vdso_my_sched_getcpu | |
| 11 | 11 | * | |
| 12 | 12 | * Written by Roland Mainz <roland.mainz@nrubsig.org> | |
| 13 | 13 | * | |
| 14 | 14 | */ | |
| 15 | 15 | ||
| 16 | 16 | #define _XOPEN_SOURCE 700 | |
| 17 | 17 | #define _GNU_SOURCE 1 | |
| 18 | 18 | ||
| 19 | 19 | #include <stdio.h> | |
| 20 | 20 | #include <stddef.h> | |
| 21 | 21 | #include <stdlib.h> | |
| 22 | 22 | #include <string.h> | |
| 23 | 23 | #include <elf.h> | |
| 24 | 24 | #include <time.h> | |
| 25 | 25 | #include <pthread.h> | |
| 26 | 26 | #include <sched.h> | |
| 27 | + | #include <unistd.h> | |
| 27 | 28 | #include <sys/auxv.h> | |
| 29 | + | #include <sys/syscall.h> | |
| 28 | 30 | ||
| 29 | 31 | /* Catch cursed idiots playing around with XPG config */ | |
| 30 | 32 | #if (_XOPEN_SOURCE < 600) | |
| 31 | 33 | #error _XOPEN_SOURCE wrong version | |
| 32 | 34 | #endif | |
| 33 | 35 | ||
| 34 | 36 | /* Build test code ? */ | |
| 35 | 37 | #define MY_SCHED_GETCPU_TESTS 1 | |
| 36 | 38 | ||
| 37 | 39 | /* debug */ | |
| 38 | 40 | #if 1 | |
| 39 | 41 | #define D(x) x | |
| 40 | 42 | #else | |
| 41 | 43 | #define D(x) | |
| 42 | 44 | #endif | |
| 43 | 45 | ||
| 44 | 46 | ||
| 45 | 47 | /* 32bit vs. 64bit */ | |
| 46 | - | #if (__PTRDIFF_WIDTH__ == 32) | |
| 48 | + | #if (__SIZEOF_PTRDIFF_T__ == 4) | |
| 47 | 49 | typedef Elf32_Ehdr ElfXX_Ehdr; | |
| 48 | 50 | typedef Elf32_Shdr ElfXX_Shdr; | |
| 49 | 51 | typedef Elf32_Sym ElfXX_Sym; | |
| 50 | - | #elif (__PTRDIFF_WIDTH__ == 64) | |
| 52 | + | #elif (__SIZEOF_PTRDIFF_T__ == 8) | |
| 51 | 53 | typedef Elf64_Ehdr ElfXX_Ehdr; | |
| 52 | 54 | typedef Elf64_Shdr ElfXX_Shdr; | |
| 53 | 55 | typedef Elf64_Sym ElfXX_Sym; | |
| 54 | 56 | #else | |
| 55 | - | #error Unsupported __PTRDIFF_WIDTH__ | |
| 57 | + | #error Unsupported __SIZEOF_PTRDIFF_T__ | |
| 56 | 58 | #endif | |
| 57 | 59 | ||
| 58 | 60 | ||
| 59 | 61 | /* | |
| 60 | 62 | * |get_linux_vdso_sym()| - Look up symbol in AUX AT_SYSINFO_EHDR | |
| 61 | 63 | */ | |
| 62 | 64 | static | |
| 63 | 65 | void *get_linux_vdso_sym(const char *funcname) | |
| 64 | 66 | { | |
| 65 | 67 | const char *dynstr; | |
| 66 | 68 | void *ret = NULL; | |
| 67 | 69 | unsigned char *vdso_addr; | |
| 68 | 70 | ssize_t i; | |
| 69 | 71 | size_t si; | |
| 70 | 72 | const char *name; | |
| 71 | 73 | ElfXX_Ehdr *elf_header; | |
| 72 | 74 | ElfXX_Shdr *section_header; | |
| 73 | 75 | ElfXX_Shdr *s; | |
| 74 | 76 | ElfXX_Shdr *s2; | |
| 75 | 77 | ElfXX_Sym *sym; | |
| 76 | 78 | ||
| 77 | 79 | /* | |
| 78 | 80 | * valgrind can disable |AT_SYSINFO_EHDR| - see | |
| 79 | 81 | * https://www.mail-archive.com/kde-bugs-dist@kde.org/msg550927.html | |
| 80 | 82 | */ | |
| 81 | 83 | vdso_addr = (unsigned char *)getauxval(AT_SYSINFO_EHDR); | |
| 82 | 84 | if (!vdso_addr) { | |
| 83 | 85 | D((void)fprintf(stderr, | |
| 84 | 86 | "get_linux_vdso_sym: getauxval(AT_SYSINFO_EHDR) failed.\n")); | |
| 85 | 87 | return NULL; | |
| 86 | 88 | } | |
| 87 | 89 | ||
| 88 | 90 | elf_header = (ElfXX_Ehdr *)vdso_addr; | |
| 89 | 91 | section_header = (ElfXX_Shdr *)(vdso_addr + elf_header->e_shoff); | |
| 90 | 92 | ||
| 91 | 93 | for (dynstr = NULL, i=0L; i < elf_header->e_shnum; i++) { | |
| 92 | 94 | s = §ion_header[i]; | |
| 93 | 95 | s2 = §ion_header[elf_header->e_shstrndx]; | |
| 94 | 96 | name = (const char*)(vdso_addr + s2->sh_offset + s->sh_name); | |
| 95 | 97 | ||
| 96 | 98 | if (strcmp(name, ".dynstr") == 0) { | |
| 97 | 99 | dynstr = (const char *)(vdso_addr + s->sh_offset); | |
| 98 | 100 | break; | |
| 99 | 101 | } | |
| 100 | 102 | } | |
| 101 | 103 | ||
| 102 | 104 | if (!dynstr) | |
| 103 | 105 | return NULL; | |
| 104 | 106 | ||
| 105 | 107 | for (i=0L; i < elf_header->e_shnum; i++) { | |
| 106 | 108 | s = §ion_header[i]; | |
| 107 | 109 | s2 = §ion_header[elf_header->e_shstrndx]; | |
| 108 | 110 | name = (const char*)(vdso_addr + s2->sh_offset + s->sh_name); | |
| 109 | 111 | ||
| 110 | 112 | /* Not .dynsym ? Then look at next section... */ | |
| 111 | 113 | if (strcmp(name, ".dynsym")) | |
| 112 | 114 | continue; | |
| 113 | 115 | ||
| 114 | 116 | for (si=0L; si < (s->sh_size/s->sh_entsize); si++) { | |
| 115 | 117 | sym = &(((ElfXX_Sym*)(vdso_addr + s->sh_offset))[si]); | |
| 116 | 118 | name = dynstr + sym->st_name; | |
| 117 | 119 | ||
| 118 | 120 | D((void)fprintf(stderr, "get_linux_vdso_sym: " | |
| 119 | 121 | "symbol=|%s| vs. |%s|\n", | |
| 120 | 122 | funcname, name)); | |
| 121 | 123 | if (!strcmp(name, funcname)) { | |
| 122 | 124 | ret = (void *)(vdso_addr + sym->st_value); | |
| 123 | 125 | break; | |
| 124 | 126 | } | |
| 125 | 127 | } | |
| 126 | 128 | ||
| 127 | 129 | if (ret) | |
| 128 | 130 | break; | |
| 129 | 131 | } | |
| 130 | 132 | ||
| 131 | 133 | return ret; | |
| 132 | 134 | } | |
| 133 | 135 | ||
| 134 | 136 | ||
| 135 | 137 | /* | |
| 136 | 138 | * Initalisation code | |
| 137 | 139 | */ | |
| 138 | 140 | ||
| 139 | 141 | /* | |
| 140 | 142 | * Note that the |getcpu()| prototype here differs between | |
| 141 | 143 | * Linux versions, the third argument (|tcache|) has been | |
| 142 | 144 | * removed, and the prototype was moved from | |
| 143 | 145 | * <linux/getcpu.h> to <bits/sched.h>. | |
| 144 | 146 | */ | |
| 145 | 147 | typedef int (getcpu_func_t)(unsigned *cpu, unsigned *node, void *tcache); | |
| 146 | 148 | ||
| 147 | 149 | static pthread_once_t my_getcpu_func_is_initialized = PTHREAD_ONCE_INIT; | |
| 148 | 150 | static getcpu_func_t *my_getcpu_func = NULL; | |
| 149 | 151 | ||
| 150 | 152 | ||
| 151 | 153 | static | |
| 152 | 154 | void my_getcpu_func_init(void) | |
| 153 | 155 | { | |
| 154 | 156 | my_getcpu_func = (getcpu_func_t *)get_linux_vdso_sym("__vdso_getcpu"); | |
| 155 | 157 | if (!my_getcpu_func) | |
| 156 | 158 | my_getcpu_func = (getcpu_func_t *)get_linux_vdso_sym("getcpu"); | |
| 157 | 159 | ||
| 158 | 160 | D((void)fprintf(stderr, "my_getcpu_func_init: " | |
| 159 | 161 | "my_getcpu_func = 0x%lx (%s)\n", | |
| 160 | 162 | (long)my_getcpu_func, | |
| 161 | 163 | (my_getcpu_func? | |
| 162 | 164 | "Using fast codepath": | |
| 163 | 165 | "Using slow codepath"))); | |
| 164 | 166 | if (!my_getcpu_func) { | |
| 165 | 167 | (void)fprintf(stderr, "my_getcpu_func_init: " | |
| 166 | 168 | "WARNING: aux symbol for getcpu not found" | |
| 167 | 169 | ", using slow libc codepath.\n"); | |
| 168 | 170 | } | |
| 169 | 171 | } | |
| 170 | 172 | ||
| 171 | 173 | ||
| 172 | 174 | /* | |
| 173 | 175 | * |my_sched_getcpu()| - our "public" interface | |
| 174 | 176 | * | |
| 175 | 177 | * Notes: | |
| 176 | 178 | * - The first call might be slower because the call to | |
| 177 | 179 | * |my_getcpu_func_init()| | |
| 178 | 180 | */ | |
| 179 | 181 | int my_sched_getcpu(void) | |
| 180 | 182 | { | |
| 181 | 183 | unsigned int cpu; | |
| 182 | 184 | int s; | |
| 183 | 185 | ||
| 184 | 186 | (void)pthread_once(&my_getcpu_func_is_initialized, | |
| 185 | 187 | my_getcpu_func_init); | |
| 186 | 188 | ||
| 187 | 189 | if (my_getcpu_func) { | |
| 188 | 190 | /* our MY_getcpu_TESTS own "fast" codepath */ | |
| 189 | 191 | s = (*my_getcpu_func)(&cpu, NULL, NULL); | |
| 190 | 192 | } | |
| 191 | 193 | else | |
| 192 | 194 | { | |
| 195 | + | #define FORCE_GETCPU_SYSCALL 1 | |
| 193 | 199 | /* | |
| 194 | 200 | * libc codepath, whatever they use - might be VDSO, | |
| 195 | 201 | * might be syscall | |
| 196 | 202 | * | |
| 197 | 203 | * Note that the prototype here differs between Linux | |
| 198 | 204 | * versions, the third argument (|tcache|) has been | |
| 199 | 205 | * removed, and the prototype was moved from | |
| 200 | 206 | * <linux/getcpu.h> to <bits/sched.h>. | |
| 201 | 207 | * Depending on glibc version this might cause | |
| 202 | 208 | * crashes. | |
| 203 | 209 | */ | |
| 204 | 210 | s = getcpu(&cpu, NULL/*, NULL*/); | |
| 211 | + | #endif /* defined(SYS_getcpu) && FORCE_GETCPU_SYSCALL */ | |
| 205 | 212 | } | |
| 206 | 213 | ||
| 207 | 214 | if (s == -1) | |
| 208 | 215 | return -1; | |
| 209 | 216 | ||
| 210 | 217 | return (int)cpu; | |
| 211 | 218 | } | |
| 212 | 219 | ||
| 213 | 220 | ||
| 214 | 221 | #ifdef MY_SCHED_GETCPU_TESTS | |
| 215 | 222 | /* | |
| 216 | 223 | * test code | |
| 217 | 224 | */ | |
| 218 | 225 | static | |
| 219 | 226 | void test_my_sched_getcpu1(void) | |
| 220 | 227 | { | |
| 221 | 228 | int cpu_num; | |
| 222 | 229 | ||
| 223 | 230 | cpu_num = my_sched_getcpu(); | |
| 224 | 231 | (void)printf("my_sched_getcpu() = %d\n", cpu_num); | |
| 225 | 232 | ||
| 226 | 233 | cpu_num = sched_getcpu(); | |
| 227 | 234 | (void)printf("sched_getcpu() = %d\n", cpu_num); | |
| 228 | 235 | } | |
| 229 | 236 | ||
| 230 | 237 | ||
| 231 | 238 | int main(int ac, char *av[]) | |
| 232 | 239 | { | |
| 233 | 240 | (void)ac; | |
| 234 | 241 | (void)av; | |
| 235 | 242 | ||
| 236 | 243 | test_my_sched_getcpu1(); | |
| 237 | 244 | ||
| 238 | 245 | return EXIT_SUCCESS; | |
| 239 | 246 | } | |
| 240 | 247 | ||
| 241 | 248 | #endif /* MY_SCHED_GETCPU_TESTS */ | |
| 242 | 249 |
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