Skip to content

Conversation

@nickdesaulniers
Copy link
Contributor

@nickdesaulniers nickdesaulniers commented Oct 30, 2021

When building the Linux kernel, one may use make LLVM=1 to set
multiple flags, akin to make CC=clang LD=ld.lld NM=llvm-nm ... (see
the link below for kernel docs explaining this further in detail).

When building kernel modules, to ensure we're using the same toolchain
as the underlying core kernel image, Kbuild will error if it's reinvoked
with a toolchain that differs. This causes DKMS to fail, since it's not
re-specifying the same compiler (or linker, etc).

Check the .comment section of the vmlinux file in the Linux kernel
source dir, and set CC= and LD= flags for make based on that.

If vmlinux does not exist, grep .config for CONFIG_CC_IS_CLANG=y and
CONFIG_LD_IS_LLD=y.

Fixes: #124
Fixes: ClangBuiltLinux/linux#1104
Link: https://docs.kernel.org/kbuild/llvm.html
Suggested-by: Colin Ian King colin.king@canonical.com
Signed-off-by: Nick Desaulniers nick.desaulniers@gmail.com

@nickdesaulniers
Copy link
Contributor Author

cc @ColinIanKing , @dileks, @torvic9

@nickdesaulniers
Copy link
Contributor Author

Since the original bug report doesn't show the failure message, here's what it looks like without this patch, FWIW:

$ sudo dkms install -m openrazer-driver -v 2.8.0

Creating symlink /var/lib/dkms/openrazer-driver/2.8.0/source ->
                 /usr/src/openrazer-driver-2.8.0

DKMS: add completed.

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area...
KERNELDIR=/lib/modules/5.15.0-rc7+/build make driver...(bad exit status: 2)
Error! Bad return status for module build on kernel: 5.15.0-rc7+ (x86_64)
Consult /var/lib/dkms/openrazer-driver/2.8.0/build/make.log for more information.
$ cat /var/lib/dkms/openrazer-driver/2.8.0/build/make.log
DKMS make.log for openrazer-driver-2.8.0 for kernel 5.15.0-rc7+ (x86_64)
Sat 30 Oct 2021 04:45:40 AM PDT
-e 
:: Compiling OpenRazer kernel modules
========================================
make -C /lib/modules/5.15.0-rc7+/build M=/var/lib/dkms/openrazer-driver/2.8.0/build/driver modules
make[1]: Entering directory '/home/nick/linux'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3cfc1757c5f6f4253f71ff5de841e6979b1befb5)
  You are using:           gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
  CC [M]  /var/lib/dkms/openrazer-driver/2.8.0/build/driver/razerkbd_driver.o
gcc: error: unrecognized command line option ‘-Qunused-arguments’
gcc: error: unrecognized command line option ‘-mretpoline-external-thunk’
gcc: error: unrecognized command line option ‘-mno-global-merge’
gcc: error: unrecognized command line option ‘-fsplit-lto-unit’
make[2]: *** [scripts/Makefile.build:277: /var/lib/dkms/openrazer-driver/2.8.0/build/driver/razerkbd_driver.o] Error 1
make[1]: *** [Makefile:1868: /var/lib/dkms/openrazer-driver/2.8.0/build/driver] Error 2
make[1]: Leaving directory '/home/nick/linux'
make: *** [Makefile:25: driver] Error 2

@torvic9
Copy link

torvic9 commented Oct 30, 2021

Unless I'm mistaken (I haven't tested the patch yet), this does not work when the kernel binary is stripped. Arch for example strips by default. Grepping for the clang string is not beautiful and less than ideal, but it does the job too.

@nickdesaulniers
Copy link
Contributor Author

this does not work when the kernel binary is stripped

$ strip vmlinux
$ llvm-readelf -p .comment vmlinux
String dump of section '.comment':
[     0] clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3cfc1757c5f6f4253f71ff5de841e6979b1befb5)
[    69] Linker: LLD 14.0.0

@nickdesaulniers
Copy link
Contributor Author

I guess my question to @scaronni and @evelikov would be: should I be checking the currently running kernel first, in preference to whatever we find in the kernel source dir?

I guess I could be running an older kernel but trying to run dkms against newer kernel sources (or vice versa)...or is that not a supported use case of dkms? The kernel sources aren't necessarily the kernel I have booted; I'm not sure which dkms prefers, generally.

@torvic9
Copy link

