Can't resolve problematic dlopen() call.

Started by
14 comments, last by the_edd 11 years, 8 months ago
I've been working mainly in Windows with VC11, but a few days ago switched to using MinGW (I had already been planning on switching, I just liked Visual Studio's plugins too much). Very quickly, I then installed GCC/G++ 4.7 on my Linux (Mint). Making a launcher executable for my library (This makes it easier for separation, or changing which library is launched without recompiling a large amount of code), I use Boost.Extension's shared_library (which simply uses LoadLibrary and dlopen (depending on the platform)) to load my dynamic/shared library (SFGame.so). This worked great on windows with both compilers, but on Linux, dlopen will fail however I use the path. Using the same path, Boost.Filesystem confirms (through the application) that the file can be accessed with the paths I'm using. My simple code: ("<" and ">" replaced with "^")
[source lang="cpp"]#include "iostream"
#include "functional"
#include "boost/extension/shared_library.hpp"
#include "boost/filesystem.hpp"

#if _WIN32 || _WIN64
# define EXT ".dll"
#elif __APPLE__
# define EXT ".dylib"
#elif __linux
# define EXT ".so"
#endif

int main( void )
{
using namespace boost::extensions;
using namespace boost::filesystem;

std::string libpath = "./SFGame.so";
path pfile( libpath );
if( exists( pfile ) )
{
std::cout ^^ "Game library: " ^^ complete( pfile ).generic_string() ^^ " exists\nSize: " ^^ file_size( pfile ) ^^ "\n";
}

shared_library lib( libpath );
if( !lib.open() )
{
std::cout ^^ "Failed to load " ^^ libpath ^^ "\n" ^^ dlerror() ^^ "\n";
return 0;
}

lib.get^void^( "Launch" )();

return 1;
}[/source]
This same code works perfectly on Windows, as I said. My output:
../../BinLinux//SFGame.so: cannot open shared object library
or:
SFGame.so: cannot open shared object library
depending on whether I use "./SFGame.so" (I assume because my Code::Blocks project file and main.cpp are in "Engine/Code/Game/" and SFGame.so is in "Engine/BinLinux/") or "SFGame.so". I can use the full path from "complete(pfile).generic_string()", which gives the same dir in the error as "./SFGame.so". Any ideas on how I could fix this from failing EVERY time? I even tried linking Launcher to Engine.so in the compiler (GNU GCC (well, g++) within Code::Blocks), but no dice.
Advertisement
Have you tried setting LD_LIBRARY_PATH to contain the directory containing the library (and removing the ./ in the library name)? The dynamic link interpreter (probably ld.so) has some rules about locating shared objects that are not the same as the rules the shell follows when launching executables.

If that fails, try using '[font=courier new,courier,monospace]strace -e trace=open[/font] [font=courier new,courier,monospace]appname[/font]' when running your launcher to see exactly the paths the loader is actually trying to open.

Stephen M. Webb
Professional Free Software Developer

I should have mentioned that, yes, I already tried setting LD_LIBRARY_PATH, and that didn't help either. It seems that at compile-time, the string passing to the dlopen gets set to that path local to the main.cpp. strace shows that it is trying to open that directory, not just displaying it, whereas filesystem is really opening the correct directory (which I already knew since it showed it). I don't know how to get it using the correct path, unless I moved my source to the folder where it should compile, but that seems like a horrible solution.
EDIT: Or it has something to do with the "Execution Working Dir" Option in Code::Blocks. I've tried setting that relatively and absolutely to BinLinux, but no change.
I accidentally downvoted your post, sorry.
Perhaps it's worth removing the IDE from the equation for the time being. One you've built the host program and the .so, does it work (with or without LD_LIBRARY_PATH modifications) if you run it from a command shell?

