Skip to content

Commit bc46c78

Browse files
authored
Add kernel-srpm-macros package (#11551)
1 parent f711b02 commit bc46c78

22 files changed

+1408
-1
lines changed

LICENSES-AND-NOTICES/SPECS/LICENSES-MAP.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LICENSES-AND-NOTICES/SPECS/data/licenses.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@
548548
"kata-containers",
549549
"kde-filesystem",
550550
"kde-settings",
551+
"kernel-srpm-macros",
551552
"kexec-tools",
552553
"keybinder3",
553554
"keycloak-httpd-client-install",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#! /bin/bash -efu
2+
3+
## A counterpart of brp-kmod-set-exec-bits that restores original kmod
4+
## file permissions
5+
6+
# If using normal root, avoid changing anything.
7+
[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] || exit 0
8+
9+
# Checking for required programs
10+
which chmod >/dev/null || exit 0
11+
12+
[ -r "$RPM_BUILD_ROOT/kmod-permissions.list" ] || exit 0
13+
14+
while read perm path; do
15+
[ -n "$perm" ] || continue
16+
17+
# Account for possible kernel module compression
18+
[ -e "$RPM_BUILD_ROOT/$path" ] || {
19+
[ \! -e "$RPM_BUILD_ROOT/$path.gz" ] || path="$path.gz"
20+
[ \! -e "$RPM_BUILD_ROOT/$path.bz2" ] || path="$path.bz2"
21+
[ \! -e "$RPM_BUILD_ROOT/$path.xz" ] || path="$path.xz"
22+
[ \! -e "$RPM_BUILD_ROOT/$path.zst" ] || path="$path.zst"
23+
}
24+
25+
chmod "$perm" "$RPM_BUILD_ROOT/$path"
26+
done < "$RPM_BUILD_ROOT/kmod-permissions.list"
27+
28+
rm -f "$RPM_BUILD_ROOT/kmod-permissions.list"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#! /bin/bash -efux
2+
3+
## A hack for making brp-strip taking into account kmod files
4+
5+
# If using normal root, avoid changing anything.
6+
[ -n "$RPM_BUILD_ROOT" -a "$RPM_BUILD_ROOT" != "/" ] || exit 0
7+
8+
# Checking for required programs
9+
which find chmod >/dev/null || exit 0
10+
11+
find "$RPM_BUILD_ROOT" \
12+
-name '*.ko' \
13+
-printf '%#m %P\n' \
14+
-exec chmod u+x '{}' \; > "$RPM_BUILD_ROOT/kmod-permissions.list"
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#! /bin/bash
2+
3+
IFS=$'\n'
4+
export LC_ALL=C
5+
6+
# Prevent elfutils from trying to download debuginfos
7+
unset DEBUGINFOD_URLS
8+
9+
for module in $(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') "$@"; do
10+
dep_pfx="ksym"
11+
# For built-in kmods, "kernel()" syntax is used instead of "ksym()"
12+
printf "%s" "$module" | grep -v "^${RPM_BUILD_ROOT}/\?lib/modules/[1-9][^/]*/kernel" > /dev/null \
13+
|| dep_pfx="kernel"
14+
15+
tmpfile=""
16+
if [ "x${module%.ko}" = "x${module}" ]; then
17+
tmpfile=$(mktemp -t ${0##*/}.XXXXXX.ko)
18+
proc_bin=
19+
case "${module##*.}" in
20+
zst)
21+
proc_bin=zstd
22+
;;
23+
xz)
24+
proc_bin=xz
25+
;;
26+
bz2)
27+
proc_bin=bzip2
28+
;;
29+
gz)
30+
proc_bin=gzip
31+
;;
32+
esac
33+
34+
[ -n "$proc_bin" ] || continue
35+
36+
"$proc_bin" -d -c - < "$module" > "$tmpfile" || continue
37+
module="$tmpfile"
38+
fi
39+
40+
# A modversion can be stored as an ELF symbol in various ways:
41+
# - An immediate symbol whose value is available directly; it shows up
42+
# in the nm or objdump -t output, for example:
43+
# $ nm mlx5_core_5.14.x86_64.ko | grep '__crc_' | head -n 3
44+
# 0000000092f175ca A __crc_mlx5_access_reg
45+
# 000000005b88c9f1 A __crc_mlx5_add_flow_rules
46+
# 00000000e7c0ec8a A __crc_mlx5_alloc_bfreg
47+
# $ objdump -t lib/modules/mlx5_core_5.14.x86_64.ko | grep __crc_ | sort -k 5,5 | head -n 3
48+
# 0000000092f175ca g *ABS* 0000000000000000 __crc_mlx5_access_reg
49+
# 000000005b88c9f1 g *ABS* 0000000000000000 __crc_mlx5_add_flow_rules
50+
# 00000000e7c0ec8a g *ABS* 0000000000000000 __crc_mlx5_alloc_bfreg
51+
# $ zgrep mlx5_access_reg ./lib/modules/5.14.0-284.15.1.el9_2.x86_64/symvers.gz
52+
# 0x92f175ca mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL
53+
# This approach was being used on x86 and arm before Linux 5.19,
54+
# for example.
55+
#
56+
# - A globally or locally visible symbol in a read-only (or not;
57+
# sometimes .rodata is not a read-only section, after all, as binutils
58+
# commit binutils-2_33~1385 has revealed (and binutils-2_35~1768 hasn't
59+
# concealed back)) section (historically .rodata, __kcrctab/__kcrctab_gpl
60+
# since Linux v5.19-rc1~139^2~2):
61+
# $ nm mlx5_core_5.14.s390x.ko | grep '__crc_' | head -n 3
62+
# 0000000000002f7c R __crc_mlx5_access_reg
63+
# 0000000000003304 R __crc_mlx5_add_flow_rules
64+
# 0000000000002d2c R __crc_mlx5_alloc_bfreg
65+
# This layout is used on ppc since Linux v4.10-rc7~15^2~1, for example,
66+
# and on all architectures since Linux 5.19. To extract the symbol
67+
# versions in this case, we get the offset and the section name
68+
# from the "objdump -t" output:
69+
# $ objdump -t lib/modules/mlx5_core_5.14.s390x.ko | grep '__crc_' | sort -k 5,5 | head -n 2
70+
# 0000000000002f7c g .rodata 0000000000000000 __crc_mlx5_access_reg
71+
# 0000000000003304 g .rodata 0000000000000000 __crc_mlx5_add_flow_rules
72+
# and the section contents from the "readelf -R" call:
73+
# $ readelf -R .rodata mlx5_core_5.14.s390x.ko
74+
# [... skipped output ...]
75+
# 0x00002f70 6c6f635f 6e6f6465 00000000 ed6560a8 loc_node.....e`.
76+
# ^^^^^^^^
77+
# comparison with the contents
78+
# of lib/modules/5.14.0-284.15.1.el9_2.s390x/symvers.gz corroborates
79+
# its correctness:
80+
# 0xed6560a8 mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL
81+
# As mentioned earlier, for the later kernel versions, __kcrctab{,_gpl}
82+
# sections are used:
83+
# $ objdump -t lib/modules/mlx5_core_6.4.x86_64.ko | grep '__crc_' | sort -k 5,5 | head -n 2
84+
# 0000000000000000 l __kcrctab_gpl 0000000000000000 __crc_mlx5_access_reg
85+
# 0000000000000090 l __kcrctab 0000000000000000 __crc_mlx5_add_flow_rules
86+
# $ readelf -R __kcrctab_gpl mlx5_core_6.4.x86_64.ko
87+
# 0x00000000 38b0d3c3 1840ce35 b99babc7 70b4700c [email protected].
88+
# ^^^^^^^^
89+
# and in lib/modules/6.4.0-0.rc1.20230511git80e62bc8487b.19.eln126.x86_64/symvers.xz
90+
# we see that it is correct (when accounted for the little endianness):
91+
# 0xc3d3b038 mlx5_access_reg drivers/net/ethernet/mellanox/mlx5/core/mlx5_core EXPORT_SYMBOL_GPL
92+
# This data, after some post-processing, can be used in the awk script
93+
# that extracts parts of the section according to the offsets got
94+
# from the "objdump -t" output.
95+
objdump -t "$module" \
96+
| awk \
97+
-v 'dep_pfx='"$dep_pfx" \
98+
-v 'module='"$module" \
99+
--non-decimal-data \
100+
'BEGIN { revbytes = 0 }
101+
102+
function check_endianness( t) {
103+
if (revbytes) return revbytes;
104+
105+
revbytes = -1;
106+
while (("readelf -h \"" module "\"" | getline t) > 0) {
107+
if (match(t, /^ Data: *2\047s complement, little endian$/)) {
108+
revbytes = 1;
109+
break;
110+
}
111+
}
112+
113+
return revbytes;
114+
}
115+
116+
function readsect(name, a, t) {
117+
a = "";
118+
while (("readelf -R \"" name "\" \"" module "\"" | getline t) > 0) {
119+
if (match(t, /^ 0x[0-9a-f]{8}/))
120+
a = a substr(t, 14, 8) substr(t, 23, 8) substr(t, 32, 8) substr(t, 41, 8);
121+
}
122+
if (check_endianness() == 1)
123+
a = gensub(/(..)(..)(..)(..)/, "\\4\\3\\2\\1", "g", a);
124+
sectdata[name] = a;
125+
}
126+
127+
match($0, /^([0-9a-f]+) [gl]...... (.*) [0-9a-f]+ __crc_(.*)$/, a) {
128+
if (a[2] == "*ABS*") {
129+
printf("%s(%s) = 0x%08x\n", dep_pfx, a[3], strtonum("0x" a[1]));
130+
} else {
131+
if (!(a[2] in sectdata)) { readsect(a[2]) }
132+
printf("%s(%s) = 0x%08s\n", dep_pfx, a[3], substr(sectdata[a[2]], (strtonum("0x" a[1]) * 2) + 1, 8))
133+
}
134+
}'
135+
136+
[ -z "$tmpfile" ] || rm -f -- "$tmpfile"
137+
done \
138+
| sort -k1,1 -u
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#! /bin/bash
2+
#
3+
# This script is called during external module building to create dependencies
4+
# both upon the RHEL kernel, and on additional external modules. Symbols that
5+
# cannot be reconciled against those provided by the kernel are assumed to be
6+
# provided by an external module and "ksym" replaces th regular "kernel" dep.
7+
8+
IFS=$'\n'
9+
export LC_ALL=C
10+
11+
# Prevent elfutils from trying to download debuginfos
12+
unset DEBUGINFOD_URLS
13+
14+
# Extract all of the symbols provided by this module.
15+
all_provides() {
16+
for module in "$@"; do
17+
tmpfile=""
18+
if [ "x${module%.ko}" = "x${module}" ]; then
19+
tmpfile=$(mktemp -t ${0##*/}.XXXXXX.ko)
20+
proc_bin=
21+
case "${module##*.}" in
22+
zst)
23+
proc_bin=zstd
24+
;;
25+
xz)
26+
proc_bin=xz
27+
;;
28+
bz2)
29+
proc_bin=bzip2
30+
;;
31+
gz)
32+
proc_bin=gzip
33+
;;
34+
esac
35+
36+
[ -n "$proc_bin" ] || continue
37+
38+
"$proc_bin" -d -c - < "$module" > "$tmpfile" || continue
39+
module="$tmpfile"
40+
fi
41+
42+
objdump -t "$module" \
43+
| awk \
44+
-v 'dep_pfx='"$dep_pfx" \
45+
-v 'module='"$module" \
46+
--non-decimal-data \
47+
'BEGIN { revbytes = 0 }
48+
49+
function check_endianness( t) {
50+
if (revbytes) return revbytes;
51+
52+
revbytes = -1;
53+
while (("readelf -h \"" module "\"" | getline t) > 0) {
54+
if (match(t, /^ Data: *2\047s complement, little endian$/)) {
55+
revbytes = 1;
56+
break;
57+
}
58+
}
59+
60+
return revbytes;
61+
}
62+
63+
function readsect(name, a, t) {
64+
a = "";
65+
while (("readelf -R \"" name "\" \"" module "\"" | getline t) > 0) {
66+
if (match(t, /^ 0x[0-9a-f]{8}/))
67+
a = a substr(t, 14, 8) substr(t, 23, 8) substr(t, 32, 8) substr(t, 41, 8);
68+
}
69+
if (revbytes) { a = gensub(/(..)(..)(..)(..)/, "\\4\\3\\2\\1", "g", a); }
70+
sectdata[name] = a;
71+
}
72+
73+
match($0, /^([0-9a-f]+) [gl]...... (.*) [0-9a-f]+ __crc_(.*)$/, a) {
74+
if (a[2] == "*ABS*") {
75+
printf("%s(%s) = 0x%08x\n", dep_pfx, a[3], strtonum("0x" a[1]));
76+
} else {
77+
if (!(a[2] in sectdata)) { readsect(a[2]) }
78+
printf("%s(%s) = 0x%08s\n", dep_pfx, a[3], substr(sectdata[a[2]], (strtonum("0x" a[1]) * 2) + 1, 8))
79+
}
80+
}'
81+
82+
[ -z "$tmpfile" ] || rm -f -- "$tmpfile"
83+
done \
84+
| sort -k1,1 -u
85+
}
86+
87+
# Extract all of the requirements of this module.
88+
all_requires() {
89+
for module in "$@"; do
90+
set -- $(/sbin/modinfo -F vermagic "$module" | sed -e 's: .*::' -e q)
91+
/sbin/modprobe --dump-modversions "$module" \
92+
| awk --non-decimal-data '
93+
BEGIN { FS = "\t" ; OFS = "\t" }
94+
{printf("%s:0x%08x\n", $2, $1)}' \
95+
| sed -r -e 's:$:\t'"$1"':'
96+
done \
97+
| sort -k1,1 -u
98+
}
99+
100+
# Filter out requirements fulfilled by the module itself.
101+
mod_requires() {
102+
join -t $'\t' -j 1 -v 1 \
103+
<(all_requires "$@") \
104+
<(all_provides "$@") \
105+
| sort -k1,1 -u
106+
}
107+
108+
if ! [ -e /sbin/modinfo -a -e /sbin/modprobe ]; then
109+
cat > /dev/null
110+
exit 0
111+
fi
112+
113+
check_kabi() {
114+
arch=$(uname -m)
115+
kabi_file="/lib/modules/kabi-current/kabi_stablelist_$arch"
116+
117+
# If not installed, output a warning and return (continue)
118+
if [ ! -f "$kabi_file" ]; then
119+
echo "" >&2
120+
echo "********************************************************************************" >&2
121+
echo "*********************** KERNEL ABI COMPATIBILITY WARNING ***********************" >&2
122+
echo "********************************************************************************" >&2
123+
echo "The kernel ABI reference files (provided by "kabi-stablelists") were not found." >&2
124+
echo "No compatibility check was performed. Please install the kABI reference files" >&2
125+
echo "and rebuild if you would like to verify compatibility with kernel ABI." >&2
126+
echo "" >&2
127+
return
128+
fi
129+
130+
unset non_kabi
131+
for symbol in "$@"; do
132+
if ! egrep "^[[:space:]]$symbol\$" $kabi_file >/dev/null; then
133+
non_kabi=("${non_kabi[@]}" "$symbol")
134+
fi
135+
done
136+
137+
if [ ${#non_kabi[@]} -gt 0 ]; then
138+
echo "" >&2
139+
echo "********************************************************************************" >&2
140+
echo "*********************** KERNEL ABI COMPATIBILITY WARNING ***********************" >&2
141+
echo "********************************************************************************" >&2
142+
echo "The following kernel symbols are not guaranteed to remain compatible with" >&2
143+
echo "future kernel updates to this RHEL release:" >&2
144+
echo "" >&2
145+
for symbol in "${non_kabi[@]}"; do
146+
printf "\t$symbol\n" >&2
147+
done
148+
echo "" >&2
149+
echo "Red Hat recommends that you consider using only official kernel ABI symbols" >&2
150+
echo "where possible. Requests for additions to the kernel ABI can be filed with" >&2
151+
echo "your partner or customer representative (component: driver-update-program)." >&2
152+
echo "" >&2
153+
fi
154+
}
155+
156+
modules=($(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') "$@")
157+
if [ ${#modules[@]} -gt 0 ]; then
158+
kernel=$(/sbin/modinfo -F vermagic "${modules[0]}" | sed -e 's: .*::' -e q)
159+
160+
# get all that kernel provides
161+
symvers=$(mktemp -t ${0##*/}.XXXXX)
162+
163+
cat /usr/src/kernels/$kernel/Module.symvers | awk '
164+
BEGIN { FS = "\t" ; OFS = "\t" }
165+
{ print $2 ":" $1 }
166+
' \
167+
| sed -r -e 's:$:\t'"$kernel"':' \
168+
| sort -k1,1 -u > $symvers
169+
170+
# Symbols matching with the kernel get a "kernel" dependency
171+
mod_req=$(mktemp -t mod_req.XXXXX)
172+
mod_requires "${modules[@]}" > "$mod_req"
173+
join -t $'\t' -j 1 $symvers "$mod_req" | sort -u \
174+
| awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print "kernel(" $1 ") = " $2 }'
175+
176+
# Symbols from elsewhere get a "ksym" dependency
177+
join -t $'\t' -j 1 -v 2 $symvers "$mod_req" | sort -u \
178+
| awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print "ksym(" $1 ") = " $2 }'
179+
180+
os_id=$(sed -nr '/^ID[[:space:]]*=/{ s/ID[[:space:]]*=[[:space:]]*//; s/^"(.*)"$/\1/; p }' /etc/os-release)
181+
if [ "rhel" = "$os_id" ]; then
182+
# Check kABI if the kabi-stablelists package is installed
183+
# Do this last so we can try to output this error at the end
184+
kabi_check_symbols=($(join -t $'\t' -j 1 $symvers "$mod_req" | sort -u \
185+
| awk 'BEGIN { FS = "[\t:]" ; OFS = "\t" } { print $1 }'))
186+
check_kabi "${kabi_check_symbols[@]}"
187+
fi
188+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
#
3+
# firmware.prov - Automatically extract any and all firmware dependencies from
4+
# kernel object (.ko) files and add to RPM deps.
5+
6+
IFS=$'\n'
7+
8+
for module in $(grep -E '/lib/modules/.+\.ko(\.gz|\.bz2|\.xz|\.zst)?$') $*;
9+
do
10+
for firmware in `/sbin/modinfo -F firmware $module`;
11+
do
12+
echo "firmware($firmware)"
13+
done
14+
done

SPECS/kernel-srpm-macros/kabi.attr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
%__kabi_provides %{_rpmconfigdir}/kabi.sh
2+
%__kabi_path ^(/boot/symvers-.*|/lib/modules/[1-9].*/symvers)\.(gz|xz)$

0 commit comments

Comments
 (0)