torvic9 commented Nov 2, 2021

this does not work when the kernel binary is stripped

$ strip vmlinux
$ llvm-readelf -p .comment vmlinux
String dump of section '.comment':
[     0] clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3cfc1757c5f6f4253f71ff5de841e6979b1befb5)
[    69] Linker: LLD 14.0.0

Oops. Sorry, you're right.
I had the problem of stripped comment section in the past for unknown reasons, but it seems to work now. So please ignore my comment.

@evelikov
Copy link
Collaborator

evelikov commented Nov 2, 2021

Mind you, despite my recent activity in the project I doubt my input carries much weight. That said:

I think the current patch is good as-is. It uses the kernel in $kernel_source_dir which is the one we care about aka build against. Grepping through the elf sections feels dirty but unless someone comes with cleaner solution it will do.

For reference, here is what my Arch box (using GCC 11) reports for the comment section:

File: /usr/lib/modules/5.14.12-arch1-1/build/vmlinux
String dump of section '.comment':
  [     0]  GCC: (GNU) 11.1.0

@scaronni
Copy link
Member

scaronni commented Nov 4, 2021

This does not work for systems with a bzImage, like RHEL based systems or Suse.. what about checking the .config file?

$ cat /usr/lib/modules/5.14.14-200.fc34.x86_64/build/.config | grep CONFIG_CC_VERSION_TEXT
CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.1 20210728 (Red Hat 11.2.1-1)"

@scaronni
Copy link
Member

scaronni commented Nov 4, 2021

I think the proposal of reading the .config file is a bit more portable, but the specific settings are available in it only if enabled.

$ cat /usr/lib/modules/5.14.14-200.fc34.x86_64/build/.config | grep -E "CONFIG_CC_IS|CONFIG_LD_IS"
CONFIG_CC_IS_GCC=y
CONFIG_LD_IS_BFD=y

@flindeberg
Copy link

Why not decide the priority between the binary and .config and grab the first positive match?

I installed linux (currently 5.15) and linux-lts (currently 5.10) from the arch-repositories, and both binaries contain a .comment matching GCC but only the the linux-kernel has both .config-strings. I also custom compiled two current linux-kernel versions with both clang and gcc as vanilla as I could, and they are both commented in binary and config. Based on my very limited sample I would suggest config over binary, as the config is likely to be intentional.

How about:

  1. Assume gcc
  2. Check config for clang, update CC if match
  3. If we did not find clang in config, check binary and update accordingly
  4. Check config for lld, update LD if match
  5. If we did not find lld in config, check binary and update accordingly

?

Would that catch all cases?

Pseudo-code (greps should probably be double-checked):

    if [[ -e $kernel_source_dir/vmlinux ]]; then
      if  cat $kernel_source_dir/.config | grep -q CONFIG_CC_IS_CLANG=y; then
        make_command="${make_command} CC=clang"
      elif  readelf -p .comment $kernel_source_dir/vmlinux | grep -q clang; then
        make_command="${make_command} CC=clang"
      fi

      if  cat $kernel_source_dir/.config | grep -q CONFIG_LD_IS_LLD=y; then
        make_command="${make_command} LD=ld.lld"
      elif  readelf -p .comment $kernel_source_dir/vmlinux | grep -q LLD; then
        make_command="${make_command} LD=ld.lld"
      fi
    fi

@nickdesaulniers
Copy link
Contributor Author

This does not work for systems with a bzImage

@scaronni what do you mean? bzImage is a compressed artifact built from the vmlinux ELF image. ie. vmlinux is built first, then compressed, then a decompressor is prepended with cat. It's not possible for distros to have a bzImage without a vmlinux in their kernel sources (unless someone rm'ed vmlinux).

I think the proposal of reading the .config file is a bit more portable

Which config, the one from the running kernel image or from the kernel sources? @flindeberg suggests checking from kernel sources.

Is cat /boot/config-$(uname -r) portable? Also, I thought distros can force their configs to be compressed? I seem to recall needing zcat to print configs on some distro...(I should check Kconfig for this).

Why not decide the priority between the binary and .config and grab the first positive match?

Right, that's my question for the maintainers of dkms. I don't mind checking the .config file, but here's a theoretical case that could be broken:

  1. the running kernel and the kernel sources aren't necessarily the same. I can have mainline checked out on disk, but have booted an older kernel.
  2. the .config in kernel sources need not match the booted system's config, due to 1 above OR I could just run make distclean in my kernel sources which would have deleted my .config.

