From b1cdc83d574d3eaa01d47f1649cc91829b8e2b79 Mon Sep 17 00:00:00 2001 From: Jessica Yu Date: Mon, 23 Jan 2017 12:43:43 -0800 Subject: [PATCH] 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. --- kmod/patch/Makefile | 1 + kmod/patch/{kpatch.lds => kpatch.lds.S} | 4 ++ kpatch-build/kpatch-build | 80 ++++++++++++++++++++----- 3 files changed, 70 insertions(+), 15 deletions(-) rename kmod/patch/{kpatch.lds => kpatch.lds.S} (96%) diff --git a/kmod/patch/Makefile b/kmod/patch/Makefile index 4bdb6c7..f34d0ef 100644 --- a/kmod/patch/Makefile +++ b/kmod/patch/Makefile @@ -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 diff --git a/kmod/patch/kpatch.lds b/kmod/patch/kpatch.lds.S similarity index 96% rename from kmod/patch/kpatch.lds rename to kmod/patch/kpatch.lds.S index 49ae8e7..b62b1db 100644 --- a/kmod/patch/kpatch.lds +++ b/kmod/patch/kpatch.lds.S @@ -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 : { diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index 8baa68e..54943d0 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -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"