pastebin - collaborative debugging tool
rovema.kpaste.net RSS


linux_vdso_my_sched_getcpu
Posted by Anonymous on Wed 30th Nov 2022 14:50
raw | new post
view followups (newest first): linux_vdso_my_sched_getcpu by Anonymous

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