I suspect DKMS has had to deal with the above scenarios, so I'd like to know how DKMS prefers to handle them. I think it's a good idea if we fail the check of .comment on the binary to triple check the .config, but those two could be out of sync. Imagine I build a kernel with LD=ld.lld, then rerun make LD=ld.bfd defconfig. Suddenly my .config doesn't match the produced binary.

Perhaps a revision to @flindeberg suggestion:

  1. assume CC=gcc+LD=ld.bfd
  2. check if .comment of the vmlinux if vmlinux exists in kernel sources has CC=clang or LD=ld.ldd and enable those.
  3. if not 1, check if .config in kernel sources has CC=clang or LD=ld.lld and enable those. Do not flip back to CC=gcc or LD=ld.bfd if they were previously set in 1.

This ignore the running vmlinux and config and depends on what's in the kernel source directory as the sole source of truth. Thoughts?

@flindeberg
Copy link

This ignore the running vmlinux and config and depends on what's in the kernel source directory as the sole source of truth. Thoughts?

As long as the running kernel is ignored I'm happy, as that would allow me to have kernels compiled with different tool-chains installed and working DKMS support.

@scaronni
Copy link
Member

@scaronni what do you mean? bzImage is a compressed artifact built from the vmlinux ELF image. ie. vmlinux is built first, then compressed, then a decompressor is prepended with cat. It's not possible for distros to have a bzImage without a vmlinux in their kernel sources (unless someone rm'ed vmlinux).

The vmlinux file on Red Hat systems is only included in the kernel-debug package which is not normally installed on a system. The requirement for building kernel modules is on kernel-devel which mostly contain only headers.

@flindeberg
Copy link

The vmlinux file on Red Hat systems is only included in the kernel-debug package which is not normally installed on a system. The requirement for building kernel modules is on kernel-devel which mostly contain only headers.

Would checking .config work if kernel-devel is installed?

@nickdesaulniers
Copy link
Contributor Author

As long as the running kernel is ignored I'm happy, as that would allow me to have kernels compiled with different tool-chains installed and working DKMS support.

SGTM

The vmlinux file on Red Hat systems is only included in the kernel-debug package which is not normally installed on a system.

What does $kernel_source_dir evaluate to on Red Hat systems? Because if I additionally check .config, I'd check $kernel_source_dir/.config, so if vmlinux might not exist in $kernel_source_dir/ would .config?

@scaronni
Copy link
Member

Yes that does:

$ rpm -qf /usr/src/kernels/5.14.17-301.fc35.x86_64/.config
kernel-devel-5.14.17-301.fc35.x86_64

@evelikov
Copy link
Collaborator

Thinking out loud:

Do we want "--use-clang" toggle in addition (or instead of) this MR?
This way people can use if should the detection provide false-negatives.

@flindeberg
Copy link

Do we want "--use-clang" toggle in addition (or instead of) this MR?

I'd say in addition to. You would definitely want detection of build tools when scripting updates of DKMS modules for all installed kernels.

A common use-case is updating your Nvidia drivers (the DKMS-version), where I would preferably run the same command for each installed kernel and only change the kernel-argument between runs.

@evelikov
Copy link
Collaborator

A common use-case is updating your Nvidia drivers (the DKMS-version), where I would preferably run the same command for each installed kernel and only change the kernel-argument between runs.

Wonder what the ratio of Nvidia drivers vs everything else 😛
Sounds like you want to use dkms match and/or we should fix dkms install --all

@scaronni
Copy link
Member

Wouldn't be easier to avoid autodetection and just add some configuration directives in /etc/dkms/framework.conf?

Autodetection would be nice, but people which need to specify a compiler for a kernel are either distribution maintainers (so the appropriate configuration should already be in packaged /etc/dkms/framework.conf) or added manually by the person who is recompiling the kernel from scratch.

Copy link

@phoepsilonix phoepsilonix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you add the environment variables CC and LD at that point, $make_command will not be an empty string, and the following conditional expression will not work correctly.

# Use the generic make and make clean commands if not specified
[[ ! $make_command ]] && make_command="make -C $kernel_source_dir M=$dkms_tree/$module/$module_version/build"

Copy link

@phoepsilonix phoepsilonix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff --git a/dkms.in b/dkms.in
index 9c98a89..784831f 100644
--- a/dkms.in
+++ b/dkms.in
@@ -575,6 +575,20 @@ 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"
 
