From edd5558acddb1ebe48046be6de25f00b8eaa66f0 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Wed, 28 Jun 2023 15:53:06 -0400 Subject: [PATCH] dracut, initcpio: make libgcc_s search universal Closes: #427. --- docs/man/dist/man7/zfsbootmenu.7 | 9 +++- docs/man/zfsbootmenu.7.rst | 4 ++ dracut/module-setup.sh | 31 ++++++-------- initcpio/install/zfsbootmenu | 16 ++++++++ zfsbootmenu/install-helpers.sh | 70 ++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 19 deletions(-) diff --git a/docs/man/dist/man7/zfsbootmenu.7 b/docs/man/dist/man7/zfsbootmenu.7 index ab3bbc164..0766eff47 100644 --- a/docs/man/dist/man7/zfsbootmenu.7 +++ b/docs/man/dist/man7/zfsbootmenu.7 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "ZFSBOOTMENU" "7" "2023-05-21" "" "ZFSBootMenu" +.TH "ZFSBOOTMENU" "7" "2023-07-17" "" "ZFSBootMenu" .SH NAME zfsbootmenu \- System Integration .SH SYNOPSIS @@ -436,6 +436,13 @@ kexec \-\-unload should be sufficient to return to the main menu. Likewise, the hook may construct and execute its own \fIkexec\fP command to alter boot\-time parameters. This may be useful, for example, to allow ZFSBootMenu to select a boot environment and then restructure the boot process to launch a Xen kernel with the selected environment configured as dom0. .UNINDENT .UNINDENT +.sp +\fBzfsbootmenu_skip_gcc_s=yes\fP +.INDENT 0.0 +.INDENT 3.5 +The ZFSBootMenu module attempts to detect and install a copy of the library \fBlibgcc_s.so\fP in its initramfs image on glibc systems. Because several executables may have latent dependencies on this library via a \fBdlopen\fP call in glibc itself, a failure to detect and install the library will cause initramfs generation to fail. If the host system has no dependencies on \fBlibgcc_s.so\fP, set \fBzfsbootmenu_skip_gcc_s=yes\fP to avoid this failure. Alternatively, if \fBlibgcc_s.so\fP is present in an undetected location, set this option and configure Dracut to explicitly install the library. +.UNINDENT +.UNINDENT .SH OPTIONS FOR MKINITCPIO .sp The \fBdracut\fP options specified above may also be specified in a mkinitcpio configuration file when \fBgenerate\-zbm\fP is configured to create images using \fBmkinitcpio\fP\&. However, whereas the \fB\fP values in the dracut configuration should be specified as a single, space\-separated string; in the mkinitcpio configuration, each \fB\fP value must be specified as a Bash array like the standard mkinitcpio arguments. diff --git a/docs/man/zfsbootmenu.7.rst b/docs/man/zfsbootmenu.7.rst index 0b89d83d3..b0e39f202 100644 --- a/docs/man/zfsbootmenu.7.rst +++ b/docs/man/zfsbootmenu.7.rst @@ -261,6 +261,10 @@ In addition to standard dracut configuration options, the ZFSBootMenu dracut mod should be sufficient to return to the main menu. Likewise, the hook may construct and execute its own *kexec* command to alter boot-time parameters. This may be useful, for example, to allow ZFSBootMenu to select a boot environment and then restructure the boot process to launch a Xen kernel with the selected environment configured as dom0. +**zfsbootmenu_skip_gcc_s=yes** + + The ZFSBootMenu module attempts to detect and install a copy of the library **libgcc_s.so** in its initramfs image on glibc systems. Because several executables may have latent dependencies on this library via a **dlopen** call in glibc itself, a failure to detect and install the library will cause initramfs generation to fail. If the host system has no dependencies on **libgcc_s.so**, set **zfsbootmenu_skip_gcc_s=yes** to avoid this failure. Alternatively, if **libgcc_s.so** is present in an undetected location, set this option and configure Dracut to explicitly install the library. + .. _zbm-mkinitcpio-options: Options for mkinitcpio diff --git a/dracut/module-setup.sh b/dracut/module-setup.sh index 1aed68022..c932cc43b 100644 --- a/dracut/module-setup.sh +++ b/dracut/module-setup.sh @@ -68,27 +68,22 @@ install() { fi done - # Workaround for zfsonlinux/zfs#4749 by ensuring libgcc_s.so(.1) is included - _ret=0 - # If zpool requires libgcc_s.so*, dracut will track and include it - if ! ldd "$( command -v zpool )" | grep -qF 'libgcc_s.so'; then - # On systems with gcc-config (Gentoo, Funtoo, etc.), use it to find libgcc_s - if command -v gcc-config >/dev/null 2>&1; then - dracut_install "/usr/lib/gcc/$(s=$(gcc-config -c); echo "${s%-*}/${s##*-}")/libgcc_s.so.1" - _ret=$? - # Otherwise, use dracut's library installation function to find the right one - elif ! inst_libdir_file "libgcc_s.so*"; then - # If all else fails, just try looking for some gcc arch directory - dracut_install /usr/lib/gcc/*/*/libgcc_s.so* - _ret=$? - fi - fi - - if [ ${_ret} -ne 0 ]; then - dfatal "Unable to install libgcc_s.so" + # Add libgcc_s as appropriate + local _libgcc_s + if ! _libgcc_s="$( find_libgcc_s )"; then + dfatal "Unable to locate libgcc_s.so" exit 1 fi + local _lib + while read -r _lib ; do + [ -n "${_lib}" ] || continue + if ! dracut_install "${_lib}"; then + dfatal "Failed to install '${_lib}'" + exit 1 + fi + done <<< "${_libgcc_s}" + # shellcheck disable=SC2154 while read -r doc ; do relative="${doc//${zfsbootmenu_module_root}\//}" diff --git a/initcpio/install/zfsbootmenu b/initcpio/install/zfsbootmenu index 8dbe3b758..159fab5ac 100644 --- a/initcpio/install/zfsbootmenu +++ b/initcpio/install/zfsbootmenu @@ -138,6 +138,22 @@ build() { # Binaries required for ZBM operation add_zbm_binaries + # Add libgcc_s as appropriate + local _libgcc_s + if ! _libgcc_s="$( find_libgcc_s )"; then + error "unable to locate libgcc_s.so" + exit 1 + fi + + local _lib + while read -r _lib ; do + [ -n "${_lib}" ] || continue + if ! add_binary "${_lib}"; then + error "Failed to install '${_lib}'" + exit 1 + fi + done <<< "${_libgcc_s}" + # On-line documentation while read -r doc; do relative="${doc#"${zfsbootmenu_module_root}/"}" diff --git a/zfsbootmenu/install-helpers.sh b/zfsbootmenu/install-helpers.sh index 78ee708cb..200439ed5 100644 --- a/zfsbootmenu/install-helpers.sh +++ b/zfsbootmenu/install-helpers.sh @@ -184,3 +184,73 @@ create_zbm_traceconf() { export zfsbootmenu_trace_baud=${zfsbootmenu_trace_baud} EOF } + +find_libgcc_s() { + local f libdirs libbase ldir zlibs matched + + # Skip detection if desired + # shellcheck disable=SC2154 + case "${zfsbootmenu_skip_gcc_s,,}" in + yes|on|1) return 0 ;; + esac + + # This is only required on glibc systems due to a dlopen in pthread_cancel + # https://github.com/openzfs/zfs/commit/24554082bd93cb90400c4cb751275debda229009 + ldconfig -p 2>/dev/null | grep -qF 'libc.so.6' || return 0 + + # Build a list of libraries linked by zpool + zlibs="$( ldd "$( command -v zpool 2>/dev/null )" )" || zlibs= + + # If zpool links libgcc_s overtly, there is no need for further action + if grep -qF 'libgcc_s.so' <<< "${zlibs}"; then + return 0 + fi + + # Query gcc-config for a current runtime profile if possible + if command -v gcc-config >/dev/null 2>&1; then + local gver + if gver="$( gcc-config -c )"; then + for f in "/usr/lib/gcc/${gver%-*}/${gver##*-}"/libgcc_s.so*; do + [ -e "${f}" ] || continue + echo "${f}" + matched="yes" + done + [ -n "${matched}" ] && return 0 + fi + fi + + # Try walking library paths to find libgcc_s + + # Search the system cache (adapted from dracut) + libdirs="$( ldconfig -pN 2>/dev/null \ + | grep -E -v '/(lib|lib64|usr/lib|usr/lib64)/[^/]*$' \ + | sed -n 's,.* => \(.*\)/.*,\1,p' | sort | uniq )" || libdirs="" + + # Search zpool dependencies to figure out system libdirs + if [[ "${zlibs}" == */lib64/* ]]; then + libbase="lib64" + else + libbase="lib" + fi + + # Look in all possible system library directories + libdirs="/${libbase} /usr/${libbase} ${libdirs}" + for ldir in ${libdirs}; do + for f in "${ldir}"/libgcc_s.so*; do + [ -e "${f}" ] || continue + echo "${f}" + matched="yes" + done + done + [ -n "${matched}" ] && return 0 + + # As a final fallback, just try to grab *any* libgcc_s from GCC + for f in /usr/lib/gcc/*/*/libgcc_s.so*; do + [ -f "${f}" ] || continue + echo "${f}" + matched="yes" + done + + [ -n "${matched}" ] && return 0 + return 1 +}