1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the ability to access symbols from shared
## 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.}
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()
unloadLib(lib)
import std/strutils
type
LibHandle* = pointer ## A handle to a dynamically loaded library.
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 {.gcsafe.}
## Gets the handle from the current executable. Returns nil if the
## library could not be loaded.
proc unloadLib*(lib: LibHandle) {.gcsafe.}
## Unloads the library `lib`.
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
## 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
## `LibraryError` if the symbol could not be found.
result = symAddr(lib, name)
if result == nil: raiseInvalidLibrary(name)
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.
# The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
# NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
# as an emulation layer on top of native functions.
# =========================================================================
#
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):
#
# =======================================================================
# Native Windows Implementation
# =======================================================================
#
type
HMODULE {.importc: "HMODULE".} = pointer
FARPROC {.importc: "FARPROC".} = pointer
proc FreeLibrary(lib: HMODULE) {.importc, header: "<windows.h>", stdcall.}
proc winLoadLibrary(path: cstring): HMODULE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
proc getProcAddress(lib: HMODULE, name: cstring): FARPROC {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
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[HMODULE](lib))
proc symAddr(lib: LibHandle, name: cstring): pointer =
result = cast[pointer](getProcAddress(cast[HMODULE](lib), name))
else:
{.error: "no implementation for dynlib".}
|