I'm having an issue I'm having hard time to trace.
I have a setup where a dynamically loaded library opens a sqlite database and it segfaults.
import dynlib, os
let lib = loadlib("./libbar.so")
if lib.isNil:
raise newException(LibraryError, "could not load libbar.so")
let runBar = cast[proc () {.nimcall.}](lib.checkedSymAddr("runBar"))
runBar()
import db_connector/db_sqlite
proc NimMain() {.cdecl, importc.}
proc library_init() {.exportc, dynlib, cdecl.} =
NimMain() # is this done correctly?
proc library_deinit() {.exportc, dynlib, cdecl.} =
GC_FullCollect()
proc runBar() {.exportc,dynlib,nimcall.} =
let db = open("bar.db", "", "", "") # remove this line for no segfault
echo "opened."
$ nim c --d:useMalloc foo.nim
$ nim c --noMain --app:lib --d:useMalloc -o:libbar.so bar.nim
$ ./foo
/path/to/foo.nim(9) foo
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault (core dumped)
It's not a db_connector issue, the crash happens within the sqlite3_open C function (same behavior with other Nim sqlite libs).
It's also not a C issue, doing the same thing with C code works fine.
// foo.c
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char *argv[]) {
char *lib = "libbar.so"
void *lib_ptr = dlopen(lib, RTLD_LAZY);
if (!lib_ptr) {
fprintf(stderr, "error loading lib %s (%s)\n", lib, dlerror());
return 1;
}
void (*func_ptr)() = dlsym(lib_ptr, "runBar");
if (!func_ptr) {
fprintf(stderr, "error loading func() (%s)\n", dlerror());
return 1;
}
func_ptr();
}
// bar.c
#include <stdio.h>
#include <sqlite3.h>
void runBar() {
sqlite3 *db;
sqlite3_open("bar.db", &db);
printf("opened.\n");
}
$ gcc foo.c -o foo
$ gcc -c bar.c FPIC
$ gcc bar.o -shared -lsqlite3 -o libbar.so
$ gcc foo.o -shared -lsqlite3 -o libfoo.so
$ ./foo
opened.
I suspect it has something to do with how I'm bootstrapping Nim in the dynlib bar.nim but I'm at a bit of a loss on how to track down the problem.
Thank you! No symbols from gdb so far but while trying to fix that I noticed libbar.so can be loaded if omitting --noMain.
So that was it- the dynamic loader appears not to be calling library_init() on my system for some reason. This works:
import db_connector/db_sqlite
proc NimMain() {.importc, cdecl.}
proc library_init() {.exportc,dynlib,cdecl.} =
echo "LOADED" # this doesn't happen
proc runBar() {.exportc,dynlib,cdecl.} =
NimMain()
let db = open("foo.db","","","")
echo "loaded"
So for now I'll be using an additional init proc in bar.nim and calling it manually in foo.nim.
Of course it would be interesting to know if library_init() is generally considered reliable enough to be used and there's something going on, or if it just normal that it doesn't work on my system but might work on others.