I'm writing an application which I hope to split into dynamic modules so the user could switch between different implementations of parts of the system, or extend functionality. But I'm having some trouble, the GC really doesn't appear to like being called from dynamic libraries. I've got this loader:
import dynlib
import os
type
setupProc = proc() {.nimcall.}
var
dll: LibHandle
setup: setupProc
echo "Trying to load libraries from " & $(getAppDir() / "dynlibs")
for library in walkFiles(getAppDir() / "dynlibs/*"):
dll = loadLib(library, true)
if dll != nil:
echo "Loaded library " & library
let setupAddr = dll.symAddr("setup")
echo "Found setup"
if setupAddr != nil:
setup = cast[setupProc](setupAddr)
if setup != nil:
setup()
else:
echo "Wasn't able to load setup() from " & library
else:
echo "Unable to load lib from " & library
This basically just tries to load every dynamic library in the dynlibs folder.
Next I have my two plugins:
# lib1.nim
proc setup() {.exportc, cdecl.} =
echo "Hello from plugin 1"
proc setup() {.exportc, cdecl.} =
echo "Hello"
var test = @["This", "is", "the", "second", "library"]
echo test[0]
And of course to compile I've used these commands:
$ nim c --nimcache:nimcache_libnimrtl -o:./libnimrtl.so ~/.choosenim/toolchains/nim-0.18.0/lib/nimrtl.nim
$ nim c -d:useNimRtl loader.nim
$ nim c --app:lib -d:useNimRtl -o:dynlibs/liblib2.so lib2.nim
$ nim c --app:lib -d:useNimRtl -o:dynlibs/liblib1.so lib1.nim
However when I run it I only get:
$ LD_LIBRARY_PATH=`realpath ./` ./loader
Trying to load libraries from /home/peter/div/pimo/libloaderror/dynlibs
Loaded library with /home/peter/div/pimo/libloaderror/dynlibs/liblib1.so:
Found setup with:
Hello from plugin 1
Loaded library with /home/peter/div/pimo/libloaderror/dynlibs/liblib2.so:
Found setup with:
Hello
Traceback (most recent call last)
loader.nim(23) loader
lib2.nim(4) setup
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Deleting plugin liblib1.so the thing seems to work:
$ rm dynlibs/liblib1.so
$ LD_LIBRARY_PATH=`realpath ./` ./loader
Trying to load libraries from /home/peter/div/pimo/libloaderror/dynlibs
Loaded library with /home/peter/div/pimo/libloaderror/dynlibs/liblib2.so:
Found setup with:
Hello
This
Changing their load order also works:
$ mv dynlibs/liblib2.so dynlibs/liblib0.so
$ LD_LIBRARY_PATH=`realpath ./` ./loader
Trying to load libraries from /home/peter/div/pimo/libloaderror/dynlibs
Loaded library with /home/peter/div/pimo/libloaderror/dynlibs/liblib0.so:
Found setup with:
Hello
This
Loaded library with /home/peter/div/pimo/libloaderror/dynlibs/liblib1.so:
Found setup with:
Hello from plugin 1
I've also seen errors like this which appears to show a bit more of what is actually causing the error out:
$ LD_LIBRARY_PATH=`realpath ./` ./loader
Trying to load libraries from /tmp/libloaderror/dynlibs
Loaded library with /tmp/libloaderror/dynlibs/liblib2.so:
Found setup with:
Hello
Traceback (most recent call last)
loader.nim(23) loader
lib2.nim(3) setup
assign.nim(155) genericSeqAssign
assign.nim(118) genericAssign
assign.nim(73) genericAssignAux
gc.nim(289) unsureAsgnRef
gc.nim(218) decRef
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
All in all the behaviour of Nim when loading dynamic libraries seems to be a bit iffy at best. I think this issue might be related: https://github.com/nim-lang/Nim/issues/6886