kpatch-build: kpatch-build module patching support

This adds support to kpatch-build for patching modules.  It builds the
entire kernel tree, vmlinux and modules, in a single pass and then
detects which modules need to be patched.  This is the easiest case
(since the user doesn't need to care about which binaries are affected)
and the safest (since the user could be wrong).

The first build with no ccache takes a long time, but after the cache is
populated, it only takes about two minutes on my laptop.  It does take
up a TON of space in the cache now though (~/.kpatch/obj is now 8GB).

Next we can add the '-t' cmdline option for advanced users to specify
build targets.
This commit is contained in:
Josh Poimboeuf 2014-06-13 17:29:29 -05:00
parent 5b2bd03a3b
commit 6c8366d414
2 changed files with 85 additions and 6 deletions

View File

@ -21,6 +21,9 @@ Installation
####Fedora 20
*NOTE: You'll need about 10GB of free disk space for the kpatch-build cache in
`~/.kpatch`.*
Install the dependencies for compiling kpatch:
sudo yum install gcc kernel-devel elfutils elfutils-devel
@ -38,6 +41,9 @@ Install the dependencies for the "kpatch-build" command:
####Ubuntu 14.04
*NOTE: You'll need about 10GB of free disk space for the kpatch-build cache in
`~/.kpatch`.*
Install the dependencies for compiling kpatch:
apt-get install make gcc libelf-dev

View File

@ -82,6 +82,51 @@ find_dirs() {
return 1
}
find_parent_obj() {
dir=$(dirname $1)
file=$(basename $1)
grepname=${1%.o}
grepname=$grepname\\\.o
if [[ $DEEP_FIND -eq 1 ]]; then
parent=$(find . -name ".*.cmd" | xargs grep -l $grepname | grep -v $dir/.${file}.cmd)
num=$(find . -name ".*.cmd" | xargs grep -l $grepname | grep -v $dir/.${file}.cmd | wc -l)
else
parent=$(grep -l $grepname $dir/.*.cmd | grep -v $dir/.${file}.cmd)
num=$(grep -l $grepname $dir/.*.cmd | grep -v $dir/.${file}.cmd | wc -l)
fi
[[ $num -eq 0 ]] && PARENT="" && return
[[ $num -gt 1 ]] && die "two parent matches for $1"
dir=$(dirname $parent)
PARENT=$(basename $parent)
PARENT=${PARENT#.}
PARENT=${PARENT%.cmd}
PARENT=$dir/$PARENT
[[ ! -e $PARENT ]] && die "ERROR: can't find parent $PARENT for $1"
}
find_kobj() {
arg=$1
KOBJFILE=$arg
dir=$(dirname $KOBJFILE)
file=$(basename $KOBJFILE)
DEEP_FIND=0
while true; do
find_parent_obj $KOBJFILE
if [[ -z $PARENT ]]; then
[[ $KOBJFILE = *built-in.o ]] && KOBJFILE=vmlinux && return
[[ $KOBJFILE = *.ko ]] && return
if [[ $DEEP_FIND -eq 0 ]]; then
DEEP_FIND=1
continue;
fi
die "invalid ancestor $KOBJFILE for $arg"
fi
KOBJFILE=$PARENT
done
}
usage() {
echo "usage: $0 [options] <patch file>" >&2
echo " -h, --help Show this help message" >&2
@ -268,20 +313,35 @@ cd "$SRCDIR" || die
patch -N -p1 --dry-run < "$PATCHFILE" || die "source patch file failed to apply"
cp "$PATCHFILE" "$APPLIEDPATCHFILE" || die
TARGETS="vmlinux modules"
echo "Building original kernel"
make mrproper >> "$LOGFILE" 2>&1 || die
make "-j$CPUS" vmlinux "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
make "-j$CPUS" $TARGETS "O=$OBJDIR" >> "$LOGFILE" 2>&1 || die
cp -LR "$DATADIR/patch" "$TEMPDIR" || die
echo "Building patched kernel"
patch -N -p1 < "$APPLIEDPATCHFILE" >> "$LOGFILE" 2>&1 || die
make "-j$CPUS" vmlinux "O=$OBJDIR" 2>&1 | tee -a "$TEMPDIR/patched_build.log" >> "$LOGFILE"
make "-j$CPUS" $TARGETS "O=$OBJDIR" 2>&1 | tee -a "$TEMPDIR/patched_build.log" >> "$LOGFILE"
[[ "${PIPESTATUS[0]}" -eq 0 ]] || die
echo "Detecting changed objects"
grep CC "$TEMPDIR/patched_build.log" | grep -v -e init/version.o -e scripts/mod/devicetable-offsets.s -e scripts/mod/file2alias.o | awk '{print $2}' > "$TEMPDIR/changed_objs"
while read line; do
[[ "$line" =~ CC ]] || continue
eval set -- "$line"
case $2 in
init/version.o) continue ;;
scripts/mod/devicetable-offsets.s) continue ;;
scripts/mod/file2alias.o) continue ;;
arch/x86/kernel/asm-offsets.s) die "a struct definition change was detected" ;;
\[M\]) obj=$3 ;;
*) obj=$2 ;;
esac
echo $obj >> $TEMPDIR/changed_objs
done < "$TEMPDIR/patched_build.log"
[[ ! -s "$TEMPDIR/changed_objs" ]] && die "no changed objects were detected"
grep -q asm-offsets.s $TEMPDIR/changed_objs && die "a struct definition change was detected"
echo "Rebuilding changed objects"
rm -rf "$OBJDIR2"
@ -293,7 +353,6 @@ for i in $(cat $TEMPDIR/changed_objs); do
$STRIPCMD "$OBJDIR2/$i" >> "$LOGFILE" 2>&1 || die
mkdir -p "$TEMPDIR/patched/$(dirname $i)"
cp -f "$OBJDIR2/$i" "$TEMPDIR/patched/$i" || die
done
patch -R -p1 < "$APPLIEDPATCHFILE" >> "$LOGFILE" 2>&1
rm -f "$APPLIEDPATCHFILE"
@ -311,12 +370,26 @@ cd "$TEMPDIR/orig"
FILES="$(find * -type f)"
cd "$TEMPDIR"
mkdir output
declare -A objnames
for i in $FILES; do
mkdir -p "output/$(dirname $i)"
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$VMLINUX" "output/$i" 2>&1 |tee -a "$LOGFILE"
cd "$OBJDIR"
find_kobj $i
objnames[$KOBJFILE]=1
if [[ $KOBJFILE = vmlinux ]]; then
KOBJFILE=$VMLINUX
else
KOBJFILE="$(readlink -f $KOBJFILE)"
fi
cd $TEMPDIR
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE" "output/$i" 2>&1 |tee -a "$LOGFILE"
[[ "${PIPESTATUS[0]}" -eq 0 ]] || die
done
echo -n "Patched objects:"
for i in "${!objnames[@]}"; do echo -n " $(basename $i)"; done
echo
echo "Building patch module: kpatch-$PATCHNAME.ko"
cp "$OBJDIR/.config" "$SRCDIR"
cd "$SRCDIR"