-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automatically detecting compiler & linker used by kernel #298
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,3 +141,61 @@ jobs: | |
|
||
- name: Remove the test module | ||
run: sudo dkms remove --all -m dkms_test -v 1.0 | ||
|
||
test-vm-clang: | ||
name: Test in Ubuntu VM with clang | ||
strategy: | ||
matrix: | ||
version: | ||
- 22.04 | ||
runs-on: ubuntu-${{ matrix.version }} | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Install dependencies | ||
run: | | ||
sudo apt update -q | ||
sudo apt install -qqy make libc6-dev llvm clang lld llvm-11 clang-11 lld-11 llvm-14 clang-14 lld-14 flex bison libssl-dev openssl | ||
|
||
- name: Remove apport | ||
run: | | ||
sudo apt remove -qqy apport | ||
|
||
- name: Build tiny kernels with clang | ||
run: | | ||
sudo apt install linux-source-5.19.0 | ||
tar -C /tmp -xf /usr/src/linux-source-5.19.0.tar.bz2 | ||
cd /tmp/linux-source-5.19.0 | ||
# make `make clean` happy | ||
mkdir ubuntu/hio | ||
touch ubuntu/hio/Makefile | ||
# LLVM=-version is supported since 5.18 | ||
make LLVM=-11 tinyconfig | ||
echo "CONFIG_MODULES=y" >> .config | ||
echo "CONFIG_MODULE_SIG=y" >> .config | ||
echo "CONFIG_MODULE_SIG_SHA1=y" >> .config | ||
echo "CONFIG_MODULE_SIG_HASH=\"sha1\"" >> .config | ||
sed -i 's/^CONFIG_LOCALVERSION=.*$/CONFIG_LOCALVERSION="-clang11"/' .config | ||
yes $'\n' | make LLVM=-11 | ||
sudo make modules_install | ||
sudo make install | ||
make clean | ||
make LLVM=-14 tinyconfig | ||
echo "CONFIG_MODULES=y" >> .config | ||
echo "CONFIG_MODULE_SIG=y" >> .config | ||
echo "CONFIG_MODULE_SIG_SHA512=y" >> .config | ||
echo "CONFIG_MODULE_SIG_HASH=\"sha512\"" >> .config | ||
sed -i 's/^CONFIG_LOCALVERSION=.*$/CONFIG_LOCALVERSION="-clang14"/' .config | ||
yes $'\n' | make LLVM=-14 | ||
sudo make modules_install | ||
sudo make install | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move this to a
|
||
|
||
- name: Install dkms | ||
run: sudo make install | ||
|
||
- name: Run tests | ||
run: | | ||
KERNEL_VER="$(cd /tmp/linux-source-5.19.0 && make kernelversion)" | ||
sudo env KERNEL_VER="${KERNEL_VER}-clang11" ./run_test.sh | ||
sudo env KERNEL_VER="${KERNEL_VER}-clang14" ./run_test.sh |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -593,23 +593,6 @@ read_conf() | |
[[ ! $make_command ]] && make_command="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build" | ||
[[ ! $clean ]] && clean="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build clean" | ||
|
||
# Check if clang was used to compile or lld was used to link the kernel. | ||
if [[ -e $kernel_source_dir/vmlinux ]]; then | ||
if readelf -p .comment $kernel_source_dir/vmlinux | grep -q clang; then | ||
make_command="${make_command} CC=clang" | ||
fi | ||
if readelf -p .comment $kernel_source_dir/vmlinux | grep -q LLD; then | ||
make_command="${make_command} LD=ld.lld" | ||
fi | ||
elif [[ -e "${kernel_config}" ]]; then | ||
if grep -q CONFIG_CC_IS_CLANG=y "${kernel_config}"; then | ||
make_command="${make_command} CC=clang" | ||
fi | ||
if grep -q CONFIG_LD_IS_LLD=y "${kernel_config}"; then | ||
make_command="${make_command} LD=ld.lld" | ||
fi | ||
fi | ||
|
||
# Set patch_array (including kernel specific patches) | ||
count=0 | ||
for ((index=0; index < ${#PATCH[@]}; index++)); do | ||
|
@@ -833,6 +816,90 @@ run_build_script() { | |
fi | ||
} | ||
|
||
# helper functions for getting cc/ld used by kernel | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mind adding a bit more context here; |
||
detect_toolchain_version() { | ||
# $1 = program name, e.g. gcc, ld.lld | ||
# $2 = major version | ||
# $3 = minor version | ||
# $4 = patch version, optional | ||
local -a filenames | ||
if [[ "$4" ]]; then | ||
filenames+=( "$1-$((10#$2)).$((10#$3)).$((10#$4))" ) | ||
fi | ||
filenames+=( | ||
"$1-$((10#$2)).$((10#$3))" | ||
"$1-$((10#$2))" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please squash this with the respective commit and add an inline comment or two. |
||
"$1" | ||
) | ||
for f in "${filenames[@]}"; do | ||
if command -V "$f" &>/dev/null; then | ||
echo "$f" | ||
return 0 | ||
fi | ||
done | ||
return 1 | ||
} | ||
detect_toolchain_from_text() { | ||
# $1 = program version text, contains x.y.z | ||
# $2 = program name, e.g. gcc, clang | ||
local patterns=( | ||
's/^.*\b([0-9]+\.[0-9]+\.[0-9]+).*$/\1/p' | ||
) | ||
local -a ver | ||
for p in "${patterns[@]}"; do | ||
readarray -d '.' -t ver <<< "$(echo "$1" | sed -En "$p")" | ||
if [[ "$ver" ]]; then | ||
if detect_toolchain_version "$2" ${ver[0]} ${ver[1]} ${ver[2]}; then | ||
return 0 | ||
fi | ||
fi | ||
done | ||
return 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to read the binary, if the KCONFIG is always available? Edit: since we always add a KCONFIG in the tests, the codepath is not covered in CI. So let's drop this, if the existing code does not work then we can worry about it. |
||
} | ||
detect_toolchain_from_kconfig() { | ||
# $1 = program name in kconfig, e.g. GCC, CLANG, LD, LLD | ||
# $2 = program name, e.g. gcc, clang, ld.bfd, ld.lld | ||
local ver_full="$(grep "^CONFIG_$1_VERSION=" "${kernel_config}" | cut -f2 -d= )" | ||
if [[ -z "${ver_full}" ]] || [[ "${ver_full}" = 0 ]]; then | ||
return 1 | ||
fi | ||
local ver_major="${ver_full:0: -4}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a couple of examples for illustration purposes - one GCC and one CLANG. For example my system has |
||
local ver_minor="${ver_full: -4:2}" | ||
local ver_patch="${ver_full: -2}" | ||
detect_toolchain_version "$2" "${ver_major}" "${ver_minor}" "${ver_patch}" | ||
return $? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The trailing |
||
} | ||
detect_toolchain_from_cc_version_text() { | ||
# since kernel 5.8 | ||
local text="$(grep "^CONFIG_CC_VERSION_TEXT=" "${kernel_config}" | cut -f2 -d= )" | ||
if [[ -z "$text" ]]; then | ||
return 1 | ||
fi | ||
if echo "$text" | grep -q gcc; then | ||
detect_toolchain_from_text "$text" "gcc" | ||
return $? | ||
elif echo "$text" | grep -q clang; then | ||
detect_toolchain_from_text "$text" "clang" | ||
return $? | ||
fi | ||
return 1 | ||
} | ||
detect_toolchain_from_vmlinux() { | ||
# $1 = string in vmlinux .comment section, e.g. clang, LLD | ||
# $2 = program name, e.g. clang, ld.lld | ||
local vmlinux="${kernel_source_dir}/vmlinux" | ||
if [[ ! -f "$vmlinux" ]]; then | ||
return 1 | ||
fi | ||
local text="$(readelf -p .comment "$vmlinux" 2>/dev/null | grep "$1" 2>/dev/null)" | ||
if [[ -z "$text" ]]; then | ||
return 1 | ||
fi | ||
detect_toolchain_from_text "$text" "$2" | ||
return $? | ||
} | ||
|
||
|
||
# Register a DKMS-ified source tree with DKMS. | ||
# This function is smart enough to register the module if we | ||
# passed a source tree or a tarball instead of relying on the source tree | ||
|
@@ -1040,22 +1107,52 @@ prepare_build() | |
$"Check $build_dir for more information." | ||
done | ||
|
||
if [[ -e "${kernel_config}" ]]; then | ||
if grep -q 'CONFIG_CC_IS_CLANG=y' "${kernel_config}"; then | ||
local cc=clang | ||
if command -v "$cc" >/dev/null; then | ||
export CC="$cc" | ||
export KERNEL_CC="$cc" | ||
# Detect CC and LD used by kernel | ||
if ! grep -qE '(^|\s)CC=' <<< "${make_command}"; then | ||
# No CC specified within MAKE in dkms.conf | ||
if [[ -e "${kernel_config}" ]]; then | ||
if grep -q '^CONFIG_CC_IS_GCC=y' "${kernel_config}"; then | ||
# Since kernel 4.18 | ||
KERNEL_CC="$(detect_toolchain_from_kconfig GCC gcc)" | ||
elif grep -q '^CONFIG_CC_IS_CLANG=y' "${kernel_config}"; then | ||
# Since kernel 4.18 | ||
KERNEL_CC="$(detect_toolchain_from_kconfig CLANG clang)" | ||
elif [[ -f "$kernel_source_dir/.kernelvariables" ]]; then | ||
# Debian specific | ||
KERNEL_CC="$(echo -e "show-%:\n\t@echo \$(\$*)\ninclude $kernel_source_dir/.kernelvariables" | make -f - show-CC)" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my testing, |
||
else | ||
KERNEL_CC="$(detect_toolchain_from_cc_version_text)" | ||
fi | ||
fi | ||
|
||
if grep -q 'CONFIG_LD_IS_LLD=y' "${kernel_config}"; then | ||
local ld=ld.lld | ||
if command -v "$ld" >/dev/null; then | ||
export LD="$ld" | ||
export KERNEL_LD="$ld" | ||
if [[ -z "$KERNEL_CC" ]]; then | ||
KERNEL_CC="$(detect_toolchain_from_vmlinux GCC gcc || \ | ||
detect_toolchain_from_vmlinux clang clang)" | ||
fi | ||
if [[ "$KERNEL_CC" ]]; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since
Wires got shorted - zfs is the one needing the exports. Although it can/will use CC/LD, which we explicit export for that purpose. |
||
make_command="${make_command} CC=${KERNEL_CC}" | ||
# the pre_build script may need these, e.g. zfs-dkms | ||
export CC="${KERNEL_CC}" KERNEL_CC | ||
fi | ||
fi | ||
if ! grep -qE '(^|\s)LD=' <<< "${make_command}"; then | ||
# No LD specified within MAKE in dkms.conf | ||
if [[ -e "${kernel_config}" ]]; then | ||
if grep -q '^CONFIG_LD_IS_LLD=y' "${kernel_config}"; then | ||
# Since kernel 5.8 | ||
KERNEL_LD="$(detect_toolchain_from_kconfig LLD ld.lld)" | ||
elif grep -q '^CONFIG_LD_IS_BFD=y' "${kernel_config}"; then | ||
# Since kernel 5.14 | ||
KERNEL_LD="$(detect_toolchain_from_kconfig LD ld.bfd)" | ||
fi | ||
fi | ||
if [[ -z "$KERNEL_LD" ]]; then | ||
KERNEL_LD="$(detect_toolchain_from_vmlinux LLD ld.lld)" | ||
fi | ||
if [[ "$KERNEL_LD" ]]; then | ||
make_command="${make_command} LD=${KERNEL_LD}" | ||
# the pre_build script may need these, e.g. zfs-dkms | ||
export LD="${KERNEL_LD}" KERNEL_LD | ||
fi | ||
fi | ||
|
||
# Run the pre_build script | ||
|
@@ -1073,12 +1170,6 @@ actual_build() | |
echo $"" | ||
echo $"Building module:" | ||
|
||
if [ -f "$kernel_source_dir/.kernelvariables" ]; then | ||
export CC=$(echo -e "show-%:\n\t@echo \$(\$*)\ninclude $kernel_source_dir/.kernelvariables" | make -f - show-CC) | ||
else | ||
unset CC | ||
fi | ||
|
||
invoke_command "$clean" "Cleaning build area" background | ||
echo $"DKMS make.log for $module-$module_version for kernel $kernelver ($arch)" >> "$build_log" | ||
date >> "$build_log" | ||
|
@@ -2304,7 +2395,7 @@ PATH="$PATH:/usr/lib/dkms" | |
umask 022 | ||
|
||
# Unset environment variables that may interfere with the build | ||
unset CC CXX CFLAGS CXXFLAGS LDFLAGS | ||
unset CC CXX LD CFLAGS CXXFLAGS LDFLAGS KERNEL_CC KERNEL_LD | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT |
||
|
||
# Set important variables | ||
current_kernel=$(uname -r) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,11 @@ cd "$(dirname -- "$0")" | |
KERNEL_VER="${KERNEL_VER:-$(uname -r)}" | ||
echo "Using kernel ${KERNEL_VER}" | ||
|
||
case "$(uname -s)" in | ||
Linux) KCONFIG="/lib/modules/${KERNEL_VER}/build/.config" ;; | ||
GNU/kFreeBSD) KCONFIG="/usr/src/kfreebsd-headers-${KERNEL_VER}/sys/.config" ;; | ||
esac | ||
|
||
# Override PATH to use the local dkms binary | ||
PATH="$(pwd):$PATH" | ||
export PATH | ||
|
@@ -159,6 +164,9 @@ genericize_expected_output() { | |
if [[ $# -ge 2 && "$2" =~ uninstall|unbuild|remove ]] ; then | ||
sed -i '/^depmod\.\.\.$/d' ${output_log} | ||
fi | ||
# Remove "CC=... LD=..." part from the "make ..." line. They have different values on different platforms | ||
sed -i '/^make /s/ CC=[0-9A-Za-z_\.\-]*[0-9A-Za-z]//' ${output_log} | ||
sed -i '/^make /s/ LD=[0-9A-Za-z_\.\-]*[0-9A-Za-z]//' ${output_log} | ||
# Signing related output. Drop it from the output, to be more generic | ||
sed -i '/^Sign command:/d' ${output_log} | ||
sed -i '/^Signing key:/d' ${output_log} | ||
|
@@ -374,7 +382,16 @@ EOF | |
else | ||
ALTER_HASH="sha512" | ||
fi | ||
echo "CONFIG_MODULE_SIG_HASH=\"${ALTER_HASH}\"" > /tmp/dkms_test_kconfig | ||
if [[ -f "$KCONFIG" ]]; then | ||
cp "$KCONFIG" /tmp/dkms_test_kconfig | ||
if grep -q '^CONFIG_MODULE_SIG_HASH=' /tmp/dkms_test_kconfig; then | ||
sed -i "s/^CONFIG_MODULE_SIG_HASH=.*$/CONFIG_MODULE_SIG_HASH=\"${ALTER_HASH}\"/" /tmp/dkms_test_kconfig | ||
else | ||
echo "CONFIG_MODULE_SIG_HASH=\"${ALTER_HASH}\"" >> /tmp/dkms_test_kconfig | ||
fi | ||
else | ||
echo "CONFIG_MODULE_SIG_HASH=\"${ALTER_HASH}\"" > /tmp/dkms_test_kconfig | ||
fi | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With my MR in mind #312, I think we can split this (+ KCONFIG above) into it's own patch and simplify:
|
||
run_with_expected_output dkms build -k "${KERNEL_VER}" -m dkms_test -v 1.0 --config /tmp/dkms_test_kconfig --force << EOF | ||
|
||
Building module: | ||
|
@@ -463,11 +480,11 @@ dkms_test/1.0, ${KERNEL_VER}, $(uname -m): installed | |
EOF | ||
|
||
echo 'Checking modinfo' | ||
run_with_expected_output sh -c "modinfo /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | head -n 4" << EOF | ||
filename: /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | ||
version: 1.0 | ||
run_with_expected_output sh -c "modinfo /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | head -n 4 | sort" << EOF | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On which platforms (distro, version, kmod version) do we need the sort? I can send a fix to kmod, if I know a bit more. But realistically: if we need the sort, it should happen before the Edit: please split it into it's own MR (alongside the other suggestions coming shortly), so we can land that ASAP. |
||
description: A Simple dkms test module | ||
filename: /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | ||
license: GPL | ||
version: 1.0 | ||
EOF | ||
|
||
if [[ "${NO_SIGNING_TOOL}" = 0 ]]; then | ||
|
@@ -585,11 +602,11 @@ if ! [[ -f "/lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_co | |
fi | ||
|
||
echo 'Checking modinfo' | ||
run_with_expected_output sh -c "modinfo /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | head -n 4" << EOF | ||
filename: /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | ||
version: 1.0 | ||
run_with_expected_output sh -c "modinfo /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | head -n 4 | sort" << EOF | ||
description: A Simple dkms test module | ||
filename: /lib/modules/${KERNEL_VER}/${expected_dest_loc}/dkms_test.ko${mod_compression_ext} | ||
license: GPL | ||
version: 1.0 | ||
EOF | ||
|
||
if [[ "${NO_SIGNING_TOOL}" = 0 ]]; then | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add 20.04 into the mix as well. How long are the run times?