Nss-modules and libc separation

The idea of nss-modules and libc separation is quite straight-forward: we should make several dynamic libraries (nss_files, nss_dns, nss_compat, nss_nis) and move appropriate code from libc to them. Appropriate code is the functions which are specified as the sources during nsdispatch(3) calls.

Unfortunately the separation process was not as harmless as it seemed during the project start. Several issues had to be solved.

Issue 1. Common functionality.

Common functionality was the almost ubiquitous problem of all nss-modules. As all nsswitch sources for the particular database usually reside in 1 file (i.e. getpwent.c), the sources functions usually use some common routines (pw_scan, for example). To move such modules from the libc with minimal changes, common functions were moved to the internal libnssutil static library. NOTE: this library must be compiled with ${PICFLAG}, because it is used solely for linking with shared libraries - i.e. nsswitch modules. It provides some common routines and is used by all modules. As it contains quite general routines (like copy_htent() and copy_netent()), it can be possibly useful even if some new nss-module would be introduced.

Another case of common functionality was the implementation of “files” and “compat” nsswitch source for “services” database (it was written by me during SoC 2005, I’m afraid). It uses 1 files_servent() function for satisfying all requests for getservbyname(), getservbyport() and getservent() functions for both “files” and “compat” sources. The problem is that nss_compat and nss_files are now separated. This issues was initially solved by linking nss_compat with static nss_files.a library and using files_servent function from it (more about why we need static versions of nss-modules libraries – see below). But such approach led to the duplicated symbols problem in libc.a (when linking it with static verions of nsswitch modules) and to complex Makefiles. So, the code was duplicated as little as possible, but all linking tricks were eliminated from nss_compat and nss_files Makefiles'.

getipnodeby**() functions had a lot of common functionality issues. It turned out that the simples way to solve them is to implement getipnodeby**() functions not through nsdispatch() calls but through gethostby**() calls. Such modifications were made and tested for compatibility with current implementations.

Issue 2. Threading and private libc includes issues.

All nss-modules use thread specific storage (thread local storage) by using either NSS_TLS_HANDLING or NETDB_THREAD_ALLOC macros from nss_tls.h and netdb_private.h respectively. Both of these files are libc-internal. And they both require all pthread-related calls to be hidden with namespace.h/un-namespace.h includes. To allow nss-modules to be moved out of the libc with minimal changes, <pthread.h> and <pthread_np.h> includes are enclosed with “namespace.h” and “un-namespace.h” in their source code. Path to libc/include is added to the standard include path for each module to allow “nss_tls.h”, “netdb_private.h” and “reentrant.h” inclusion (the only pricate libc include file, that is used by the modules besides the includes, that were just named, is “res_config.h”).

The drawback of such decision is the dependency of the nss-modules code on the libc code. This dependency can be broken if all modules will use only NSS_TLS_HANDLING macro to handle thread local storage data – this will make netdb_private.h unneeded. The nss_tls.h can be modified not to use hidden versions of pthread calls and can be placed in the libnssutil folder (it would have to be left in libc – as it is used not only from nss-modules but also from the libc itself). res_config.h is currently used only by nss_files and nss_dns modules. It can also be placed into the libnssutil folder, as it is used only from nss-modules or it can even be placed into the nss_dns – if nss_files would be modified not to use it anymore.

Issue 3. Statically linked binaries.

Statically lined binaries can’t call dlopen(). But when all nss-modules are moved out from the libc, dlopen() is the only way to use them. To solve this issue, not only the dynamic versions of the nss-modules, but also their static versions, should be built. libc’s Makefile was modified to link statically built nss-modules in.

# Include nss-modules's sources so that statically linked apps can work
# normally
NSS_STATIC+= ${.OBJDIR}/../nss_files/libnss_files.a
NSS_STATIC+= ${.OBJDIR}/../nss_dns/libnss_dns.a
NSS_STATIC+= ${.OBJDIR}/../nss_compat/libnss_compat.a
.if ${MK_NIS} != "no"
NSS_STATIC+= ${.OBJDIR}/../nss_nis/libnss_nis.a
.endif
NSS_STATIC+= ${.OBJDIR}/../libnssutil/libnssutil.a

# NSS-modules should be linked into the libc.a
nss_static_modules.o:
        ${LD} -o ${.TARGET} -r --whole-archive ${NSS_STATIC}

# libc.so should have stubs instead of module-load
# functions
nss_stubs.So:
        ${CC} ${PICFLAG} -DPIC ${CFLAGS}\
        -c ${.CURDIR}/net/nss_stubs.c -o ${.TARGET}
        
        
.if ${MK_PROFILE} != "no"
nss_static_modules.po:
        ${LD} -o ${.TARGET} -r --whole-archive ${NSS_STATIC}
.endif
        
DPSRC=  nss_static_modules.c nss_stubs.c
STATICOBJS+= nss_static_modules.o
SOBJS+= nss_stubs.So
CLEANFILES+= nss_static_modules.o nss_stubs.So

nsdispatch.c has the nss_load_builtin_modules(), which loads the statically linked into the libc modules at program startup. In the shared libc.so each modules’ entry function is now replaced with a stub, which do nothing. In static libc.a each modules’ real entry functions are now used. wsdispatch.c was slightly modified to correctly distinguish real module entry functions from a stub.

Results

Result 1. Nss-modules separated.

All nss-modules were moved from the libc: nss_files, nss_compat, nss_dns, nss_nis, nss_icmp (experimental version). All these modules along with libnssutil reside in src/lib.

Result 2. "peforma-actual-lookups" support extended.

Caching daemon (cached) was extended to support “perform-actual-lookups” mode for all implemented nsswitch databases. It was impossible before because of the built-in-the-libc nss-modules, which sources functions were libc-internal.

Result 3. "shells" nsswitch implementation reworked.

“shells” nsswitch database implementation was rewritten to be more consistent with other nsswitch databases and usable by caching daemon.

MichaelBushkov/NsswitchModulesSeparation (last edited 2022-10-05T22:57:34+0000 by KubilayKocak)