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.
[1]
glibc linker namespace
[2]
rust musl cydlib