From fd5c07ba69e580a16d1754e425530176a56b7982 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 15 Jun 2021 13:09:37 +0200 Subject: [PATCH] Install shared objects under their ABI names, avoiding symlinks (#1652867) This adds patches proposed upstream to enable downgrades across major glibc versions. File list construction and the find-debuginfo.sh wrapper had to be adjusted to cope with the changed file name patterns. --- glibc-nosymlink-1.patch | 31 ++++++++ glibc-nosymlink-2.patch | 78 ++++++++++++++++++++ glibc-nosymlink-3.patch | 129 ++++++++++++++++++++++++++++++++ glibc-nosymlink-4.patch | 158 ++++++++++++++++++++++++++++++++++++++++ glibc.spec | 30 ++++++-- wrap-find-debuginfo.sh | 21 +++++- 6 files changed, 435 insertions(+), 12 deletions(-) create mode 100644 glibc-nosymlink-1.patch create mode 100644 glibc-nosymlink-2.patch create mode 100644 glibc-nosymlink-3.patch create mode 100644 glibc-nosymlink-4.patch diff --git a/glibc-nosymlink-1.patch b/glibc-nosymlink-1.patch new file mode 100644 index 0000000..767b6a2 --- /dev/null +++ b/glibc-nosymlink-1.patch @@ -0,0 +1,31 @@ +Patch series proposed upstream: + + + +Author: Florian Weimer +Date: Wed Jun 9 14:12:46 2021 +0200 + + nptl_db: Install libthread_db under a regular implementation name + + Currently, the name is always libthread_db-1.0.so. It does not change + with the glibc version, like the other libraries. + + GDB hard-codes libthread_db.so.1 (the soname), so this change does not + affect loading libthread_db. + + Tested on x86_64-linux-gnu, in an environment where the nptl GDB tests + actually run. + +diff --git a/nptl_db/Makefile b/nptl_db/Makefile +index ea721c1dcfce6e7b..1f79c018a1f9fe19 100644 +--- a/nptl_db/Makefile ++++ b/nptl_db/Makefile +@@ -21,8 +21,6 @@ subdir := nptl_db + + include ../Makeconfig + +-nptl_db-version = 1.0 +- + extra-libs = libthread_db + extra-libs-others := $(extra-libs) + diff --git a/glibc-nosymlink-2.patch b/glibc-nosymlink-2.patch new file mode 100644 index 0000000..92c330b --- /dev/null +++ b/glibc-nosymlink-2.patch @@ -0,0 +1,78 @@ +Patch series proposed upstream: + + + +Author: Florian Weimer +Date: Wed Jun 9 14:12:46 2021 +0200 + + Makerules: Remove lib-version, $(subdir-version) + + Also clarify that the “versioned” term refers to the soname, not the glibc + version (which also ends up in the installed file name). + + I verified on x86_64-linux-gnu that “make install” produces the same + files. + +diff --git a/Makerules b/Makerules +index ca9885436e15784b..d3f29d0b8991efc7 100644 +--- a/Makerules ++++ b/Makerules +@@ -982,22 +982,21 @@ install-lib.so := $(filter %.so,$(install-lib:%_pic.a=%.so)) + install-lib := $(filter-out %.so %_pic.a,$(install-lib)) + + ifeq (yes,$(build-shared)) +-# Find which .so's have versions. ++# Find which .so's have a version number in their soname. + versioned := $(strip $(foreach so,$(install-lib.so),\ + $(patsubst %,$(so),$($(so)-version)))) + + install-lib.so-versioned := $(filter $(versioned), $(install-lib.so)) + install-lib.so-unversioned := $(filter-out $(versioned), $(install-lib.so)) + +-# For versioned libraries, we install three files: ++# For libraries whose soname have version numbers, we install three files: + # $(inst_libdir)/libfoo.so -- for linking, symlink or ld script + # $(inst_slibdir)/libfoo.so.NN -- for loading by SONAME, symlink + # $(inst_slibdir)/libfoo-X.Y.Z.so -- the real shared object file +-lib-version := $(firstword $($(subdir)-version) $(version)) + install-lib-nosubdir: $(install-lib.so-unversioned:%=$(inst_slibdir)/%) \ + $(foreach L,$(install-lib.so-versioned),\ + $(inst_libdir)/$L \ +- $(inst_slibdir)/$(L:.so=)-$(lib-version).so \ ++ $(inst_slibdir)/$(L:.so=)-$(version).so \ + $(inst_slibdir)/$L$($L-version)) + + # Install all the unversioned shared libraries. +@@ -1125,7 +1124,6 @@ include $(o-iterator) + + generated += $(foreach o,$(versioned),$o$($o-version)) + +-ifeq (,$($(subdir)-version)) + define o-iterator-doit + $(inst_slibdir)/$o$($o-version): $(inst_slibdir)/$(o:.so=)-$(version).so \ + $(+force); +@@ -1140,23 +1138,7 @@ $(inst_slibdir)/$(o:.so=)-$(version).so: $(objpfx)$o $(+force); + endef + object-suffixes-left := $(versioned) + include $(o-iterator) +-else +-define o-iterator-doit +-$(inst_slibdir)/$o$($o-version): \ +- $(inst_slibdir)/$(o:.so=)-$($(subdir)-version).so $(+force); +- $$(make-shlib-link) +-endef +-object-suffixes-left := $(versioned) +-include $(o-iterator) +- +-define o-iterator-doit +-$(inst_slibdir)/$(o:.so=)-$($(subdir)-version).so: $(objpfx)$o $(+force); +- $$(do-install-program) +-endef +-object-suffixes-left := $(versioned) +-include $(o-iterator) +-endif +-endif ++endif # ifneq (,$(versioned)) + + define do-install-so + $(do-install-program) diff --git a/glibc-nosymlink-3.patch b/glibc-nosymlink-3.patch new file mode 100644 index 0000000..76080d4 --- /dev/null +++ b/glibc-nosymlink-3.patch @@ -0,0 +1,129 @@ +Patch series proposed upstream: + + + +Author: Florian Weimer +Date: Wed Jun 9 14:12:46 2021 +0200 + + elf: Generalize name-based DSO recognition in ldconfig + + This introduces and the _dl_is_dso function. A + test ensures that the official names of libc.so, ld.so, and their + versioned names are recognized. + +diff --git a/elf/Makefile b/elf/Makefile +index 5c47daee12505a47..1751f5ec6772eceb 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -223,7 +223,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ + tst-single_threaded tst-single_threaded-pthread \ + tst-tls-ie tst-tls-ie-dlmopen argv0test \ + tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \ +- tst-tls20 tst-tls21 tst-dlmopen-dlerror ++ tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-dl-is_dso + # reldep9 + tests-internal += loadtest unload unload2 circleload1 \ + neededtest neededtest2 neededtest3 neededtest4 \ +diff --git a/elf/dl-is_dso.h b/elf/dl-is_dso.h +new file mode 100644 +index 0000000000000000..94e00966a16e0df5 +--- /dev/null ++++ b/elf/dl-is_dso.h +@@ -0,0 +1,33 @@ ++/* Heuristic for recognizing DSO file names. ++ Copyright (C) 2021 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++/* Returns true if the file name looks like a DSO name. */ ++static bool ++_dl_is_dso (const char *name) ++{ ++ /* Recognize lib*.so*, ld-*.so*, ld.so.*, ld64.so.*. ld-*.so* ++ matches both platform dynamic linker names like ld-linux.so.2, ++ and versioned dynamic loader names like ld-2.12.so. */ ++ return (((strncmp (name, "lib", 3) == 0 || strncmp (name, "ld-", 3) == 0) ++ && strstr (name, ".so") != NULL) ++ || strncmp (name, "ld.so.", 6) == 0 ++ || strncmp (name, "ld64.so.", 8) == 0); ++} +diff --git a/elf/ldconfig.c b/elf/ldconfig.c +index 96bf7700b2efd37a..1037e8d0cf8d28b6 100644 +--- a/elf/ldconfig.c ++++ b/elf/ldconfig.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + + #include + +@@ -842,9 +843,7 @@ search_dir (const struct dir_entry *entry) + subdirectory (if not already processing a glibc-hwcaps + subdirectory)? The dynamic linker is also considered as + shared library. */ +- if (((strncmp (direntry->d_name, "lib", 3) != 0 +- && strncmp (direntry->d_name, "ld-", 3) != 0) +- || strstr (direntry->d_name, ".so") == NULL) ++ if (!_dl_is_dso (direntry->d_name) + && (direntry->d_type == DT_REG + || (entry->hwcaps == NULL + && !is_hwcap_platform (direntry->d_name)))) +diff --git a/elf/tst-dl-is_dso.c b/elf/tst-dl-is_dso.c +new file mode 100644 +index 0000000000000000..48d2cc9fbe9edbc6 +--- /dev/null ++++ b/elf/tst-dl-is_dso.c +@@ -0,0 +1,35 @@ ++/* Test heuristic for recognizing DSO file names. ++ Copyright (C) 2021 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ /* Official ABI names. */ ++ TEST_VERIFY (_dl_is_dso (LIBC_SO)); ++ TEST_VERIFY (_dl_is_dso (LD_SO)); ++ /* Version-based names. The version number does not matter. */ ++ TEST_VERIFY (_dl_is_dso ("libc-2.12.so")); ++ TEST_VERIFY (_dl_is_dso ("ld-2.12.so")); ++ return 0; ++} ++ ++#include diff --git a/glibc-nosymlink-4.patch b/glibc-nosymlink-4.patch new file mode 100644 index 0000000..7a292f0 --- /dev/null +++ b/glibc-nosymlink-4.patch @@ -0,0 +1,158 @@ +Patch series proposed upstream: + + + +Author: Florian Weimer +Date: Wed Jun 9 14:12:46 2021 +0200 + + Install shared objects under their ABI names + + Previously, the installed objects were named like libc-2.33.so, + and the ABI soname libc.so.6 was just a symbolic link. + + The Makefile targets to install these symbolic links are no longer + needed after this, so they are removed with this commit. The more + general $(make-link) command (which invokes scripts/rellns-sh) is + retained because other symbolic links are still needed. + +diff --git a/INSTALL b/INSTALL +index 56ed01d4386ad8b7..7f054f422d990d8a 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -199,6 +199,16 @@ if 'CFLAGS' is specified it must enable optimization. For example: + RELRO and a read-only global offset table (GOT), at the cost of + slightly increased program load times. + ++'--disable-major-minor-libraries' ++ Do not install shared objects under file names that contain the ++ major and minor version of the GNU C Library. By default, such ++ names are used, and the names defined by the ABI are provided as ++ symbolic links only. This causes problems with certain package ++ managers during library upgrades and (in particular) downgrades, so ++ this option can be used to install these shared objects directly ++ under their ABI-defined names, without an additional indirection ++ via symbolic links. ++ + '--enable-pt_chown' + The file 'pt_chown' is a helper binary for 'grantpt' (*note + Pseudo-Terminals: Allocation.) that is installed setuid root to fix +diff --git a/Makefile b/Makefile +index 242d36de914c516f..c115c652a0b8c1ce 100644 +--- a/Makefile ++++ b/Makefile +@@ -109,12 +109,6 @@ elf/ldso_install: + # Ignore the error if we cannot update /etc/ld.so.cache. + ifeq (no,$(cross-compiling)) + ifeq (yes,$(build-shared)) +-install: install-symbolic-link +-.PHONY: install-symbolic-link +-install-symbolic-link: subdir_install +- $(symbolic-link-prog) $(symbolic-link-list) +- rm -f $(symbolic-link-list) +- + install: + -test ! -x $(elf-objpfx)ldconfig || LC_ALL=C \ + $(elf-objpfx)ldconfig $(addprefix -r ,$(install_root)) \ +diff --git a/Makerules b/Makerules +index d3f29d0b8991efc7..6efff78fbe44bdca 100644 +--- a/Makerules ++++ b/Makerules +@@ -989,14 +989,12 @@ versioned := $(strip $(foreach so,$(install-lib.so),\ + install-lib.so-versioned := $(filter $(versioned), $(install-lib.so)) + install-lib.so-unversioned := $(filter-out $(versioned), $(install-lib.so)) + +-# For libraries whose soname have version numbers, we install three files: ++# For libraries whose soname have version numbers, we install two files: + # $(inst_libdir)/libfoo.so -- for linking, symlink or ld script +-# $(inst_slibdir)/libfoo.so.NN -- for loading by SONAME, symlink +-# $(inst_slibdir)/libfoo-X.Y.Z.so -- the real shared object file ++# $(inst_slibdir)/libfoo.so.NN -- for loading by SONAME + install-lib-nosubdir: $(install-lib.so-unversioned:%=$(inst_slibdir)/%) \ + $(foreach L,$(install-lib.so-versioned),\ + $(inst_libdir)/$L \ +- $(inst_slibdir)/$(L:.so=)-$(version).so \ + $(inst_slibdir)/$L$($L-version)) + + # Install all the unversioned shared libraries. +@@ -1029,35 +1027,10 @@ ln -f $(objpfx)/$(@F) $@ + endef + endif + +-ifeq (yes,$(build-shared)) +-ifeq (no,$(cross-compiling)) +-symbolic-link-prog := $(elf-objpfx)sln +-symbolic-link-list := $(elf-objpfx)symlink.list +-define make-shlib-link +-echo `$(..)scripts/rellns-sh -p $< $@` $@ >> $(symbolic-link-list) +-endef +-else # cross-compiling +-# We need a definition that can be used by elf/Makefile's install rules. +-symbolic-link-prog = $(LN_S) +-endif +-endif +-ifndef make-shlib-link +-define make-shlib-link +-rm -f $@ +-$(LN_S) `$(..)scripts/rellns-sh -p $< $@` $@ +-endef +-endif +- + ifdef libc.so-version +-# For a library specified to be version N, install three files: +-# libc.so -> libc.so.N (e.g. libc.so.6) +-# libc.so.6 -> libc-VERSION.so (e.g. libc-1.10.so) +- +-$(inst_slibdir)/libc.so$(libc.so-version): $(inst_slibdir)/libc-$(version).so \ +- $(+force) +- $(make-shlib-link) +-$(inst_slibdir)/libc-$(version).so: $(common-objpfx)libc.so $(+force) ++$(inst_slibdir)/libc.so$(libc.so-version): $(common-objpfx)libc.so $(+force) + $(do-install-program) ++ + install: $(inst_slibdir)/libc.so$(libc.so-version) + + # This fragment of linker script gives the OUTPUT_FORMAT statement +@@ -1125,15 +1098,7 @@ include $(o-iterator) + generated += $(foreach o,$(versioned),$o$($o-version)) + + define o-iterator-doit +-$(inst_slibdir)/$o$($o-version): $(inst_slibdir)/$(o:.so=)-$(version).so \ +- $(+force); +- $$(make-shlib-link) +-endef +-object-suffixes-left := $(versioned) +-include $(o-iterator) +- +-define o-iterator-doit +-$(inst_slibdir)/$(o:.so=)-$(version).so: $(objpfx)$o $(+force); ++$(inst_slibdir)/$o$($o-version): $(objpfx)$o $(+force); + $$(do-install-program) + endef + object-suffixes-left := $(versioned) +diff --git a/elf/Makefile b/elf/Makefile +index 1751f5ec6772eceb..02f634c19241949f 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -628,20 +628,14 @@ $(objpfx)trusted-dirs.st: Makefile $(..)Makeconfig + CPPFLAGS-dl-load.c += -I$(objpfx). -I$(csu-objpfx). + + ifeq (yes,$(build-shared)) +-$(inst_slibdir)/$(rtld-version-installed-name): $(objpfx)ld.so $(+force) ++$(inst_rtlddir)/$(rtld-installed-name): $(objpfx)ld.so $(+force) + $(make-target-directory) + $(do-install-program) + +-$(inst_rtlddir)/$(rtld-installed-name): \ +- $(inst_slibdir)/$(rtld-version-installed-name) \ +- $(inst_slibdir)/libc-$(version).so +- $(make-target-directory) +- $(make-shlib-link) +- + # Special target called by parent to install just the dynamic linker. + .PHONY: ldso_install + ldso_install: $(inst_rtlddir)/$(rtld-installed-name) +-endif ++endif # $(build-shared) + + + # Workarounds for ${exec_prefix} expansion in configure variables. diff --git a/glibc.spec b/glibc.spec index d053268..2e36dc2 100644 --- a/glibc.spec +++ b/glibc.spec @@ -97,7 +97,7 @@ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 14%{?dist} +Release: 15%{?dist} # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -141,13 +141,13 @@ Source12: ChangeLog.old # the definition of __debug_install_post. %{lua: local wrapper = rpm.expand("%{SOURCE10}") -local ldso = rpm.expand("%{glibc_sysroot}/%{_lib}/ld-%{VERSION}.so") +local sysroot = rpm.expand("%{glibc_sysroot}") local original = rpm.expand("%{macrobody:__debug_install_post}") -- Strip leading newline. It confuses the macro redefinition. -- Avoid embedded newlines that confuse the macro definition. original = original:match("^%s*(.-)%s*$"):gsub("\\\n", "") rpm.define("__debug_install_post bash " .. wrapper - .. " " .. ldso .. " " .. original) + .. " " .. sysroot .. " " .. original) } ############################################################################## @@ -171,6 +171,10 @@ Patch30: glibc-deprecated-selinux-makedb.patch Patch31: glibc-deprecated-selinux-nscd.patch Patch32: glibc-gconv-modules-revert.patch Patch33: glibc-rh697421.patch +Patch34: glibc-nosymlink-1.patch +Patch35: glibc-nosymlink-2.patch +Patch36: glibc-nosymlink-3.patch +Patch37: glibc-nosymlink-4.patch ############################################################################## # Continued list of core "glibc" package information: @@ -1759,7 +1763,7 @@ EOF # Move the NSS-related files to the NSS subpackages. Be careful not # to pick up .debug files, and the -devel symbolic links. for module in db hesiod; do - grep -E "/libnss_$module(\.so\.[0-9.]+|-[0-9.]+\.so)$" \ + grep -E "/libnss_$module\\.so\\.[0-9.]+\$" \ master.filelist > nss_$module.filelist done grep -E "%{_prefix}/bin/makedb$" master.filelist >> nss_db.filelist @@ -1777,7 +1781,7 @@ grep '/libnss_[a-z]*\.so$' master.filelist > nss-devel.filelist ############################################################################### # Prepare the libnsl-related file lists. -grep '/libnsl-[0-9.]*.so$' master.filelist > libnsl.filelist +grep -E '/libnsl\.so\.[0-9]+$' master.filelist > libnsl.filelist test $(wc -l < libnsl.filelist) -eq 1 %if %{with benchtests} @@ -1870,8 +1874,10 @@ echo ====================PLT RELOCS END================== # Obtain a way to run the dynamic loader. Avoid matching the symbolic # link and then pick the first loader (although there should be only -# one). -run_ldso="$(find %{glibc_sysroot}/%{_lib}/ld-*.so -type f | LC_ALL=C sort | head -n1) --library-path %{glibc_sysroot}/%{_lib}" +# one). See wrap-find-debuginfo.sh. +ldso_path="$(find %{glibc_sysroot}/ -regextype posix-extended \ + -regex '.*/ld(-.*|64|)\.so\.[0-9]+$' -type f | LC_ALL=C sort | head -n1)" +run_ldso="$ldso_path --library-path %{glibc_sysroot}/%{_lib}" # Show the auxiliary vector as seen by the new library # (even if we do not perform the valgrind test). @@ -1937,12 +1943,17 @@ install_libs = { "anl", "BrokenLocale", "c", "dl", "m", "mvec", -- We are going to remove these libraries. Generally speaking we remove -- all core libraries in the multilib directory. --- We employ a tight match where X.Y is in [2.0,9.9*], so we would +-- For the versioned install names, the version are [2.0,9.9*], so we -- match "libc-2.0.so" and so on up to "libc-9.9*". +-- For the unversioned install names, we match the library plus ".so." +-- followed by digests. remove_regexps = {} for i = 1, #install_libs do + -- Versioned install name. remove_regexps[i] = ("lib" .. install_libs[i] .. "%%-[2-9]%%.[0-9]+%%.so$") + -- Unversioned install name. + remove_regexps[i] = ("lib" .. install_libs[i] .. "%%.so%.[0-9]+$") end -- Two exceptions: @@ -2175,6 +2186,9 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Tue Jun 15 2021 Florian Weimer - 2.33.9000-15 +- Install shared objects under their ABI names, avoiding symlinks (#1652867) + * Mon Jun 14 2021 Siddhesh Poyarekar - 2.33.9000-14 - Add a conditional dependency for glibc-gconv-extra.i686 in x86_64. diff --git a/wrap-find-debuginfo.sh b/wrap-find-debuginfo.sh index 598c0d3..e73f264 100644 --- a/wrap-find-debuginfo.sh +++ b/wrap-find-debuginfo.sh @@ -2,11 +2,12 @@ # Wrapper script for find-debuginfo.sh # # Usage: -# wrap-find-debuginfo.sh LDSO-PATH SCRIPT-PATH SCRIPT-ARGS... +# wrap-find-debuginfo.sh SYSROOT-PATH SCRIPT-PATH SCRIPT-ARGS... # -# The wrapper saves the original versions of the file at LDSO-PATH, +# The wrapper saves the original version of ld.so found in SYSROOT-PATH, # invokes SCRIPT-PATH with SCRIPT-ARGS, and then restores the -# LDSO-PATH file. As a result, LDSO-PATH has unchanged debuginfo even +# LDSO-PATH file, followed by note merging and DWZ compression. +# As a result, ld.so has (mostly) unchanged debuginfo even # after debuginfo extraction. set -ex @@ -25,11 +26,23 @@ cleanup () { } trap cleanup 0 -ldso_path="$1" +sysroot_path="$1" shift script_path="$1" shift +# See ldso_path setting in glibc.spec. +ldso_path= +for ldso_candidate in `find "$sysroot_path" -regextype posix-extended \ + -regex '.*/ld(-.*|64|)\.so\.[0-9]+$' -type f` ; do + if test -z "$ldso_path" ; then + ldso_path="$ldso_candidate" + else + echo "error: multiple ld.so candidates: $ldso_path, $ldso_candidate" + exit 1 + fi +done + # Preserve the original file. cp "$ldso_path" "$ldso_tmp"