A bit of background

The tipi and cmake-re binaries for Linux linked against libnsl.so.1 for several versions because of an incidental chain of dependencies. This requirement became an issue as widely used Linux distributions (Ubuntu 24.04 or CentOS) stopped shipping that legacy shared object which in turn prevented our command line tools from functionning as intended.

What is libnsl.so.1 one might ask? It is a legacy Network Services Library that implementes SunRPC and NIS (Yellow Pages) support. It enabled remote procedure calls and centralized user authentication in and against older Unix systems. Many legacy applications still depend on it for RPC-based communication.

Explanation of the problem

After a while of digging we came to the conclusion that libnsl was linked against by c-ares which in turn is a dependency of libcurl in our case.

In c-ares's build system the mechanism is quite simple:

check_library_exists (nsl gethostbyname "" HAVE_LIBNSL)
...
if (HAVE_LIBNSL)
  list (APPEND CARES_DEPENDENT_LIBS nsl)
endif ()
...
target_link_libraries (${PROJECT_NAME} PUBLIC ${CARES_DEPENDENT_LIBS})
...
list (APPEND CMAKE_REQUIRED_LIBRARIES ${CARES_DEPENDENT_LIBS})

Note how list (APPEND CMAKE_REQUIRED_LIBRARIES ${CARES_DEPENDENT_LIBS}) would pollute CMake's global state in the case of a FetchContent / add_subdirectory()-ed usage of c-ares. We built Hermetic FetchContent to avoid these transitive pollutions by dependencies.

Basically, the build system checks for the symbol gethostbyname in (lib)nsl and links the library if it can be found. The catch however is that none of our application code uses the facilities provided by libnsl but it rather is a consequence of the mere presence of libnsl on the system that is used to build our applications.

Sadly the c-ares build system doesn't offer us a non-intrusive option to disable the inclusion of nsl into the build.

Looking further up the dependency tree we see that c-ares can be substituted by another DNS resolver in libcurl to eliminate the dependency alltogether - in our case this has us rely on the libc's gethostbyname() instead which has been available approximatively forever by now (circa mid 2001 which is vintage all things considered).

Simply setting ENABLE_ARES to OFF in the libcurl build and removing c-ares from the build did the trick for us as libcurl will rely on the libc provided facilities by default:

check_library_exists("c" gethostbyname "" NOT_NEED_LIBNSL)
if(NOT NOT_NEED_LIBNSL)
  check_library_exists_concat("nsl"    gethostbyname  HAVE_LIBNSL)
endif()

Results

On the bottom line, the visible improvement is that our single Linux binary release is now running on even more Linux distributions (from the Ubuntu 16.04 vintage to today) with our almost fully statically linked binary.

In addition to the above our latest and greatest tipi & cmake-re release contains the following:

v0.0.67 - Dashing Duck πŸ¦†

Features
  • Windows on ARM with Visual Studio build support and CMake Visual Studio Toolset Arch argument specification
Bug Fixes
  • Fix unconditional test execution in cmake-re containerized and remote build
  • Removed dependency on libnsl.so on Linux to improve compatibility
  • Fix local containerized build failing on docker runtime search errors for some projects

Give it a go and tell us if you like it!

As per usual the release can be found here: tipi & cmake-re v0.0.67 at github.com/tipi-build/cli.


Author image
Author image

Lambour Luc and Yannic Staudt

@lambourl & @pysco68

software engineer at tipi & co-founder of tipi