From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 830E015806E for ; Fri, 26 May 2023 04:03:42 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id DC77CE0899; Fri, 26 May 2023 04:02:36 +0000 (UTC) Received: from smtp.gentoo.org (dev.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 27954E0883 for ; Fri, 26 May 2023 04:02:36 +0000 (UTC) From: Ionen Wolkens To: gentoo-dev@lists.gentoo.org Subject: [gentoo-dev] [PATCH 3/4] linux-mod-r1.eclass: new eclass, rewrite of linux-mod.eclass Date: Fri, 26 May 2023 00:02:18 -0400 Message-Id: <20230526040219.10852-4-ionen@gentoo.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230526040219.10852-1-ionen@gentoo.org> References: <20230526040219.10852-1-ionen@gentoo.org> Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-dev@lists.gentoo.org Reply-to: gentoo-dev@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Archives-Salt: 72d17ecf-7d2e-47ea-afa9-0f3a2e3c665d X-Archives-Hash: d6ced3ad79d29451e4f627187f8c8c75 Here's a rough overview of -r0 -> -r1 differences with occasional rationale behind them if felt relevant (for migrating, refer to the eclassdocs instead as this does not really document usage changes): Features that did not exist in previous eclass (not exhaustive): * automatic modules signing support, been often requested and users would instead use messy bashrc hacks to accomplish this (enabled with USE=modules-sign) * modules (manual) stripping support to allow stripping *before* signing and compression (can be disabled with USE=-strip) * can auto-select toolchain to match kernel, e.g. if built with clang-15 then it won't use gcc nor clang-16 if possible (will warn if the matching compiler went missing) * helper functions to profit from the above 3 even if not using linux-mod-r1_src_compile+install (e.g. for zfs-kmod) * generic supported kernel version checks (min/max) which comes with an encouragement to use LTS kernels for out-of-tree modules (but max is not enforced, just makes a strong suggestion) * linux-mod-r1_src_install now does einstalldocs * can guess some common build targets rather than just 'module', largely removing the need for BUILD_TARGETS * user-oriented MODULES_EXTRA_EMAKE among other few variables * various additional sanity checks hopefully making issues clearer for users and ebuilds a bit harder to write wrong "Features" that existed but were not kept (not exhaustive): * support for =4.14.x * allowing doing all in global scope using variables ran through `eval` (this often led to all sort of variable misuse in global scope) * MODULESD_* support, originally meant to keep but it is used by only 5 packages and holds very little meaning that I can see even in these (when needed, packages should write their own .conf) * moduledb, was being updated for nothing in postinst/postrm despite the tool that can use this (sys-kernel/module-rebuild) being gone from the tree since Feb 2014 * convert_to_m(), only 1 in-tree ebuild uses this right now (svgalib) * various other functions with no consumers were dropped, some were likely meant to be @INTERNAL, get-KERNEL_CC was never used either and now there's ${KERNEL_CC} * running 'clean' by default, this sometime led to race conditions by attempting to clean and build at same time in e.g. nvidia-drivers (if an ebuild truly need this, it can be specified manually) * BUILD_FIXES support, this is set by linux-info.eclass but has no real relevance that I can see (ebuilds have sometime wrongly used it) * undocumented feature CONFIG_CHECK="@CONFIG:modname" (or so?) meant for automagic based on kernel config is no longer supported, this also removes the also undocumented MODULE_IGNORE used by it (found 0 ebuilds using these in the tree, can be done manually if needed) * converting CONFIG_CHECK to non-fatal for running again on binary merge when (while *possible*) it's rather unlikely would build modules for a different kernel than the one that will be used * having preinst and postrm exports, removed -> originally wanted to remove pkg_setup too but it complicated things with linux-info's own pkg_setup and made the eclass feel less convenient and error-prone with environment handling Dependency changes: * virtual/libelf DEPEND removed, building objtool (which uses this) is not handled by the eclass and does not seem auto-built by make if missing, as such the dependency is not used *here* but rather by dist-kernels and source packages which both already request it. * sys-apps/kmod[tools] BDEPEND+IDEPEND added, and removed from DEPEND (linux-mod-r0 uses it similarly but lacks the eapi7+8 adjustment) * modules-sign? ( dev-libs/openssl virtual/pkgconfig ) BDEPEND for building sign-file, unlike objtool it may need rebuilds for openssl and is handled here * dependencies are no longer guarded by "kernel_linux? ( )", it only served to silence pkgcheck and then give build failures (linux-only ebuilds should be masked on non-Linux profiles or, if mixed, use a masked MODULES_OPTIONAL_IUSE which *can* be kernel_linux). Tentative changes: * drop KERNEL_ABI support, (nowadays) kernel seems to append its own -m32/-m64 and should be no need for multilib.eclass complications (tested to work *at least* with x32[userland]+64bit[kernel]) * ^ but add hppa2.0->64 kgcc64 switching like kernel-build.eclass * drop addpredict wrt bug #653286, assuming no longer relevant given unable to reproduce even with kernel-4.14.315+split-debug+some misc modules, perhaps would with spl but that (removed) ebuild is too broken to try Misc changes: * misc -> extra default install dir, to match the kernel's defaults (this is also where zfs-kmod already installs due to that default) Three bugs were addressed, but not closing given -r0 remains affected: * bug #447352: modules signing is supported * bug #759238: arguably not an issue anymore in -r0 either due to CHECKCONFIG_DONOTHING=1 (bug #862315) now existing, but -r1 additionally makes it non-fatal if a whitelist exists in the kernel * bug #816024: trying to select toolchain better is a -r1 highlight Bug: https://bugs.gentoo.org/447352 Bug: https://bugs.gentoo.org/759238 Bug: https://bugs.gentoo.org/816024 Signed-off-by: Ionen Wolkens --- eclass/linux-mod-r1.eclass | 1199 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1199 insertions(+) create mode 100644 eclass/linux-mod-r1.eclass diff --git a/eclass/linux-mod-r1.eclass b/eclass/linux-mod-r1.eclass new file mode 100644 index 000000000000..27f4ceeb2d85 --- /dev/null +++ b/eclass/linux-mod-r1.eclass @@ -0,0 +1,1199 @@ +# Copyright 2023 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: linux-mod-r1.eclass +# @MAINTAINER: +# Ionen Wolkens +# Gentoo Kernel project +# @AUTHOR: +# Ionen Wolkens +# @SUPPORTED_EAPIS: 8 +# @PROVIDES: linux-info +# @BLURB: Functions for installing out-of-tree Linux kernel modules +# @DESCRIPTION: +# See the linux-mod-r1_src_compile function documentation for in-depth +# usage, and see the example further down for a quick overview. +# +# @SUBSECTION linux-mod -> linux-mod-r1 migration overview +# 0. Define a src_compile if missing, local variables below go there. +# 1. MODULE_NAMES="name(libdir:srcdir:objdir)" +# BUILD_TARGETS="target" +# -> local modlist=( name=libdir:srcdir:objdir:target(s) ) +# - try without :target first, it is now almost always unnecessary +# - srcdir defaults to the current directory, and note that paths +# can be relative to that (should typically *not* pass ${S}) +# - "name(misc)" or "(extra)" are fine just as modlist=( name ) +# 2. BUILD_PARAMS and/or BUILD_FIXES +# -> local modargs=( VAR="${KV_OUT_DIR}" ... ) +# - CC/LD and similar are unneeded, always passed (V=1 too) +# - eval (aka eval "${BUILD_PARAMS}") is /not/ used for this anymore +# 3. s/linux-mod_/linux-mod-r1/g +# 4. _preinst+_postrm can be dropped, keep linux-mod-r1_pkg_postinst +# 5. linux-mod-r1_src_install now runs einstalldocs, adjust as needed +# 6. if *not* using linux-mod-r1_src_compile/install, should look at: +# modules_makeargs_to_array + linux_domodule + modules_post_process +# 7. If any, clang<->gcc switching custom workarounds can be dropped +# 8. See MODULES_KERNEL_MAX/_MIN if had or need kernel version checks. +# +# Not an exhaustive list, verify that no installed files are missing +# after. Look for "command not found" errors in the build log too. +# +# @EXAMPLE: +# +# If source directory S had a layout such as: +# - Makefile (builds a gentoo.ko in current directory) +# - gamepad/Makefile (want to install to kernel/drivers/hid) +# - gamepad/obj/ (the built gamepad.ko ends up here) +# +# then: +# +# @CODE +# CONFIG_CHECK="INPUT_FF_MEMLESS" # gamepad needs it to rumble +# MODULES_KERNEL_MIN=5.4 # needs features introduced in 5.4 +# +# src_compile() { +# local modlist=( +# # module-name=install-dir:source-dir:build-dir:make-target(s) +# gentoo +# gamepad=kernel/drivers/hid:gamepad:gamepad/obj +# ) +# local modargs=( +# # Makefile *here* uses this, should inspect Makefiles for the +# # right source variable (about KV_, see linux-info eclass). +# NIH_KERNEL_SOURCE_VARIABLE="${KV_OUT_DIR}" +# ) +# +# linux-mod-r1_src_compile +# } +# @CODE +# +# Or if using the package's build system directly is more convenient: +# +# @CODE +# src_compile() { +# local emakeargs=( +# KDIR="${KV_OUT_DIR}" +# KSRC="${KV_DIR}" +# ) +# modules_makeargs_to_array emakeargs # adds ARCH, CC, etc.. +# +# emake "${emakeargs[@]}" +# } +# +# src_install() { +# emake ... install # or linux_domodule ... +# +# modules_post_process # strip->sign->compress +# } +# @CODE +# +# (please ensure linux-mod-r1_pkg_postinst is ran in either methods) + +case ${EAPI} in + 8) ;; + *) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;; +esac + +if [[ ! ${_LINUX_MOD_R1_ECLASS} ]]; then +_LINUX_MOD_R1_ECLASS=1 + +inherit edo linux-info multiprocessing toolchain-funcs + +IUSE="dist-kernel modules-sign +strip ${MODULES_OPTIONAL_IUSE}" + +RDEPEND=" + sys-apps/kmod[tools] + dist-kernel? ( virtual/dist-kernel:= ) +" +DEPEND=" + virtual/linux-sources +" +BDEPEND=" + sys-apps/kmod[tools] + modules-sign? ( + dev-libs/openssl + virtual/pkgconfig + ) +" +IDEPEND=" + sys-apps/kmod[tools] +" + +if [[ -n ${MODULES_OPTIONAL_IUSE} ]]; then + : "${MODULES_OPTIONAL_IUSE#+}? ( | )" + RDEPEND=${_/|/${RDEPEND}} DEPEND=${_/|/${DEPEND}} \ + BDEPEND=${_/|/${BDEPEND}} IDEPEND=${_/|/${IDEPEND}} +fi + +# @ECLASS_VARIABLE: KERNEL_CHOST +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Can be set to the CHOST value to use when selecting the toolchain +# for building kernel modules. This is similar to setting the kernel +# build system's CROSS_COMPILE variable minus the trailing dash. +# +# If this does not auto-select the desired toolchain, finer control +# can be achieved by setting the not directly documented (but valid) +# variables: +# +# KERNEL_{CC,CXX,LD,AR,NM,OBJCOPY,OBJDUMP,READELF,STRIP} +# +# If in doubt, do not set any of this. +# +# Default if unset: auto-detection, typically same as the current CHOST + +# @ECLASS_VARIABLE: MODULES_EXTRA_EMAKE +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Extra arguments to pass to emake when building modules. +# Can contain arguments with quoted spaces, e.g. +# @CODE +# ..._EMAKE="KCFLAGS='-fzomg-optimize -fsuper-strict-aliasing' ..." +# @CODE + +# @ECLASS_VARIABLE: MODULES_I_WANT_FULL_CONTROL +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# When set to a non-empty value, disables passing most of the eclass' +# defaults to emake when building modules. Users' MODULES_EXTRA_EMAKE +# and ebuilds' modargs will still be used if set. Primarily +# intended for expert users with modified kernel Makefiles. +# +# May want to look at KERNEL_CHOST before considering this. + +# @ECLASS_VARIABLE: MODULES_SIGN_HASH +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Used with USE=modules-sign. Can be set to hash algorithm to use +# during signature generation. +# +# Rather than set this, it is recommended to select using the kernel's +# configuration to ensure proper support (e.g. CONFIG_MODULE_SIG_SHA256), +# and then it will be auto-detected here. +# +# Valid values: sha512,sha384,sha256,sha224,sha1 +# +# Default if unset: kernel CONFIG_MODULE_SIG_HASH's value + +# @ECLASS_VARIABLE: MODULES_SIGN_KEY +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Used with USE=modules-sign. Can be set to the path of the private +# key in PEM format to use, or a PKCS#11 URI. +# +# If path is relative (e.g. "certs/name.pem"), it is assumed to be +# relative to the kernel build directory being used. +# +# If the key requires a passphrase or PIN, the used kernel sign-file +# utility recognizes the KBUILD_SIGN_PIN environment variable. Be +# warned that the package manager may store this value in binary +# packages, database files, temporary files, and possibly logs. This +# eclass unsets the variable after use to mitigate the issue (notably +# for shared binary packages), but use this with care. +# +# Default if unset: kernel CONFIG_MODULE_SIG_KEY's value which itself +# defaults to certs/signing_key.pem + +# @ECLASS_VARIABLE: MODULES_SIGN_CERT +# @USER_VARIABLE +# @DESCRIPTION: +# Used with USE=modules-sign. Can be set to the path of the X.509 +# public key certificate to use. +# +# If path is relative (e.g. "certs/name.x509"), it is assumed to be +# relative to the kernel build directory being used. +: "${MODULES_SIGN_CERT:=certs/signing_key.x509}" + +# @ECLASS_VARIABLE: INSTALL_MOD_PATH +# @USER_VARIABLE +# @DEFAULT_UNSET +# @DESCRIPTION: +# Prefix to install modules to, i.e. /lib/modules. +# Recognized to mimic the kernel's own ``make modules_install``. +# +# If in doubt, do not set this. + +# @ECLASS_VARIABLE: MODULES_KERNEL_MAX +# @DEFAULT_UNSET +# @DESCRIPTION: +# If set to a kernel version (format: 1, 1.2, or 1.2.3), will print a +# warning if the used version is greater than (ver_test -gt) to this +# value using the same amount of version components (i.e. MAX=1.2 +# allows 1.2.3, but MAX=1.2.2 does not). +# +# This should *only* be used for modules that are known to break +# frequently on upgrades. If setting this to a non-LTS kernel, then +# should also take care to test and update this value regularly with +# new major kernel releases not to let the warning become stale and +# ignored by users. +# +# Not fatal to allow users to try or self-patch easily, but the (large) +# warning is difficult to miss. If need a fatal check for more serious +# issues (e.g. filesystem corruption), please do it manually. +# +# This is intended to reduce the amount of bug reports for recurring +# expected issues that can be easily mitigated by using LTS kernels +# and waiting for new releases. +# +# If used, must be set before linux-mod-r1_pkg_setup is called. + +# @ECLASS_VARIABLE: MODULES_KERNEL_MIN +# @DEFAULT_UNSET +# @DESCRIPTION: +# If set to a kernel version (format: 1, 1.2, or 1.2.3), will abort if +# the used version is less than (ver_test -lt) this value. +# +# Should only be used if known broken, or if upstream recommends a sane +# minimum. Not particularly necessary for kernels that are no longer +# in the tree. +# +# If used, must be set before linux-mod-r1_pkg_setup is called. + +# @ECLASS_VARIABLE: MODULES_OPTIONAL_IUSE +# @PRE_INHERIT +# @DEFAULT_UNSET +# @DESCRIPTION: +# May contain a single flag to be added to IUSE optionally prefixed +# with a + sign to enable it by default. Doing so makes *all* of +# linux-mod-r1's functions and dependencies a no-op unless the flag +# is enabled. This includes phases, e.g. linux-mod-r1_pkg_setup will +# not process CONFIG_CHECK unless the flag is set. +# +# The typical recommended value is "+modules". +# +# Note that modules being optional can be useful even if user space +# tools require them (e.g. installing in a chroot or prefix when the +# modules are loaded on the host, saves setting up linux sources). +# However, if tools are non-trivial to build, it may be preferable +# to split into two packages than use this variable due to requiring +# rebuilds every kernel upgrades. + +# @FUNCTION: linux-mod-r1_pkg_setup +# @DESCRIPTION: +# Required before using other functions from this eclass, and will: +# 1. run linux-info_pkg_setup (see linux-info.eclass) +# -> implies processing CONFIG_CHECK, and providing KV_* variables +# (MODULES and TRIM_UNUSED_KSYMS are checked by default) +# 2. prepare toolchain to match the kernel +# -> sets KERNEL_{CHOST,CC,CXX,LD,AR,NM,OBJCOPY,OBJDUMP,READELF,STRIP} +# 3. perform various sanity check to fail early on issues +linux-mod-r1_pkg_setup() { + debug-print-function ${FUNCNAME[0]} "${@}" + [[ ${MERGE_TYPE} != binary ]] || return 0 + _MODULES_GLOBAL[ran:pkg_setup]=1 + _modules_check_function ${#} 0 0 || return 0 + _modules_check_migration + + _modules_prepare_kernel + + # note: modules-specific check_modules_supported could probably be + # removed from linux-info in the future as this is a sufficient check + local CONFIG_CHECK="${CONFIG_CHECK} MODULES" + + # kernel will not typically know about symbols we use (bug #591832), + # but stay non-fatal if kernel has an exception list set (bug #759238) + # note: possible to bypass either way with CHECKCONFIG_DONOTHING=1 + if [[ $(linux_chkconfig_string UNUSED_KSYMS_WHITELIST) == \"+(?)\" ]]; then + CONFIG_CHECK+=" ~!TRIM_UNUSED_KSYMS" + else + CONFIG_CHECK+=" !TRIM_UNUSED_KSYMS" + fi + + linux-info_pkg_setup + + _modules_prepare_sign + _modules_prepare_toolchain +} + +# @FUNCTION: linux-mod-r1_src_compile +# @DESCRIPTION: +# Builds modules, see the eclass' example for a quick overview. +# Uses the variables modlist and modargs as described below: +# +# * local modlist=( ... ) - list of modules to build, set as: +# +# module-name=install-dir:source-dir:build-dir:make-target +# +# > module-name: Resulting name, aka .ko (required). +# +# > install-dir: Kernel modules sub-directory to install the module +# to (/lib/modules/version//name.ko). Will be used when +# run linux-mod-r1_src_install. May want to consider the values of +# INSTALL_MOD_DIR(Makefile) or DEST_MODULE_LOCATION(dkms.conf) if it +# exists, but it can be anything. +# -> Default: extra +# +# > source-dir: Directory containing the Makefile to build the module, +# path can be relative to the current directory or absolute. +# -> Default: current directory +# +# > build-dir: Directory that will hold the built module-name.ko. +# -> Default: same as source-dir's value +# +# > make-target: Almost always unneeded but, if defaults are not right, +# then can specify the Makefile's target(s) to build the module/extras. +# Multiple targets can be used with spaces, e.g. :"first second". +# -> Default: specially tries modules, module, .ko, , +# default, all, empty target, and runs the first found usable +# +# Missing elements results in defaults being used, e.g. this is valid: +# modlist=( name1 name2=:source name3=install::build ) +# +# Tip: If all modules need the same arguments, they can be repeated by: +# modlist=( {mod1,mod2,mod3}=arguments ) +# +# * local modargs=( ... ) - extra arguments to pass to emake +# +# Makefile should notably be inspected for which variable it uses +# to find the kernel's build directory then, e.g. KDIR="${KV_OUT_DIR}" +# as appropriate. Note that typically want to pass KV_OUT_DIR(build) +# rather than KV_DIR(sources) if not both. This allows users to do +# out-of-source kernel builds and still build modules. +# +# Passing common toolchain variables such as CC or LD is not needed +# here as they are passed by default. +# +# --- +# +# Allowed to be called multiple times with a different modlist if need +# different make arguments per modules or intermediate steps -- albeit, +# if atypical, may want to build manually (see eclass' example). +linux-mod-r1_src_compile() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 0 0 || return 0 + + [[ ${modlist@a} == *a* && ${#modlist[@]} -gt 0 ]] || + die "${FUNCNAME[0]} was called without a 'modlist' array" + + # run this again to verify built files access with src_compile's user + _modules_sanity_kernelbuilt + + local -a emakeargs + modules_makeargs_to_array emakeargs + + [[ ${modargs@a} == *a* ]] && emakeargs+=( "${modargs[@]}" ) + + local -A built=() + local build mod name target + for mod in "${modlist[@]}"; do + # note modlist was not made a [name]= associative array to preserve + # ordering, but is still using = to improve readability + name=${mod%%=*} + [[ -n ${name} && ${name} != *:* ]] || die "invalid mod entry '${mod}'" + + # 0:install-dir 1:source-dir 2:build-dir 3:make-target(s) + mod=${mod#"${name}"} + IFS=: read -ra mod <<<"${mod#=}" + [[ ${#mod[@]} -le 4 ]] || die "too many ':' in ${name}'s modlist" + + [[ ${mod[1]:=${PWD}} != /* ]] && mod[1]=${PWD}/${mod[1]} + [[ ${mod[2]:=${mod[1]}} != /* ]] && mod[2]=${PWD}/${mod[2]} + _MODULES_INSTALL[${mod[2]}/${name}.ko]=${mod[0]:-extra} + + pushd "${mod[1]}" >/dev/null || die + + if [[ -z ${mod[3]} ]]; then + # guess between commonly used targets if none given, fallback to + # an empty target without trying to see the error output + for target in module{s,} "${name}"{.ko,} default all; do + nonfatal emake "${emakeargs[@]}" -q "${target}" &>/dev/null + if [[ ${?} -eq 1 ]]; then + mod[3]=${target} + break + fi + done + fi + + # sometime modules are all from same source dir and built all at once, + # make will not rebuild either way but can skip the unnecessary noise + build= + for target in ${mod[3]:-&}; do + if ! has "${target}" ${built[${mod[1]}]}; then + build=1 + built[${mod[1]}]+=" ${target} " + fi + done + + if [[ ${build} ]]; then + einfo "Building ${name} module in ${mod[1]} ..." + + # allow word splitting for rare cases of multiple targets + emake "${emakeargs[@]}" ${mod[3]} + else + einfo "Building ${name} module in ${mod[1]} ... already done." + fi + + popd >/dev/null || die + done +} + +# @FUNCTION: linux-mod-r1_src_install +# @DESCRIPTION: +# Installs modules built by linux-mod-r1_src_compile using +# linux_domodule, then runs modules_post_process and einstalldocs. +linux-mod-r1_src_install() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 0 0 || return 0 + + (( ${#_MODULES_INSTALL[@]} )) || + die "${FUNCNAME[0]} was called without running linux-mod-r1_src_compile" + + ( + for mod in "${!_MODULES_INSTALL[@]}"; do + linux_moduleinto "${_MODULES_INSTALL[${mod}]}" + linux_domodule "${mod}" + done + ) + + modules_post_process + + einstalldocs +} + +# @FUNCTION: linux-mod-r1_pkg_postinst +# @DESCRIPTION: +# Updates module dependencies using depmod. +linux-mod-r1_pkg_postinst() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 0 0 || return 0 + + _modules_update_depmod + + # post_process ensures modules were installed and that the eclass' USE + # are likely not no-ops (unfortunately postinst itself may be missed) + [[ -v _MODULES_GLOBAL[ran:post_process] ]] || + eqawarn "QA Notice: neither linux-mod-r1_src_install nor modules_post_process were used" +} + +# @FUNCTION: linux_domodule +# @USAGE: ... +# @DESCRIPTION: +# Installs Linux modules (.ko files). +# +# See linux_moduleinto for more information and changing directories. +linux_domodule() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 1 '' "..." || return 0 + ( + # bug #642240: questionable how used/useful this is through ebuilds, + # but old linux-mod-r0, kernel, and some other module-related build + # systems recognize it leaving us the odd one out + insinto "${INSTALL_MOD_PATH}/lib/modules/${KV_FULL}/${_MODULES_GLOBAL[moduleinto]:-extra}" + doins "${@}" + ) +} + +# @FUNCTION: linux_moduleinto +# @USAGE: +# @DESCRIPTION: +# Directory to install modules into when calling linux_domodule. +# Relative to kernel modules path as in: +# ${ED}${INSTALL_MOD_PATH}/lib/modules/${KV_FULL}/ +# +# Can contain subdiretories, e.g. kernel/fs. +# +# If not called, defaults to "extra". On the kernel build system, +# this is like setting INSTALL_MOD_DIR which has the same default. +linux_moduleinto() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 1 1 "" || return 0 + _MODULES_GLOBAL[moduleinto]=${1} +} + +# @FUNCTION: modules_makeargs_to_array +# @USAGE: +# @DESCRIPTION: +# Convenience function to append eclass' default modules make args such +# as CC="${KERNEL_CC}" and ARCH="$(tc-arch-kernel)" to an array that can +# be used to, e.g. `emake "${array-name[@]}"`. +# +# Primarily intended for when not relying on linux-mod-r1_src_compile. +modules_makeargs_to_array() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 1 1 "" || return 0 + + local -n _modules_args=${1} + + _modules_args+=( ARCH="$(tc-arch-kernel)" ) + + if [[ ${MODULES_I_WANT_FULL_CONTROL} ]]; then + # keep ARCH given it is otherwise very broken in ebuilds, but users + # can still override the value through this MODULES_EXTRA_EMAKE + # (eval is to handle quoted spaces, die is for syntax errors) + eval "_modules_args+=( ${MODULES_EXTRA_EMAKE} )" || die + else + # many of these are unlikely to be useful here, but still trying to be + # complete given never know what out-of-tree modules may use + _modules_args+=( + V=1 + # redundant with V, but needed sometimes (e.g. virtualbox-modules) + KBUILD_VERBOSE=1 + + # wrt bug #550428, given most toolchain variables are being passed to + # make, setting CROSS in the environment would change very little + # (instead set KERNEL_CHOST which will affect other variables, + # or MODULES_I_WANT_FULL_CONTROL if do not want any of this) + CROSS_COMPILE="${KERNEL_CHOST}-" + + HOSTCC="$(tc-getBUILD_CC)" + HOSTCXX="$(tc-getBUILD_CXX)" + + # fwiw this function is not meant to pollute the environment + HOSTCFLAGS="$(tc-export_build_env; echo "${BUILD_CFLAGS}")" + HOSTCXXFLAGS="$(tc-export_build_env; echo "${BUILD_CXXFLAGS}")" + HOSTLDFLAGS="$(tc-export_build_env; echo "${BUILD_LDFLAGS}")" + + HOSTPKG_CONFIG="$(tc-getBUILD_PKG_CONFIG)" + + CC="${KERNEL_CC}" + CXX="${KERNEL_CXX}" + LD="${KERNEL_LD}" + AR="${KERNEL_AR}" + NM="${KERNEL_NM}" + OBJCOPY="${KERNEL_OBJCOPY}" + OBJDUMP="${KERNEL_OBJDUMP}" + READELF="${KERNEL_READELF}" + STRIP=: + ) + + eval "_modules_args+=( ${MODULES_EXTRA_EMAKE} )" || die + fi +} + +# @FUNCTION: modules_post_process +# @USAGE: [] +# @DESCRIPTION: +# Strip, sign, verify, and compress all .ko modules found under +# . Should typically *not* be called directly as it will +# be run by linux-mod-r1_src_install. +# +# should exist under ${ED}${INSTALL_MOD_PATH}. +# If unspecified it defaults to /lib/modules/${KV_FULL} +# +# Filenames may change due to compression, so any operations on +# these should be performed prior. +# +# This is intended for use when modules were installed some other way. +# +# If installing through modules_install rather than linux_domodule, +# could need to manually disable related features by doing, e.g.: +# @CODE +# local makeargs=( +# ... +# CONFIG_MODULE_{SIG_ALL,COMPRESS_{GZIP,XZ,ZSTD}}= +# DEPMOD=: +# ) +# emake "${makeargs[@]}" modules_install +# modules_post_process +# @CODE +# +# Warning: If this finds no modules it will abort, which can happen if +# modules were unexpectedly pre-compressed (likely due to the kernel +# config) as it only looks at .ko filenames. +modules_post_process() { + debug-print-function ${FUNCNAME[0]} "${@}" + _modules_check_function ${#} 0 1 '[]' || return 0 + [[ ${EBUILD_PHASE} == install ]] || + die "${FUNCNAME[0]} can only be called in the src_install phase" + + local path=${ED}${INSTALL_MOD_PATH}${1-/lib/modules/${KV_FULL}} + local -a mods + [[ -d ${path} ]] && mapfile -td '' mods < <( + find "${path}" -type f -name '*.ko' -print0 || die + ) + (( ${#mods[@]} )) || + die "${FUNCNAME[0]} was called with no installed modules to process" + + _modules_process_strip "${mods[@]}" + _modules_process_sign "${mods[@]}" + _modules_sanity_modversion "${mods[@]}" # after strip/sign in case broke it + _modules_process_compress "${mods[@]}" + + _MODULES_GLOBAL[ran:post_process]=1 +} + +# @ECLASS_VARIABLE: _MODULES_GLOBAL +# @INTERNAL +# @DESCRIPTION: +# General use associative array to avoid defining separate globals. +declare -gA _MODULES_GLOBAL=() + +# @ECLASS_VARIABLE: _MODULES_INSTALL +# @INTERNAL +# @DESCRIPTION: +# List of modules from linux-mod-r1_src_compile to be installed. +declare -gA _MODULES_INSTALL=() + +# @FUNCTION: _modules_check_function +# @USAGE: [ []] +# @RETURN: 0 or 1 if caller should do nothing +# @INTERNAL +# @DESCRIPTION: +# Checks for MODULES_OPTIONAL_IUSE, and aborts if amount of arguments +# does not add up or if it was called before linux-mod-r1_pkg_setup. +_modules_check_function() { + [[ -z ${MODULES_OPTIONAL_IUSE} ]] || + use "${MODULES_OPTIONAL_IUSE#+}" || return 1 + + [[ ${#} == 0 || ${1} -ge ${2} && ( ! ${3} || ${1} -le ${3} ) ]] || + die "Usage: ${FUNCNAME[1]} ${4-(no arguments)}" + + [[ -v _MODULES_GLOBAL[ran:pkg_setup] ]] || + die "${FUNCNAME[1]} was called without running linux-mod-r1_pkg_setup" +} + +# @FUNCTION: _modules_check_migration +# @INTERNAL +# @DESCRIPTION: +# Dies if see obsolete variables from the linux-mod-r0 eclass being +# used likely due to an incomplete migration. This function should be +# removed after linux-mod-r0 is @DEAD not to fail for nothing if users +# happen to have these in their environment given the naming for some +# is a bit generic. +_modules_check_migration() { + _modules_check_var() { + [[ -z ${!1} ]] || + die "${1} is obsolete, see ${2} in linux-mod-r1 eclassdocs" + } + # the 'I' on this one is notably sneaky and could silently be ignored + _modules_check_var MODULES_OPTIONAL_USE MODULES_OPTIONAL_IUSE + _modules_check_var MODULES_OPTIONAL_USE_IUSE_DEFAULT MODULES_OPTIONAL_IUSE + _modules_check_var BUILD_PARAMS modargs + _modules_check_var BUILD_TARGETS modlist + _modules_check_var MODULE_NAMES modlist + [[ -z ${!MODULESD_*} ]] || + die "MODULESD_* variables are no longer supported, replace by handcrafted .conf files if needed" + + # Ignored variables: + # - BUILD_FIXES: seen in some ebuilds but was undocumented and linux-info + # still sets it preventing from blocking it entirely + # - ECONF_PARAMS: documented but was a no-op in linux-mod too +} + +# @FUNCTION: _modules_prepare_kernel +# @INTERNAL +# @DESCRIPTION: +# Uses linux-info to find kernel sources (sets KV_ variables), then +# performs sanity checks to see if usable to build modules and abort +# otherwise. +_modules_prepare_kernel() { + get_version + + # linux-info allows skipping checks if SKIP_KERNEL_CHECK is set and + # then require_configured_kernel will not abort, but no sources means + # 100% failure for building modules and so just abort now (the proper + # way to allow skipping sources here is MODULES_OPTIONAL_IUSE) + [[ -n ${KV_FULL} ]] || + die "kernel sources are required to build kernel modules" + + require_configured_kernel + + _modules_sanity_kernelbuilt + _modules_sanity_kernelversion +} + +# @FUNCTION: _modules_prepare_sign +# @INTERNAL +# @DESCRIPTION: +# Determines arguments to pass to sign-file (hash/keys), and performs +# basic sanity checks to abort early if signing does not look possible. +_modules_prepare_sign() { + use modules-sign || return 0 + + _modules_sign_die() { + eerror "USE=modules-sign requires additional configuration, please see the" + eerror "kernel[1] documentation and the linux-mod-r1 eclass[2] user variables." + eerror "[1] https://www.kernel.org/doc/html/v${KV_MAJOR}.${KV_MINOR}/admin-guide/module-signing.html" + eerror "[2] https://devmanual.gentoo.org/eclass-reference/linux-mod-r1.eclass/index.html" + die "USE=modules-sign is set but ${*}" + } + + linux_chkconfig_present MODULE_SIG || + _modules_sign_die "CONFIG_MODULE_SIG is not set in the kernel" + + if [[ -z ${MODULES_SIGN_HASH} ]]; then + : "$(linux_chkconfig_string MODULE_SIG_HASH)" + MODULES_SIGN_HASH=${_//\"} + [[ -n ${MODULES_SIGN_HASH} ]] || + _modules_sign_die "CONFIG_MODULE_SIG_HASH is not set in the kernel" + fi + + if [[ -z ${MODULES_SIGN_KEY} ]]; then + : "$(linux_chkconfig_string MODULE_SIG_KEY)" + MODULES_SIGN_KEY=${_//\"} + [[ -n ${MODULES_SIGN_KEY} ]] || + _modules_sign_die "CONFIG_MODULE_SIG_KEY is not set in the kernel" + fi + + [[ ${MODULES_SIGN_KEY} != @(/|pkcs11:)* ]] && + MODULES_SIGN_KEY=${KV_OUT_DIR}/${MODULES_SIGN_KEY} + [[ ${MODULES_SIGN_CERT} != /* ]] && + MODULES_SIGN_CERT=${KV_OUT_DIR}/${MODULES_SIGN_CERT} + + # assumes users know what they are doing if using a pkcs11 URI + [[ ${MODULES_SIGN_KEY} == pkcs11:* || -f ${MODULES_SIGN_KEY} ]] || + _modules_sign_die "the private key '${MODULES_SIGN_KEY}' was not found" + [[ -f ${MODULES_SIGN_CERT} ]] || + _modules_sign_die "the public key certificate '${MODULES_SIGN_CERT}' was not found" +} + +# @FUNCTION: _modules_prepare_toolchain +# @INTERNAL +# @DESCRIPTION: +# Sets KERNEL_{CC,CXX,LD,AR,NM,OBJCOPY,OBJDUMP,READELF,STRIP} based on +# the kernel configuration and KERNEL_CHOST (also set if missing) that +# *should* be usable to build modules. +# +# Tries to match compiler type (gcc or clang), and major version. +# Users can set KERNEL_ variables themselves to override. +# +# Also performs some sanity checks and informs about possible issues. +# +# These variables are normally manipulated by the kernel's LLVM=1 with +# the exception of CXX that is included anyway given *some* out-of-tree +# modules use it, e.g. nvidia-drivers[kernel-open]. +_modules_prepare_toolchain() { + # note that the kernel adds -m32/-m64 by default (for e.g. x32), but + # may need automagic here if want a different toolchain (e.g. kgcc64) + [[ -z ${KERNEL_CHOST} ]] && linux_chkconfig_present 64BIT && + case ${CHOST} in + # matching kernel-build.eclass, see for details + hppa2.0-*) KERNEL_CHOST=${CHOST/2.0/64};; + esac + + # recognizing KERNEL_CHOST given CROSS_COMPILE seems too generic here, + # but should rarely be necessary unless different userland and kernel + : "${KERNEL_CHOST:=${CHOST}}" + + einfo "Preparing ${KERNEL_CHOST} toolchain for kernel modules (override with KERNEL_CHOST) ..." + + _modules_tc_best() { + [[ -z ${!1} ]] && read -r ${1} < <(type -P -- "${@:2}") + } + + local gccv clangv tool + if linux_chkconfig_present CC_IS_GCC; then + gccv=$(linux_chkconfig_string GCC_VERSION) + gccv=${gccv::2} # major version, will break on gcc-100... + # chost-gcc-ver > chost-gcc > gcc-ver > gcc + _modules_tc_best KERNEL_CC {"${KERNEL_CHOST}-",}gcc{"-${gccv}",} + _modules_tc_best KERNEL_CXX {"${KERNEL_CHOST}-",}g++{"-${gccv}",} + # unknown what was used exactly here, but prefer non-llvm with gcc + for tool in AR NM OBJCOPY OBJDUMP READELF STRIP; do + _modules_tc_best KERNEL_${tool} \ + {"${KERNEL_CHOST}-",}{gcc-,}${tool,,} + done + elif linux_chkconfig_present CC_IS_CLANG; then + clangv=$(linux_chkconfig_string CLANG_VERSION) + clangv=${clangv::2} + # like gcc, but try directories to get same version on all tools + # (not using get_llvm_prefix to avoid conflicts with ebuilds using + # llvm slots for non-modules reasons, e.g. sets llvm_check_deps) + _modules_tc_best KERNEL_CC \ + {"${BROOT}/usr/lib/llvm/${clangv}/bin/",}{"${KERNEL_CHOST}-",}clang{"-${clangv}",} + _modules_tc_best KERNEL_CXX \ + {"${BROOT}/usr/lib/llvm/${clangv}/bin/",}{"${KERNEL_CHOST}-",}clang++{"-${clangv}",} + for tool in AR NM OBJCOPY OBJDUMP READELF STRIP; do + _modules_tc_best KERNEL_${tool} \ + {"${BROOT}/usr/lib/llvm/${clangv}/bin/",}{"${KERNEL_CHOST}-",}{llvm-,}${tool,,} + done + fi + + if linux_chkconfig_present LD_IS_BFD; then + _modules_tc_best KERNEL_LD {"${KERNEL_CHOST}-",}ld.bfd + elif linux_chkconfig_present LD_IS_LLD; then + # also match with clang if it was used + _modules_tc_best KERNEL_LD \ + {${clangv+"${BROOT}/usr/lib/llvm/${clangv}/bin/"},}{"${KERNEL_CHOST}-",}ld.lld + fi + + # if any variables are still empty, fallback to normal defaults + local CHOST=${KERNEL_CHOST} + : "${KERNEL_CC:=$(tc-getCC)}" + : "${KERNEL_CXX:=$(tc-getCXX)}" + : "${KERNEL_LD:=$(tc-getLD)}" + : "${KERNEL_AR:=$(tc-getAR)}" + : "${KERNEL_NM:=$(tc-getNM)}" + : "${KERNEL_OBJCOPY:=$(tc-getOBJCOPY)}" + : "${KERNEL_OBJDUMP:=$(tc-getOBJDUMP)}" + : "${KERNEL_READELF:=$(tc-getREADELF)}" + : "${KERNEL_STRIP:=$(tc-getSTRIP)}" + + # for toolchain-funcs, uses CPP > CC but set both not to make assumptions + local CC=${KERNEL_CC} CPP="${KERNEL_CC} -E" LD=${KERNEL_LD} + + # show results, skip line wrap to avoid standing out too much + einfo "Toolchain picked for kernel modules (override with KERNEL_CC, _LD, ...):"\ + "'${KERNEL_CC}' '${KERNEL_CXX}' '${KERNEL_LD}' '${KERNEL_AR}'"\ + "'${KERNEL_NM}' '${KERNEL_OBJCOPY}' '${KERNEL_OBJDUMP}'"\ + "'${KERNEL_READELF}' '${KERNEL_STRIP}'" + + # hack: kernel adds --thinlto-cache-dir to KBUILD_LDFLAGS with ThinLTO + # resulting in sandbox violations and we cannot safely override that + # variable, using *both* {LDFLAGS_MODULE,ldflags-y}=--thinlto-cache-dir= + # can work but raises concerns about breaking packages that may use these + if linux_chkconfig_present LTO_CLANG_THIN && tc-ld-is-lld; then + KERNEL_LD=${T}/linux-mod-r1_ld.lld + printf '#!/usr/bin/env sh\nexec %s "${@}" --thinlto-cache-dir=\n' \ + "${LD}" > "${KERNEL_LD}" || die + chmod +x -- "${KERNEL_LD}" || die + fi + + # perform a (fatal) check for gcc plugins mismatch + _modules_sanity_gccplugins + + # warn if final picked CC type or major version is mismatching, arguably + # should be fatal too but not forcing without being sure it is broken + local warn + if [[ -v gccv ]]; then + if ! tc-is-gcc; then + warn="gcc-${gccv} but '${KERNEL_CC}' is not gcc" + elif [[ $(gcc-major-version) -ne "${gccv}" ]]; then + warn="gcc-${gccv} but '${KERNEL_CC}' is gcc-$(gcc-major-version)" + fi + elif [[ -v clangv ]]; then + if ! tc-is-clang; then + warn="clang-${clangv} but '${KERNEL_CC}' is not clang" + elif [[ $(clang-major-version) -ne "${clangv}" ]]; then + warn="clang-${clangv} but '${KERNEL_CC}' is clang-$(clang-major-version)" + fi + fi + + if [[ -v warn ]]; then + ewarn "Warning: kernel ${KV_FULL} is built with ${warn}" + ewarn "This could result in modules build failure, it is recommended to either" + ewarn "\`make clean\` and rebuild the kernel with the current toolchain, or set" + ewarn "the KERNEL_CC variable to point to the same compiler." + fi +} + +# @FUNCTION: _modules_process_compress +# @USAGE: ... +# @INTERNAL +# @DESCRIPTION: +# If enabled in the kernel configuration, this compresses the given +# modules using the same format. +_modules_process_compress() { + local -a compress + if linux_chkconfig_present MODULE_COMPRESS_XZ; then + compress=(xz -qT"$(makeopts_jobs)" --memlimit-compress=50%) + elif linux_chkconfig_present MODULE_COMPRESS_GZIP; then + if type -P pigz &>/dev/null; then + compress=(pigz -p"$(makeopts_jobs)") + else + compress=(gzip) + fi + elif linux_chkconfig_present MODULE_COMPRESS_ZSTD; then + compress=(zstd -qT"$(makeopts_jobs)" --rm) + fi + + if [[ -v compress ]]; then + # could fail, assumes have commands that were needed for the kernel + einfo "Compressing modules (matching the kernel configuration) ..." + edob "${compress[@]}" -- "${@}" + fi +} + +# @FUNCTION: _modules_process_sign +# @USAGE: ... +# @INTERNAL +# @DESCRIPTION: +# Cryptographically signs the given modules when USE=modules-sign is +# enabled. +_modules_process_sign() { + use modules-sign || return 0 + + # scripts/sign-file used to be a perl script but is now written in C, + # and it could either be missing or broken given it links with openssl + # (no subslot rebuilds on kernel sources), trivial to compile regardless + local sign= + if [[ -f ${KV_DIR}/scripts/sign-file.c ]]; then + sign=${T}/linux-mod-r1_sign-file + ( + # unfortunately using the kernel's Makefile is inconvenient (no + # simple build target for this), may need revisiting on changes + einfo "Compiling sign-file ..." + tc-export_build_env + nonfatal edob $(tc-getBUILD_CC) ${BUILD_CFLAGS} ${BUILD_CPPFLAGS} \ + $($(tc-getBUILD_PKG_CONFIG) --cflags libcrypto) \ + ${BUILD_LDFLAGS} -o "${sign}" "${KV_DIR}"/scripts/sign-file.c \ + $($(tc-getBUILD_PKG_CONFIG) --libs libcrypto || echo -lcrypto) + ) || { + einfo "Trying fallback ..." + sign= + } + fi + + if [[ -z ${sign} ]]; then + if [[ -x ${KV_OUT_DIR}/scripts/sign-file ]]; then + sign=${KV_OUT_DIR}/scripts/sign-file # try if built + elif [[ -x ${KV_DIR}/scripts/sign-file ]]; then + sign=${KV_DIR}/scripts/sign-file # old kernel (... +# @INTERNAL +# @DESCRIPTION: +# Strips the given modules of unneeded symbols when USE=strip is +# enabled, and informs the package manager not to regardless. +_modules_process_strip() { + # letting the package manager handle this complicates scenarios + # where we want to either compress the pre-stripped module, or + # sign the module without its signature becoming invalid on merge + dostrip -x "${@#"${ED}"}" + + if use strip; then + einfo "Stripping modules ..." + edob "${KERNEL_STRIP}" --strip-unneeded -- "${@}" + fi +} + +# @FUNCTION: _modules_sanity_gccplugins +# @INTERNAL +# @DESCRIPTION: +# Performs a basic build test to detect gcc plugins mismatch issues +# and, if so, dies with a clearer error given it often confuses users. +# +# Note: may need occasional review to ensure the test still works, +# albeit issue is mitigated by _modules_prepare_toolchain's version +# checks and is not critical. To test, should be sufficient to enable +# a gcc plugin in the kernel, build with a old gcc, then build a module +# by setting KERNEL_CC=gcc-. +_modules_sanity_gccplugins() { + linux_chkconfig_present GCC_PLUGINS || return 0 + + local tmp=${T}/linux-mod-r1_gccplugins + mkdir -p -- "${tmp}" || die + + echo "obj-m += test.o" > "${tmp}"/Kbuild || die + :> "${tmp}"/test.c || die + + local -a emakeargs=( M="${tmp}" ) + modules_makeargs_to_array emakeargs + + # always fails, but interested in the stderr messages + local output=$( + cd -- "${KV_OUT_DIR}" && # fwiw skip non-POSIX -C in eclasses + LC_ALL=C nonfatal emake "${emakeargs[@]}" 2>&1 >/dev/null + ) + + if [[ ${output} == *"error: incompatible gcc/plugin version"* ]]; then + eerror "Detected kernel was built with a different gcc/plugin version, and" + eerror "this will prevent building modules. Please \`make clean\` and rebuild" + eerror "the kernel using the current toolchain." + die "kernel ${KV_FULL} needs to be rebuilt" + fi +} + +# @FUNCTION: _modules_sanity_kernelbuilt +# @INTERNAL +# @DESCRIPTION: +# Checks if the kernel seems fully built by having a Module.symvers +# that is also readable, abort otherwise. +# +# About readability, occasionally users build their kernel as root with +# umask 0077 and then the package manager's user cannot read built files +# leaving them confused. +# +# Given user and access can very between phases (notably src_compile), +# it makes sense to run this check more than once. +# +# Note: +# This is an alternate version of linux-info's check_kernel_built +# which probably will not need to exist there if linux-mod-r0 is +# gone, error it gives is also modules-specific and fits better here. +# +# The old check_kernel_built checks version.h and suggests running +# modules_prepare if missing, but that does not create Module.symvers. +# Nowadays the kernel makes unresolved symbols fatal by default +# meaning that all modules will fail unless KBUILD_MODPOST_WARN=1 +# which seem questionable to support. So rather than version.h, this +# checks and require Module.symvers, and suggests a full build if +# missing (if really must, users can bypass by touching the file). +# nvidia-drivers (for one) further checks this file directly to do +# configure tests that will break badly without. +_modules_sanity_kernelbuilt() { + local symvers=${KV_OUT_DIR}/Module.symvers + + if [[ ! -f ${symvers} ]]; then + eerror "'${symvers}' was not found implying that the" + eerror "linux-${KV_FULL} tree at that location has not been built." + eerror + eerror "Please verify that this is the intended kernel version, then perform" + eerror "a full build[1] (i.e. make && make modules_install && make install)." + eerror + eerror "Alternatively, consider a distribution kernel[2] that does not need" + eerror "these manual steps (e.g. sys-kernel/gentoo-kernel or gentoo-kernel-bin)." + eerror + eerror "[1] https://wiki.gentoo.org/wiki/Kernel/Configuration#Build" + eerror "[2] https://wiki.gentoo.org/wiki/Project:Distribution_Kernel" + die "built kernel sources are required to build kernel modules" + fi + + if [[ ! -r ${symvers} ]]; then + eerror "'${symvers}' exists but cannot be read by the" + eerror "user id(${EUID}) of the package manager, likely implying no world" + eerror "read access permissions:" + eerror + eerror " $(ls -l -- "${symvers}")" + eerror + eerror "Causes may vary, but a common one is building the kernel with a umask" + eerror "value of '0077' rather than the more typical '0022' (run the \`umask\`" + eerror "command to confirm, as root if was building the kernel using it)." + eerror + eerror "Many other files are likely affected and will lead to build failures." + eerror "It is recommended to clean the sources and rebuild with \`umask 0022\`" + eerror "rather than attempt to fix the permissions manually." + die "no read access permission to the generated kernel files" + fi +} + +# @FUNCTION: _modules_sanity_kernelversion +# @INTERNAL +# @DESCRIPTION: +# Prints a warning if the kernel version is greater than to +# MODULES_KERNEL_MAX (while only considering same amount of version +# components), or aborts if it is less than MODULES_KERNEL_MIN +_modules_sanity_kernelversion() { + local kv=${KV_MAJOR}.${KV_MINOR}.${KV_PATCH} + + if [[ -n ${MODULES_KERNEL_MIN} ]] && + ver_test "${kv}" -lt "${MODULES_KERNEL_MIN}" + then + eerror "${P} requires a kernel version of at least >=${MODULES_KERNEL_MIN}," + eerror "but the current kernel is ${KV_FULL}. Please update." + die "kernel ${KV_FULL} is too old" + fi + + if [[ -n ${MODULES_KERNEL_MAX} ]]; then + : "${MODULES_KERNEL_MAX//[^.]/}" + local -i count=${#_} + + if ver_test "$(ver_cut 1-$((count+1)) "${kv}")" \ + -gt "${MODULES_KERNEL_MAX}" + then + # add .x to 1 missing component to make, e.g. <=1.2.x more natural, + # not <1.3 given users sometimes see it as 1.3 support at a glance + local max=${MODULES_KERNEL_MAX} + [[ ${count} -lt 2 ]] && max+=.x + + ewarn + ewarn " *** WARNING *** " + ewarn + ewarn "${PN} is known to break easily with new kernel versions and," + ewarn "with the current kernel (${KV_FULL}), it was either hardly" + ewarn "tested or is known broken. It is recommended to use one of:" + ewarn + ewarn " <=sys-kernel/gentoo-kernel-${max}" + ewarn " <=sys-kernel/gentoo-sources-${max}" + ewarn + ewarn "or equivalent rather than file downstream bug reports if run into" + ewarn "issues, then wait for upstream fixes and a new release. Ideally," + ewarn "with out-of-tree modules, use an LTS (Long Term Support) kernel" + ewarn "branch[1]. If in doubt, Gentoo's stable kernels are always LTS" + ewarn "and can be easily used even on ~testing systems." + ewarn + ewarn "[1] https://www.kernel.org/category/releases.html" + ewarn + fi + fi +} + +# @FUNCTION: _modules_sanity_modversion +# @USAGE: ... +# @INTERNAL +# @DESCRIPTION: +# Checks if the passed module(s) do not seem obviously broken and the +# builtin versions match ${KV_FULL}, otherwise die with an explanation. +# +# If receive a bug with a version error, an easy way to reproduce is to +# set KERNEL_DIR with the sources of a different kernel version than +# both the ones pointed by /usr/src/linux and `uname -r`. +_modules_sanity_modversion() { + local mod ver + for mod; do + # modinfo can read different-arch modules, being fatal *should* be safe + # and serve as a basic sanity check to ensure the module is valid + read -rd ' ' ver < <( + LC_ALL=C modinfo -F vermagic -- "${mod}" || + die "modinfo failed to read module '${mod}' (broken module?)" + ) + [[ -n ${ver} ]] || + die "modinfo found no kernel version in '${mod}' (broken module?)" + + if [[ ${ver} != "${KV_FULL}" ]]; then + eerror "A module seem to have been built for kernel version '${ver}'" + eerror "while it was meant for '${KV_FULL}'. This may indicate an" + eerror "ebuild issue (e.g. used runtime \`uname -r\` kernel rather than" + eerror "the chosen sources). Please report this to the ebuild's maintainer." + die "module and source version mismatch in '${mod}'" + fi + done +} + +# @FUNCTION: _modules_update_depmod +# @INTERNAL +# @DESCRIPTION: +# If possible, update module dependencies using depmod and System.map, +# otherwise prompt user to handle it. System.map may notably no longer +# be available on binary merges. +_modules_update_depmod() { + # prefer /lib/modules' path given it is what depmod operates on, + # and is mostly foolproof when it comes to ROOT (relative symlink) + local map=${EROOT}/lib/modules/${KV_FULL}/build/System.map + + if [[ ! -f ${map} ]]; then + # KV_OUT_DIR may still be right even on a different system, but state + # of (E)ROOT is unknown, e.g. could be from KERNEL_DIR=${OLDROOT}/... + map=${KV_OUT_DIR}/System.map + + # last resort, typical but may not be mounted/readable/installed + [[ ! -f ${map} ]] && + map=${EROOT}/boot/System.map-${KV_FULL} + fi + + einfo "Updating module dependencies for kernel ${KV_FULL} ..." + if [[ -f ${map} ]]; then + nonfatal edob depmod -ae -F "${map}" -b "${EROOT:-/}" "${KV_FULL}" && + return 0 + else + eerror + eerror "System.map for kernel ${KV_FULL} was not found, may be due to the" + eerror "built kernel sources no longer being available and lacking the fallback:" + eerror + eerror "${EROOT}/boot/System.map-${KV_FULL}" + fi + eerror + eerror "Some modules may not load without updating manually using depmod." +} + +fi + +EXPORT_FUNCTIONS pkg_setup src_compile src_install pkg_postinst -- 2.40.1