1

In my code, I have a for loop where first I am calling dlopen to load a shared object, then calling a function of the loaded library, and then dlclose to unload it. The first iteration of the loop works as expected but during the second iteration (when i=1) dlopen call is causing segmentation fault (core dumped).

    void *handle;
    char* (*goImg)();
    char *error;
    int i;
    for (i=0; i<5; i++) {
        handle = dlopen("mylib.so", RTLD_LAZY);
        if (!handle) {
            fputs (dlerror(), stderr);
            exit(1);
        }

       goImg = dlsym(handle, "writeToFileInWrapper");
       if ((error = dlerror()) != NULL)  {
           fputs(error, stderr);
           exit(1); }
       goImg();

        if (!handle) {
            fputs (dlerror(), stderr);
            exit(1);
        }
       dlclose(handle);
   }

Script to generate mylib.so:

echo "Building golang code to create an archive file."
go build -buildmode=c-archive -o bin/goHelper.a goHelper.go

echo "Building wrapperCodeInC to be consumed as a shared library."
gcc -c -fPIC -o bin/shared/wrapperCodeInC.o -I./bin -I./wrapper wrapper/wrapperCodeInC.c

gcc -s -shared -lpthread -Wl,-Bsymbolic -o bin/mylib.so -Wl,--whole-archive bin/goHelper.a -Wl,--no-whole-archive bin/shared/wrapperCodeInC.o

Here, goHelper.go has few functions written in go language and wrapperCodeInC.c has the wrapper functions to invoke those go functions.

In the first run of the loop dlopen(), goImg(), and dlclose() works as expected but then during the second run (i=1), dlopen is dumping core. Any idea what could be causing this?

Note: If I remove -Wl,-Bsymbolic from the build file, then I get an error similar to this issue: https://github.com/golang/go/issues/30822

If I add flag RTLD_NODELETE in dlopen call (dlopen("mylib.so", RTLD_LAZY | RTLD_NODELETE )), then all the iterations run fine but I am not sure if that is the right thing to do.

Aayush
  • 33
  • 4
  • Hi, welcome in SO. You can use programs `valgrind` and `gdb` to find the bugs in your program. – Lorinczy Zsigmond Nov 11 '21 at 05:36
  • Please provide [a minimal, complete and verifiable example](https://stackoverflow.com/help/mcve) – kiner_shah Nov 11 '21 at 05:38
  • You cannot dlclose a go archive. The runtime is still running, you just unmapped it’s memory – JimB Nov 11 '21 at 09:30
  • 1
    As for the last part, opening the library with `RTLD_NODELETE` means that calling `dlclose` does nothing. – JimB Nov 11 '21 at 14:06
  • The dlclose() might still free some resources, so using `RTLD_NODELETE` and `dlclose()` is the way to go. Note, however, that repeatedly loading the *same* shared library over and over may cause you to run out of *other* resources: it's wise to keep a cache of which ones you've loaded, and to simply *re-use* those. – torek Nov 11 '21 at 16:18

1 Answers1

1

dlclose doesn't work on Go shared libraries, and it's been an open issue since 2015. The root of the issue seems to be that Go offers no way to gracefully terminate any background threads that the runtime might have started, and which might still be running at the time you call dlclose.

Besides that limitation, dlclose is apparently not required to do much of anything anyway.

See also Using Go in C limitations.

Thus, it seems that you may as well call attention to the library's lack of "unloadability" by using RTLD_NODELETE.

If this is part of a more general "plug-in" system, where you could have libraries written in several different languages, then you might not want to apply RTLD_NODELETE to all of them. In that case, you could try using the -z nodelete linker option when creating the shared library. Then the code that calls dlopen doesn't need to be aware of the quirks of any particular .so file it loads. (There's code in Go to add that parameter automatically; I think it's used if you use go tool link to do your linking instead of running gcc directly.)

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467