diff options
Diffstat (limited to 'lib/pure/dynlib.nim')
-rw-r--r-- | lib/pure/dynlib.nim | 189 |
1 files changed, 142 insertions, 47 deletions
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index c6794be67..a162fe37f 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -8,43 +8,95 @@ # ## This module implements the ability to access symbols from shared -## libraries. On POSIX this uses the ``dlsym`` mechanism, on -## Windows ``LoadLibrary``. +## libraries. On POSIX this uses the `dlsym` mechanism, on +## Windows `LoadLibrary`. +## +## Examples +## ======== +## +## Loading a simple C function +## --------------------------- +## +## The following example demonstrates loading a function called `greet` +## from a library that is determined at runtime based upon a language choice. +## If the library fails to load or the function `greet` is not found, +## it quits with a failure error code. +## +runnableExamples: + type + GreetFunction = proc (): cstring {.gcsafe, stdcall.} -type - LibHandle* = pointer ## a handle to a dynamically loaded library + proc loadGreet(lang: string) = + let lib = + case lang + of "french": + loadLib("french.dll") + else: + loadLib("english.dll") + assert lib != nil, "Error loading library" + + let greet = cast[GreetFunction](lib.symAddr("greet")) + assert greet != nil, "Error loading 'greet' function from library" + + echo greet() -{.deprecated: [TLibHandle: LibHandle].} + unloadLib(lib) + + +import std/strutils + +type + LibHandle* = pointer ## A handle to a dynamically loaded library. -proc loadLib*(path: string, global_symbols=false): LibHandle - ## loads a library from `path`. Returns nil if the library could not +proc loadLib*(path: string, globalSymbols = false): LibHandle {.gcsafe.} + ## Loads a library from `path`. Returns nil if the library could not ## be loaded. -proc loadLib*(): LibHandle - ## gets the handle from the current executable. Returns nil if the +proc loadLib*(): LibHandle {.gcsafe.} + ## Gets the handle from the current executable. Returns nil if the ## library could not be loaded. -proc unloadLib*(lib: LibHandle) - ## unloads the library `lib` +proc unloadLib*(lib: LibHandle) {.gcsafe.} + ## Unloads the library `lib`. proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} = - ## raises an `EInvalidLibrary` exception. - var e: ref LibraryError - new(e) - e.msg = "could not find symbol: " & $name - raise e - -proc symAddr*(lib: LibHandle, name: cstring): pointer - ## retrieves the address of a procedure/variable from `lib`. Returns nil + ## Raises a `LibraryError` exception. + raise newException(LibraryError, "could not find symbol: " & $name) + +proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.} + ## Retrieves the address of a procedure/variable from `lib`. Returns nil ## if the symbol could not be found. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer = - ## retrieves the address of a procedure/variable from `lib`. Raises - ## `EInvalidLibrary` if the symbol could not be found. + ## Retrieves the address of a procedure/variable from `lib`. Raises + ## `LibraryError` if the symbol could not be found. result = symAddr(lib, name) if result == nil: raiseInvalidLibrary(name) -when defined(posix): +proc libCandidates*(s: string, dest: var seq[string]) = + ## Given a library name pattern `s`, write possible library names to `dest`. + var le = strutils.find(s, '(') + var ri = strutils.find(s, ')', le+1) + if le >= 0 and ri > le: + var prefix = substr(s, 0, le - 1) + var suffix = substr(s, ri + 1) + for middle in split(substr(s, le + 1, ri - 1), '|'): + libCandidates(prefix & middle & suffix, dest) + else: + add(dest, s) + +proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle = + ## Loads a library with name matching `pattern`, similar to what the `dynlib` + ## pragma does. Returns nil if the library could not be loaded. + ## + ## .. warning:: this proc uses the GC and so cannot be used to load the GC. + var candidates = newSeq[string]() + libCandidates(pattern, candidates) + for c in candidates: + result = loadLib(c, globalSymbols) + if not result.isNil: break + +when defined(posix) and not defined(nintendoswitch): # # ========================================================================= # This is an implementation based on the dlfcn interface. @@ -53,24 +105,66 @@ when defined(posix): # as an emulation layer on top of native functions. # ========================================================================= # - var - RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int - RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int - - proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".} - proc dlopen(path: cstring, mode: int): LibHandle {. - importc, header: "<dlfcn.h>".} - proc dlsym(lib: LibHandle, name: cstring): pointer {. - importc, header: "<dlfcn.h>".} - - proc loadLib(path: string, global_symbols=false): LibHandle = - var flags = RTLD_NOW - if global_symbols: flags = flags or RTLD_GLOBAL - return dlopen(path, flags) - proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW) - proc unloadLib(lib: LibHandle) = dlclose(lib) - proc symAddr(lib: LibHandle, name: cstring): pointer = - return dlsym(lib, name) + import std/posix + + proc loadLib(path: string, globalSymbols = false): LibHandle = + let flags = + if globalSymbols: RTLD_NOW or RTLD_GLOBAL + else: RTLD_NOW + + dlopen(path, flags) + + proc loadLib(): LibHandle = dlopen(nil, RTLD_NOW) + proc unloadLib(lib: LibHandle) = discard dlclose(lib) + proc symAddr(lib: LibHandle, name: cstring): pointer = dlsym(lib, name) + +elif defined(nintendoswitch): + # + # ========================================================================= + # Nintendo switch DevkitPro sdk does not have these. Raise an error if called. + # ========================================================================= + # + + proc dlclose(lib: LibHandle) = + raise newException(OSError, "dlclose not implemented on Nintendo Switch!") + proc dlopen(path: cstring, mode: int): LibHandle = + raise newException(OSError, "dlopen not implemented on Nintendo Switch!") + proc dlsym(lib: LibHandle, name: cstring): pointer = + raise newException(OSError, "dlsym not implemented on Nintendo Switch!") + proc loadLib(path: string, global_symbols = false): LibHandle = + raise newException(OSError, "loadLib not implemented on Nintendo Switch!") + proc loadLib(): LibHandle = + raise newException(OSError, "loadLib not implemented on Nintendo Switch!") + proc unloadLib(lib: LibHandle) = + raise newException(OSError, "unloadLib not implemented on Nintendo Switch!") + proc symAddr(lib: LibHandle, name: cstring): pointer = + raise newException(OSError, "symAddr not implemented on Nintendo Switch!") + +elif defined(genode): + # + # ========================================================================= + # Not implemented for Genode without POSIX. Raise an error if called. + # ========================================================================= + # + + template raiseErr(prc: string) = + raise newException(OSError, prc & " not implemented, compile with POSIX support") + + proc dlclose(lib: LibHandle) = + raiseErr(OSError, "dlclose") + proc dlopen(path: cstring, mode: int): LibHandle = + raiseErr(OSError, "dlopen") + proc dlsym(lib: LibHandle, name: cstring): pointer = + raiseErr(OSError, "dlsym") + proc loadLib(path: string, global_symbols = false): LibHandle = + raiseErr(OSError, "loadLib") + proc loadLib(): LibHandle = + raiseErr(OSError, "loadLib") + proc unloadLib(lib: LibHandle) = + raiseErr(OSError, "unloadLib") + proc symAddr(lib: LibHandle, name: cstring): pointer = + raiseErr(OSError, "symAddr") + elif defined(windows) or defined(dos): # @@ -79,22 +173,23 @@ elif defined(windows) or defined(dos): # ======================================================================= # type - THINSTANCE {.importc: "HINSTANCE".} = pointer + HMODULE {.importc: "HMODULE".} = pointer + FARPROC {.importc: "FARPROC".} = pointer - proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.} - proc winLoadLibrary(path: cstring): THINSTANCE {. + proc FreeLibrary(lib: HMODULE) {.importc, header: "<windows.h>", stdcall.} + proc winLoadLibrary(path: cstring): HMODULE {. importc: "LoadLibraryA", header: "<windows.h>", stdcall.} - proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. + proc getProcAddress(lib: HMODULE, name: cstring): FARPROC {. importc: "GetProcAddress", header: "<windows.h>", stdcall.} - proc loadLib(path: string, global_symbols=false): LibHandle = + proc loadLib(path: string, globalSymbols = false): LibHandle = result = cast[LibHandle](winLoadLibrary(path)) proc loadLib(): LibHandle = result = cast[LibHandle](winLoadLibrary(nil)) - proc unloadLib(lib: LibHandle) = FreeLibrary(cast[THINSTANCE](lib)) + proc unloadLib(lib: LibHandle) = FreeLibrary(cast[HMODULE](lib)) proc symAddr(lib: LibHandle, name: cstring): pointer = - result = getProcAddress(cast[THINSTANCE](lib), name) + result = cast[pointer](getProcAddress(cast[HMODULE](lib), name)) else: {.error: "no implementation for dynlib".} |