If so, it may be the case that your IDE is setting the working directory inappropriately before exec()ing the process.
Okay, it seems like that was correct, though it seems that I do have to use an absolute path to the file, it works when compiled via terminal using g++-4.7, not using LD_LIBRARY_PATH (though I guess that would allow me to use relative paths). Code::Blocks still has the problem with the execution directory empty. Any thoughts on how to fix this? I guess I could just compile my Launcher with the terminal when I need to, but I'd prefer to be able to compile with my IDE (especially since it works fine on Windows, I feel like there should be an easy way to resolve this). It also seems like the same problem occurs with two dynamic libraries linked at compile/link time (of course, through Code::Blocks) (I use my Launcher as an executable to run my Game.so, which uses Engine.so to run and control the other, lower-level libraries (boost, glfw, alut, etc), so dynamic linking is important (though I use static linking when possible)).

Code::Blocks still has the problem with the execution directory empty. Any thoughts on how to fix this?


What do you mean by an empty execution directory? Whether you compile the your code from within your IDE or use the same commands from a shell, you should end up with an identical executable (except for internal timestamps, etc).

What is probable is that Code::Blocks has an option to specify the working directory for processes when you launch/debug them through the IDE. This would not be an option that effects the resulting binary in any way. So, just checking: do you understand the concept of a working directory (aka current directory aka current working directory :))?
Oh, I mean, Code::Blocks has an option for "Execution working dir", and I tried it empty, but the same problem occurred. Running the binary result either through the IDE or from the terminal makes no difference, unless I compile it via terminal. That is why the option seemed suspicious to me. Through this, I should think I do understand "working directory" as it would be used for file paths within the executable. Despite these, I don't know how the problem persists even using an absolute directory to the file, but I feel it's something with libdl.so, and less to do with working directory, but I have no idea what to do about it.
I can pretty much guarantee that if there was such a glaring bug in dlopen() it would have been noticed and fixed some time in the last 20, 30, or 40 years. that should be a hint as to where the problem isn't.

Does strace show a fail when opening the shared object even when you use an absolute path? Are there additional symbols thatneed resolving in the dlopened object, and they're not getting resolved in the particular load set you're using from the IDE? Is there an architecture problem (you can not use 64-bit DSOs from a 32-bit app, and the reverse is usually problematic as well)?

Does it work properly if you set LD_PRELOAD to your DSO? Does setting LD_WARN spew warnings? Have you tried setting LD_DEBUG (hint: try LD_DEBUG=help first)?

Stephen M. Webb
Professional Free Software Developer

Now, using full strace (just "strace ./Launcher"), it seems that it detects the file correctly with an absolute path, but then it does a few things, and then it tries opening the file with some relative directory that wouldn't be correct anyways. This is the part of the output that applies:

brk(0) = 0x1310000
brk(0x1331000) = 0x1331000
open("/home/taylorsnead/Documents/ShadowFox/NewEngine/BinLinux/SFGame.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300!\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=27024, ...}) = 0
mmap(NULL, 2122168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8d2ab03000
mprotect(0x7f8d2ab09000, 2093056, PROT_NONE) = 0
mmap(0x7f8d2ad08000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x5000) = 0x7f8d2ad08000
close(3) = 0
open("../../BinLinux//SFEngine.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
munmap(0x7f8d2ab03000, 2122168) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8d2bcfe000
write(1, "Failed to load /home/taylorsnead"..., 82Failed to load /home/taylorsnead/Documents/ShadowFox/NewEngine/BinLinux/SFGame.so
) = 82
write(1, "../../BinLinux//SFEngine.so: can"..., 87../../BinLinux//SFEngine.so: cannot open shared object file: No such file or directory
) = 87

But, I don't know where it gets this new directory. Even using a relative directory, as with Boost.Filesystem, it sees the file, but dlopen (I guess) just converts it to a directory relative to the code/project files, which won't work with the executable. I'm using the same compiler for the libraries and executable, all (I guess, I don't specify to the compiler (G++)) 32-bit. Trying "LD_PRELOAD=SFGame.so /bin/ls" just says that SFGame.so can't be preloaded:
ERROR: ld.so: object 'SFGame.so' from LD_PRELOAD cannot be preloaded: ignored.
Using LD_PRELOAD with the path to Launcher instead of ls makes it run Launcher, which of course fails. Setting it without a path doesn't change the result of the strace of Launcher.

This topic is closed to new replies.

Advertisement