I think the suggestion to replace dlopen()
using LD_PRELOAD
is only a partial solution -- you won't catch dependencies of a library loaded with dlopen()
that way.
In the end, I couldn't see any way of doing this without scraping the internal state of the dynamic linker itself. It found that there's a _rtld_global
symbol exported from ld.so
that has the information, but that you have to use private Glibc headers to interpret it.
The following is a Python snippet that will (assuming my reading of the Glibc sources is correct) print all the shared libraries in the global namespace in the order that they will be searched. Libraries loaded with RTLD_LOCAL
will not be printed.
The fact that it relies on implementation details of Glibc means this approach is fraught with peril, but for my testing/auditing purposes I think it'll do nicely.
import ctypes
# Abridged type declarations pillaged from Glibc. See:
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/generic/ldsodefs.h
# - https://sourceware.org/git/?p=glibc.git;a=blob;f=include/link.h
class link_map(ctypes.Structure):
_fields_ = [
("l_addr", ctypes.c_size_t),
("l_name", ctypes.c_char_p),
]
class r_scope_elem(ctypes.Structure):
_fields_ = [
("r_list", ctypes.POINTER(ctypes.POINTER(link_map))),
("r_nlist", ctypes.c_uint),
]
class rtld_global(ctypes.Structure):
_fields_ = [
("_ns_loaded", ctypes.POINTER(link_map)),
("_ns_nloaded", ctypes.c_uint),
("_ns_main_searchlist", ctypes.POINTER(r_scope_elem)),
]
_rtld_global = rtld_global.in_dll(ctypes.CDLL(None), "_rtld_global")
searchlist = _rtld_global._ns_main_searchlist[0]
print [searchlist.r_list[n][0].l_name for n in xrange(searchlist.r_nlist)]
On my CentOS 7 system, this prints:
['', '/lib64/libpython2.7.so.1.0', '/lib64/libpthread.so.0', '/lib64/libdl.so.2',
'/lib64/libutil.so.1', '/lib64/libm.so.6', '/lib64/libc.so.6',
'/lib64/ld-linux-x86-64.so.2']