[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gentoo-dev
Subject:    [gentoo-dev] [PATCH 3/4] linux-mod-r1.eclass: new eclass, rewrite of linux-mod.eclass
From:       Ionen Wolkens <ionen () gentoo ! org>
Date:       2023-05-26 4:02:18
Message-ID: 20230526040219.10852-4-ionen () gentoo ! org
[Download RAW message or body]

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 <kernel-2.6(!) modules, eclass only tested with >=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 <ionen@gentoo.org>
---
 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 <ionen@gentoo.org>
+# Gentoo Kernel project <kernel@gentoo.org>
+# @AUTHOR:
+# Ionen Wolkens <ionen@gentoo.org>
+# @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. <modpath>/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 <module-name>.ko (required).
+#
+# > install-dir: Kernel modules sub-directory to install the module
+# to (/lib/modules/version/<install-dir>/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, <name>.ko, <name>,
+# 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: <module>...
+# @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 '' "<module>..." || 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: <install-dir>
+# @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}/<install-dir>
+#
+# 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 "<install-dir>" || return 0
+	_MODULES_GLOBAL[moduleinto]=${1}
+}
+
+# @FUNCTION: modules_makeargs_to_array
+# @USAGE: <array-name>
+# @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 "<array-name>" || 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: [<path>]
+# @DESCRIPTION:
+# Strip, sign, verify, and compress all .ko modules found under
+# <path>.  Should typically *not* be called directly as it will
+# be run by linux-mod-r1_src_install.
+#
+# <path> 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 '[<path>]' || 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: [<args-count> <args-min> <args-max> [<usage-string>]]
+# @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: <module>...
+# @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: <module>...
+# @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 (<linux-4.4)
+		else
+			die "USE=modules-sign is set but '${KV_DIR}/scripts/sign-file.c' is not usable"
+		fi
+	fi
+
+	einfo "Signing modules ..."
+
+	# good odds the private key has limited access, and with the kernel's
+	# automated method it is likely to be -rw------- root:root (but is rarely
+	# an issue given portage's src_install used here normally runs as root)
+	[[ ${MODULES_SIGN_KEY} == pkcs11:* || -r ${MODULES_SIGN_KEY} ]] ||
+		die "USE=modules-sign is set but lacking read access to the signing key at '${MODULES_SIGN_KEY}'"
+
+	local mod
+	for mod; do
+		edob "${sign}" "${MODULES_SIGN_HASH}" "${MODULES_SIGN_KEY}" \
+			"${MODULES_SIGN_CERT}" "${mod}"
+	done
+
+	# unset to at least be out of the environment file in, e.g. shared binpkgs
+	unset KBUILD_SIGN_PIN
+}
+
+# @FUNCTION: _modules_process_strip
+# @USAGE: <module>...
+# @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-<major-version+1>.
+_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: <module>...
+# @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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic