DynamicLoader.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
  3. #if defined(_WIN32)
  4. # define NOMINMAX // hide min,max to not conflict with <limits>
  5. #endif
  6. #include "kwsysPrivate.h"
  7. #include KWSYS_HEADER(DynamicLoader.hxx)
  8. #include KWSYS_HEADER(Configure.hxx)
  9. #include KWSYS_HEADER(Encoding.hxx)
  10. // Work-around CMake dependency scanning limitation. This must
  11. // duplicate the above list of headers.
  12. #if 0
  13. # include "Configure.hxx.in"
  14. # include "DynamicLoader.hxx.in"
  15. #endif
  16. // This file actually contains several different implementations:
  17. // * NOOP for environments without dynamic libs
  18. // * HP machines which uses shl_load
  19. // * Mac OS X 10.2.x and earlier which uses NSLinkModule
  20. // * Windows which uses LoadLibrary
  21. // * BeOS / Haiku
  22. // * FreeMiNT for Atari
  23. // * Default implementation for *NIX systems (including Mac OS X 10.3 and
  24. // later) which use dlopen
  25. //
  26. // Each part of the ifdef contains a complete implementation for
  27. // the static methods of DynamicLoader.
  28. #define CHECK_OPEN_FLAGS(var, supported, ret) \
  29. do { \
  30. /* Check for unknown flags. */ \
  31. if ((var & AllOpenFlags) != var) { \
  32. return ret; \
  33. } \
  34. \
  35. /* Check for unsupported flags. */ \
  36. if ((var & (supported)) != var) { \
  37. return ret; \
  38. } \
  39. } while (0)
  40. namespace KWSYS_NAMESPACE {
  41. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  42. const std::string& libname)
  43. {
  44. return DynamicLoader::OpenLibrary(libname, 0);
  45. }
  46. }
  47. #if !KWSYS_SUPPORTS_SHARED_LIBS
  48. // Implementation for environments without dynamic libs
  49. # include <string.h> // for strerror()
  50. namespace KWSYS_NAMESPACE {
  51. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  52. const std::string& libname, int flags)
  53. {
  54. return 0;
  55. }
  56. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  57. {
  58. if (!lib) {
  59. return 0;
  60. }
  61. return 1;
  62. }
  63. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  64. DynamicLoader::LibraryHandle lib, const std::string& sym)
  65. {
  66. return 0;
  67. }
  68. const char* DynamicLoader::LastError()
  69. {
  70. return "General error";
  71. }
  72. } // namespace KWSYS_NAMESPACE
  73. #elif defined(__hpux)
  74. // Implementation for HPUX machines
  75. # include <dl.h>
  76. # include <errno.h>
  77. namespace KWSYS_NAMESPACE {
  78. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  79. const std::string& libname, int flags)
  80. {
  81. CHECK_OPEN_FLAGS(flags, 0, 0);
  82. return shl_load(libname.c_str(), BIND_DEFERRED | DYNAMIC_PATH, 0L);
  83. }
  84. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  85. {
  86. if (!lib) {
  87. return 0;
  88. }
  89. return !shl_unload(lib);
  90. }
  91. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  92. DynamicLoader::LibraryHandle lib, const std::string& sym)
  93. {
  94. void* addr;
  95. int status;
  96. /* TYPE_PROCEDURE Look for a function or procedure. (This used to be default)
  97. * TYPE_DATA Look for a symbol in the data segment (for example,
  98. * variables).
  99. * TYPE_UNDEFINED Look for any symbol.
  100. */
  101. status = shl_findsym(&lib, sym.c_str(), TYPE_UNDEFINED, &addr);
  102. void* result = (status < 0) ? (void*)0 : addr;
  103. // Hack to cast pointer-to-data to pointer-to-function.
  104. return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
  105. }
  106. const char* DynamicLoader::LastError()
  107. {
  108. // TODO: Need implementation with errno/strerror
  109. /* If successful, shl_findsym returns an integer (int) value zero. If
  110. * shl_findsym cannot find sym, it returns -1 and sets errno to zero.
  111. * If any other errors occur, shl_findsym returns -1 and sets errno to one
  112. * of these values (defined in <errno.h>):
  113. * ENOEXEC
  114. * A format error was detected in the specified library.
  115. * ENOSYM
  116. * A symbol on which sym depends could not be found.
  117. * EINVAL
  118. * The specified handle is invalid.
  119. */
  120. if (errno == ENOEXEC || errno == ENOSYM || errno == EINVAL) {
  121. return strerror(errno);
  122. }
  123. // else
  124. return 0;
  125. }
  126. } // namespace KWSYS_NAMESPACE
  127. #elif defined(__APPLE__) && (MAC_OS_X_VERSION_MAX_ALLOWED < 1030)
  128. // Implementation for Mac OS X 10.2.x and earlier
  129. # include <mach-o/dyld.h>
  130. # include <string.h> // for strlen
  131. namespace KWSYS_NAMESPACE {
  132. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  133. const std::string& libname, int flags)
  134. {
  135. CHECK_OPEN_FLAGS(flags, 0, 0);
  136. NSObjectFileImageReturnCode rc;
  137. NSObjectFileImage image = 0;
  138. rc = NSCreateObjectFileImageFromFile(libname.c_str(), &image);
  139. // rc == NSObjectFileImageInappropriateFile when trying to load a dylib file
  140. if (rc != NSObjectFileImageSuccess) {
  141. return 0;
  142. }
  143. NSModule handle = NSLinkModule(image, libname.c_str(),
  144. NSLINKMODULE_OPTION_BINDNOW |
  145. NSLINKMODULE_OPTION_RETURN_ON_ERROR);
  146. NSDestroyObjectFileImage(image);
  147. return handle;
  148. }
  149. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  150. {
  151. // NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED
  152. // With this option the memory for the module is not deallocated
  153. // allowing pointers into the module to still be valid.
  154. // You should use this option instead if your code experience some problems
  155. // reported against Panther 10.3.9 (fixed in Tiger 10.4.2 and up)
  156. bool success = NSUnLinkModule(lib, NSUNLINKMODULE_OPTION_NONE);
  157. return success;
  158. }
  159. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  160. DynamicLoader::LibraryHandle lib, const std::string& sym)
  161. {
  162. void* result = 0;
  163. // Need to prepend symbols with '_' on Apple-gcc compilers
  164. std::string rsym = '_' + sym;
  165. NSSymbol symbol = NSLookupSymbolInModule(lib, rsym.c_str());
  166. if (symbol) {
  167. result = NSAddressOfSymbol(symbol);
  168. }
  169. // Hack to cast pointer-to-data to pointer-to-function.
  170. return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
  171. }
  172. const char* DynamicLoader::LastError()
  173. {
  174. return 0;
  175. }
  176. } // namespace KWSYS_NAMESPACE
  177. #elif defined(_WIN32) && !defined(__CYGWIN__)
  178. // Implementation for Windows win32 code but not cygwin
  179. # include <windows.h>
  180. # include <stdio.h>
  181. namespace KWSYS_NAMESPACE {
  182. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  183. const std::string& libname, int flags)
  184. {
  185. CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, nullptr);
  186. DWORD llFlags = 0;
  187. if (flags & SearchBesideLibrary) {
  188. llFlags |= LOAD_WITH_ALTERED_SEARCH_PATH;
  189. }
  190. return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(),
  191. nullptr, llFlags);
  192. }
  193. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  194. {
  195. return (int)FreeLibrary(lib);
  196. }
  197. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  198. DynamicLoader::LibraryHandle lib, const std::string& sym)
  199. {
  200. // TODO: The calling convention affects the name of the symbol. We
  201. // should have a tool to help get the symbol with the desired
  202. // calling convention. Currently we assume cdecl.
  203. //
  204. // MSVC:
  205. // __cdecl = "func" (default)
  206. // __fastcall = "@_func@X"
  207. // __stdcall = "_func@X"
  208. //
  209. // Note that the "@X" part of the name above is the total size (in
  210. // bytes) of the arguments on the stack.
  211. void* result;
  212. const char* rsym = sym.c_str();
  213. result = (void*)GetProcAddress(lib, rsym);
  214. return *reinterpret_cast<DynamicLoader::SymbolPointer*>(&result);
  215. }
  216. # define DYNLOAD_ERROR_BUFFER_SIZE 1024
  217. const char* DynamicLoader::LastError()
  218. {
  219. wchar_t lpMsgBuf[DYNLOAD_ERROR_BUFFER_SIZE + 1];
  220. DWORD error = GetLastError();
  221. DWORD length = FormatMessageW(
  222. FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error,
  223. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  224. lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, nullptr);
  225. static char str[DYNLOAD_ERROR_BUFFER_SIZE + 1];
  226. if (length < 1) {
  227. /* FormatMessage failed. Use a default message. */
  228. _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE,
  229. "DynamicLoader encountered error 0x%X. "
  230. "FormatMessage failed with error 0x%X",
  231. error, GetLastError());
  232. return str;
  233. }
  234. if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, str,
  235. DYNLOAD_ERROR_BUFFER_SIZE, nullptr, nullptr)) {
  236. /* WideCharToMultiByte failed. Use a default message. */
  237. _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE,
  238. "DynamicLoader encountered error 0x%X. "
  239. "WideCharToMultiByte failed with error 0x%X",
  240. error, GetLastError());
  241. }
  242. return str;
  243. }
  244. } // namespace KWSYS_NAMESPACE
  245. #elif defined(__BEOS__)
  246. // Implementation for BeOS / Haiku
  247. # include <string.h> // for strerror()
  248. # include <be/kernel/image.h>
  249. # include <be/support/Errors.h>
  250. namespace KWSYS_NAMESPACE {
  251. static image_id last_dynamic_err = B_OK;
  252. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  253. const std::string& libname, int flags)
  254. {
  255. CHECK_OPEN_FLAGS(flags, 0, 0);
  256. // image_id's are integers, errors are negative. Add one just in case we
  257. // get a valid image_id of zero (is that even possible?).
  258. image_id rc = load_add_on(libname.c_str());
  259. if (rc < 0) {
  260. last_dynamic_err = rc;
  261. return 0;
  262. }
  263. return rc + 1;
  264. }
  265. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  266. {
  267. if (!lib) {
  268. last_dynamic_err = B_BAD_VALUE;
  269. return 0;
  270. } else {
  271. // The function dlclose() returns 0 on success, and non-zero on error.
  272. status_t rc = unload_add_on(lib - 1);
  273. if (rc != B_OK) {
  274. last_dynamic_err = rc;
  275. return 0;
  276. }
  277. }
  278. return 1;
  279. }
  280. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  281. DynamicLoader::LibraryHandle lib, const std::string& sym)
  282. {
  283. // Hack to cast pointer-to-data to pointer-to-function.
  284. union
  285. {
  286. void* pvoid;
  287. DynamicLoader::SymbolPointer psym;
  288. } result;
  289. result.psym = nullptr;
  290. if (!lib) {
  291. last_dynamic_err = B_BAD_VALUE;
  292. } else {
  293. // !!! FIXME: BeOS can do function-only lookups...does this ever
  294. // !!! FIXME: actually _want_ a data symbol lookup, or was this union
  295. // !!! FIXME: a leftover of dlsym()? (s/ANY/TEXT for functions only).
  296. status_t rc =
  297. get_image_symbol(lib - 1, sym.c_str(), B_SYMBOL_TYPE_ANY, &result.pvoid);
  298. if (rc != B_OK) {
  299. last_dynamic_err = rc;
  300. result.psym = nullptr;
  301. }
  302. }
  303. return result.psym;
  304. }
  305. const char* DynamicLoader::LastError()
  306. {
  307. const char* retval = strerror(last_dynamic_err);
  308. last_dynamic_err = B_OK;
  309. return retval;
  310. }
  311. } // namespace KWSYS_NAMESPACE
  312. #elif defined(__MINT__)
  313. // Implementation for FreeMiNT on Atari
  314. # define _GNU_SOURCE /* for program_invocation_name */
  315. # include <dld.h>
  316. # include <errno.h>
  317. # include <malloc.h>
  318. # include <string.h>
  319. namespace KWSYS_NAMESPACE {
  320. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  321. const std::string& libname, int flags)
  322. {
  323. CHECK_OPEN_FLAGS(flags, 0, nullptr);
  324. char* name = (char*)calloc(1, libname.size() + 1);
  325. dld_init(program_invocation_name);
  326. strncpy(name, libname.c_str(), libname.size());
  327. dld_link(libname.c_str());
  328. return (void*)name;
  329. }
  330. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  331. {
  332. dld_unlink_by_file((char*)lib, 0);
  333. free(lib);
  334. return 0;
  335. }
  336. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  337. DynamicLoader::LibraryHandle lib, const std::string& sym)
  338. {
  339. // Hack to cast pointer-to-data to pointer-to-function.
  340. union
  341. {
  342. void* pvoid;
  343. DynamicLoader::SymbolPointer psym;
  344. } result;
  345. result.pvoid = dld_get_symbol(sym.c_str());
  346. return result.psym;
  347. }
  348. const char* DynamicLoader::LastError()
  349. {
  350. return dld_strerror(dld_errno);
  351. }
  352. } // namespace KWSYS_NAMESPACE
  353. #else
  354. // Default implementation for *NIX systems (including Mac OS X 10.3 and
  355. // later) which use dlopen
  356. # include <dlfcn.h>
  357. namespace KWSYS_NAMESPACE {
  358. DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(
  359. const std::string& libname, int flags)
  360. {
  361. CHECK_OPEN_FLAGS(flags, 0, nullptr);
  362. return dlopen(libname.c_str(), RTLD_LAZY);
  363. }
  364. int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
  365. {
  366. if (lib) {
  367. // The function dlclose() returns 0 on success, and non-zero on error.
  368. return !dlclose(lib);
  369. }
  370. // else
  371. return 0;
  372. }
  373. DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
  374. DynamicLoader::LibraryHandle lib, const std::string& sym)
  375. {
  376. // Hack to cast pointer-to-data to pointer-to-function.
  377. union
  378. {
  379. void* pvoid;
  380. DynamicLoader::SymbolPointer psym;
  381. } result;
  382. result.pvoid = dlsym(lib, sym.c_str());
  383. return result.psym;
  384. }
  385. const char* DynamicLoader::LastError()
  386. {
  387. return dlerror();
  388. }
  389. } // namespace KWSYS_NAMESPACE
  390. #endif