glibc provides a very powerful function dlmopen
, allows user to load dynamic shared objects (DSOs) into a new isolated namespace[1].
The application still loads the DSOs it depends (or DT_NEEDED
), as a result, they’re multiple copies of DSOs, loaded into the same address space.
However, because the abstraction provied by dlmopen
, they’re logically isolated. Which also means the symbol resolving in the new linker namspace
won’t across the namesapce boundry, maybe except symbols within ld-linux.so
. Same as dlopen
, the handle returned is a pointer to
struct link_map
, iterating over the link_map
shows ld-linux.so
has a NULL
load address without _DYNAMIC
section.
Why it useful? you might ask? There’re some cases we might need to inject our own DSO (using LD_PRELOAD
for example), at the same time discriminate
its dependencies (libc.so
, libpthread.so
, etc..). An alternative approach would be creating a free standing DSO; however it is a lot more
complicated and restricted. For instance, the free standing DSO cannot have anything related to pthreads
, nor thread local storage (TLS), because
it almost impossible to mantain binary compatibily with two different libc implementation, even the same libc with different versions. This could
be devastating for language like rust
, rust
libstd
embraces tls
heavily, even without any explicit threads, even worse, many of rust
types,
their drop
might also call pthread_destroy*
implicitly. I believe that could be the reason why x86_64-linux-unknown-musl
(has +static-crt
) cannot produce cdylib
(unless for the musl host toolchain) crate type[2].
It becomes more interesting if we combine dlmopen
with LD_PRELOAD
. LD_PRELOAD
itself has nothing to do with linker namespace, however,
we can make a small loader, just to be LD_PRELOAD
-ed, then in the very loader, call dlmopen
to actually load the DSO we’re interested (dlmopen
also loads all its dependencies)
into a new (linker) namespace; so it could be used as a better LD_PRELOAD
; though I’m not quite very whether or not this is the intended use-case
for dlmopen
; it is a quite useful trick, with our DSO and its dependencies loaded into different regions, we can do fancy stuff, such as create seccomp-bpf rules just for those regions,
while still using the same system shared libraries, without worrying clobber the DSOs loaded for the application.
[2] rust musl cydlib