Skip to content

Commit

Permalink
dracut, initcpio: make libgcc_s search universal
Browse files Browse the repository at this point in the history
Closes: #427.
  • Loading branch information
ahesford committed Jul 17, 2023
1 parent deefabd commit edd5558
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 19 deletions.
9 changes: 8 additions & 1 deletion docs/man/dist/man7/zfsbootmenu.7
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<executable\-list>\fP values in the dracut configuration should be specified as a single, space\-separated string; in the mkinitcpio configuration, each \fB<executable\-list>\fP value must be specified as a Bash array like the standard mkinitcpio arguments.
Expand Down
4 changes: 4 additions & 0 deletions docs/man/zfsbootmenu.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 13 additions & 18 deletions dracut/module-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}\//}"
Expand Down
16 changes: 16 additions & 0 deletions initcpio/install/zfsbootmenu
Original file line number Diff line number Diff line change
Expand Up @@ -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}/"}"
Expand Down
70 changes: 70 additions & 0 deletions zfsbootmenu/install-helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit edd5558

Please sign in to comment.