dynlib.nim 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. ##
  13. ## Examples
  14. ## ========
  15. ##
  16. ## Loading a simple C function
  17. ## ---------------------------
  18. ##
  19. ## The following example demonstrates loading a function called `greet`
  20. ## from a library that is determined at runtime based upon a language choice.
  21. ## If the library fails to load or the function `greet` is not found,
  22. ## it quits with a failure error code.
  23. ##
  24. runnableExamples:
  25. type
  26. GreetFunction = proc (): cstring {.gcsafe, stdcall.}
  27. proc loadGreet(lang: string) =
  28. let lib =
  29. case lang
  30. of "french":
  31. loadLib("french.dll")
  32. else:
  33. loadLib("english.dll")
  34. assert lib != nil, "Error loading library"
  35. let greet = cast[GreetFunction](lib.symAddr("greet"))
  36. assert greet != nil, "Error loading 'greet' function from library"
  37. echo greet()
  38. unloadLib(lib)
  39. import std/strutils
  40. type
  41. LibHandle* = pointer ## A handle to a dynamically loaded library.
  42. proc loadLib*(path: string, globalSymbols = false): LibHandle {.gcsafe.}
  43. ## Loads a library from `path`. Returns nil if the library could not
  44. ## be loaded.
  45. proc loadLib*(): LibHandle {.gcsafe.}
  46. ## Gets the handle from the current executable. Returns nil if the
  47. ## library could not be loaded.
  48. proc unloadLib*(lib: LibHandle) {.gcsafe.}
  49. ## Unloads the library `lib`.
  50. proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
  51. ## Raises a `LibraryError` exception.
  52. raise newException(LibraryError, "could not find symbol: " & $name)
  53. proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.}
  54. ## Retrieves the address of a procedure/variable from `lib`. Returns nil
  55. ## if the symbol could not be found.
  56. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer =
  57. ## Retrieves the address of a procedure/variable from `lib`. Raises
  58. ## `LibraryError` if the symbol could not be found.
  59. result = symAddr(lib, name)
  60. if result == nil: raiseInvalidLibrary(name)
  61. proc libCandidates*(s: string, dest: var seq[string]) =
  62. ## Given a library name pattern `s`, write possible library names to `dest`.
  63. var le = strutils.find(s, '(')
  64. var ri = strutils.find(s, ')', le+1)
  65. if le >= 0 and ri > le:
  66. var prefix = substr(s, 0, le - 1)
  67. var suffix = substr(s, ri + 1)
  68. for middle in split(substr(s, le + 1, ri - 1), '|'):
  69. libCandidates(prefix & middle & suffix, dest)
  70. else:
  71. add(dest, s)
  72. proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle =
  73. ## Loads a library with name matching `pattern`, similar to what the `dynlib`
  74. ## pragma does. Returns nil if the library could not be loaded.
  75. ##
  76. ## .. warning:: this proc uses the GC and so cannot be used to load the GC.
  77. result = default(LibHandle)
  78. var candidates = newSeq[string]()
  79. libCandidates(pattern, candidates)
  80. for c in candidates:
  81. result = loadLib(c, globalSymbols)
  82. if not result.isNil: break
  83. when defined(posix) and not defined(nintendoswitch):
  84. #
  85. # =========================================================================
  86. # This is an implementation based on the dlfcn interface.
  87. # The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
  88. # NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
  89. # as an emulation layer on top of native functions.
  90. # =========================================================================
  91. #
  92. import std/posix
  93. proc loadLib(path: string, globalSymbols = false): LibHandle =
  94. let flags =
  95. if globalSymbols: RTLD_NOW or RTLD_GLOBAL
  96. else: RTLD_NOW
  97. dlopen(path, flags)
  98. proc loadLib(): LibHandle = dlopen(nil, RTLD_NOW)
  99. proc unloadLib(lib: LibHandle) = discard dlclose(lib)
  100. proc symAddr(lib: LibHandle, name: cstring): pointer = dlsym(lib, name)
  101. elif defined(nintendoswitch):
  102. #
  103. # =========================================================================
  104. # Nintendo switch DevkitPro sdk does not have these. Raise an error if called.
  105. # =========================================================================
  106. #
  107. proc dlclose(lib: LibHandle) =
  108. raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
  109. proc dlopen(path: cstring, mode: int): LibHandle =
  110. raise newException(OSError, "dlopen not implemented on Nintendo Switch!")
  111. proc dlsym(lib: LibHandle, name: cstring): pointer =
  112. raise newException(OSError, "dlsym not implemented on Nintendo Switch!")
  113. proc loadLib(path: string, global_symbols = false): LibHandle =
  114. raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
  115. proc loadLib(): LibHandle =
  116. raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
  117. proc unloadLib(lib: LibHandle) =
  118. raise newException(OSError, "unloadLib not implemented on Nintendo Switch!")
  119. proc symAddr(lib: LibHandle, name: cstring): pointer =
  120. raise newException(OSError, "symAddr not implemented on Nintendo Switch!")
  121. elif defined(genode):
  122. #
  123. # =========================================================================
  124. # Not implemented for Genode without POSIX. Raise an error if called.
  125. # =========================================================================
  126. #
  127. template raiseErr(prc: string) =
  128. raise newException(OSError, prc & " not implemented, compile with POSIX support")
  129. proc dlclose(lib: LibHandle) =
  130. raiseErr(OSError, "dlclose")
  131. proc dlopen(path: cstring, mode: int): LibHandle =
  132. raiseErr(OSError, "dlopen")
  133. proc dlsym(lib: LibHandle, name: cstring): pointer =
  134. raiseErr(OSError, "dlsym")
  135. proc loadLib(path: string, global_symbols = false): LibHandle =
  136. raiseErr(OSError, "loadLib")
  137. proc loadLib(): LibHandle =
  138. raiseErr(OSError, "loadLib")
  139. proc unloadLib(lib: LibHandle) =
  140. raiseErr(OSError, "unloadLib")
  141. proc symAddr(lib: LibHandle, name: cstring): pointer =
  142. raiseErr(OSError, "symAddr")
  143. elif defined(windows) or defined(dos):
  144. #
  145. # =======================================================================
  146. # Native Windows Implementation
  147. # =======================================================================
  148. #
  149. type
  150. HMODULE {.importc: "HMODULE".} = pointer
  151. FARPROC {.importc: "FARPROC".} = pointer
  152. proc FreeLibrary(lib: HMODULE) {.importc, header: "<windows.h>", stdcall.}
  153. proc winLoadLibrary(path: cstring): HMODULE {.
  154. importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
  155. proc getProcAddress(lib: HMODULE, name: cstring): FARPROC {.
  156. importc: "GetProcAddress", header: "<windows.h>", stdcall.}
  157. proc loadLib(path: string, globalSymbols = false): LibHandle =
  158. result = cast[LibHandle](winLoadLibrary(path))
  159. proc loadLib(): LibHandle =
  160. result = cast[LibHandle](winLoadLibrary(nil))
  161. proc unloadLib(lib: LibHandle) = FreeLibrary(cast[HMODULE](lib))
  162. proc symAddr(lib: LibHandle, name: cstring): pointer =
  163. result = cast[pointer](getProcAddress(cast[HMODULE](lib), name))
  164. else:
  165. {.error: "no implementation for dynlib".}