1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点(Linux Kernel 6.12編)

Last updated at Posted at 2025-07-28

はじめに

以下の記事にて、クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点を紹介しました。

上記の記事で、クロスコンパイルした linux-headers-.deb をターゲット上にインストールした時に、このパッケージに含まれる modpost プログラム)が、クロスコンパイルしたホストコンピューターのアーキテクチャでビルドされたままでターゲットコンピューター上では動かない問題があることを説明しました。
また、上記の記事でこの問題の解決策として、linux-header-
.deb をターゲットコンピューターにインストールする際に modpost などの各種ツールを自動的に再ビルドする方法を説明しました。

この問題が Linux Kernel 6.12 で進展がありました。linux-headers-*.deb をクロスコンパイルでビルドする際にターゲットのアーキテクチャにあわせて再ビルドするようになったのです。この記事では、その変更点、その問題点、および改善案を説明します。

Linux Kernel 6.12 での変更点

script/package/builddeb は各種 Debian Package をビルドする時に呼び出されるシェルスクリプトです。

Linux Kernel 6.12 では script/package/builddeb が次のようになっています。

script/package/builddeb
  :
  (前略)
  :
install_kernel_headers () {
	pdir=debian/$1
	version=${1#linux-headers-}

	CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"

	mkdir -p $pdir/lib/modules/$version/
	ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
}
  :
  (中略)
  :

package=$1

case "${package}" in
*-dbg)
	install_linux_image_dbg "${package}";;
linux-image-*|user-mode-linux-*)
	install_linux_image "${package}";;
linux-libc-dev)
	install_libc_headers "${package}";;
linux-headers-*)
	install_kernel_headers "${package}";;
esac

linux-headers-*.deb をビルドする場合は、install_kernel_headers() が呼び出されます。install_kernel_headers() にて scripts/package/install-extmod-build を呼んでいます。

scripts/package/install-extmod-build は次のようになっています。

script/package/install-extmod-build
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only

set -eu

destdir=${1}

is_enabled() {
	grep -q "^$1=y" include/config/auto.conf
}

