kpatch-build: build dynrelas or klp relas depending on kernel version

Introduce a second phase in the kpatch-build process that creates kpatch
modules or livepatch modules that use the new klp rela sections depending on
the kernel version being worked on. This change uses the two new programs to
either create a patch module that uses dynrelas (create-kpatch-module) or a
patch module that uses klp rela and arch sections + klp symbols marked with the
correct Elf flags (create-klp-module).

For klp patch modules, the --unique flag for ld is needed to prevent
.parainstructions and .altinstructions sections from different objects
from being merged, as arch_klp_init_object_loaded() applies these sections
per-object.
This commit is contained in:
Jessica Yu 2017-01-23 12:43:43 -08:00
parent 355996e366
commit b1cdc83d57
3 changed files with 70 additions and 15 deletions

View File

@ -1,6 +1,7 @@
KPATCH_NAME ?= patch
KPATCH_BUILD ?= /lib/modules/$(shell uname -r)/build
KPATCH_MAKE = $(MAKE) -C $(KPATCH_BUILD) M=$(PWD)
LDFLAGS += $(KPATCH_LDFLAGS)
obj-m += kpatch-$(KPATCH_NAME).o

View File

@ -1,8 +1,12 @@
__kpatch_funcs = ADDR(.kpatch.funcs);
__kpatch_funcs_end = ADDR(.kpatch.funcs) + SIZEOF(.kpatch.funcs);
#ifdef __KPATCH_MODULE__
__kpatch_dynrelas = ADDR(.kpatch.dynrelas);
__kpatch_dynrelas_end = ADDR(.kpatch.dynrelas) + SIZEOF(.kpatch.dynrelas);
__kpatch_checksum = ADDR(.kpatch.checksum);
#endif
SECTIONS
{
.kpatch.hooks.load : {

View File

@ -88,6 +88,7 @@ cleanup() {
[[ "$DEBUG" -eq 0 ]] && rm -rf "$TEMPDIR"
rm -rf "$RPMTOPDIR"
unset KCFLAGS
unset KCPPFLAGS
}
clean_cache() {
@ -96,6 +97,26 @@ clean_cache() {
mkdir -p "$OBJDIR"
}
check_pipe_status() {
rc="${PIPESTATUS[0]}"
if [[ $rc = 139 ]]; then
# There doesn't seem to be a consistent/portable way of
# accessing the last executed command in bash, so just
# pass in the script name for now..
warn "$1 SIGSEGV"
if ls core* &> /dev/null; then
cp core* /tmp
die "core file at /tmp/$(ls core*)"
fi
die "no core file found, run 'ulimit -c unlimited' and try to recreate"
fi
}
# $1 >= $2
function version_gte {
[ "$1" = "`echo -e "$1\n$2" | sort -rV | head -n1`" ]
}
find_dirs() {
if [[ -e "$SCRIPTDIR/create-diff-object" ]]; then
# git repo
@ -461,11 +482,23 @@ else
fi
fi
# Build variables - Set some defaults, then adjust features
# according to .config and kernel version
KBUILD_EXTRA_SYMBOLS=""
KPATCH_LDFLAGS=""
KPATCH_MODULE=true
# kernel option checking: CONFIG_DEBUG_KERNEL and CONFIG_LIVEPATCH
grep -q "CONFIG_DEBUG_KERNEL=y" "$OBJDIR/.config" || die "kernel doesn't have 'CONFIG_DEBUG_KERNEL' enabled"
if grep "CONFIG_LIVEPATCH=y" "$OBJDIR/.config" > /dev/null; then
# The kernel supports livepatch.
KBUILD_EXTRA_SYMBOLS=""
if version_gte ${ARCHVERSION//-*/} 4.7.0; then
# Use new .klp.rela. sections
KPATCH_MODULE=false
if version_gte ${ARCHVERSION//-*/} 4.9.0; then
KPATCH_LDFLAGS="--unique=.parainstructions --unique=.altinstructions"
fi
fi
else
# No support for livepatch in the kernel. Kpatch core module is needed.
find_core_symvers || die "unable to find Module.symvers for kpatch core module"
@ -560,15 +593,7 @@ for i in $FILES; do
# create-diff-object orig.o patched.o kernel-object output.o Module.symvers patch-mod-name
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE" \
"output/$i" "$OBJDIR/Module.symvers" "kpatch_${PATCHNAME//-/_}" 2>&1 |tee -a "$LOGFILE"
rc="${PIPESTATUS[0]}"
if [[ $rc = 139 ]]; then
warn "create-diff-object SIGSEGV"
if ls core* &> /dev/null; then
cp core* /tmp
die "core file at /tmp/$(ls core*)"
fi
die "no core file found, run 'ulimit -c unlimited' and try to recreate"
fi
check_pipe_status create-diff-object
# create-diff-object returns 3 if no functional change is found
[[ $rc -eq 0 ]] || [[ $rc -eq 3 ]] || ERROR=$(expr $ERROR "+" 1)
if [[ $rc -eq 0 ]]; then
@ -598,6 +623,9 @@ done
echo
export KCFLAGS="-I$DATADIR/patch"
if $KPATCH_MODULE; then
export KCPPFLAGS="-D__KPATCH_MODULE__"
fi
echo "Building patch module: kpatch-$PATCHNAME.ko"
cp "$OBJDIR/.config" "$SRCDIR"
@ -612,14 +640,36 @@ if [[ ! -z $UBUNTU_KERNEL ]]; then
fi
cd "$TEMPDIR/output"
ld -r -o ../patch/output.o $(find . -name "*.o") >> "$LOGFILE" 2>&1 || die
md5sum ../patch/output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die
objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/output.o || die
rm -f checksum.tmp
ld -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") >> "$LOGFILE" 2>&1 || die
if $KPATCH_MODULE; then
# Add .kpatch.checksum for kpatch script
md5sum ../patch/tmp_output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die
objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/tmp_output.o || die
rm -f checksum.tmp
"$TOOLSDIR"/create-kpatch-module $TEMPDIR/patch/tmp_output.o $TEMPDIR/patch/output.o 2>&1 |tee -a "$LOGFILE"
check_pipe_status create-kpatch-module
else
cp $TEMPDIR/patch/tmp_output.o $TEMPDIR/patch/output.o || die
fi
cd "$TEMPDIR/patch"
KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$PATCHNAME" \
KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \
make "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
if ! $KPATCH_MODULE; then
if [[ -z "$KPATCH_LDFLAGS" ]]; then
extra_flags="--no-klp-arch-sections"
fi
cp $TEMPDIR/patch/kpatch-$PATCHNAME.ko $TEMPDIR/patch/kpatch-tmp.ko || die
"$TOOLSDIR"/create-klp-module $extra_flags $TEMPDIR/patch/kpatch-tmp.ko $TEMPDIR/patch/kpatch-$PATCHNAME.ko 2>&1 |tee -a "$LOGFILE"
check_pipe_status create-klp-module
fi
cp -f "$TEMPDIR/patch/kpatch-$PATCHNAME.ko" "$BASE" || die
[[ "$DEBUG" -eq 0 ]] && rm -f "$LOGFILE"