dynlib.nim 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. ## .. code-block::nim
  25. ##
  26. ## import dynlib
  27. ##
  28. ## type
  29. ## greetFunction = proc(): cstring {.gcsafe, stdcall.}
  30. ##
  31. ## let lang = stdin.readLine()
  32. ##
  33. ## let lib = case lang
  34. ## of "french":
  35. ## loadLib("french.dll")
  36. ## else:
  37. ## loadLib("english.dll")
  38. ##
  39. ## if lib == nil:
  40. ## echo "Error loading library"
  41. ## quit(QuitFailure)
  42. ##
  43. ## let greet = cast[greetFunction](lib.symAddr("greet"))
  44. ##
  45. ## if greet == nil:
  46. ## echo "Error loading 'greet' function from library"
  47. ## quit(QuitFailure)
  48. ##
  49. ## let greeting = greet()
  50. ##
  51. ## echo greeting
  52. ##
  53. ## unloadLib(lib)
  54. ##
  55. import strutils
  56. type
  57. LibHandle* = pointer ## a handle to a dynamically loaded library
  58. proc loadLib*(path: string, global_symbols=false): LibHandle {.gcsafe.}
  59. ## loads a library from `path`. Returns nil if the library could not
  60. ## be loaded.
  61. proc loadLib*(): LibHandle {.gcsafe.}
  62. ## gets the handle from the current executable. Returns nil if the
  63. ## library could not be loaded.
  64. proc unloadLib*(lib: LibHandle) {.gcsafe.}
  65. ## unloads the library `lib`
  66. proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
  67. ## raises an `EInvalidLibrary` exception.
  68. var e: ref LibraryError
  69. new(e)
  70. e.msg = "could not find symbol: " & $name
  71. raise e
  72. proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.}
  73. ## retrieves the address of a procedure/variable from `lib`. Returns nil
  74. ## if the symbol could not be found.
  75. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer =
  76. ## retrieves the address of a procedure/variable from `lib`. Raises
  77. ## `EInvalidLibrary` if the symbol could not be found.
  78. result = symAddr(lib, name)
  79. if result == nil: raiseInvalidLibrary(name)
  80. proc libCandidates*(s: string, dest: var seq[string]) =
  81. ## given a library name pattern `s` write possible library names to `dest`.
  82. var le = strutils.find(s, '(')
  83. var ri = strutils.find(s, ')', le+1)
  84. if le >= 0 and ri > le:
  85. var prefix = substr(s, 0, le - 1)
  86. var suffix = substr(s, ri + 1)
  87. for middle in split(substr(s, le + 1, ri - 1), '|'):
  88. libCandidates(prefix & middle & suffix, dest)
  89. else:
  90. add(dest, s)
  91. proc loadLibPattern*(pattern: string, global_symbols=false): LibHandle =
  92. ## loads a library with name matching `pattern`, similar to what `dlimport`
  93. ## pragma does. Returns nil if the library could not be loaded.
  94. ## Warning: this proc uses the GC and so cannot be used to load the GC.
  95. var candidates = newSeq[string]()
  96. libCandidates(pattern, candidates)
  97. for c in candidates:
  98. result = loadLib(c, global_symbols)
  99. if not result.isNil: break
  100. when defined(posix):
  101. #
  102. # =========================================================================
  103. # This is an implementation based on the dlfcn interface.
  104. # The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
  105. # NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
  106. # as an emulation layer on top of native functions.
  107. # =========================================================================
  108. #
  109. var
  110. RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
  111. RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
  112. proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
  113. proc dlopen(path: cstring, mode: int): LibHandle {.
  114. importc, header: "<dlfcn.h>".}
  115. proc dlsym(lib: LibHandle, name: cstring): pointer {.
  116. importc, header: "<dlfcn.h>".}
  117. proc loadLib(path: string, global_symbols=false): LibHandle =
  118. var flags = RTLD_NOW
  119. if global_symbols: flags = flags or RTLD_GLOBAL
  120. return dlopen(path, flags)
  121. proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW)
  122. proc unloadLib(lib: LibHandle) = dlclose(lib)
  123. proc symAddr(lib: LibHandle, name: cstring): pointer =
  124. return dlsym(lib, name)
  125. elif defined(nintendoswitch):
  126. #
  127. # =========================================================================
  128. # Nintendo switch DevkitPro sdk does not have these. Raise an error if called.
  129. # =========================================================================
  130. #
  131. proc dlclose(lib: LibHandle) =
  132. raise newException(OSError, "dlclose not implemented on Nintendo Switch!")
  133. proc dlopen(path: cstring, mode: int): LibHandle =
  134. raise newException(OSError, "dlopen not implemented on Nintendo Switch!")
  135. proc dlsym(lib: LibHandle, name: cstring): pointer =
  136. raise newException(OSError, "dlsym not implemented on Nintendo Switch!")
  137. proc loadLib(path: string, global_symbols=false): LibHandle =
  138. raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
  139. proc loadLib(): LibHandle =
  140. raise newException(OSError, "loadLib not implemented on Nintendo Switch!")
  141. proc unloadLib(lib: LibHandle) =
  142. raise newException(OSError, "unloadLib not implemented on Nintendo Switch!")
  143. proc symAddr(lib: LibHandle, name: cstring): pointer =
  144. raise newException(OSError, "symAddr not implemented on Nintendo Switch!")
  145. elif defined(windows) or defined(dos):
  146. #
  147. # =======================================================================
  148. # Native Windows Implementation
  149. # =======================================================================
  150. #
  151. when defined(cpp):
  152. type
  153. THINSTANCE {.importc: "HINSTANCE".} = object
  154. x: pointer
  155. else:
  156. type
  157. THINSTANCE {.importc: "HINSTANCE".} = pointer
  158. proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
  159. proc winLoadLibrary(path: cstring): THINSTANCE {.
  160. importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
  161. proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
  162. importc: "GetProcAddress", header: "<windows.h>", stdcall.}
  163. proc loadLib(path: string, global_symbols=false): LibHandle =
  164. result = cast[LibHandle](winLoadLibrary(path))
  165. proc loadLib(): LibHandle =
  166. result = cast[LibHandle](winLoadLibrary(nil))
  167. proc unloadLib(lib: LibHandle) = FreeLibrary(cast[THINSTANCE](lib))
  168. proc symAddr(lib: LibHandle, name: cstring): pointer =
  169. result = getProcAddress(cast[THINSTANCE](lib), name)
  170. else:
  171. {.error: "no implementation for dynlib".}