find_in_scripts() {
	find scripts \
		\( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \
		! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print
}

mkdir -p "${destdir}"

(
	cd "${srctree}"
	echo Makefile
	find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*'
	find "arch/${SRCARCH}" -name generated -prune -o -name include -type d -print
	find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform
	find include \( -name config -o -name generated \) -prune -o \( -type f -o -type l \) -print
	find_in_scripts
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"

{
	if is_enabled CONFIG_OBJTOOL; then
		echo tools/objtool/objtool
	fi

	echo Module.symvers
	echo "arch/${SRCARCH}/include/generated"
	echo include/config/auto.conf
	echo include/config/kernel.release
	echo include/generated
	find_in_scripts

	if is_enabled CONFIG_GCC_PLUGINS; then
		find scripts/gcc-plugins -name '*.so'
	fi
} | tar -c -f - -T - | tar -xf - -C "${destdir}"

# When ${CC} and ${HOSTCC} differ, rebuild host programs using ${CC}.
#
# This caters to host programs that participate in Kbuild. objtool and
# resolve_btfids are out of scope.
if [ "${CC}" != "${HOSTCC}" ]; then
	echo "Rebuilding host programs with ${CC}..."

	cat <<-'EOF' >  "${destdir}/Kbuild"
	subdir-y := scripts
	EOF

	# HOSTCXX is not overridden. The C++ compiler is used to build:
	# - scripts/kconfig/qconf, which is unneeded for external module builds
	# - GCC plugins, which will not work on the installed system even after
	#   being rebuilt.
	#
	# Use the single-target build to avoid the modpost invocation, which
	# would overwrite Module.symvers.
	"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/

	cat <<-'EOF' >  "${destdir}/scripts/Kbuild"
	subdir-y := basic
	hostprogs-always-y := mod/modpost
	mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
	EOF

	# Run once again to rebuild scripts/basic/ and scripts/mod/modpost.
	"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/

	rm -f "${destdir}/Kbuild" "${destdir}/scripts/Kbuild"
fi

find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete

# When ${CC} and ${HOSTCC} differ, rebuild host programs using ${CC}. から始まる部分が、Linux Kernel 6.12 にて追加されました。どうやらここでクロスコンパイル環境ならば、script/basic と script/mod/modpost をターゲットアーキテクチャにあわせて再ビルドしているようです。

これで長い間悩んでいた問題が一件落着と思いましたが、残念ながらそうは問屋が卸さなかったようで、これはこれで新た問題が発生しました。新たな問題は次の章で説明します。

Linux Kernel 6.12 での問題点

クロスコンパイル時に script/mod/modpost をターゲットにあわせて再ビルドするようになったため、script/mod/modpost がクロスコンパイルした環境に依存するようになりました。

例えば、ホストコンピューター(arch=amd64,OS=Ubuntu24.04,cc=gcc-13.3.0) で ターゲットコンピューター(arch=arm64)用のLinux Kernel をビルドしたとします。ここでビルドした linux-headers-*.deb をターゲットコンピューター(OS=Debian12,cc=gcc-12.2.0) にインストールして、fclkcfg などの kernel module をターゲットコンピューター上でセルフビルドすると、次のようなエラーが出て失敗しました。

shell$ make
make -C /lib/modules/6.12.27-zynqmp-fpga-generic/build ARCH=arm64 CROSS_COMPILE= M=/home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg CONFIG_FCLKCFG=m modules
make[1]: Entering directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
  You are using:           gcc (Debian 12.2.0-14) 12.2.0
  CC [M]  /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/fclkcfg.o
  MODPOST /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/Module.symvers
scripts/mod/modpost: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by scripts/mod/modpost)
make[3]: *** [scripts/Makefile.modpost:145: /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/Module.symvers] Error 1
make[2]: *** [/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic/Makefile:1901: modpost] Error 2
make[1]: *** [Makefile:224: __sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'
make: *** [Makefile:35: all] Error 2

途中で C Compiler のバージョンが違うという表示はひとまずおいておくとして、問題は scripts/mod/modpost が失敗していることです。どうやら scripts/mod/modpost が参照している動的ライブラリのバージョンが異っているのが原因のようです。

Linux Kernel 6.12 での改善案

結局、従来通りに、ターゲットコンピューターにインストールする際に modpost などの各種ツールを自動的に再ビルドするようにしました。

具体的にはまず、次のような script/package/install-extmod-build-generic を用意します。

script/package/install-extmod-build-generic
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only

#
# install-extmod-build(generic)
# 
set -eu

script_name=$0
verbose=0

while [ $# -gt 0 ]; do
    case "$1" in
	-v|--verbose)
	    verbose=1
	    shift
	    ;;
	-a|--arch)
	    shift
	    [ $# -eq 0 ] && echo "Missing argument for $1" >&2 && exit 1
	    SRCARCH=$1
	    shift
	    ;;
	-s|--srctree)
	    shift
	    [ $# -eq 0 ] && echo "Missing argument for $1" >&2 && exit 1
	    srctree=$1
	    shift
	    ;;
        -*)
            echo "Unknown option: $1" >&2
            exit 1
            ;;
	*)
	    destdir=$1
	    shift
	    ;;
    esac
done

test -n "${destdir}"
test -n "${srctree}"
test -n "${SRCARCH}"

if [ $verbose -gt 0 ]; then
    echo "## $script_name: destdir = ${destdir}"
    echo "## $script_name: srctree = ${srctree}"
    echo "## $script_name: SRCARCH = ${SRCARCH}"
fi

#
# start scripts/package/install-extmod-build
# 
is_enabled() {
	grep -q "^$1=y" include/config/auto.conf
}

