kpatch-build: clang support

Add support for clang-built kernels. This is completely automatic, we
check if the kernel was built with clang by looking for
CONFIG_CC_IS_CLANG in config.

This has almost no effect on non-clang built kernels with one exception:
we now do compliler checks _after_ we download kernel sources which is a
waste of resources in case when compilers don't match.

Signed-off-by: Artem Savkov <asavkov@redhat.com>
This commit is contained in:
Artem Savkov 2021-01-27 11:41:49 +01:00
parent 2af31916ce
commit 41de9e763a
5 changed files with 111 additions and 48 deletions

View File

@ -62,7 +62,7 @@ vagrant-integration-quick:
$(MAKE) -C $(INTEGRATION_DIR) vagrant-quick
check:
shellcheck kpatch/kpatch kpatch-build/kpatch-build kpatch-build/kpatch-gcc
shellcheck kpatch/kpatch kpatch-build/kpatch-build kpatch-build/kpatch-cc
shellcheck test/difftree.sh test/integration/kpatch-test \
test/integration/lib.sh test/integration/rebase-patches \
test/integration/test-vagrant \

View File

@ -40,7 +40,7 @@ $(PLUGIN): gcc-plugins/ppc64le-plugin.c
install: all
$(INSTALL) -d $(LIBEXECDIR)
$(INSTALL) $(TARGETS) kpatch-gcc $(LIBEXECDIR)
$(INSTALL) $(TARGETS) kpatch-cc $(LIBEXECDIR)
$(INSTALL) -d $(BINDIR)
$(INSTALL) kpatch-build $(BINDIR)

View File

