file(GET_RUNTIME_DEPENDENCIES) should understand glibc-hwcaps on Linux/glibc
I was made aware that file(GET_RUNTIME_DEPENDENCIES)
is used for packaging software. It appears not to understand that certain file paths on glibc-based Linux contain additional, optimised libraries that are used by the dynamic linker at runtime if the CPU supports the necessary features. This feature was added to glibc 2.26 back in 2017. The CMake feature must be aware of this because:
- it MUST NOT deploy only the optimised library, without the baseline present
- it SHOULD deploy all the additional libraries if they exist
The way this feature works is that the library in question installs multiple files in subdirs of $LIBDIR
and the dynamic loader will find it. For example, for libpng16 on openSUSE Tumbleweed:
$ find /lib64/ -name libpng16.so.16 -ls
3822267 4 lrwxrwxrwx 1 root root 19 Jun 24 11:25 /lib64/glibc-hwcaps/x86-64-v3/libpng16.so.16 -> libpng16.so.16.40.0
3821671 4 lrwxrwxrwx 1 root root 19 Jun 24 11:22 /lib64/libpng16.so.16 -> libpng16.so.16.40.0
At runtime, the dynamic linker will choose one of the two based on internal heuristics. For an AVX2-enabled machine ("x86-64 version 3"), for an arbitrary application:
$ ldd app
linux-vdso.so.1 (0x00007ffcaed0f000)
libpng16.so.16 => /lib64/glibc-hwcaps/x86-64-v3/libpng16.so.16.40.0 (0x00007fa63734c000)
libc.so.6 => /lib64/libc.so.6 (0x00007fa637107000)
libz.so.1 => /lib64/libz.so.1 (0x00007fa6370eb000)
libm.so.6 => /lib64/libm.so.6 (0x00007fa637004000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa6373c0000)
But if this application were built with CMake and were being packaged by it with the libpng16 dependency, the deployment:
- MUST include
/lib64/libpng16.so.16
->/lib64/libpng16.so.16.40.0
- SHOULD include
/lib64/glibc-hwcaps/x86-64-v3/libpng16.so.16
->/lib64/glibc-hwcaps/x86-64-v3/libpng16.so.16.40.0
(Unless the user chooses not to support older-than-v3 systems, but that's a feature request)
The hwcaps feature is listed by ldconfig:
$ ldconfig -v -N -X
/usr/local/lib64: (from /etc/ld.so.conf:1)
/usr/local/lib: (from /etc/ld.so.conf:2)
/lib: (from <builtin>:0)
/lib64: (from <builtin>:0)
bunch of libraries
/lib64/glibc-hwcaps/x86-64-v2: (hwcap: "x86-64-v2") (from <builtin>:0)
/lib64/glibc-hwcaps/x86-64-v3: (hwcap: "x86-64-v3") (from <builtin>:0)
libpng16.so.16 -> libpng16.so.16.40.0
/lib64/glibc-hwcaps/x86-64-v4: (hwcap: "x86-64-v4") (from <builtin>:0)
But note the directory names have changed. When the feature was added in 2017, the subdirs were "haswell" and "haswell/avx512_1" and compatibility with the old names has since been removed. On Rocky Linux 8 (glibc 2.28):
# ldconfig -v -N -X 2> /dev/null
/lib: (from <builtin>:0)
/lib64: (from <builtin>:0)
bunch of libraries
/lib64/glibc-hwcaps/x86-64-v4: (hwcap: "x86-64-v4") (from <builtin>:0)
/lib64/glibc-hwcaps/x86-64-v3: (hwcap: "x86-64-v3") (from <builtin>:0)
/lib64/glibc-hwcaps/x86-64-v2: (hwcap: "x86-64-v2") (from <builtin>:0)
/lib/sse2: (hwcap: 0x0000000000000001) (from <builtin>:0)
/lib64/sse2: (hwcap: 0x0000000000000001) (from <builtin>:0)
/lib64/tls: (hwcap: 0x8000000000000000) (from <builtin>:0)
/lib64/haswell: (hwcap: 0x0004000000000000) (from <builtin>:0)
/lib64/haswell/avx512_1: (hwcap: 0x0004000000000004) (from <builtin>:0)
Therefore, for maximum compatibility, CMake MAY deploy using both new and old names, using symlinks to link one to the other. Using as example my multi-subarch build of QtCore:
$ find -name libQt6Core.so.6 -ls
1997362 0 lrwxrwxrwx 1 tjmaciei users 21 Jun 23 08:56 ./libQt6Core.so.6 -> libQt6Core.so.6.7.0
1998005 0 lrwxrwxrwx 1 tjmaciei users 21 Jun 23 08:56 ./glibc-hwcaps/x86-64-v4/libQt6Core.so.6 -> libQt6Core.so.6.7.0
1997623 0 lrwxrwxrwx 1 tjmaciei users 21 Jun 23 08:56 ./glibc-hwcaps/x86-64-v3/libQt6Core.so.6 -> libQt6Core.so.6.7.0
1997365 0 lrwxrwxrwx 1 tjmaciei users 43 Jun 23 08:56 ./haswell/libQt6Core.so.6 -> ../glibc-hwcaps/x86-64-v3/libQt6Core.so.6
1997367 0 lrwxrwxrwx 1 tjmaciei users 46 Jun 23 08:56 ./haswell/avx512_1/libQt6Core.so.6 -> ../../glibc-hwcaps/x86-64-v4/libQt6Core.so.6