pastebin - collaborative debugging tool
rovema.kpaste.net RSS


linux_vdso_my_sched_getcpu
Posted by Anonymous on Tue 28th Mar 2023 14:49
raw | new post
modification of post by Anonymous (view diff)

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

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at