How to get the absolute library file name corresponding to a relative path given to dlopen?
In my program I have code like the following
/* libname may be a relative path */
void loadLib(char const *libname) {
void *handle = dlopen(libname);
/* ... */
dlclose(handle);
}
Within /* .. */
, I need to read the memory map file /proc/self/maps
, to find the virtual memory address at which libname
is mapped to and I also need to open the library to find certain sections in it. For this, I need the absolute name that dlopen
found by searching in the various places (like, in the ldconfig
cache file). How can I receive that file name?
This is what I finally ended up with (yes, this is C++ code, nonetheless the C tag makes sense for this question because dlopen
is used with both C++ and C and my question is eligible for both and POSIX specifies it for C.).
boost::shared_ptr<void> dl;
if(void *handle = dlopen(libfile, RTLD_LAZY)) {
dl.reset(handle, &dlclose);
} else {
printdlerr();
return -1;
}
/* update sofile to be an absolute file name */
{
struct link_map *map;
dlinfo(dl.get(), RTLD_DI_LINKMAP, &map);
if(!map) {
return -1;
}
char *real = rea开发者_运维技巧lpath(map->l_name, NULL);
if(!real)
return -1;
sofile.reset(real, &free);
}
libfile
is the relative / plain filename. The map will yield a non-plain file name (i.e not foo.so
but may be ./foo.so
). Afterwards I used realpath
to get the final absolute path name. It works nicely!
you could use
... dlinfo(handle, RTLD_DI_LINKMAP, p)
p->l_name ...
where p is of type Link_map**
see man dlinfo for details
The only solution is to mimic the system's algorithm. This isn't as difficult as it sounds (although as always, the devil is in the details): I use the following to find the executable path:
std::string retval = our_argv0;
if ( !isAbsolute( retval ) )
{
char const* tmp = getenv( "PATH" );
if ( tmp == NULL )
throw std::runtime_error( "$PATH not set" );
std::vector<std::string> dirs( split( std::string( tmp ), ":" ) );
std::vector<std::string>::const_iterator i = dirs.begin();
while ( i != dirs.end()
&& ! access( (*i + '/' + retval).c_str(), X_OK ) == 0)
++ i;
if ( i == dirs.end() )
throw std::runtime_error("Cannot find load path");
retval = *i + '/' + retval;
}
return std::string(
retval.begin(),
std::find( retval.rbegin(), retval.rend(), '/' ).base() );
You should be able to adapt it for a library, using the name of the
library instead of argv[0]
, LD_LIBRARY_PATH
instead of PATH
, and
the appropriate default instead of throwing if it isn't set. There
are probably special cases which it doesn't handle, but the above works
for us to find the executable. (split
and isAbsolute
are other
functions in our library, which do the obvious things.)
One option I can think of is using function pathfind()
:
char *pathfind(const char *path, const char *name, const char *mode);
DL can be loaded from one of three locations: current directory, directory where the exec was located and LD_LIBRARY_PATH - you can check the last two - and use the pathfind
with the getenv("LD_LIBRARY_PATH")
for path
parameter to try search for another one.
精彩评论