diff --git a/README.md b/README.md index f8faece..f694e25 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,79 @@ # kernel-srpm-macros -The kernel-srpm-macros package \ No newline at end of file +The kernel-srpm-macros package + +This source package results in two "binary" packages, kernel-rpm-macros +and kernel-srpm-macros. The role and locations of files is as follows: + +kernel-srpm-macros: + +/usr/lib/rpm/fileattrs/kmod.attr + For /lib/modules/XYZ/modules.builtin and .../MODULE.ko.XYZ + specifies a Lua script to generate kmod(MODULE.ko) + "Provides:" deps +/usr/lib/rpm/macros.d/macros.kernel-srpm + Automatically included by all rpm invocations. + Defines %kernel_arches (try rpm --eval '%kernel_arches') + Question: what uses this macro? I didn't find any users. + +kernel-rpm-macros: + +/usr/lib/rpm/redhat/find-provides.ksyms + Runs from rpmbuild at the end of kernel builds and 3rd party module + builds, to extract + kernel(SYMBOL)=0xHASH "Provides:" deps from modules in kernel packages, + and ksym(SYMBOL)=0xHASH for 3rd party modules. + Takes input list of files on stdin. +/usr/lib/rpm/fileattrs/provided_ksyms.attr + For newer rpmbuild with "internal dependency generators", + this file specifies that find-provides.ksyms should be run + only on .ko.XYZ files + (with "external dependency generators", the script needs to + filter out the files by itself). + +/usr/lib/rpm/redhat/find-requires.ksyms + ? +/usr/lib/rpm/fileattrs/required_ksyms.attr + For newer rpmbuild with "internal dependency generators", + this file specifies that find-requires.ksyms should be run + only on .ko.XYZ files, and not for /lib/modules/XYZ/kernel/* + (IOW: only for building 3rd-party modules) + +/usr/lib/rpm/redhat/find-provides.d/modalias.prov + Runs from rpmbuild at the end of kernel builds and 3rd party module + builds, to extract + modalias(pci:SOMETHING)=OPTIONALLY_VERSION "Provides:" deps from modules + in kernel and 3rd party modules packages. + Takes input list of files on stdin. + Question: what uses these deps? +/usr/lib/rpm/fileattrs/modalias.attr + For newer rpmbuild with "internal dependency generators", + this file specifies that modalias.prov should be run + only on .ko.XYZ files + +/usr/lib/rpm/redhat/find-provides.d/firmware.prov + ? + +/usr/lib/rpm/kabi.sh + Runs from rpmbuild at the end of kernel build to extract + kernel(SYMBOL)=0xHASH "Provides:" deps. +/usr/lib/rpm/fileattrs/kabi.attr + For newer rpmbuild with "internal dependency generators", + this file specifies that kabi.sh should be run on + /boot/symvers-* and /lib/modules/XYZ/symvers.gz + +/usr/lib/rpm/macros.d/macros.kmp + Automatically included by all rpm invocations. + Defines a number of macros. + +/usr/lib/rpm/redhat/brp-kmod-restore-perms + ? +/usr/lib/rpm/redhat/brp-kmod-set-exec-bit + ? + +/usr/lib/rpm/redhat/kmodtool + ? +/usr/lib/rpm/redhat/rpmsort + ? +/usr/lib/rpm/redhat/symset-table + ? diff --git a/brp-kmod-restore-perms b/brp-kmod-restore-perms new file mode 100755 index 0000000..ffabefc --- /dev/null +++ b/brp-kmod-restore-perms @@ -0,0 +1,28 @@ +#! /bin/bash -efu + +## A counterpart of brp-kmod-set-exec-bits that restores original kmod +## file permissions + +# If using normal root, avoid changing anything. +[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] || exit 0 + +# Checking for required programs +which chmod >/dev/null || exit 0 + +[ -r "$RPM_BUILD_ROOT/kmod-permissions.list" ] || exit 0 + +while read perm path; do + [ -n "$perm" ] || continue + + # Account for possible kernel module compression + [ -e "$RPM_BUILD_ROOT/$path" ] || { + [ \! -e "$RPM_BUILD_ROOT/$path.gz" ] || path="$path.gz" + [ \! -e "$RPM_BUILD_ROOT/$path.bz2" ] || path="$path.bz2" + [ \! -e "$RPM_BUILD_ROOT/$path.xz" ] || path="$path.xz" + [ \! -e "$RPM_BUILD_ROOT/$path.zst" ] || path="$path.zst" + } + + chmod "$perm" "$RPM_BUILD_ROOT/$path" +done < "$RPM_BUILD_ROOT/kmod-permissions.list" + +rm -f "$RPM_BUILD_ROOT/kmod-permissions.list" diff --git a/brp-kmod-set-exec-bit b/brp-kmod-set-exec-bit new file mode 100755 index 0000000..eccd5b4 --- /dev/null +++ b/brp-kmod-set-exec-bit @@ -0,0 +1,14 @@ +#! /bin/bash -efux + +## A hack for making brp-strip taking into account kmod files + +# If using normal root, avoid changing anything. +[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] || exit 0 + +# Checking for required programs +which find chmod >/dev/null || exit 0 + +find "$RPM_BUILD_ROOT" \ + -name '*.ko' \ + -printf '%#m %P\n' \ + -exec chmod u+x '{}' \; > "$RPM_BUILD_ROOT/kmod-permissions.list" diff --git a/find-provides.ksyms b/find-provides.ksyms index 0526cd7..230fc09 100755 --- a/find-provides.ksyms +++ b/find-provides.ksyms @@ -1,24 +1,138 @@ #! /bin/bash IFS=$'\n' +export LC_ALL=C -for module in $(grep -E '/lib/modules/.+\.ko$'); do - if [[ -n $(nm $module | sed -r -ne 's:^0*([0-9a-f]+) A __crc_(.+):0x\1 \2:p') ]]; then - nm $module \ - | sed -r -ne 's:^0*([0-9a-f]+) A __crc_(.+):0x\1 \2:p' \ - | awk --non-decimal-data '{printf("ksym(%s) = 0x%08x\n", $2, $1)}' \ - | LC_ALL=C sort -u - else - ELFRODATA=$(readelf -R .rodata $module | awk '/0x/{printf $2$3$4$5}') - if [[ -n $(readelf -h $module | grep "little endian") ]]; then - RODATA=$(echo $ELFRODATA | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/g') - else - RODATA=$ELFRODATA - fi - for sym in $(nm $module | sed -r -ne 's:^0*([0-9a-f]+) R __crc_(.+):0x\1 \2:p'); do - echo $sym $RODATA - done \ - | awk --non-decimal-data '{printf("ksym(%s) = 0x%08s\n", $2, substr($3,($1*2)+1,8))}' \ - | LC_ALL=C sort -u +# Prevent elfutils from trying to download debuginfos +unset DEBUGINFOD_URLS + +for module in $(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') "$@"; do + dep_pfx="ksym" + # For built-in kmods, "kernel()" syntax is used instead of "ksym()" + printf "%s" "$module" | grep -v "^${RPM_BUILD_ROOT}/\?lib/modules/[1-9][^/]*/kernel" > /dev/null \ + || dep_pfx="kernel" + + tmpfile="" + if [ "x${module%.ko}" = "x${module}" ]; then + tmpfile=$(mktemp -t ${0##*/}.XXXXXX.ko) + proc_bin= + case "${module##*.}" in + zst) + proc_bin=zstd + ;; + xz) + proc_bin=xz + ;; + bz2) + proc_bin=bzip2 + ;; + gz) + proc_bin=gzip + ;; + esac + + [ -n "$proc_bin" ] || continue + + "$proc_bin" -d -c - < "$module" > "$tmpfile" || continue + module="$tmpfile" fi -done + + # A modversion can be stored as an ELF symbol in various ways: + # - An immediate symbol whose value is available directly; it shows up + # in the nm or objdump -t output, for example: + # $ nm mlx5_core_5.14.x86_64.ko | grep '__crc_' | head -n 3 + # 0000000092f175ca A __crc_mlx5_access_reg + # 000000005b88c9f1 A __crc_mlx5_add_flow_rules + # 00000000e7c0ec8a A __crc_mlx5_alloc_bfreg + # $ objdump -t lib/modules/mlx5_core_5.14.x86_64.ko | grep __crc_ | sort -k 5,5 | head -n 3 + # 0000000092f175ca g *ABS* 0000000000000000 __crc_mlx5_access_reg + # 000000005b88c9f1 g *ABS* 0000000000000000 __crc_mlx5_add_flow_rules + # 00000000e7c0ec8a g *ABS* 0000000000000000 __crc_mlx5_alloc_bfreg + # $ zgrep mlx5_access_reg ./lib/modules/5.14.0-284.15.1.el9_2.x86_64/symvers.gz + # 0x92f175ca mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL + # This approach was being used on x86 and arm before Linux 5.19, + # for example. + # + # - A globally or locally visible symbol in a read-only (or not; + # sometimes .rodata is not a read-only section, after all, as binutils + # commit binutils-2_33~1385 has revealed (and binutils-2_35~1768 hasn't + # concealed back)) section (historically .rodata, __kcrctab/__kcrctab_gpl + # since Linux v5.19-rc1~139^2~2): + # $ nm mlx5_core_5.14.s390x.ko | grep '__crc_' | head -n 3 + # 0000000000002f7c R __crc_mlx5_access_reg + # 0000000000003304 R __crc_mlx5_add_flow_rules + # 0000000000002d2c R __crc_mlx5_alloc_bfreg + # This layout is used on ppc since Linux v4.10-rc7~15^2~1, for example, + # and on all architectures since Linux 5.19. To extract the symbol + # versions in this case, we get the offset and the section name + # from the "objdump -t" output: + # $ objdump -t lib/modules/mlx5_core_5.14.s390x.ko | grep '__crc_' | sort -k 5,5 | head -n 2 + # 0000000000002f7c g .rodata 0000000000000000 __crc_mlx5_access_reg + # 0000000000003304 g .rodata 0000000000000000 __crc_mlx5_add_flow_rules + # and the section contents from the "readelf -R" call: + # $ readelf -R .rodata mlx5_core_5.14.s390x.ko + # [... skipped output ...] + # 0x00002f70 6c6f635f 6e6f6465 00000000 ed6560a8 loc_node.....e`. + # ^^^^^^^^ + # comparison with the contents + # of lib/modules/5.14.0-284.15.1.el9_2.s390x/symvers.gz corroborates + # its correctness: + # 0xed6560a8 mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL + # As mentioned earlier, for the later kernel versions, __kcrctab{,_gpl} + # sections are used: + # $ objdump -t lib/modules/mlx5_core_6.4.x86_64.ko | grep '__crc_' | sort -k 5,5 | head -n 2 + # 0000000000000000 l __kcrctab_gpl 0000000000000000 __crc_mlx5_access_reg + # 0000000000000090 l __kcrctab 0000000000000000 __crc_mlx5_add_flow_rules + # $ readelf -R __kcrctab_gpl mlx5_core_6.4.x86_64.ko + # 0x00000000 38b0d3c3 1840ce35 b99babc7 70b4700c 8....@.5....p.p. + # ^^^^^^^^ + # and in lib/modules/6.4.0-0.rc1.20230511git80e62bc8487b.19.eln126.x86_64/symvers.xz + # we see that it is correct (when accounted for the little endianness): + # 0xc3d3b038 mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL + # This data, after some post-processing, can be used in the awk script + # that extracts parts of the section according to the offsets got + # from the "objdump -t" output. + objdump -t "$module" \ + | awk \ + -v 'dep_pfx='"$dep_pfx" \ + -v 'module='"$module" \ + --non-decimal-data \ + 'BEGIN { revbytes = 0 } + + function check_endianness( t) { + if (revbytes) return revbytes; + + revbytes = -1; + while (("readelf -h \"" module "\"" | getline t) > 0) { + if (match(t, /^ Data: *2\047s complement, little endian$/)) { + revbytes = 1; + break; + } + } + + return revbytes; + } + + function readsect(name, a, t) { + a = ""; + while (("readelf -R \"" name "\" \"" module "\"" | getline t) > 0) { + if (match(t, /^ 0x[0-9a-f]{8}/)) + a = a substr(t, 14, 8) substr(t, 23, 8) substr(t, 32, 8) substr(t, 41, 8); + } + if (check_endianness() == 1) + a = gensub(/(..)(..)(..)(..)/, "\\4\\3\\2\\1", "g", a); + sectdata[name] = a; + } + + match($0, /^([0-9a-f]+) [gl]...... (.*) [0-9a-f]+ __crc_(.*)$/, a) { + if (a[2] == "*ABS*") { + printf("%s(%s) = 0x%08x\n", dep_pfx, a[3], strtonum("0x" a[1])); + } else { + if (!(a[2] in sectdata)) { readsect(a[2]) } + printf("%s(%s) = 0x%08s\n", dep_pfx, a[3], substr(sectdata[a[2]], (strtonum("0x" a[1]) * 2) + 1, 8)) + } + }' + + [ -z "$tmpfile" ] || rm -f -- "$tmpfile" +done \ +| sort -k1,1 -u diff --git a/find-requires.ksyms b/find-requires.ksyms index b11c7bb..8ac7c40 100755 --- a/find-requires.ksyms +++ b/find-requires.ksyms @@ -1,27 +1,87 @@ #! /bin/bash +# +# This script is called during external module building to create dependencies +# both upon the RHEL kernel, and on additional external modules. Symbols that +# cannot be reconciled against those provided by the kernel are assumed to be +# provided by an external module and "ksym" replaces th regular "kernel" dep. IFS=$'\n' +export LC_ALL=C + +# Prevent elfutils from trying to download debuginfos +unset DEBUGINFOD_URLS # Extract all of the symbols provided by this module. all_provides() { - if [[ -n $(nm "$@" | sed -r -ne 's:^0*([0-9a-f]+) A __crc_(.+):0x\1 \2:p') ]]; then - nm "$@" \ - | sed -r -ne 's:^0*([0-9a-f]+) A __crc_(.+):0x\1 \2:p' \ - | awk --non-decimal-data '{printf("0x%08x\t%s\n", $1, $2)}' \ - | LC_ALL=C sort -k2,2 -u - else - ELFRODATA=$(readelf -R .rodata "$@" | awk '/0x/{printf $2$3$4$5}') - if [[ -n $(readelf -h "$@" | grep "little endian") ]]; then - RODATA=$(echo $ELFRODATA | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/g') - else - RODATA=$ELFRODATA + for module in "$@"; do + tmpfile="" + if [ "x${module%.ko}" = "x${module}" ]; then + tmpfile=$(mktemp -t ${0##*/}.XXXXXX.ko) + proc_bin= + case "${module##*.}" in + zst) + proc_bin=zstd + ;; + xz) + proc_bin=xz + ;; + bz2) + proc_bin=bzip2 + ;; + gz) + proc_bin=gzip + ;; + esac + + [ -n "$proc_bin" ] || continue + + "$proc_bin" -d -c - < "$module" > "$tmpfile" || continue + module="$tmpfile" fi - for sym in $(nm "$@" | sed -r -ne 's:^0*([0-9a-f]+) R __crc_(.+):0x\1 \2:p'); do - echo $sym $RODATA - done \ - | awk --non-decimal-data '{printf("0x%08s\t%s\n", substr($3,($1*2)+1,8), $2)}' \ - | LC_ALL=C sort -k2,2 -u - fi + + objdump -t "$module" \ + | awk \ + -v 'dep_pfx='"$dep_pfx" \ + -v 'module='"$module" \ + --non-decimal-data \ + 'BEGIN { revbytes = 0 } + + function check_endianness( t) { + if (revbytes) return revbytes; + + revbytes = -1; + while (("readelf -h \"" module "\"" | getline t) > 0) { + if (match(t, /^ Data: *2\047s complement, little endian$/)) { + revbytes = 1; + break; + } + } + + return revbytes; + } + + function readsect(name, a, t) { + a = ""; + while (("readelf -R \"" name "\" \"" module "\"" | getline t) > 0) { + if (match(t, /^ 0x[0-9a-f]{8}/)) + a = a substr(t, 14, 8) substr(t, 23, 8) substr(t, 32, 8) substr(t, 41, 8); + } + if (revbytes) { a = gensub(/(..)(..)(..)(..)/, "\\4\\3\\2\\1", "g", a); } + sectdata[name] = a; + } + + match($0, /^([0-9a-f]+) [gl]...... (.*) [0-9a-f]+ __crc_(.*)$/, a) { + if (a[2] == "*ABS*") { + printf("%s(%s) = 0x%08x\n", dep_pfx, a[3], strtonum("0x" a[1])); + } else { + if (!(a[2] in sectdata)) { readsect(a[2]) } + printf("%s(%s) = 0x%08s\n", dep_pfx, a[3], substr(sectdata[a[2]], (strtonum("0x" a[1]) * 2) + 1, 8)) + } + }' + + [ -z "$tmpfile" ] || rm -f -- "$tmpfile" + done \ + | sort -k1,1 -u } # Extract all of the requirements of this module. @@ -31,18 +91,18 @@ all_requires() { /sbin/modprobe --dump-modversions "$module" \ | awk --non-decimal-data ' BEGIN { FS = "\t" ; OFS = "\t" } - {printf("0x%08x\t%s\n", $1, $2)}' \ + {printf("%s:0x%08x\n", $2, $1)}' \ | sed -r -e 's:$:\t'"$1"':' done \ - | LC_ALL=C sort -k2,2 -u + | sort -k1,1 -u } # Filter out requirements fulfilled by the module itself. mod_requires() { - LC_ALL=C join -t $'\t' -j 2 -v 1 \ + join -t $'\t' -j 1 -v 1 \ <(all_requires "$@") \ <(all_provides "$@") \ - | LC_ALL=C sort -k1,1 -u + | sort -k1,1 -u } if ! [ -e /sbin/modinfo -a -e /sbin/modprobe ]; then @@ -50,7 +110,50 @@ if ! [ -e /sbin/modinfo -a -e /sbin/modprobe ]; then exit 0 fi -modules=($(grep -E '/lib/modules/.+\.ko$')) +check_kabi() { + arch=$(uname -m) + kabi_file="/lib/modules/kabi-current/kabi_stablelist_$arch" + + # If not installed, output a warning and return (continue) + if [ ! -f "$kabi_file" ]; then + echo "" >&2 + echo "********************************************************************************" >&2 + echo "*********************** KERNEL ABI COMPATIBILITY WARNING ***********************" >&2 + echo "********************************************************************************" >&2 + echo "The kernel ABI reference files (provided by "kabi-stablelists") were not found." >&2 + echo "No compatibility check was performed. Please install the kABI reference files" >&2 + echo "and rebuild if you would like to verify compatibility with kernel ABI." >&2 + echo "" >&2 + return + fi + + unset non_kabi + for symbol in "$@"; do + if ! egrep "^[[:space:]]$symbol\$" $kabi_file >/dev/null; then + non_kabi=("${non_kabi[@]}" "$symbol") + fi + done + + if [ ${#non_kabi[@]} -gt 0 ]; then + echo "" >&2 + echo "********************************************************************************" >&2 + echo "*********************** KERNEL ABI COMPATIBILITY WARNING ***********************" >&2 + echo "********************************************************************************" >&2 + echo "The following kernel symbols are not guaranteed to remain compatible with" >&2 + echo "future kernel updates to this RHEL release:" >&2 + echo "" >&2 + for symbol in "${non_kabi[@]}"; do + printf "\t$symbol\n" >&2 + done + echo "" >&2 + echo "Red Hat recommends that you consider using only official kernel ABI symbols" >&2 + echo "where possible. Requests for additions to the kernel ABI can be filed with" >&2 + echo "your partner or customer representative (component: driver-update-program)." >&2 + echo "" >&2 + fi +} + +modules=($(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') "$@") if [ ${#modules[@]} -gt 0 ]; then kernel=$(/sbin/modinfo -F vermagic "${modules[0]}" | sed -e 's: .*::' -e q) @@ -59,16 +162,27 @@ if [ ${#modules[@]} -gt 0 ]; then cat /usr/src/kernels/$kernel/Module.symvers | awk ' BEGIN { FS = "\t" ; OFS = "\t" } - { print $2 "\t" $1 } + { print $2 ":" $1 } ' \ | sed -r -e 's:$:\t'"$kernel"':' \ - | LC_ALL=C sort -k1,1 -u > $symvers + | sort -k1,1 -u > $symvers # Symbols matching with the kernel get a "kernel" dependency - LC_ALL=C join -t $'\t' -j 1 $symvers <(mod_requires "${modules[@]}") | LC_ALL=C sort -u \ - | awk '{ FS = "\t" ; OFS = "\t" } { print "kernel(" $1 ") = " $2 }' + mod_req=$(mktemp -t mod_req.XXXXX) + mod_requires "${modules[@]}" > "$mod_req" + join -t $'\t' -j 1 $symvers "$mod_req" | sort -u \ + | awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print "kernel(" $1 ") = " $2 }' # Symbols from elsewhere get a "ksym" dependency - LC_ALL=C join -t $'\t' -j 1 -v 2 $symvers <(mod_requires "${modules[@]}") | LC_ALL=C sort -u \ - | awk '{ FS = "\t" ; OFS = "\t" } { print "ksym(" $1 ") = " $2 }' + join -t $'\t' -j 1 -v 2 $symvers "$mod_req" | sort -u \ + | awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print "ksym(" $1 ") = " $2 }' + + os_id=$(sed -nr '/^ID[[:space:]]*=/{ s/ID[[:space:]]*=[[:space:]]*//; s/^"(.*)"$/\1/; p }' /etc/os-release) + if [ "rhel" = "$os_id" ]; then + # Check kABI if the kabi-stablelists package is installed + # Do this last so we can try to output this error at the end + kabi_check_symbols=($(join -t $'\t' -j 1 $symvers "$mod_req" | sort -u \ + | awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print $1 }')) + check_kabi "${kabi_check_symbols[@]}" + fi fi diff --git a/firmware.prov b/firmware.prov index 3614369..8c5b7e4 100644 --- a/firmware.prov +++ b/firmware.prov @@ -5,7 +5,7 @@ IFS=$'\n' -for module in $(grep -E '/lib/modules/.+\.ko$') $*; +for module in $(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') $*; do for firmware in `/sbin/modinfo -F firmware $module`; do diff --git a/kabi.attr b/kabi.attr new file mode 100644 index 0000000..5500b17 --- /dev/null +++ b/kabi.attr @@ -0,0 +1,2 @@ +%__kabi_provides %{_rpmconfigdir}/kabi.sh +%__kabi_path ^(/boot/symvers-.*|/lib/modules/[1-9].*/symvers)\.(gz|xz)$ diff --git a/kabi.sh b/kabi.sh new file mode 100644 index 0000000..dd49b8b --- /dev/null +++ b/kabi.sh @@ -0,0 +1,22 @@ +#!/bin/bash +x +# +# kabi.sh - Automatically extract any kernel symbol checksum from the +# symvers file and add to RPM deps. This is used to move the +# checksum checking from modprobe to rpm install for 3rd party +# modules (so they can fail during install and not at load). + +IFS=$'\n' + +for symvers in $(grep -E '(/boot/symvers-.*|/lib/modules/[1-9].*/symvers)\.(gz|xz)') "$@"; +do + cat_prog="cat" + case "$symvers" in + *.gz) cat_prog="zcat" ;; + *.xz) cat_prog="xzcat" ;; + esac + + # We generate dependencies only for symbols exported by vmlinux itself + # and not for kmods here as they are spread across subpackages, + # so Provides: generation for kmods is handled by find-provides.ksyms. + "$cat_prog" "$symvers" | awk '/[^ ]* [^ ]* vmlinux .*/ { print "kernel(" $2 ") = " $1 }' +done diff --git a/kernel-srpm-macros.spec b/kernel-srpm-macros.spec index 6188dcb..058c08e 100644 --- a/kernel-srpm-macros.spec +++ b/kernel-srpm-macros.spec @@ -1,6 +1,7 @@ Name: kernel-srpm-macros Version: 1.0 -Release: 6%{?dist} +# when bumping version and resetting release, don't forget to bump version of kernel-rpm-macros as well +Release: 27%{?dist} Summary: RPM macros that list arches the full kernel is built on # This package only exist in Fedora repositories # The license is the standard (MIT) specified in @@ -10,17 +11,23 @@ License: MIT URL: https://src.fedoraproject.org/rpms/kernel-srpm-macros BuildArch: noarch # We are now the ones shipping kmod.attr -Conflicts: redhat-rpm-config <= 186 +Conflicts: redhat-rpm-config < 205 +# macros.kmp, kmodtool and rpmsort were moved from kernel-rpm-macros +# to kernel-srpm-macros in 1.0-9/185-9 +Conflicts: kernel-rpm-macros < 205 # Macros Source0: macros.kernel-srpm Source1: macros.kmp -# Dependency generator scripts (deprecated) +# Dependency generator scripts Source100: find-provides.ksyms Source101: find-requires.ksyms Source102: firmware.prov Source103: modalias.prov +Source104: provided_ksyms.attr +Source105: required_ksyms.attr +Source106: modalias.attr # Dependency generators & their rules Source200: kmod.attr @@ -30,6 +37,14 @@ Source300: kmodtool Source301: rpmsort Source302: symset-table +# kabi provides generator +Source400: kabi.attr +Source401: kabi.sh + +# BRPs +Source500: brp-kmod-set-exec-bit +Source501: brp-kmod-restore-perms + %global rrcdir /usr/lib/rpm/redhat @@ -39,10 +54,27 @@ the full kernel is built on. The variable to use is kernel_arches. %package -n kernel-rpm-macros -Version: 185 -Release: %{release}%{?dist}.1 +Version: 205 Summary: Macros and scripts for building kernel module packages -Requires: redhat-rpm-config >= 13 +# rpmsort is GPL-2.0-or-later +License: MIT AND GPL-2.0-or-later +Requires: redhat-rpm-config >= 205 + +# for brp-kmod-compress +Requires: %{_bindir}/xz +# for brp-kmod-compress, brp-kmod-set-exec-bit +Requires: %{_bindir}/find +# for find-provides.ksyms, find-requires.ksyms, kmodtool +Requires: %{_bindir}/sed +# for find-provides.ksyms, find-requires.ksyms +Requires: %{_bindir}/awk +Requires: %{_bindir}/grep +Requires: %{_bindir}/nm +Requires: %{_bindir}/objdump +Requires: %{_bindir}/readelf +# for find-requires.ksyms +Requires: %{_sbindir}/modinfo +Requires: %{_sbindir}/modprobe %description -n kernel-rpm-macros Macros and scripts for building kernel module packages. @@ -62,7 +94,7 @@ cp -p %{sources} . mkdir -p %{buildroot}/%{_rpmconfigdir}/macros.d install -p -m 0644 -t %{buildroot}/%{_rpmconfigdir}/macros.d macros.kernel-srpm %if 0%{?rhel} >= 8 - sed -i 's/^%%kernel_arches.*/%%kernel_arches x86_64 s390x ppc64le aarch64/' \ + sed -i 's/^%%kernel_arches.*/%%kernel_arches x86_64 s390x ppc64le aarch64 riscv64/' \ %{buildroot}/%{_rpmconfigdir}/macros.d/macros.kernel-srpm %endif @@ -71,9 +103,15 @@ mkdir -p %{buildroot}%{_fileattrsdir} install -p -m 755 -t %{buildroot}%{rrcdir} kmodtool rpmsort symset-table install -p -m 755 -t %{buildroot}%{rrcdir} find-provides.ksyms find-requires.ksyms install -p -m 755 -t %{buildroot}%{rrcdir}/find-provides.d firmware.prov modalias.prov +install -p -m 755 -t %{buildroot}%{rrcdir} brp-kmod-restore-perms brp-kmod-set-exec-bit install -p -m 644 -t %{buildroot}%{_rpmconfigdir}/macros.d macros.kmp install -p -m 644 -t %{buildroot}%{_fileattrsdir} kmod.attr +install -p -m 644 -t "%{buildroot}%{_fileattrsdir}" kabi.attr +install -p -m 755 -t "%{buildroot}%{_rpmconfigdir}" kabi.sh + +install -p -m 644 -t "%{buildroot}%{_fileattrsdir}" provided_ksyms.attr required_ksyms.attr +install -p -m 644 -t "%{buildroot}%{_fileattrsdir}" modalias.attr %files %{_rpmconfigdir}/macros.d/macros.kernel-srpm @@ -81,16 +119,96 @@ install -p -m 644 -t %{buildroot}%{_fileattrsdir} kmod.attr %files -n kernel-rpm-macros %{_rpmconfigdir}/macros.d/macros.kmp +%{_rpmconfigdir}/kabi.sh +%{_fileattrsdir}/kabi.attr +%{_fileattrsdir}/modalias.attr +%{_fileattrsdir}/provided_ksyms.attr +%{_fileattrsdir}/required_ksyms.attr %dir %{rrcdir}/find-provides.d -%{rrcdir}/kmodtool -%{rrcdir}/rpmsort +%{rrcdir}/brp-kmod-restore-perms +%{rrcdir}/brp-kmod-set-exec-bit %{rrcdir}/symset-table %{rrcdir}/find-provides.ksyms %{rrcdir}/find-requires.ksyms %{rrcdir}/find-provides.d/firmware.prov %{rrcdir}/find-provides.d/modalias.prov +%{rrcdir}/kmodtool +%{rrcdir}/rpmsort %changelog +* Thu Jul 24 2025 Fedora Release Engineering - 1.0-27 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + +* Tue Apr 01 2025 Andrea Bolognani - 1.0-26 +- Finish adding riscv64 (thanks Zhengyu He) + +* Fri Jan 17 2025 Fedora Release Engineering - 1.0-25 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + +* Thu Jul 18 2024 Fedora Release Engineering - 1.0-24 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Wed Mar 06 2024 David Abdurachmanov - 1.0-23 +- Add riscv64 + +* Wed Jan 24 2024 Fedora Release Engineering - 1.0-22 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sun Jan 21 2024 Fedora Release Engineering - 1.0-21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Thu Jul 20 2023 Fedora Release Engineering - 1.0-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue May 09 2023 Eugene Syromiatnikov - 1.0-19 +- Capture local __crc_* symbols for "Provides: kernel()". +- Add support for XZ compression for the symvers file. +- Fix "Provides: kernel()" generation when both __kcrctab and __kcrctab_gpl + are present. +- Fix regression for "Provides:" generation in cases when modversions are stored + in a section that is over 64K in size. +- Speedup and cleanup changes in find-provides.ksyms and find-requires.ksyms. +- Fix regex usage in kmod.attr. (Denys Vlasenko) +- Implement modalias "Provides:" grouping. (Denys Vlasenko) +- Speedup and cleanup changes in modalias.prov and kmod.attr. (Denys Vlasenko) + +* Tue Mar 30 2023 Eugene Syromiatnikov - 1.0-18 +- Avoid triggering debuginfod during elfutils tools usage. + +* Tue Jan 31 2023 Eugene Syromiatnikov - 1.0-17 +- Support storing of __crc_* symbols in sections other than .rodata. +- Work around a change in type of __crc_* symbols for some kmods printed by nm + on ppc64le and s390x. + +* Thu Jan 19 2023 Fedora Release Engineering - 1.0-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Thu Jul 21 2022 Fedora Release Engineering - 1.0-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Thu Jan 20 2022 Fedora Release Engineering - 1.0-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Thu Nov 18 2021 Miro Hrončok - 1.0-13 +- Bump kernel-rpm-macros to 205 to provide clear upgrade path + +* Thu Nov 18 2021 Miro Hrončok - 1.0-12 +- Correct conflicts to redhat-rpm-macros < 205 +- Move Perl scripts back to kernel-rpm-macros to avoid Perl in the default buildroot + +* Thu Nov 18 2021 Eugene Syromiatnikov - 1.0-11 +- Add conflicts of redhat-rpm-macros < 204 as macros.kmp, kmodtool, + and rpmsort were moved from the latter to the former. +- Remove RHEL-specific kABI bits from find-requires.ksyms and macros.kmp. + +* Thu Nov 18 2021 Eugene Syromiatnikov - 1.0-10 +- Add conflicts of kernel-srpm-macros with kernel-rpm-macros < 185-9 + as macros.kmp, kmodtool, and rpmsort were moved from the latter + to the former. + +* Thu Nov 18 2021 Eugene Syromiatnikov - 1.0-9 +- Update scripts with RHEL-specific changes. + * Thu Jul 22 2021 Fedora Release Engineering - 1.0-6 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild diff --git a/kmod.attr b/kmod.attr index 44a8d56..8c57d0b 100644 --- a/kmod.attr +++ b/kmod.attr @@ -1,21 +1,54 @@ -%__kmod_path ^/lib/modules/.*/(modules.builtin|.*ko) +%__kmod_path ^/lib/modules/.*/(modules.builtin|.*\.ko|.*\.ko\.gz|.*\.ko\.bz2|.*\.ko\.xz|.*\.ko\.zst)$ + +# Notes on Lua: +# The backslash in strings (like "\n" newline) needs to be doubled +# because we are inside rpm macro. Single backslashes before most chars +# disappear (removed by rpm's parser), so "\n" turns into just "n". +# In string.gsub patterns, unlike regexps, backslash has no special meaning. +# It can't escape . and such. (Use one-character set [.] to represent +# literal period, or lua's percent escape: %.) +# Pipe (|) has no special meaning too. + %__kmod_provides() %{lua: function basename(fn) - return string.gsub(fn, "(.*/)(.*)", "%2") + local b = string.gsub(fn, ".*/", "") + -- the above adjusts gsub() result to 1 value + -- "return f()" construct would return _all_ values, two in case of gsub() + return b + end + function strip_compress_sfx(fn) + local cnt + fn, cnt = string.gsub(fn, "%.gz$", "") + if cnt == 1 then return fn; end + fn, cnt = string.gsub(fn, "%.bz2$", "") + if cnt == 1 then return fn; end + fn, cnt = string.gsub(fn, "%.xz$", "") + if cnt == 1 then return fn; end + fn, cnt = string.gsub(fn, "%.zst$", "") + return fn end function printdep(mod) - print("kmod("..mod..")") + print("kmod("..mod..") ") end - local fn = rpm.expand("%{1}") + local fn = rpm.expand("%1") local bn = basename(fn) if bn == "modules.builtin" then for l in io.lines(fn) do - printdep(basename(l)) + local builtin_mod = basename(l) + printdep(builtin_mod) + local nocompr = strip_compress_sfx(builtin_mod) + if nocompr ~= builtin_mod then + printdep(nocompr) + end end else - local mod = string.match(bn, "%g+.ko") + local mod = string.match(bn, "%g+%.ko") if mod then - printdep(mod) + printdep(mod) + local nocompr = strip_compress_sfx(mod) + if nocompr ~= mod then + printdep(nocompr) + end end end } diff --git a/kmodtool b/kmodtool index 1308af2..3066987 100755 --- a/kmodtool +++ b/kmodtool @@ -1,7 +1,12 @@ #!/bin/bash # kmodtool - Helper script for building kernel module RPMs -# Copyright (c) 2003-2006 Ville Skyttä , +# An original version appeared in Fedora. This version is +# generally called only by the %kernel_module_package RPM macro +# during the process of building Driver Update Packages (which +# are also known as "kmods" in the Fedora community). +# +# Copyright (c) 2003-2010 Ville Skyttä , # Thorsten Leemhuis # Jon Masters # @@ -24,34 +29,43 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# Changelog: +# +# 2010/07/28 - Add fixes for filelists in line with LF standard +# - Remove now defunct "framepointer" kernel variant +# - Change version to "rhel6-rh2" as a consequence. +# +# 2010/01/10 - Simplified for RHEL6. We are working on upstream +# moving to a newer format and in any case do not +# need to retain support for really old systems. + shopt -s extglob myprog="kmodtool" -myver="0.10.10_kmp2" -knownvariants=@(BOOT|PAE|@(big|huge)mem|debug|enterprise|kdump|?(large)smp|uml|xen[0U]?(-PAE)|xen) +myver="0.10.10_rhel9" +knownvariants=@(debug|kdump|zfcpdump) kmod_name= kver= verrel= variant= -kmp= get_verrel () { verrel=${1:-$(uname -r)} - verrel=${verrel%%$knownvariants} + verrel=${verrel/%[.+]$knownvariants/} } print_verrel () { - get_verrel $@ + get_verrel "$@" echo "${verrel}" } get_variant () { - get_verrel $@ + get_verrel "$@" variant=${1:-$(uname -r)} - variant=${variant##$verrel} + variant=${variant/#$verrel?(.+)/} variant=${variant:-'""'} } @@ -61,117 +75,176 @@ print_variant () echo "${variant}" } +# Detect flavor separator character. We have to do that due to +# a systemd-tailored patch for kernel spec[1][2] introduced in Fedora and then +# imported in RHEL 8 that broke all OOT kmod infrastructure for the flavored +# kernels. +# +# [1] https://lists.fedoraproject.org/pipermail/kernel/2013-June/004262.html +# [2] https://src.fedoraproject.org/rpms/kernel/c/faf25207dc86666a611c45ae3ffaf385c170bd2a +# +# $1 - kver +# $2 - variant +get_variant_char () +{ + variant="$2" + [ "$variant" != "default" ] || variant="" + + get_verrel "$1" + + variant_char="" + [ -n "$variant" ] || return 0 + + # We expect that the flavored kernel is already installed in the buildroot + variant_char="+" + [ -e "/usr/src/kernels/${verrel}+${variant}" ] && return 0 + + variant_char="." +} + +print_variant_char () +{ + get_variant_char "$@" + echo "${variant_char}" +} + +print_kernel_source () +{ + get_variant_char "$@" + echo "/usr/src/kernels/${verrel}${variant_char}${variant}" +} + +get_filelist() { + local IFS=$'\n' + filelist=($(cat)) + + if [ ${#filelist[@]} -gt 0 ]; + then + for ((n = 0; n < ${#filelist[@]}; n++)); + do + line="${filelist[n]}" + line=$(echo "$line" \ + | sed -e "s/%verrel/$verrel/g" \ + | sed -e "s/%variant/$variant/g" \ + | sed -e "s/%dashvariant/$dashvariant/g" \ + | sed -e "s/%dotvariant/$dotvariant/g" \ + | sed -e "s/\+%1/$dotvariant/g" \ + | sed -e "s/\.%1/$dotvariant/g" \ + | sed -e "s/\-%1/$dotvariant/g" \ + | sed -e "s/%2/$verrel/g") + echo "$line" + done + else + echo "%defattr(644,root,root,755)" + echo "/lib/modules/${verrel}${dotvariant}" + fi +} + + get_rpmtemplate () { local variant="${1}" + + get_variant_char "${verrel}" "${variant}" + local dashvariant="${variant:+-${variant}}" - case "$verrel" in - *.el*) kdep="kernel${dashvariant}-%{_target_cpu} = ${verrel}" ;; - *.EL*) kdep="kernel${dashvariant}-%{_target_cpu} = ${verrel}" ;; - *) kdep="kernel-%{_target_cpu} = ${verrel}${variant}" ;; - esac + local dotvariant="${variant:+${variant_char}${variant}}" echo "%package -n kmod-${kmod_name}${dashvariant}" - if [ -z "$kmp_provides_summary" ]; then + if [ -z "$kmod_provides_summary" ]; then echo "Summary: ${kmod_name} kernel module(s)" fi - if [ -z "$kmp_provides_group" ]; then + if [ -z "$kmod_provides_group" ]; then echo "Group: System Environment/Kernel" fi - if [ ! -z "$kmp_version" ]; then - echo "Version: %{kmp_version}" + if [ ! -z "$kmod_version" ]; then + echo "Version: %{kmod_version}" fi - if [ ! -z "$kmp_release" ]; then - echo "Release: %{kmp_release}" - fi - - if [ ! -z "$kmp" ]; then - echo "%global _use_internal_dependency_generator 0" + if [ ! -z "$kmod_release" ]; then + echo "Release: %{kmod_release}" fi cat <= ${verrel}${dotvariant} +Provides: kernel${dashvariant}-modules >= ${verrel} Provides: ${kmod_name}-kmod = %{?epoch:%{epoch}:}%{version}-%{release} +Requires(post): /usr/sbin/depmod +Requires(postun): /usr/sbin/depmod +Requires(post): /usr/sbin/weak-modules +Requires(postun): /usr/sbin/weak-modules EOF - if [ -z "$kmp" ]; then - echo "Requires: ${kdep}" + if [ "yes" != "$nobuildreqs" ] + then + cat <= %{?epoch:%{epoch}:}%{version} -# - - cat < /dev/null || : +if [ -e "/boot/System.map-${verrel}${dotvariant}" ]; then + /usr/sbin/depmod -aeF "/boot/System.map-${verrel}${dotvariant}" "${verrel}${dotvariant}" > /dev/null || : +fi + +modules=( \$(find /lib/modules/${verrel}${dotvariant}/extra/${kmod_name} | grep -E '\.ko(\.gz|\.bz2|\.xz|\.zst)?$') ) +if [ -x "/usr/sbin/weak-modules" ]; then + printf '%s\n' "\${modules[@]}" \ + | /usr/sbin/weak-modules --add-modules fi EOF - if [ ! -z "$kmp" ]; then - cat < /var/run/rpm-kmod-${kmod_name}${dashvariant}-modules +rpm -ql kmod-${kmod_name}${dashvariant}-%{kmod_version}-%{kmod_release}.$(arch) | grep -E '\.ko(\.gz|\.bz2|\.xz|\.zst)?$' > /var/run/rpm-kmod-${kmod_name}${dashvariant}-modules EOF - fi - - cat < /dev/null || : -EOF +if [ -e "/boot/System.map-${verrel}${dotvariant}" ]; then + /usr/sbin/depmod -aeF "/boot/System.map-${verrel}${dotvariant}" "${verrel}${dotvariant}" > /dev/null || : +fi - if [ ! -z "$kmp" ]; then - cat <