@ -49,7 +49,7 @@ LOGFILE="$CACHEDIR/build.log"
RELEASE_FILE=/etc/os-release
DEBUG=0
SKIPCLEANUP=0
SKIPGCCCHECK=0
SKIPCOMPILERCHECK=0
ARCH_KCFLAGS=""
DEBUG_KCFLAGS=""
declare -a PATCH_LIST
@ -219,10 +219,11 @@ find_core_symvers() {
}
gcc_version_from_file() {
readelf -p .comment "$1" | grep -o 'GCC:.*' | head -n 1
readelf -p .comment "$1" | grep -m 1 -o 'GCC:.*'
}
gcc_version_check() {
local target="$1"
local c="$TEMPDIR/test.c" o="$TEMPDIR/test.o"
local out gccver kgccver
@ -231,11 +232,7 @@ gcc_version_check() {
echo 'void main(void) {}' > "$c"
out="$(gcc -c -pg -ffunction-sections -o "$o" "$c" 2>&1)"
gccver="$(gcc_version_from_file "$o")"
if [[ -n "$OOT_MODULE" ]]; then
kgccver="$(gcc_version_from_file "$OOT_MODULE")"
else
kgccver="$(gcc_version_from_file "$VMLINUX")"
fi
kgccver="$(gcc_version_from_file "$target")"
if [[ -n "$out" ]]; then
warn "gcc >= 4.8 required for -pg -ffunction-settings"
@ -254,7 +251,31 @@ gcc_version_check() {
warn "gcc/kernel version mismatch"
echo "gcc version: $gccver"
echo "kernel version: $kgccver"
echo "Install the matching gcc version (recommended) or use --skip-gcc-check"
echo "Install the matching gcc version (recommended) or use --skip-compiler-check"
echo "to skip the version matching enforcement (not recommended)"
return 1
fi
return
}
clang_version_from_file() {
readelf -p .comment "$1" | grep -m 1 -Eo 'clang version [0-9.]+'
}
clang_version_check() {
local target="$1"
local clangver kclangver
clangver=$(clang --version | grep -m 1 -Eo 'clang version [0-9.]+')
kclangver="$(clang_version_from_file "$target")"
# ensure clang version matches that used to build the kernel
if [[ "$clangver" != "$kclangver" ]]; then
warn "clang/kernel version mismatch"
echo "clang version: $clangver"
echo "kernel version: $kclangver"
echo "Install the matching clang version (recommended) or use --skip-compiler-check"
echo "to skip the version matching enforcement (not recommended)"
return 1
fi
@ -439,28 +460,28 @@ module_name_string() {
usage() {
echo "usage: $(basename "$0") [options] <patch1 ... patchN>" >&2
echo " patchN Input patchfile(s)" >&2
echo " -h, --help Show this help message" >&2
echo " -a, --archversion Specify the kernel arch version" >&2
echo " -r, --sourcerpm Specify kernel source RPM" >&2
echo " -s, --sourcedir Specify kernel source directory" >&2
echo " -c, --config Specify kernel config file" >&2
echo " -v, --vmlinux Specify original vmlinux" >&2
echo " -j, --jobs Specify the number of make jobs" >&2
echo " -t, --target Specify custom kernel build targets" >&2
echo " -n, --name Specify the name of the kpatch module" >&2
echo " -o, --output Specify output folder" >&2
echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2
echo " in <CACHEDIR>/tmp" >&2
echo " (can be specified multiple times)" >&2
echo " -e, --oot-module Enable patching out-of-tree module," >&2
echo " specify current version of module" >&2
echo " --skip-cleanup Skip post-build cleanup" >&2
echo " --skip-gcc-check Skip gcc version matching check" >&2
echo " (not recommended)" >&2
echo " patchN Input patchfile(s)" >&2
echo " -h, --help Show this help message" >&2
echo " -a, --archversion Specify the kernel arch version" >&2
echo " -r, --sourcerpm Specify kernel source RPM" >&2
echo " -s, --sourcedir Specify kernel source directory" >&2
echo " -c, --config Specify kernel config file" >&2
echo " -v, --vmlinux Specify original vmlinux" >&2
echo " -j, --jobs Specify the number of make jobs" >&2
echo " -t, --target Specify custom kernel build targets" >&2
echo " -n, --name Specify the name of the kpatch module" >&2
echo " -o, --output Specify output folder" >&2
echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2
echo " in <CACHEDIR>/tmp" >&2
echo " (can be specified multiple times)" >&2
echo " -e, --oot-module Enable patching out-of-tree module," >&2
echo " specify current version of module" >&2
echo " --skip-cleanup Skip post-build cleanup" >&2
echo " --skip-compiler-check Skip compiler version matching check" >&2
echo " (not recommended)" >&2
}
options="$(getopt -o ha:r:s:c:v:j:t:n:o:de: -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,debug,skip-gcc-check,skip-cleanup" -- "$@")" || die "getopt failed"
options="$(getopt -o ha:r:s:c:v:j:t:n:o:de: -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,debug,skip-gcc-check,skip-compiler-check,skip-cleanup" -- "$@")" || die "getopt failed"
eval set -- "$options"
@ -528,8 +549,11 @@ while [[ $# -gt 0 ]]; do
SKIPCLEANUP=1
;;
--skip-gcc-check)
echo "WARNING: Skipping gcc version matching check (not recommended)"
SKIPGCCCHECK=1
echo "DEPRECATED: --skip-gcc-check is deprecated, use --skip-compiler-check instead"
;&
--skip-compiler-check)
echo "WARNING: Skipping compiler version matching check (not recommended)"
SKIPCOMPILERCHECK=1
;;
*)
[[ "$1" = "--" ]] && shift && continue
@ -631,10 +655,6 @@ fi
find_dirs || die "can't find supporting tools"
if [[ "$SKIPGCCCHECK" -eq 0 ]]; then
gcc_version_check || die
fi
if [[ -n "$USERSRCDIR" ]]; then
echo "Using source directory at $USERSRCDIR"
@ -751,6 +771,8 @@ CONFIG_PARAVIRT=0
CONFIG_UNWINDER_ORC=0
CONFIG_JUMP_LABEL=0
CONFIG_MODVERSIONS=0
CONFIG_CC_IS_CLANG=0
CONFIG_LD_IS_LLD=0
if grep -q "CONFIG_LIVEPATCH=y" "$CONFIGFILE" && (kernel_is_rhel || kernel_version_gte 4.9.0); then
@ -777,6 +799,8 @@ grep -q "CONFIG_PARAVIRT=y" "$CONFIGFILE" && CONFIG_PARAVIRT=1
grep -q "CONFIG_UNWINDER_ORC=y" "$CONFIGFILE" && CONFIG_UNWINDER_ORC=1
grep -q "CONFIG_JUMP_LABEL=y" "$CONFIGFILE" && CONFIG_JUMP_LABEL=1
grep -q "CONFIG_MODVERSIONS=y" "$CONFIGFILE" && CONFIG_MODVERSIONS=1
grep -q "CONFIG_CC_IS_CLANG=y" "$CONFIGFILE" && CONFIG_CC_IS_CLANG=1
grep -q "CONFIG_LD_IS_LLD=y" "$CONFIGFILE" && CONFIG_LD_IS_LLD=1
# unsupported kernel option checking
grep -q "CONFIG_DEBUG_INFO_SPLIT=y" "$CONFIGFILE" && die "kernel option 'CONFIG_DEBUG_INFO_SPLIT' not supported"
@ -791,6 +815,23 @@ if grep -q "CONFIG_DEBUG_INFO_BTF=y" "$CONFIGFILE" ; then
sed -i 's/CONFIG_DEBUG_INFO_BTF/DISABLED_FOR_KPATCH_BUILD/g' "$SRCDIR"/scripts/link-vmlinux.sh || die
fi
if [[ "$CONFIG_CC_IS_CLANG" -eq 1 ]]; then
echo "WARNING: Clang support is experimental"
fi
if [[ "$SKIPCOMPILERCHECK" -eq 0 ]]; then
if [[ -n "$OOT_MODULE" ]]; then
target="$OOT_MODULE"
else
target="$VMLINUX"
fi
if [[ "$CONFIG_CC_IS_CLANG" -eq 0 ]]; then
gcc_version_check "$target" || die
else
clang_version_check "$target" || die
fi
fi
echo "Testing patch file(s)"
cd "$SRCDIR" || die
verify_patch_files
@ -816,9 +857,27 @@ fi
echo "Building original source"
[[ -n "$OOT_MODULE" ]] || ./scripts/setlocalversion --save-scmversion || die
unset KPATCH_GCC_TEMPDIR
KPATCH_CC_PREFIX="$TOOLSDIR/kpatch-cc "
declare -a MAKEVARS
if [ "$CONFIG_CC_IS_CLANG" -eq 1 ]; then
MAKEVARS+=("CC=${KPATCH_CC_PREFIX}clang")
MAKEVARS+=("HOSTCC=clang")
else
MAKEVARS+=("CC=${KPATCH_CC_PREFIX}gcc")
fi
if [ "$CONFIG_LD_IS_LLD" -eq 1 ]; then
MAKEVARS+=("LD=${KPATCH_CC_PREFIX}ld.lld")
MAKEVARS+=("HOSTLD=ld.lld")
else
MAKEVARS+=("LD=${KPATCH_CC_PREFIX}ld")
fi
# $TARGETS used as list, no quotes.
# shellcheck disable=SC2086
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " make "-j$CPUS" $TARGETS 2>&1 | logger || die
make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die
# Save original module symvers
cp -f "$SRCDIR/Module.symvers" "$TEMPDIR/Module.symvers" || die
@ -832,9 +891,7 @@ KPATCH_GCC_SRCDIR="$SRCDIR"
export KPATCH_GCC_SRCDIR
# $TARGETS used as list, no quotes.
# shellcheck disable=SC2086
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " \
KBUILD_MODPOST_WARN=1 \
make "-j$CPUS" $TARGETS 2>&1 | logger || die
KBUILD_MODPOST_WARN=1 make "${MAKEVARS[@]}" "-j$CPUS" $TARGETS 2>&1 | logger || die
# source.c:(.section+0xFF): undefined reference to `symbol'
grep "undefined reference" "$LOGFILE" | sed -r "s/^.*\`(.*)'$/\\1/" \
@ -1026,10 +1083,16 @@ if [[ -z "$OOT_MODULE" ]]; then
else
KPATCH_BUILD="/lib/modules/$ARCHVERSION/build"
fi
# We no longer need kpatch-cc
for ((idx=0; idx<${#MAKEVARS[@]}; idx++)); do
MAKEVARS[$idx]=${MAKEVARS[$idx]/${KPATCH_CC_PREFIX}/}
done
KPATCH_BUILD="$KPATCH_BUILD" KPATCH_NAME="$MODNAME" \
KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \
make 2>&1 | logger || die
make "${MAKEVARS[@]}" 2>&1 | logger || die
if [[ "$USE_KLP" -eq 1 ]]; then
if [[ "$USE_KLP_ARCH" -eq 0 ]]; then

View File

@ -13,7 +13,7 @@ fi
declare -a args=("$@")
if [[ "$TOOLCHAINCMD" =~ "gcc" ]] ; then
if [[ "$TOOLCHAINCMD" =~ ^(.*-)?gcc$ || "$TOOLCHAINCMD" =~ ^(.*-)?clang$ ]] ; then
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
obj="$2"
@ -61,7 +61,7 @@ if [[ "$TOOLCHAINCMD" =~ "gcc" ]] ; then
fi
shift
done
elif [[ "$TOOLCHAINCMD" = "ld" ]] ; then
elif [[ "$TOOLCHAINCMD" =~ ^(.*-)?ld || "$TOOLCHAINCMD" =~ ^(.*-)?ld.lld ]] ; then
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
obj="$2"

View File

@ -54,12 +54,12 @@ effect.
--skip-cleanup
Skip post-build cleanup
--skip-gcc-check
Skips check that ensures that the system gcc version and
the gcc version that built the kernel match. Skipping this
check is not recommended, but is useful if the exact gcc
--skip-compiler-check
Skips check that ensures that the system compiler version and
the compiler version that built the kernel match. Skipping this
check is not recommended, but is useful if the exact compiler
version is not available or is not easily installed. Use
only when confident that the two versions of gcc output
only when confident that the two versions of compiler output
identical objects for a given target. Otherwise, use of
this option might result in unexpected changed objects
being detected.