#!/bin/bash # vim: tabstop=4 shiftwidth=4 expandtab set -o nounset set -o errexit set -o pipefail # TODO: log output to a file instead of /dev/null CUR_DIR="$PWD" SCRIPT="`basename $BASH_SOURCE`" SCRIPT_DIR="`dirname $BASH_SOURCE`" SCRIPT_DIR="`readlink -f \"$SCRIPT_DIR\"`" KMOD_DIR="$SCRIPT_DIR/kmod" KPATCH_REG="$KMOD_DIR/kpatch-register.o" KPATCH_LDS="$KMOD_DIR/kpatch.lds" KPATCH_GEN="$SCRIPT_DIR/kpatch-diff-gen/kpatch-diff-gen" KPATCHGCC="$SCRIPT_DIR/kpatch-gcc" JOBS=4 MAKE_CMD="make -j$JOBS vmlinux" export CROSS_COMPILE="$KPATCHGCC " # TODO make in function PROGRESS_FILE="kpatch-in-progress" PATCHED= PATCH= KERNEL_DIR= OUTPUT_DIR= usage () { echo "usage: $SCRIPT -p [PATCH] -k [KERNEL DIR] -o [OUTPUT DIR]" >&2 exit 1 } scriptecho () { echo "$SCRIPT: $*" } cleanup_objs () { find "$KERNEL_DIR" -name "*.orig_kpatch" -o -name "*.kpatch_gen" | while read file; do rm -f $file done } cleanup () { scriptecho "cleaning up..." rm -f "$PROGRESS_FILE" cleanup_objs if [ $PATCHED ]; then cd "$KERNEL_DIR" patch -p1 -R < "$PATCH" > /dev/null export CROSS_COMPILE="$KPATCHGCC " $MAKE_CMD > /dev/null fi rm -rf "$TMPDIR" } die () { echo "$SCRIPT: error: $*" >&2 exit 1 } while getopts "p:k:o:" arg; do case "$arg" in p) PATCH="$OPTARG" ;; k) KERNEL_DIR="$OPTARG" ;; o) OUTPUT_DIR="$OPTARG" ;; *) usage ;; esac done [ ! "$PATCH" ] || [ ! "$KERNEL_DIR" ] || [ ! "$OUTPUT_DIR" ] && usage [ ! -f "$PATCH" ] && die "$PATCH doesn't exist" [ ! -d "$KERNEL_DIR" ] && die "$KERNEL_DIR doesn't exist" mkdir -p "$OUTPUT_DIR" PATCH="`readlink -f \"$PATCH\"`" KERNEL_DIR="`readlink -f \"$KERNEL_DIR\"`" OUTPUT_DIR="`readlink -f \"$OUTPUT_DIR\"`" TMPDIR="`mktemp -d`" trap cleanup exit scriptecho "compiling original kernel" rm -f "$PROGRESS_FILE" cleanup_objs cd "$KERNEL_DIR" $MAKE_CMD > /dev/null cp vmlinux vmlinux.orig_kpatch scriptecho "patching kernel" patch -p1 < "$PATCH" > /dev/null PATCHED=1 scriptecho "compiling patched kernel" touch "$PROGRESS_FILE" $MAKE_CMD > /dev/null rm -f "$PROGRESS_FILE" scriptecho "diffing binaries" find . -type f -name '*.o.orig_kpatch' | while read file; do origfile="${file#./}" newfile="${origfile%.orig_kpatch}" [ ! -f "$newfile" ] && die "can't find \"$newfile\"" num="`readelf -s \"$file\" |awk '{print $4}' |grep -c FILE`" [ "$num" = 0 ] && die "unsupported change in (assembly?) file \"$file\"" [ "$num" -gt 1 ] && die "\"$newfile\" has too many FILE symbols" scriptecho "object changed: \"$newfile\"" "$KPATCH_GEN" "$origfile" "$newfile" -v vmlinux.orig_kpatch -o "$newfile.kpatch_gen" done scriptecho "generating kpatch modules" unset CROSS_COMPILE cp -a "$KMOD_DIR" "$TMPDIR/kmod" make -C "$KERNEL_DIR" M="$TMPDIR/kmod" kpatch-patch-hook.o > /dev/null cd $TMPDIR/kmod find "$KERNEL_DIR" -name "*.kpatch_gen" -exec ld -m elf_x86_64 -r -o kpatch-patch-foo.o kpatch-patch-hook.o kpatch.lds {} + make -C "$KERNEL_DIR" M="$TMPDIR/kmod" kpatch.ko > /dev/null make -C "$KERNEL_DIR" M="$TMPDIR/kmod" kpatch-patch.ko > /dev/null cp kpatch.ko kpatch-patch.ko "$OUTPUT_DIR" scriptecho success!