mirror of https://github.com/dynup/kpatch
Add support for building out-of-tree modules
This commit is contained in:
parent
fb2801e4a5
commit
51a8fad34f
24
README.md
24
README.md
|
@ -648,9 +648,27 @@ sys_nanosleep(), etc?**
|
|||
|
||||
**Q. Can you patch out-of-tree modules?**
|
||||
|
||||
- Yes, though it's currently a bit of a manual process. See this
|
||||
[message](https://www.redhat.com/archives/kpatch/2015-June/msg00004.html) on
|
||||
the kpatch mailing list for more information.
|
||||
Yes! There's a few requirements, and the feature is still in its infancy.
|
||||
|
||||
1. You need to use the `--out-of-tree` flag to specify the version of the
|
||||
module that's currently running on the machine.
|
||||
2. `--sourcedir` has to be passed with a directory containing the same
|
||||
version of code as the running module, all set up and ready to build with a
|
||||
`make` command. For example, some modules need `autogen.sh` and
|
||||
`./configure` to have been run with the appropriate flags to match the
|
||||
currently-running module.
|
||||
3. If the `Module.symvers` file for the out-of-tree module doesn't appear
|
||||
in the root of the provided source directory, a symlink needs to be created
|
||||
in that directory that points to its actual location.
|
||||
4. Usually you'll need to pass the `-t` flag as well, to specify the proper
|
||||
`make` target names.
|
||||
5. This has only been tested for a single out-of-tree module per patch, and
|
||||
not for out-of-tree modules with dependencies on other out-of-tree modules
|
||||
built separately.
|
||||
|
||||
***Sample invocation***
|
||||
|
||||
`kpatch-build -s ~/test/ -t default -e /lib/modules/$(uname -r)/extra/test.ko test.patch`
|
||||
|
||||
|
||||
Get involved
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
# - Either uses a specified kernel source directory or downloads the kernel
|
||||
# source package for the currently running kernel
|
||||
# - Unpacks and prepares the source package for building if necessary
|
||||
# - Builds the base kernel (vmlinux)
|
||||
# - Builds the patched kernel and monitors changed objects
|
||||
# - Builds the base kernel or module
|
||||
# - Builds the patched kernel/module 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
|
||||
|
||||
|
@ -52,6 +52,7 @@ SKIPGCCCHECK=0
|
|||
ARCH_KCFLAGS=""
|
||||
declare -a PATCH_LIST
|
||||
APPLIED_PATCHES=0
|
||||
OOT_MODULE=
|
||||
|
||||
warn() {
|
||||
echo "ERROR: $1" >&2
|
||||
|
@ -187,7 +188,7 @@ find_core_symvers() {
|
|||
}
|
||||
|
||||
gcc_version_from_file() {
|
||||
readelf -p .comment "$1" | grep -o 'GCC:.*'
|
||||
readelf -p .comment "$1" | grep -o 'GCC:.*' | head -n 1
|
||||
}
|
||||
|
||||
gcc_version_check() {
|
||||
|
@ -199,7 +200,11 @@ gcc_version_check() {
|
|||
echo 'void main(void) {}' > "$c"
|
||||
out="$(gcc -c -pg -ffunction-sections -o "$o" "$c" 2>&1)"
|
||||
gccver="$(gcc_version_from_file "$o")"
|
||||
if [[ -n "$OOT_MODULE" ]]; then
|
||||
kgccver="$(gcc_version_from_file "$OOT_MODULE")"
|
||||
else
|
||||
kgccver="$(gcc_version_from_file "$VMLINUX")"
|
||||
fi
|
||||
rm -f "$c" "$o"
|
||||
|
||||
if [[ -n "$out" ]]; then
|
||||
|
@ -392,12 +397,14 @@ usage() {
|
|||
echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2
|
||||
echo " in <CACHEDIR>/tmp" >&2
|
||||
echo " (can be specified multiple times)" >&2
|
||||
echo " -e, --oot-module Enable patching out-of-tree module," >&2
|
||||
echo " specify current version of module" >&2
|
||||
echo " --skip-cleanup Skip post-build cleanup" >&2
|
||||
echo " --skip-gcc-check Skip gcc version matching check" >&2
|
||||
echo " (not recommended)" >&2
|
||||
}
|
||||
|
||||
options="$(getopt -o ha:r:s:c:v:j:t:n:o:d -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,debug,skip-gcc-check,skip-cleanup" -- "$@")" || die "getopt failed"
|
||||
options="$(getopt -o ha:r:s:c:v:j:t:n:o:de: -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,debug,skip-gcc-check,skip-cleanup" -- "$@")" || die "getopt failed"
|
||||
|
||||
eval set -- "$options"
|
||||
|
||||
|
@ -455,6 +462,11 @@ while [[ $# -gt 0 ]]; do
|
|||
echo "DEBUG mode enabled"
|
||||
fi
|
||||
;;
|
||||
-e|--oot-module)
|
||||
[[ ! -f "$2" ]] && die "out-of-tree module '$2' not found"
|
||||
OOT_MODULE="$(readlink -f "$2")"
|
||||
shift
|
||||
;;
|
||||
--skip-cleanup)
|
||||
echo "Skipping cleanup"
|
||||
SKIPCLEANUP=1
|
||||
|
@ -483,8 +495,7 @@ if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then
|
|||
fi
|
||||
|
||||
if [[ -n "$ARCHVERSION" ]] && [[ -n "$VMLINUX" ]]; then
|
||||
warn "--archversion is incompatible with --vmlinux"
|
||||
exit 1
|
||||
die "--archversion is incompatible with --vmlinux"
|
||||
fi
|
||||
|
||||
if [[ -n "$SRCRPM" ]]; then
|
||||
|
@ -498,6 +509,11 @@ if [[ -n "$SRCRPM" ]]; then
|
|||
ARCHVERSION="${ARCHVERSION#alt-}"
|
||||
fi
|
||||
|
||||
if [[ -n "$OOT_MODULE" ]] && [[ -z "$USERSRCDIR" ]]; then
|
||||
warn "--oot-module requires --sourcedir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ensure cachedir and tempdir are setup properly and cleaned
|
||||
mkdir -p "$TEMPDIR" || die "Couldn't create $TEMPDIR"
|
||||
rm -rf "${TEMPDIR:?}"/*
|
||||
|
@ -510,11 +526,15 @@ if [[ -n "$USERSRCDIR" ]]; then
|
|||
fi
|
||||
SRCDIR="$USERSRCDIR"
|
||||
|
||||
if [[ -z "$OOT_MODULE" ]]; then
|
||||
[[ -z "$VMLINUX" ]] && VMLINUX="$SRCDIR"/vmlinux
|
||||
[[ ! -e "$VMLINUX" ]] && die "can't find vmlinux"
|
||||
|
||||
# Extract the target kernel version from vmlinux in this case.
|
||||
ARCHVERSION="$(strings "$VMLINUX" | grep -m 1 -e "^Linux version" | awk '{ print($3); }')"
|
||||
else
|
||||
ARCHVERSION="$(modinfo -F vermagic "$OOT_MODULE" | awk '{print $1}')"
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ -z "$ARCHVERSION" ]] && ARCHVERSION="$(uname -r)"
|
||||
|
@ -563,7 +583,15 @@ if [[ -n "$USERSRCDIR" ]]; then
|
|||
echo "Using source directory at $USERSRCDIR"
|
||||
|
||||
# save original vmlinux before it gets overwritten by sourcedir build
|
||||
[[ "$VMLINUX" -ef "$SRCDIR"/vmlinux ]] && cp -f "$VMLINUX" "$TEMPDIR/vmlinux" && VMLINUX="$TEMPDIR/vmlinux"
|
||||
if [[ -z "$OOT_MODULE" ]] && [[ "$VMLINUX" -ef "$SRCDIR"/vmlinux ]]; then
|
||||
cp -f "$VMLINUX" "$TEMPDIR/vmlinux"
|
||||
VMLINUX="$TEMPDIR/vmlinux"
|
||||
fi
|
||||
|
||||
# For external modules, use the running kernel's config
|
||||
if [[ -n "$OOT_MODULE" ]] && [[ -z "$CONFIGFILE" ]]; then
|
||||
CONFIGFILE="/boot/config-${ARCHVERSION}"
|
||||
fi
|
||||
|
||||
elif [[ -e "$SRCDIR"/.config ]] && [[ -e "$VERSIONFILE" ]] && [[ "$(cat "$VERSIONFILE")" = "$ARCHVERSION" ]]; then
|
||||
echo "Using cache at $SRCDIR"
|
||||
|
@ -710,18 +738,20 @@ if [[ $DEBUG -ge 4 ]]; then
|
|||
export KPATCH_GCC_DEBUG=1
|
||||
fi
|
||||
|
||||
echo "Building original kernel"
|
||||
./scripts/setlocalversion --save-scmversion || die
|
||||
echo "Building original source"
|
||||
[[ -n "$OOT_MODULE" ]] || ./scripts/setlocalversion --save-scmversion || die
|
||||
unset KPATCH_GCC_TEMPDIR
|
||||
# $TARGETS used as list, no quotes.
|
||||
# shellcheck disable=SC2086
|
||||
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " make "-j$CPUS" $TARGETS 2>&1 | logger || die
|
||||
|
||||
echo "Building patched kernel"
|
||||
echo "Building patched source"
|
||||
apply_patches
|
||||
mkdir -p "$TEMPDIR/orig" "$TEMPDIR/patched"
|
||||
KPATCH_GCC_TEMPDIR="$TEMPDIR"
|
||||
export KPATCH_GCC_TEMPDIR
|
||||
KPATCH_GCC_SRCDIR="$SRCDIR"
|
||||
export KPATCH_GCC_SRCDIR
|
||||
# $TARGETS used as list, no quotes.
|
||||
# shellcheck disable=SC2086
|
||||
CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " \
|
||||
|
@ -739,7 +769,7 @@ if [[ ! -e "$TEMPDIR/changed_objs" ]]; then
|
|||
die "no changed objects found"
|
||||
fi
|
||||
|
||||
grep -q vmlinux "$SRCDIR/Module.symvers" || die "truncated $SRCDIR/Module.symvers file"
|
||||
[[ -n "$OOT_MODULE" ]] || grep -q vmlinux "$SRCDIR/Module.symvers" || die "truncated $SRCDIR/Module.symvers file"
|
||||
|
||||
# Read as words, no quotes.
|
||||
# shellcheck disable=SC2013
|
||||
|
@ -792,15 +822,25 @@ for i in $FILES; do
|
|||
find_kobj "$i"
|
||||
cd "$TEMPDIR" || die
|
||||
if [[ -e "orig/$i" ]]; then
|
||||
if [[ "$KOBJFILE" = vmlinux ]]; then
|
||||
if [[ "$(basename "$KOBJFILE")" = vmlinux ]]; then
|
||||
KOBJFILE_NAME=vmlinux
|
||||
KOBJFILE_PATH="$VMLINUX"
|
||||
SYMTAB="${TEMPDIR}/${KOBJFILE_NAME}.symtab"
|
||||
SYMVERS_FILE="$SRCDIR/Module.symvers"
|
||||
elif [[ "$(basename "$KOBJFILE")" = "$(basename "$OOT_MODULE")" ]]; then
|
||||
KOBJFILE_NAME="$(basename --suffix=.ko "$OOT_MODULE")"
|
||||
KOBJFILE_PATH="$OOT_MODULE"
|
||||
SYMTAB="${TEMPDIR}/module/${KOBJFILE_NAME}.symtab"
|
||||
SYMVERS_FILE="$TEMPDIR/Module.symvers"
|
||||
BUILDDIR="/lib/modules/$ARCHVERSION/build/"
|
||||
cp "$SRCDIR/Module.symvers" "$SYMVERS_FILE"
|
||||
awk '{ print $1 "\t" $2 "\t" $3 "\t" $4}' "${BUILDDIR}/Module.symvers" >> "$SYMVERS_FILE"
|
||||
else
|
||||
KOBJFILE_NAME=$(basename "${KOBJFILE%.ko}")
|
||||
KOBJFILE_NAME="${KOBJFILE_NAME/-/_}"
|
||||
KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE"
|
||||
SYMTAB="${KOBJFILE_PATH}.symtab"
|
||||
SYMVERS_FILE="$SRCDIR/Module.symvers"
|
||||
fi
|
||||
|
||||
eu-readelf -s "$KOBJFILE_PATH" > "$SYMTAB"
|
||||
|
@ -808,7 +848,7 @@ for i in $FILES; do
|
|||
# create-diff-object orig.o patched.o parent-name parent-symtab
|
||||
# Module.symvers patch-mod-name output.o
|
||||
"$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE_NAME" \
|
||||
"$SYMTAB" "$SRCDIR/Module.symvers" "${MODNAME//-/_}" \
|
||||
"$SYMTAB" "$SYMVERS_FILE" "${MODNAME//-/_}" \
|
||||
"output/$i" 2>&1 | logger 1
|
||||
check_pipe_status create-diff-object
|
||||
# create-diff-object returns 3 if no functional change is found
|
||||
|
@ -870,8 +910,12 @@ else
|
|||
fi
|
||||
|
||||
cd "$TEMPDIR/patch" || die
|
||||
|
||||
KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$MODNAME" \
|
||||
if [[ -z "$OOT_MODULE" ]]; then
|
||||
KPATCH_BUILD="$SRCDIR"
|
||||
else
|
||||
KPATCH_BUILD="/lib/modules/$ARCHVERSION/build"
|
||||
fi
|
||||
KPATCH_BUILD="$KPATCH_BUILD" KPATCH_NAME="$MODNAME" \
|
||||
KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \
|
||||
KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \
|
||||
make 2>&1 | logger || die
|
||||
|
|
|
@ -23,6 +23,7 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then
|
|||
[[ "$obj" = */.tmp_mc_*.o ]] && break;
|
||||
|
||||
[[ "$obj" = */.tmp_*.o ]] && obj="${obj/.tmp_/}"
|
||||
relobj=${obj//$KPATCH_GCC_SRCDIR\//}
|
||||
case "$obj" in
|
||||
*.mod.o|\
|
||||
*built-in.o|\
|
||||
|
@ -47,9 +48,9 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then
|
|||
break
|
||||
;;
|
||||
*.o)
|
||||
mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$obj")"
|
||||
[[ -e "$obj" ]] && cp -f "$obj" "$KPATCH_GCC_TEMPDIR/orig/$obj"
|
||||
echo "$obj" >> "$KPATCH_GCC_TEMPDIR/changed_objs"
|
||||
mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")"
|
||||
[[ -e "$obj" ]] && cp -f "$obj" "$KPATCH_GCC_TEMPDIR/orig/$relobj"
|
||||
echo "$relobj" >> "$KPATCH_GCC_TEMPDIR/changed_objs"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
|
@ -63,10 +64,11 @@ elif [[ "$TOOLCHAINCMD" = "ld" ]] ; then
|
|||
while [ "$#" -gt 0 ]; do
|
||||
if [ "$1" = "-o" ]; then
|
||||
obj="$2"
|
||||
relobj=${obj//$KPATCH_GCC_SRCDIR\//}
|
||||
case "$obj" in
|
||||
*.ko)
|
||||
mkdir -p "$KPATCH_GCC_TEMPDIR/module/$(dirname "$obj")"
|
||||
cp -f "$obj" "$KPATCH_GCC_TEMPDIR/module/$obj"
|
||||
mkdir -p "$KPATCH_GCC_TEMPDIR/module/$(dirname "$relobj")"
|
||||
cp -f "$obj" "$KPATCH_GCC_TEMPDIR/module/$relobj"
|
||||
break
|
||||
;;
|
||||
.tmp_vmlinux*|vmlinux)
|
||||
|
|
Loading…
Reference in New Issue