+    if [[ -e $kernel_source_dir/vmlinux ]]; then
+      if  cat $kernel_source_dir/.config | grep -q CONFIG_CC_IS_CLANG=y; then
+        make_command="${make_command} CC=clang"
+      elif  readelf -p .comment $kernel_source_dir/vmlinux | grep -q clang; then
+        make_command="${make_command} CC=clang"
+      fi
+
+      if  cat $kernel_source_dir/.config | grep -q CONFIG_LD_IS_LLD=y; then
+        make_command="${make_command} LD=ld.lld"
+      elif  readelf -p .comment $kernel_source_dir/vmlinux | grep -q LLD; 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

@nickdesaulniers
Copy link
Contributor Author

I've updated my commit to additionally check .config if vmlinux is not found. PTAL.

When building the Linux kernel, one may use `make LLVM=1` to set
multiple flags, akin to `make CC=clang LD=ld.lld NM=llvm-nm ...` (see
the link below for kernel docs explaining this further in detail).

When building kernel modules, to ensure we're using the same toolchain
as the underlying core kernel image, Kbuild will error if it's reinvoked
with a toolchain that differs. This causes DKMS to fail, since it's not
re-specifying the same compiler (or linker, etc).

Check the .comment section of the vmlinux file in the Linux kernel
source dir, and set CC= and LD= flags for make based on that.

If vmlinux does not exist, grep .config for CONFIG_CC_IS_CLANG=y and
CONFIG_LD_IS_LLD=y.

Fixes: dkms-project#124
Fixes: ClangBuiltLinux/linux#1104
Link: https://docs.kernel.org/kbuild/llvm.html
Suggested-by: Colin Ian King <colin.king@canonical.com>
Signed-off-by: Nick Desaulniers <nick.desaulniers@gmail.com>
@nickdesaulniers
Copy link
Contributor Author

If you add the environment variables CC and LD at that point, $make_command will not be an empty string, and the following conditional expression will not work correctly.

Updated. PTAL

@scaronni scaronni self-assigned this Nov 20, 2021
@scaronni scaronni merged commit abc6220 into dkms-project:master Nov 20, 2021
@nickdesaulniers nickdesaulniers deleted the llvm_support branch November 24, 2021 00:35
@nickdesaulniers
Copy link
Contributor Author

thanks everyone :)

@flindeberg
Copy link

DKMS works for for me now, installed nvidia DKMS-drivers for both an llvm/clang-kernel and a gcc-kernel with pacman-hooks, so I'm happy :-)

@RyanHakurei
Copy link

installed nvidia DKMS-drivers for both an llvm/clang-kernel

How? It always errors out for me with invalid kernel config.

@nickdesaulniers
Copy link
Contributor Author

How? It always errors out for me with invalid kernel config.

@RyanHakurei can you post your error message (and log file if the error message says anything about writing an error to a log file , IIRC)? Perhaps file a new issue with that info and CC me plz?

@RyanHakurei
Copy link

RyanHakurei commented May 7, 2022

@nickdesaulniers Lemme get a new issue fired up, because no modules build successfully I just mentioned Nvidia since you mentioned it working.

==> dkms install --no-depmod nvidia/510.68.02 -k 5.17.5-256-tkg-cfs-llvm
Error! Bad return status for module build on kernel: 5.17.5-256-tkg-cfs-llvm (x86_64)
Consult /var/lib/dkms/nvidia/510.68.02/build/make.log for more information.
DKMS make.log for nvidia-510.68.02 for kernel 5.17.5-256-tkg-cfs-llvm (x86_64)
Sat May  7 07:01:21 PM EDT 2022
make[1]: Entering directory '/usr/lib/modules/5.17.5-256-tkg-cfs-llvm/build'

  ERROR: Kernel configuration is invalid.
         include/generated/autoconf.h or include/config/auto.conf are missing.
         Run 'make oldconfig && make prepare' on kernel src to fix it.

make[1]: *** [Makefile:729: include/config/auto.conf] Error 1
make[1]: Leaving directory '/usr/lib/modules/5.17.5-256-tkg-cfs-llvm/build'
make: *** [Makefile:82: modules] Error 2

For anyone else following this: #222

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Building VirtualBox kernel modules with Clang and DKMS building with clang rather than gcc

7 participants