kpatch/kpatch-build/kpatch-build
Seth Jennings 7d747e86fd remove compressed cache
The compression of the cache during initial build time and
the removal and (re)decompression of the cache for subsequent
builds takes a large amount of time and causes significant I/O.

This commit removes the compressed cache and, instead, keeps
the cache uncompressed and maintained in a known state.  If
the "applied-patch" file does not exist, then the cache is
in the unpatched state.  If the file does exist, the cache is
in a patched state and can be returned to an unpatched state
with "patch -R -p1 < applied-patch".

The if cache is detected and is in the patched state, the patch
is removed and vmlinux is rebuilt to obtain the base vmlinux.

Signed-off-by: Seth Jennings <sjenning@redhat.com>
2014-02-18 15:25:11 -06:00

167 lines
5.0 KiB
Bash
Executable File

#!/bin/bash
# kpatch build script
# This script takes a patch based on the version of the kernel
# currently running and creates a kernel module that will
# replace modified functions in the kernel such that the
# patched code takes effect.
# This script currently only works on Fedora and will need to be adapted to
# work on other distros.
# This script:
# - Downloads the kernel src rpm for the currently running kernel
# - Unpacks and prepares the src rpm for building
# - Builds the base kernel (vmlinux)
# - Builds the patched kernel and monitors changed objects
# - Builds the patched objects with gcc flags -f[function|data]-sections
# - Runs kpatch tools to create and link the patch kernel module
BASE="$PWD"
LOGFILE="/tmp/kpatch-build-$(date +%s).log"
TOOLSDIR="$(readlink -f $(dirname $0))"
ARCHVERSION="$(uname -r)"
DISTROVERSION="${ARCHVERSION%*.*}"
CPUS="$(grep -c ^processor /proc/cpuinfo)"
LOCALVERSION="$(uname -r)"
LOCALVERSION="-${LOCALVERSION##*-}"
KSRCDIR="$HOME/.kpatch/$ARCHVERSION"
KSRCDIR_DIR="$(dirname $KSRCDIR)"
TEMPDIR=
STRIPCMD="strip -d --keep-file-symbols"
APPLIEDPATCHFILE="applied-patch"
die() {
if [[ -z $1 ]]; then
echo "ERROR: kpatch build failed. Check $LOGFILE for more details." >&2
else
echo "ERROR: $1" >&2
fi
exit 1
}
find_data_dir() {
# git repo
DATADIR="$(readlink -f $TOOLSDIR/../kmod)"
[[ -e "$DATADIR" ]] && return
# installation path
DATADIR="$(readlink -f $TOOLSDIR/../../share/kpatch)"
[[ -e "$DATADIR" ]] && return
return 1
}
if [[ "$#" -ne 1 ]]; then
echo "usage: $0 patchfile" >&2
exit 2
fi
PATCHFILE="$(readlink -f $1)"
if [[ ! -f "$PATCHFILE" ]]; then
echo "ERROR: patch file $PATCHFILE not found" >&2
exit 3
fi
PATCHNAME="$(basename $PATCHFILE)"
if [[ "$PATCHNAME" =~ \.patch ]] || [[ "$PATCHNAME" =~ \.diff ]]; then
PATCHNAME="${PATCHNAME%.*}"
fi
TEMPDIR="$(mktemp -d)" || die "mktemp failed"
trap "rm -rf $TEMPDIR" EXIT INT TERM
if [[ -d "$KSRCDIR" ]]; then
echo "Using cache at $KSRCDIR"
cd "$KSRCDIR" || die
if [[ -f "$APPLIEDPATCHFILE" ]]; then
patch -R -p1 < "$APPLIEDPATCHFILE" || die "the kpatch cache is corrupted. \"rm -rf $KSRCDIR\" and try again"
rm -f "$APPLIEDPATCHFILE"
fi
make "-j$CPUS" vmlinux >> "$LOGFILE" 2>&1 || die
else
rpm -q --quiet rpmdevtools || die "rpmdevtools not installed"
rpm -q --quiet yum-utils || die "yum-utils not installed"
echo "Downloading kernel source for $ARCHVERSION"
yumdownloader --source --destdir "$TEMPDIR" "kernel-$ARCHVERSION" >> "$LOGFILE" 2>&1 || die
echo "Unpacking kernel source"
rpmdev-setuptree >> "$LOGFILE" 2>&1 || die
rpm -ivh "$TEMPDIR/kernel-$DISTROVERSION.src.rpm" >> "$LOGFILE" 2>&1 || die
rpmbuild -bp "--target=$(uname -m)" "$HOME/rpmbuild/SPECS/kernel.spec" >> "$LOGFILE" 2>&1 ||
die "rpmbuild -bp failed. you may need to run 'yum-builddep kernel' first."
mkdir -p "$KSRCDIR_DIR"
mv "$HOME"/rpmbuild/BUILD/kernel-*/linux-"$ARCHVERSION" "$KSRCDIR" >> "$LOGFILE" 2>&1 || die
echo "Building original kernel"
cd "$KSRCDIR"
echo "$LOCALVERSION" > localversion || die
make "-j$CPUS" vmlinux >> "$LOGFILE" 2>&1 || die
fi
find_data_dir || (echo "can't find data dir" >&2 && die)
cp -LR "$DATADIR/patch" "$TEMPDIR" || die
cp vmlinux "$TEMPDIR" || die
echo "Building patched kernel"
cp "$PATCHFILE" "$APPLIEDPATCHFILE" || die
patch -p1 < "$APPLIEDPATCHFILE" >> "$LOGFILE" 2>&1 || die
make "-j$CPUS" vmlinux > "$TEMPDIR/patched_build.log" 2>&1 || die
echo "Detecting changed objects"
grep CC "$TEMPDIR/patched_build.log" | grep -v init/version.o | awk '{print $2}' >> "$TEMPDIR/changed_objs"
if [[ $? -ne 0 ]]; then
echo "No changed objects"
exit 1
fi
echo "Rebuilding changed objects"
mkdir "$TEMPDIR/patched"
for i in $(cat $TEMPDIR/changed_objs); do
rm -f "$i"
KCFLAGS="-ffunction-sections -fdata-sections" make "$i" >> "$LOGFILE" 2>&1 || die
$STRIPCMD "$i" >> "$LOGFILE" 2>&1 || die
mkdir -p "$TEMPDIR/patched/$(dirname $i)"
cp -f "$i" "$TEMPDIR/patched/$i" || die
done
patch -R -p1 < "$APPLIEDPATCHFILE" >> "$LOGFILE" 2>&1
rm -f "$APPLIEDPATCHFILE"
mkdir "$TEMPDIR/orig"
for i in $(cat $TEMPDIR/changed_objs); do
rm -f "$i"
KCFLAGS="-ffunction-sections -fdata-sections" make "$i" >> "$LOGFILE" 2>&1 || die
$STRIPCMD -d "$i" >> "$LOGFILE" 2>&1 || die
mkdir -p "$TEMPDIR/orig/$(dirname $i)"
cp -f "$i" "$TEMPDIR/orig/$i" || die
done
echo "Extracting new and modified ELF sections"
cd "$TEMPDIR/orig"
FILES="$(find * -type f)"
cd "$TEMPDIR"
mkdir output
for i in $FILES; do
mkdir -p "output/$(dirname $i)"
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "output/$i" >> "$LOGFILE" 2>&1 || die
done
echo "Building patch module: kpatch-$PATCHNAME.ko"
cd "$TEMPDIR/output"
ld -r -o ../patch/output.o $FILES >> "$LOGFILE" 2>&1 || die
cd "$TEMPDIR/patch"
"$TOOLSDIR"/add-patches-section output.o ../vmlinux >> "$LOGFILE" 2>&1 || die
KPATCH_BUILD="$KSRCDIR" KPATCH_NAME="$PATCHNAME" make >> "$LOGFILE" 2>&1 || die
$STRIPCMD "kpatch-$PATCHNAME.ko" >> "$LOGFILE" 2>&1 || die
"$TOOLSDIR"/link-vmlinux-syms "kpatch-$PATCHNAME.ko" ../vmlinux >> "$LOGFILE" 2>&1 || die
cp -f "$TEMPDIR/patch/kpatch-$PATCHNAME.ko" "$BASE" || die
rm -f "$LOGFILE"
echo "SUCCESS"