init.c.tpl 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright 2018-2022 Yury Gribov
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Use of this source code is governed by MIT license that can be
  7. * found in the LICENSE.txt file.
  8. */
  9. #ifndef _GNU_SOURCE
  10. #define _GNU_SOURCE // For RTLD_DEFAULT
  11. #endif
  12. #include <dlfcn.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <stdio.h>
  16. #include <assert.h>
  17. // Sanity check for ARM to avoid puzzling runtime crashes
  18. #ifdef __arm__
  19. # if defined __thumb__ && ! defined __THUMB_INTERWORK__
  20. # error "ARM trampolines need -mthumb-interwork to work in Thumb mode"
  21. # endif
  22. #endif
  23. #ifdef __cplusplus
  24. extern "C" {
  25. #endif
  26. #define CHECK(cond, fmt, ...) do { \
  27. if(!(cond)) { \
  28. fprintf(stderr, "implib-gen: $load_name: " fmt "\n", ##__VA_ARGS__); \
  29. assert(0 && "Assertion in generated code"); \
  30. abort(); \
  31. } \
  32. } while(0)
  33. #define HAS_DLOPEN_CALLBACK $has_dlopen_callback
  34. #define HAS_DLSYM_CALLBACK $has_dlsym_callback
  35. #define NO_DLOPEN $no_dlopen
  36. #define LAZY_LOAD $lazy_load
  37. static void *lib_handle;
  38. static int do_dlclose;
  39. static int is_lib_loading;
  40. #if ! NO_DLOPEN
  41. static void *load_library() {
  42. if(lib_handle)
  43. return lib_handle;
  44. is_lib_loading = 1;
  45. #if HAS_DLOPEN_CALLBACK
  46. extern void *$dlopen_callback(const char *lib_name);
  47. lib_handle = $dlopen_callback("$load_name");
  48. CHECK(lib_handle, "failed to load library '$load_name' via callback '$dlopen_callback'");
  49. #else
  50. lib_handle = dlopen("$load_name", RTLD_LAZY | RTLD_GLOBAL);
  51. CHECK(lib_handle, "failed to load library '$load_name' via dlopen: %s", dlerror());
  52. #endif
  53. do_dlclose = 1;
  54. is_lib_loading = 0;
  55. return lib_handle;
  56. }
  57. static void __attribute__((destructor)) unload_lib() {
  58. if(do_dlclose && lib_handle)
  59. dlclose(lib_handle);
  60. }
  61. #endif
  62. #if ! NO_DLOPEN && ! LAZY_LOAD
  63. static void __attribute__((constructor)) load_lib() {
  64. load_library();
  65. }
  66. #endif
  67. // TODO: convert to single 0-separated string
  68. static const char *const sym_names[] = {
  69. $sym_names
  70. 0
  71. };
  72. #define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1)
  73. extern void *_${lib_suffix}_tramp_table[];
  74. // Can be sped up by manually parsing library symtab...
  75. void _${lib_suffix}_tramp_resolve(int i) {
  76. assert((unsigned)i < SYM_COUNT);
  77. CHECK(!is_lib_loading, "library function '%s' called during library load", sym_names[i]);
  78. void *h = 0;
  79. #if NO_DLOPEN
  80. // Library with implementations must have already been loaded.
  81. if (lib_handle) {
  82. // User has specified loaded library
  83. h = lib_handle;
  84. } else {
  85. // User hasn't provided us the loaded library so search the global namespace.
  86. # ifndef IMPLIB_EXPORT_SHIMS
  87. // If shim symbols are hidden we should search
  88. // for first available definition of symbol in library list
  89. h = RTLD_DEFAULT;
  90. # else
  91. // Otherwise look for next available definition
  92. h = RTLD_NEXT;
  93. # endif
  94. }
  95. #else
  96. h = load_library();
  97. CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]);
  98. #endif
  99. #if HAS_DLSYM_CALLBACK
  100. extern void *$dlsym_callback(void *handle, const char *sym_name);
  101. _${lib_suffix}_tramp_table[i] = $dlsym_callback(h, sym_names[i]);
  102. CHECK(_${lib_suffix}_tramp_table[i], "failed to resolve symbol '%s' via callback $dlsym_callback", sym_names[i]);
  103. #else
  104. // Dlsym is thread-safe so don't need to protect it.
  105. _${lib_suffix}_tramp_table[i] = dlsym(h, sym_names[i]);
  106. CHECK(_${lib_suffix}_tramp_table[i], "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror());
  107. #endif
  108. }
  109. // Helper for user to resolve all symbols
  110. void _${lib_suffix}_tramp_resolve_all(void) {
  111. size_t i;
  112. for(i = 0; i < SYM_COUNT; ++i)
  113. _${lib_suffix}_tramp_resolve(i);
  114. }
  115. // Allows user to specify manually loaded implementation library.
  116. void _${lib_suffix}_tramp_set_handle(void *handle) {
  117. lib_handle = handle;
  118. do_dlclose = 0;
  119. }
  120. // Resets all resolved symbols. This is needed in case
  121. // client code wants to reload interposed library multiple times.
  122. void _${lib_suffix}_tramp_reset(void) {
  123. memset(_${lib_suffix}_tramp_table, 0, SYM_COUNT * sizeof(_${lib_suffix}_tramp_table[0]));
  124. lib_handle = 0;
  125. do_dlclose = 0;
  126. }
  127. #ifdef __cplusplus
  128. } // extern "C"
  129. #endif