find_in_scripts() {
	find scripts \
		\( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \
		! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print
}

mkdir -p "${destdir}"

(
	cd "${srctree}"
	echo Makefile
	find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*'
	find "arch/${SRCARCH}" -name generated -prune -o -name include -type d -print
	find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform
	find include \( -name config -o -name generated \) -prune -o \( -type f -o -type l \) -print
	find_in_scripts
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"

(
	cd "${srctree}"
	if is_enabled CONFIG_OBJTOOL; then
		echo tools/objtool/objtool
	fi

	echo Module.symvers
	echo "arch/${SRCARCH}/include/generated"
	echo include/config/auto.conf
	echo include/config/kernel.release
	echo include/generated
	find_in_scripts

	if is_enabled CONFIG_GCC_PLUGINS; then
		find scripts/gcc-plugins -name '*.so'
	fi
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"

find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete
#
# end of scripts/package/install-extmod-build
# 

#
# copy files for postinst
#
(
	cd "${srctree}"
	find . \( -name Makefile\* -o -name Kconfig\* -o -name \*.pl \) -print
	find include tools/include security/selinux/include \( -type f -o -type l \) -print
	find scripts \( -type f -o -type l \) -print
	find "arch/${SRCARCH}" \( -name Kbuild.platforms -o -name Platform \) -print
	find $(find "arch/${SRCARCH}" -name include -o -name scripts -o -name tools -type d) -type f
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"
#
# remove binary files for postinst
#
find "${destdir}" -type f -exec file {} + | grep 'ELF' | cut -d: -f1 | xargs rm -f
#
# copy .config manually to be where it's expected to be
#
cp "${srctree}/.config" "${destdir}/.config"
#
# patch Makefile for postinst
# 
cat <<-'EOF' | patch -p0 -d "${destdir}"
--- Makefile	2025-07-26 15:43:33.031678200 +0900
+++ Makefile	2025-07-26 15:41:24.334781500 +0900
@@ -276,6 +276,7 @@
 			 outputmakefile rustavailable rustfmt rustfmtcheck
 no-sync-config-targets := $(no-dot-config-targets) %install modules_sign kernelrelease \
 			  image_name
+no-sync-config-targets += linux-headers-postinst
 single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %.symtypes %/
 
 config-build	:=
@@ -1220,6 +1221,11 @@
 	$(Q)$(MAKE) $(build)=scripts/mod
 	$(Q)$(MAKE) $(build)=. prepare
 
+# linux-headers debian package postinst
+PHONY += linux-headers-postinst
+linux-headers-postinst: archscripts scripts
+	$(Q)$(MAKE) $(build)=scripts/mod
+
 # All the preparing..
 prepare: prepare0
 ifdef CONFIG_RUST
EOF

script/package/install-extmod-build-generic は script/package/install-extmod-build を元に改良しました。
スクリプトの前半は、script/package/install-extmod-build と同じように必要なファイルを作業用のディレクトリにコピーしています。
スクリプトの中盤は、postinst(インストール後に自動的に実行する処理)に必要なファイルを作業用のディレクトリに追加し、不要なバイナリファイルを削除しています。
スクリプトの後半は、postinst で実行する make コマンドのために、Makefile に linux-headers-postinst ターゲットを追加しています。

さらに script/package/builddeb を次のように変更します。

script/package/builddeb
  :
  (前略)
  :
install_kernel_headers () {
	pdir=debian/$1
	version=${1#linux-headers-}

	CC="${DEB_HOST_GNU_TYPE}-gcc" "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"

	mkdir -p $pdir/lib/modules/$version/
	ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
}

install_kernel_headers_generic () {
	pdir=debian/$1
	version=${1#linux-headers-}
	"${srctree}/scripts/package/install-extmod-build-generic" "${pdir}/usr/src/linux-headers-${version}"
	mkdir -m 755 -p "$pdir/DEBIAN"
	cat <<EOF >> $pdir/DEBIAN/postinst
#!/bin/sh -e

make -C /usr/src/linux-headers-$version linux-headers-postinst

EOF
	chmod 755 $pdir/DEBIAN/postinst
	mkdir -p $pdir/lib/modules/$version/
	ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
}
  :
  (中略)
  :
package=$1

case "${package}" in
*-dbg)
	install_linux_image_dbg "${package}";;
linux-image-*|user-mode-linux-*)
	install_linux_image "${package}";;
linux-libc-dev)
	install_libc_headers "${package}";;
linux-headers-*)
	install_kernel_headers_generic "${package}";;
esac

linux-headers-*.deb をビルドする場合は、install_kernel_headers() ではなく install_kernel_headers_generic() を呼び出すように変更します。

install_kernel_headers_generic() では、scripts/package/install-extmod-build-generic を呼んだ後、Debian Package がターゲットにインストールした後に自動的に実行されるスクリプト postinst を追加しています。postinst では、make -C /usr/src/linux-headers-$version linux-headers-postinst を実行します。

さらに、scripts/package/mkdebian も少し変更します。具体的には次のように依存関係を整理します。

diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian
index fc3b7fa70..8ff6607f8 100755
--- a/scripts/package/mkdebian
+++ b/scripts/package/mkdebian
@@ -201,7 +201,7 @@ Build-Depends: debhelper-compat (= 12)
 Build-Depends-Arch: bc, bison, cpio, flex,
  gcc-${host_gnu} <!pkg.${sourcename}.nokernelheaders>,
  kmod, libelf-dev:native,
- libssl-dev:native, libssl-dev <!pkg.${sourcename}.nokernelheaders>,
+ libssl-dev:native,
  rsync
 Homepage: https://www.kernel.org/

このように変更した scripts/package を使ってビルドした linux-headers-*.deb をターゲットにインストールと次のように、自動的に各種ツールがターゲット用にビルドされます。

shell$ dpkg -i linux-headers-6.12.27-zynqmp-fpga-generic_6.12.27-zynqmp-fpga-3_arm64.deb
Selecting previously unselected package linux-headers-6.12.27-zynqmp-fpga-generic.
(Reading database ... 40346 files and directories currently installed.)
Preparing to unpack linux-headers-6.12.27-zynqmp-fpga-generic_6.12.27-zynqmp-fpga-3_arm64.deb ...
Unpacking linux-headers-6.12.27-zynqmp-fpga-generic (6.12.27-zynqmp-fpga-3) ...
Setting up linux-headers-6.12.27-zynqmp-fpga-generic (6.12.27-zynqmp-fpga-3) ...
make: Entering directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/dtc/dtc.o
  HOSTCC  scripts/dtc/flattree.o
  HOSTCC  scripts/dtc/fstree.o
  HOSTCC  scripts/dtc/data.o
  HOSTCC  scripts/dtc/livetree.o
  HOSTCC  scripts/dtc/treesource.o
  HOSTCC  scripts/dtc/srcpos.o
  HOSTCC  scripts/dtc/checks.o
  HOSTCC  scripts/dtc/util.o
  HOSTCC  scripts/dtc/dtc-lexer.lex.o
  HOSTCC  scripts/dtc/dtc-parser.tab.o
  HOSTLD  scripts/dtc/dtc
  HOSTCC  scripts/dtc/libfdt/fdt.o
  HOSTCC  scripts/dtc/libfdt/fdt_ro.o
  HOSTCC  scripts/dtc/libfdt/fdt_wip.o
  HOSTCC  scripts/dtc/libfdt/fdt_sw.o
  HOSTCC  scripts/dtc/libfdt/fdt_rw.o
  HOSTCC  scripts/dtc/libfdt/fdt_strerror.o
  HOSTCC  scripts/dtc/libfdt/fdt_empty_tree.o
  HOSTCC  scripts/dtc/libfdt/fdt_addresses.o
  HOSTCC  scripts/dtc/libfdt/fdt_overlay.o
  HOSTCC  scripts/dtc/fdtoverlay.o
  HOSTLD  scripts/dtc/fdtoverlay
  HOSTCC  scripts/selinux/genheaders/genheaders
  HOSTCC  scripts/selinux/mdp/mdp
  HOSTCC  scripts/kallsyms
  HOSTCC  scripts/sorttable
  HOSTCC  scripts/asn1_compiler
  CC      scripts/mod/empty.o
  HOSTCC  scripts/mod/mk_elfconfig
  MKELF   scripts/mod/elfconfig.h
  HOSTCC  scripts/mod/modpost.o
  CC      scripts/mod/devicetable-offsets.s
  HOSTCC  scripts/mod/file2alias.o
  HOSTCC  scripts/mod/sumversion.o
  HOSTCC  scripts/mod/symsearch.o
  HOSTLD  scripts/mod/modpost
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
make[1]: warning:  Clock skew detected.  Your build may be incomplete.
make: warning:  Clock skew detected.  Your build may be incomplete.
make: Leaving directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'  

無事に Kernel Module のセルフコンパイルも成功しました。

shell$ make
make -C /lib/modules/6.12.27-zynqmp-fpga-generic/build ARCH=arm64 CROSS_COMPILE= M=/home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg CONFIG_FCLKCFG=m modules
make[1]: Entering directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
  You are using:           gcc (Debian 12.2.0-14) 12.2.0
  CC [M]  /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/fclkcfg.o
  MODPOST /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/Module.symvers
  CC [M]  /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/fclkcfg.mod.o
  CC [M]  /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/.module-common.o
  LD [M]  /home/fpga/work/fclkcfg-kmod-dpkg/fclkcfg/fclkcfg.ko
make[1]: Leaving directory '/usr/src/linux-headers-6.12.27-zynqmp-fpga-generic'

linux Kernel 6.12.27 用のパッチファイルは以下の URL で公開しています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?