slibtool: a skinny libtool implementation, written in C

January 4, 2020 ยท View on GitHub

slibtool is an independent reimplementation of the widely used libtool, written in C. slibtool is designed to be a clean, fast, easy-to-use libtool drop-in replacement, and is accordingly aimed at package authors, distro developers, and system integrators. slibtool maintains compatibility with libtool in nearly every aspect of the tool's functionality as well as semantics, leaving out (or turning into a no-op) only a small number of features that are no longer needed on modern systems.

Being a compiled binary, and although not primarily written for the sake of performance, building a package with slibtool is often faster than with its script-based counterpart. The resulting performance gain would normally vary between packages, and is most noticeable in builds that invoke libtool a large number of times, and which are characterized by the short compilation duration of individual translation units.

why reimplement libtool?

Midipix targets use the PE binary format, and so require import library integration on the one hand, yet follow ELF naming conventions (i.e. libfoo.so.x.y.z) on the other. In consequence, midipix shared libraries are not fully supported by existing libtool implementations. As an interim solution used for the porting of gcc, a libtool script generated by ltmain.sh was hand-edited so that it would correctly produce the host's shared libraries as part of the toolchain build process.

Bypassing the libtool script generated by a package's ltmain.sh requires overriding the LIBTOOL variable as part of invoking make(1). In that sense, and unless midipix support is added to every single ltmain.sh in every single package that uses autotools, it does not truly matter whether the surrogate libtool that one chooses be a script, a program written in C, or something else.

At the time of this writing, an average libtool script consists of about 10K lines of code with several hundred case-esac statements and a similar number of if-then blocks. In view of the length of the generated script, and in light of its challenging logic and overall complexity, a rewrite of the tool in C seemed to be the right choice not only with respect to quality, readability, and anticipated performance, but also in terms of development effort, ease of customization, and long-term maintenance.

Requirements for building slibtool

  • a C toolchain, consisting of
    • a C compiler such as gcc, clang, or cparser;
    • the compiler should support -std=c99;
    • the system's libc should support -D_XOPEN_SOURCE=700.

If building for (native) mingw development, please cross-build slibtool under your posix environment of choice (e.g. midipix, cygwin, msys2) and install it alongside the rest of your (cross-built) mingw toolchain.

If your system libc only supports -D_XOPEN_SOURCE=600 then you should be able to build slibtool after slightly tweaking the code and/or setting a few config-time environment variables (such as CFLAGS and LDFLAGS).

Usage

With most packages, simply adding LIBTOOL=slibtool to the make invocation should suffice. To have slibtool operate in debug mode (outputting a colorized raw argument vector), use LIBTOOL=dlibtool instead, or alternatively add --debug to the libtool flags inside of your Makefile.

Corner cases

Some programs which make heavy use of recursive make are known to drop the LIBTOOL value at some point in the process, and as such you may additionally need to export the environment variable; export MAKE="make LIBTOOL=slibtool".

slibtool additionally installs two symlinks that are the equivalent of -disable-shared and -disable-static, named slibtool-static and slibtool-shared, respectively. These symlinks should be used when building packages that have the above switches hard-coded into the generated libtool script; two examples of such packages are binutils and gdb, which both have shared library builds disabled by default. A quick way to determine whether this invocation form is appropriate with a specific package is to run ./libtool --features from the build directory, then check whether the output has either shared or static library builds disabled. When slibtool is invoked as a basic tool (i.e. slibtool, dlibtool, etc.), then building of both a shared library and a static archive is enabled by default.

Differences from GNU libtool

While slibtool aims to be compatible with all common usages of libtool at the build system level, there exist several differences at the implementation level that should be noted.

  • .la wrappers are always generated, but by default are never installed; .la wrappers contain key information that is provided to libtool when generating a shared library or static archive, and which is needed when passing the archive or library as an input argument in .la form to the compiler driver in subsequent link steps. Since slibtool is entirely independent of the above wrappers with respect to its own functionality, and given its announced goal to remain compatible with the script-based libtool as much as possible, slibtool's behavior is to always produce .la wrappers on the one hand, yet fully ignore their content on the other.

    Despite their internal nature, installed .la wrappers are often [ab]used in strange and mysterious ways by distro-related tools other than libtool itself. For the sake of distributions that depend on the aforementioned wrappers, slibtool comes with three special symlinks named clibtool, clibtool-shared, and clibtool-static, respectively. The 'c' in clibtool stands for compatible, and accordingly indicates an end-user's preference towards perfect emulation of legacy behavior.

  • -rpath argument values are passed to the compiler and linker as needed only; -rpath is often [mis]used by libtool to add redundant paths to a program's linker search path. When using slibtool, -rpath argument values are only forwarded to the compiler and linker when pointing to non-default linker directories, and are accordingly filtered out when pointing to default library locations (i.e. /usr/lib, /lib, and so on).

  • no-ops

    • -R, which adds a path to the generated program's run-time search path; this switch is currently not needed, and is accordingly ignored.
    • -export-symbols, which exports symbols listed in a given file, and -export-symbols-regex, which exports symbols matching a regex; both are unimplemented because similar functionality has long been provided by the compiler or toolchain; gcc has supported setting ELF visibility since v4.0, for instance, and PE import library support is provided by slibtool via the --output-def linker switch and a subsequent invocation of dlltool.
    • -no-install, which considers the executable wrapper to be optional; this switch is not needed on modern systems, and is accordingly ignored.
  • No libltdl. libltdl is nowadays no longer needed; it serves to provide a dlopen() functionality on systems which do not offer it, i.e. HP-UX and BeOS, however since neither HP-UX nor BeOS is currently supported by slibtool, there exists no urgent need to provide equivalent functionality.

Development

Major changes to slibtool should be discussed on #midipix prior to pursuing a considerable effort, and patches should be sent, gpg-signed, to the project maintainer; before hacking on slibtool, please take a moment of your time and read the CONTRIB document.

As you finalize your changes to the code, please consider building at least once with cparser as the compiler. cparser is excellent at catching logical errors and semantic flaws, and as of the time of this writing is capable of spotting bugs that go unnoticed by other major compilers. For a few examples of such hidden bugs, see commits 94d109f, 1142bf2, and 55c95a8.

License

slibtool is distributed under a permissive MIT license.