aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/validate_map.sh
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2022-09-04 23:23:00 +0300
committerLasse Collin <lasse.collin@tukaani.org>2022-09-08 15:01:29 +0300
commit913ddc5572b9455fa0cf299be2e35c708840e922 (patch)
tree68160a4aad510f03a3bd52535893c8b81f95e3cb /src/liblzma/validate_map.sh
parentCMake: Add xz symlinks. (diff)
downloadxz-913ddc5572b9455fa0cf299be2e35c708840e922.tar.xz
liblzma: Vaccinate against an ill patch from RHEL/CentOS 7.
RHEL/CentOS 7 shipped with 5.1.2alpha, including the threaded encoder that is behind #ifdef LZMA_UNSTABLE in the API headers. In 5.1.2alpha these symbols are under XZ_5.1.2alpha in liblzma.map. API/ABI compatibility tracking isn't done between development releases so newer releases didn't have XZ_5.1.2alpha anymore. Later RHEL/CentOS 7 updated xz to 5.2.2 but they wanted to keep the exported symbols compatible with 5.1.2alpha. After checking the ABI changes it turned out that >= 5.2.0 ABI is backward compatible with the threaded encoder functions from 5.1.2alpha (but not vice versa as fixes and extensions to these functions were made between 5.1.2alpha and 5.2.0). In RHEL/CentOS 7, XZ Utils 5.2.2 was patched with xz-5.2.2-compat-libs.patch to modify liblzma.map: - XZ_5.1.2alpha was added with lzma_stream_encoder_mt and lzma_stream_encoder_mt_memusage. This matched XZ Utils 5.1.2alpha. - XZ_5.2 was replaced with XZ_5.2.2. It is clear that this was an error; the intention was to keep using XZ_5.2 (XZ_5.2.2 has never been used in XZ Utils). So XZ_5.2.2 lists all symbols that were listed under XZ_5.2 before the patch. lzma_stream_encoder_mt and _mt_memusage are included too so they are listed both here and under XZ_5.1.2alpha. The patch didn't add any __asm__(".symver ...") lines to the .c files. Thus the resulting liblzma.so exports the threaded encoder functions under XZ_5.1.2alpha only. Listing the two functions also under XZ_5.2.2 in liblzma.map has no effect without matching .symver lines. The lack of XZ_5.2 in RHEL/CentOS 7 means that binaries linked against unpatched XZ Utils 5.2.x won't run on RHEL/CentOS 7. This is unfortunate but this alone isn't too bad as the problem is contained within RHEL/CentOS 7 and doesn't affect users of other distributions. It could also be fixed internally in RHEL/CentOS 7. The second problem is more serious: In XZ Utils 5.2.2 the API headers don't have #ifdef LZMA_UNSTABLE for obvious reasons. This is true in RHEL/CentOS 7 version too. Thus now programs using new APIs can be compiled without an extra #define. However, the programs end up depending on symbol version XZ_5.1.2alpha (and possibly also XZ_5.2.2) instead of XZ_5.2 as they would with an unpatched XZ Utils 5.2.2. This means that such binaries won't run on other distributions shipping XZ Utils >= 5.2.0 as they don't provide XZ_5.1.2alpha or XZ_5.2.2; they only provide XZ_5.2 (and XZ_5.0). (This includes RHEL/CentOS 8 as the patch luckily isn't included there anymore with XZ Utils 5.2.4.) Binaries built by RHEL/CentOS 7 users get distributed and then people wonder why they don't run on some other distribution. Seems that people have found out about the patch and been copying it to some build scripts, seemingly curing the symptoms but actually spreading the illness further and outside RHEL/CentOS 7. The ill patch seems to be from late 2016 (RHEL 7.3) and in 2017 it had spread at least to EasyBuild. I heard about the events only recently. :-( This commit splits liblzma.map into two versions: one for GNU/Linux and another for other OSes that can use symbol versioning (FreeBSD, Solaris, maybe others). The Linux-specific file and the matching additions to .c files add full compatibility with binaries that have been built against a RHEL/CentOS-patched liblzma. Builds for OSes other than GNU/Linux won't get the vaccine as they should be immune to the problem (I really hope that no build script uses the RHEL/CentOS 7 patch outside GNU/Linux). The RHEL/CentOS compatibility symbols XZ_5.1.2alpha and XZ_5.2.2 are intentionally put *after* XZ_5.2 in liblzma_linux.map. This way if one forgets to #define HAVE_SYMBOL_VERSIONS_LINUX when building, the resulting liblzma.so.5 will have lzma_stream_encoder_mt@@XZ_5.2 since XZ_5.2 {...} is the first one that lists that function. Without HAVE_SYMBOL_VERSIONS_LINUX @XZ_5.1.2alpha and @XZ_5.2.2 will be missing but that's still a minor problem compared to only having lzma_stream_encoder_mt@@XZ_5.1.2alpha! The "local: *;" line was moved to XZ_5.0 so that it doesn't need to be moved around. It doesn't matter where it is put. Having two similar liblzma_*.map files is a bit silly as it is, at least for now, easily possible to generate the generic one from the Linux-specific file. But that adds extra steps and increases the risk of mistakes when supporting more than one build system. So I rather maintain two files in parallel and let validate_map.sh check that they are in sync when "make mydist" is run. This adds .symver lines for lzma_stream_encoder_mt@XZ_5.2.2 and lzma_stream_encoder_mt_memusage@XZ_5.2.2 even though these weren't exported by RHEL/CentOS 7 (only @@XZ_5.1.2alpha was for these two). I added these anyway because someone might misunderstand the RHEL/CentOS 7 patch and think that @XZ_5.2.2 (@@XZ_5.2.2) versions were exported too. At glance one could suggest using __typeof__ to copy the function prototypes when making aliases. However, this doesn't work trivially because __typeof__ won't copy attributes (lzma_nothrow, lzma_pure) and it won't change symbol visibility from hidden to default (done by LZMA_API()). Attributes could be copied with __copy__ attribute but that needs GCC 9 and a fallback method would be needed anyway. This uses __symver__ attribute with GCC >= 10 and __asm__(".symver ...") with everything else. The attribute method is required for LTO (-flto) support with GCC. Using -flto with GCC older than 10 is now broken on GNU/Linux and will not be fixed (can silently result in a broken liblzma build that has dangerously incorrect symbol versions). LTO builds with Clang seem to work with the traditional __asm__(".symver ...") method. Thanks to Boud Roukema for reporting the problem and discussing the details and testing the fix.
Diffstat (limited to 'src/liblzma/validate_map.sh')
-rw-r--r--src/liblzma/validate_map.sh113
1 files changed, 105 insertions, 8 deletions
diff --git a/src/liblzma/validate_map.sh b/src/liblzma/validate_map.sh
index 3aee4668..2bf6f8b9 100644
--- a/src/liblzma/validate_map.sh
+++ b/src/liblzma/validate_map.sh
@@ -2,7 +2,79 @@
###############################################################################
#
-# Check liblzma.map for certain types of errors
+# Check liblzma_*.map for certain types of errors.
+#
+# liblzma_generic.map is for FreeBSD and Solaris and possibly others
+# except GNU/Linux.
+#
+# liblzma_linux.map is for GNU/Linux only. This and the matching extra code
+# in the .c files make liblzma >= 5.2.7 compatible with binaries that were
+# linked against ill-patched liblzma in RHEL/CentOS 7. By providing the
+# compatibility in official XZ Utils release will hopefully prevent people
+# from further copying the broken patch to other places when they want
+# compatibility with binaries linked on RHEL/CentOS 7. The long version
+# of the story:
+#
+# RHEL/CentOS 7 shipped with 5.1.2alpha, including the threaded
+# encoder that is behind #ifdef LZMA_UNSTABLE in the API headers.
+# In 5.1.2alpha these symbols are under XZ_5.1.2alpha in liblzma.map.
+# API/ABI compatibility tracking isn't done between development
+# releases so newer releases didn't have XZ_5.1.2alpha anymore.
+#
+# Later RHEL/CentOS 7 updated xz to 5.2.2 but they wanted to keep
+# the exported symbols compatible with 5.1.2alpha. After checking
+# the ABI changes it turned out that >= 5.2.0 ABI is backward
+# compatible with the threaded encoder functions from 5.1.2alpha
+# (but not vice versa as fixes and extensions to these functions
+# were made between 5.1.2alpha and 5.2.0).
+#
+# In RHEL/CentOS 7, XZ Utils 5.2.2 was patched with
+# xz-5.2.2-compat-libs.patch to modify liblzma.map:
+#
+# - XZ_5.1.2alpha was added with lzma_stream_encoder_mt and
+# lzma_stream_encoder_mt_memusage. This matched XZ Utils 5.1.2alpha.
+#
+# - XZ_5.2 was replaced with XZ_5.2.2. It is clear that this was
+# an error; the intention was to keep using XZ_5.2 (XZ_5.2.2
+# has never been used in XZ Utils). So XZ_5.2.2 lists all
+# symbols that were listed under XZ_5.2 before the patch.
+# lzma_stream_encoder_mt and _mt_memusage are included too so
+# they are listed both here and under XZ_5.1.2alpha.
+#
+# The patch didn't add any __asm__(".symver ...") lines to the .c
+# files. Thus the resulting liblzma.so exports the threaded encoder
+# functions under XZ_5.1.2alpha only. Listing the two functions
+# also under XZ_5.2.2 in liblzma.map has no effect without
+# matching .symver lines.
+#
+# The lack of XZ_5.2 in RHEL/CentOS 7 means that binaries linked
+# against unpatched XZ Utils 5.2.x won't run on RHEL/CentOS 7.
+# This is unfortunate but this alone isn't too bad as the problem
+# is contained within RHEL/CentOS 7 and doesn't affect users
+# of other distributions. It could also be fixed internally in
+# RHEL/CentOS 7.
+#
+# The second problem is more serious: In XZ Utils 5.2.2 the API
+# headers don't have #ifdef LZMA_UNSTABLE for obvious reasons.
+# This is true in RHEL/CentOS 7 version too. Thus now programs
+# using new APIs can be compiled without an extra #define. However,
+# the programs end up depending on symbol version XZ_5.1.2alpha
+# (and possibly also XZ_5.2.2) instead of XZ_5.2 as they would
+# with an unpatched XZ Utils 5.2.2. This means that such binaries
+# won't run on other distributions shipping XZ Utils >= 5.2.0 as
+# they don't provide XZ_5.1.2alpha or XZ_5.2.2; they only provide
+# XZ_5.2 (and XZ_5.0). (This includes RHEL/CentOS 8 as the patch
+# luckily isn't included there anymore with XZ Utils 5.2.4.)
+#
+# Binaries built by RHEL/CentOS 7 users get distributed and then
+# people wonder why they don't run on some other distribution.
+# Seems that people have found out about the patch and been copying
+# it to some build scripts, seemingly curing the symptoms but
+# actually spreading the illness further and outside RHEL/CentOS 7.
+# Adding compatibility in an official XZ Utils release should work
+# as a vaccine against this ill patch and stop it from spreading.
+# The vaccine is kept GNU/Linux-only as other OSes should be immune
+# (hopefully it hasn't spread via some build script to other OSes).
#
# Author: Lasse Collin
#
@@ -18,11 +90,11 @@ STATUS=0
cd "$(dirname "$0")"
-# Get the list of symbols that aren't defined in liblzma.map.
+# Get the list of symbols that aren't defined in liblzma_generic.map.
SYMS=$(sed -n 's/^extern LZMA_API([^)]*) \([a-z0-9_]*\)(.*$/\1;/p' \
api/lzma/*.h \
| sort \
- | grep -Fve "$(sed '/[{}:*]/d;/^$/d;s/^ //' liblzma.map)")
+ | grep -Fve "$(sed '/[{}:*]/d;/^$/d;s/^ //' liblzma_generic.map)")
# Check that there are no old alpha or beta versions listed.
VER=$(cd ../.. && sh build-aux/version.sh)
@@ -30,21 +102,41 @@ NAMES=
case $VER in
*alpha | *beta)
NAMES=$(sed -n 's/^.*XZ_\([^ ]*\)\(alpha\|beta\) .*$/\1\2/p' \
- liblzma.map | grep -Fv "$VER")
+ liblzma_generic.map | grep -Fv "$VER")
;;
esac
# Check for duplicate lines. It can catch missing dependencies.
-DUPS=$(sort liblzma.map | sed '/^$/d;/^global:$/d' | uniq -d)
+DUPS=$(sort liblzma_generic.map | sed '/^$/d;/^global:$/d' | uniq -d)
+
+# Check that liblzma_linux.map is in sync with liblzma_generic.map.
+# The RHEL/CentOS 7 compatibility symbols are in a fixed location
+# so it makes it easy to remove them for comparison with liblzma_generic.map.
+#
+# NOTE: Putting XZ_5.2 before the compatibility symbols XZ_5.1.2alpha
+# and XZ_5.2.2 in liblzma_linux.map is important: If liblzma_linux.map is
+# incorrectly used without #define HAVE_SYMBOL_VERSIONS_LINUX, only the first
+# occurrence of each function name will be used from liblzma_linux.map;
+# the rest are ignored by the linker. Thus having XZ_5.2 before the
+# compatibility symbols means that @@XZ_5.2 will be used for the symbols
+# listed under XZ_5.2 {...} and the same function names later in
+# the file under XZ_5.1.2alpha {...} and XZ_5.2.2 {...} will be
+# ignored (@XZ_5.1.2alpha or @XZ_5.2.2 won't be added at all when
+# the #define HAVE_SYMBOL_VERSIONS_LINUX isn't used).
+IN_SYNC=
+if ! sed '109,123d' liblzma_linux.map \
+ | cmp -s - liblzma_generic.map; then
+ IN_SYNC=no
+fi
# Print error messages if needed.
-if test -n "$SYMS$NAMES$DUPS"; then
+if test -n "$SYMS$NAMES$DUPS$IN_SYNC"; then
echo
- echo 'validate_map.sh found problems from liblzma.map:'
+ echo 'validate_map.sh found problems from liblzma_*.map:'
echo
if test -n "$SYMS"; then
- echo 'liblzma.map lacks the following symbols:'
+ echo 'liblzma_generic.map lacks the following symbols:'
echo "$SYMS"
echo
fi
@@ -61,6 +153,11 @@ if test -n "$SYMS$NAMES$DUPS"; then
echo
fi
+ if test -n "$IN_SYNC"; then
+ echo "liblzma_generic.map and liblzma_linux.map aren't in sync"
+ echo
+ fi
+
STATUS=1
fi