I migrated this question from Stack Overflow; although there is a programming aspect to it, the more I've tried to understand what's going on, the more I think this may have more to do with RPATH
and $ORIGIN
, which I think are Unix/Linux-related. (My question also didn't get a lot of traction at Stack Overflow) I'll strip out as much of the programming-related aspects of the question as seems reasonable (e.g. the CMakeLists.txt), but I can reintroduce them if a responder says that they are relevant.
I've created an executable that loads a module library (or "shared library" or "plugin library" as it is variously called) from a subdirectory of where the executable is. The directory structure is:
build/
├── main # the executable
└── plugins
└── libmy-plugin.so
To keep the programming aspects of this question at arm's length, I won't show the full C code (unless someone says it does not make the question off-topic for this site). But it is effectively the "hello-world" equivalent of loading a module library, with one twist: it loads the plugin library from a relative path. Specifically, it loads plugins/libmy-plugin.so
. The bare minimum code snippet would be:
const char* plugin = "plugins/libmy-plugin.so";
void* handle = dlopen(plugin, RTLD_NOW);
The man-page for dlopen
says:
The function dlopen() loads the dynamic shared object (shared library) file named by the null-terminated string filename and returns an opaque "handle" for the loaded object...If filename is NULL, then the returned handle is for the main program. If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname.
The man-page for ld-linux.so
says:
$ORIGIN (or equivalently ${ORIGIN})
This expands to the directory containing the program or shared object. Thus, an application located in somedir/app could be compiled with
gcc -Wl,-rpath,'$ORIGIN/../lib'
so that it finds an associated shared object in somedir/lib no matter where somedir is located in the directory hierarchy. This facilitates the creation of "turn-key" applications that do not need to be installed into special directories, but can instead be unpacked into any directory and still find their own shared objects.
As far as I am aware how to check, my compiled binary does have its RPATH
set to $ORIGIN
:
$ objdump -x ./build/main | grep PATH
RUNPATH $ORIGIN
$
$ readelf -d ./build/main
Dynamic section at offset 0x2d98 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN]
...
By my reading, since $ORIGIN
"expands to the directory containing the program" and "[i]f filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname", this should mean that a program with its RPATH
set to $ORIGIN
that calls dlopen()
with a filename that includes a slash (and is a relative path), then the search path for that module library should be relative to the directory containing the program.
But this isn't what I observe. What I actually observe is that the executable only finds the module library when my CWD is the directory containing the executable. If I am in any other directory, and specify the full relative/absolute path to the executable, it does not find the module library (and seems unaffected by setting LD_LIBRARY_PATH
):
$ ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ LD_LIBRARY_PATH=./build ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ LD_LIBRARY_PATH=/home/user/dev/cmake-learn/rpath/build/ ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ cd build/
$ ./main
all good
$
I went back and forth in my head whether this was a programming question or a Unix/Linux question, and as I mentioned, I have a nonspecific feeling this is related to Unix/Linux module search paths, but if the community thinks this is really a programming question, I'll try my luck at Stack Overflow again.
(A commenter at my original question at Stack Overflow said the search path is always relative to the CWD, but it wasn't a fully explanatory answer, so I wasn't able to reconcile that claim with my reading of the noted man page sections)