dynlib.nim 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the ability to access symbols from shared
  10. ## libraries. On POSIX this uses the ``dlsym`` mechanism, on
  11. ## Windows ``LoadLibrary``.
  12. import strutils
  13. type
  14. LibHandle* = pointer ## a handle to a dynamically loaded library
  15. {.deprecated: [TLibHandle: LibHandle].}
  16. proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.}
  17. ## loads a library from `path`. Returns nil if the library could not
  18. ## be loaded.
  19. proc loadLib*(): LibHandle {.gcsafe.}
  20. ## gets the handle from the current executable. Returns nil if the
  21. ## library could not be loaded.
  22. proc unloadLib*(lib: LibHandle) {.gcsafe.}
  23. ## unloads the library `lib`
  24. proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
  25. ## raises an `EInvalidLibrary` exception.
  26. var e: ref LibraryError
  27. new(e)
  28. e.msg = "could not find symbol: " & $name
  29. raise e
  30. proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.}
  31. ## retrieves the address of a procedure/variable from `lib`. Returns nil
  32. ## if the symbol could not be found.
  33. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer =
  34. ## retrieves the address of a procedure/variable from `lib`. Raises
  35. ## `EInvalidLibrary` if the symbol could not be found.
  36. result = symAddr(lib, name)
  37. if result == nil: raiseInvalidLibrary(name)
  38. proc libCandidates*(s: string, dest: var seq[string]) =
  39. ## given a library name pattern `s` write possible library names to `dest`.
  40. var le = strutils.find(s, '(')
  41. var ri = strutils.find(s, ')', le+1)
  42. if le >= 0 and ri > le:
  43. var prefix = substr(s, 0, le - 1)
  44. var suffix = substr(s, ri + 1)
  45. for middle in split(substr(s, le + 1, ri - 1), '|'):
  46. libCandidates(prefix & middle & suffix, dest)
  47. else:
  48. add(dest, s)
  49. proc loadLibPattern*(pattern: string, global_symbols=false): LibHandle =
  50. ## loads a library with name matching `pattern`, similar to what `dlimport`
  51. ## pragma does. Returns nil if the library could not be loaded.
  52. ## Warning: this proc uses the GC and so cannot be used to load the GC.
  53. var candidates = newSeq[string]()
  54. libCandidates(pattern, candidates)
  55. for c in candidates:
  56. result = loadLib(c, global_symbols)
  57. if not result.isNil: break
  58. when defined(posix):
  59. #
  60. # =========================================================================
  61. # This is an implementation based on the dlfcn interface.
  62. # The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
  63. # NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
  64. # as an emulation layer on top of native functions.
  65. # =========================================================================
  66. #
  67. var
  68. RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
  69. RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
  70. proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
  71. proc dlopen(path: cstring, mode: int): LibHandle {.
  72. importc, header: "<dlfcn.h>".}
  73. proc dlsym(lib: LibHandle, name: cstring): pointer {.
  74. importc, header: "<dlfcn.h>".}
  75. proc loadLib(path: string, global_symbols=false): LibHandle =
  76. var flags = RTLD_NOW
  77. if global_symbols: flags = flags or RTLD_GLOBAL
  78. return dlopen(path, flags)
  79. proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW)
  80. proc unloadLib(lib: LibHandle) = dlclose(lib)
  81. proc symAddr(lib: LibHandle, name: cstring): pointer =
  82. return dlsym(lib, name)
  83. elif defined(windows) or defined(dos):
  84. #
  85. # =======================================================================
  86. # Native Windows Implementation
  87. # =======================================================================
  88. #
  89. when defined(cpp):
  90. type
  91. THINSTANCE {.importc: "HINSTANCE".} = object
  92. x: pointer
  93. else:
  94. type
  95. THINSTANCE {.importc: "HINSTANCE".} = pointer
  96. proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
  97. proc winLoadLibrary(path: cstring): THINSTANCE {.
  98. importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
  99. proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
  100. importc: "GetProcAddress", header: "<windows.h>", stdcall.}
  101. proc loadLib(path: string, global_symbols=false): LibHandle =
  102. result = cast[LibHandle](winLoadLibrary(path))
  103. proc loadLib(): LibHandle =
  104. result = cast[LibHandle](winLoadLibrary(nil))
  105. proc unloadLib(lib: LibHandle) = FreeLibrary(cast[THINSTANCE](lib))
  106. proc symAddr(lib: LibHandle, name: cstring): pointer =
  107. result = getProcAddress(cast[THINSTANCE](lib), name)
  108. else:
  109. {.error: "no implementation for dynlib".}