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