pastebin - collaborative debugging tool
rovema.kpaste.net RSS


linux_vdso_myclockgettime1.c - fast clock_gettime implementation
Posted by Anonymous on Wed 5th Oct 2022 09:51
raw | new post
modification of post by Anonymous (view diff)

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