From: Joe Lawrence <joe.lawrence@redhat.com>
To: live-patching@vger.kernel.org
Cc: Josh Poimboeuf <jpoimboe@kernel.org>, Song Liu <song@kernel.org>,
Miroslav Benes <mbenes@suse.cz>, Petr Mladek <pmladek@suse.com>,
Yafang Shao <laoar.shao@gmail.com>
Subject: [RFC PATCH v2 5/7] livepatch/klp-build: add basic out-of-tree module support
Date: Wed, 26 Aug 2026 15:49:58 -0400 [thread overview]
Message-ID: <20260826195000.455905-6-joe.lawrence@redhat.com> (raw)
In-Reply-To: <20260826195000.455905-1-joe.lawrence@redhat.com>
klp-build is currently limited to patching in-tree kernel modules.
Introduce a --oot-dir option to enable livepatch generation for basic
out-of-tree (OOT) modules. This requires the associated kernel tree to
be fully built (Module.symvers must exist).
The OOT workflow is as follows:
cd /path/to/built-kernel
./scripts/livepatch/klp-build --oot-dir /path/to/mymodule my-fix.patch
With this option, klp-build performs two builds (original and patched)
of the OOT module via `make M=...` instead of a full kernel rebuild.
The resulting objects are then processed and diffed to produce the final
livepatch .ko.
Note that --oot-dir drives the build via `make M=`, assuming a simple
Kbuild/Makefile layout. This is suited for small test modules and bug
reproducers rather than full OOT drivers with complex build systems
(which should use --orig-dir/--patched-dir instead).
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
scripts/livepatch/klp-build | 84 ++++++++++++++++++++++++++++---------
1 file changed, 64 insertions(+), 20 deletions(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index b52a8489d9f6..b60f5a5da31e 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -21,6 +21,7 @@ shopt -s lastpipe
unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE XTRACE
+OOT_DIR=""
REPLACE=1
SHORT_CIRCUIT=0
JOBS="$(getconf _NPROCESSORS_ONLN)"
@@ -137,6 +138,7 @@ Options:
Advanced Options:
-d, --debug Show symbol/reloc cloning decisions
+ --oot-dir=<DIR> Out-of-tree module source directory
-S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
1|orig Build original kernel (default)
2|patched Build patched kernel
@@ -160,7 +162,7 @@ process_args() {
local patch
short="hfj:o:vdS:T"
- long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
+ long="help,show-first-changed,jobs:,oot-dir:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
echo; usage; exit
@@ -202,6 +204,10 @@ process_args() {
keep_tmp=1
shift
;;
+ --oot-dir)
+ OOT_DIR="$2"
+ shift 2
+ ;;
-S | --short-circuit)
[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
keep_tmp=1
@@ -364,11 +370,21 @@ check_unsupported_patches() {
get_patch_files "$patch" | mapfile -t files
for file in "${files[@]}"; do
+ # In and out-of-tree paths to reject
case "$file" in
- lib/*|*/vdso/*|*/realmode/rm/*|*.S)
+ *.S)
die "${patch}: unsupported patch to $file"
;;
esac
+
+ # In-tree paths to reject
+ if [[ -z "$OOT_DIR" ]]; then
+ case "$file" in
+ lib/*|*/vdso/*|*/realmode/rm/*)
+ die "${patch}: unsupported patch to $file"
+ ;;
+ esac
+ fi
done
done
}
@@ -377,13 +393,14 @@ apply_patch() {
local patch="$1"
shift
local extra_args=("$@")
+ local patch_target="${OOT_DIR:-$PWD}"
local drift_regex="with fuzz|offset [0-9]+ line"
local output
local status
[[ ! -f "$patch" ]] && die "$patch doesn't exist"
status=0
- output=$(patch -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
+ output=$(patch -d "$patch_target" -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
if [[ "$status" -ne 0 ]]; then
echo "$output" >&2
die "$patch did not apply"
@@ -398,9 +415,10 @@ apply_patch() {
revert_patch() {
local patch="$1"
+ local patch_target="${OOT_DIR:-$PWD}"
local tmp=()
- patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
+ patch -d "$patch_target" -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
for p in "${APPLIED_PATCHES[@]}"; do
[[ "$p" == "$patch" ]] && continue
@@ -436,10 +454,17 @@ validate_patches() {
}
do_init() {
- # We're not yet smart enough to handle anything other than in-tree
- # builds in pwd.
[[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
+ if [[ -n "$OOT_DIR" ]]; then
+ [[ -d "$OOT_DIR" ]] || die "module directory not found: $OOT_DIR"
+ OOT_DIR="$(realpath "$OOT_DIR")"
+ [[ -f "$OOT_DIR/Kbuild" || -f "$OOT_DIR/Makefile" ]] ||
+ die "no Kbuild or Makefile in $OOT_DIR"
+ [[ -f "$PWD/Module.symvers" ]] ||
+ die "kernel must be built first (no Module.symvers in $PWD)"
+ fi
+
if (( SHORT_CIRCUIT >= 2 )); then
[[ -f "$ORIG_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_DIR"
fi
@@ -471,6 +496,7 @@ do_init() {
refresh_patch() {
local patch="$1"
local tmpdir="$PATCH_TMP_DIR"
+ local patch_target="${OOT_DIR:-$PWD}"
local input_files=()
local output_files=()
@@ -483,11 +509,11 @@ refresh_patch() {
get_patch_output_files "$patch" | mapfile -t output_files
# Copy orig source files to 'a'
- echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a"
+ ( cd "$patch_target" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" )
# Copy patched source files to 'b'
apply_patch "$patch" "--silent"
- echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
+ ( cd "$patch_target" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" )
revert_patch "$patch"
# Diff 'a' and 'b' to make a clean patch
@@ -529,6 +555,7 @@ clean_kernel() {
cmd=("make")
cmd+=("--silent")
cmd+=("-j$JOBS")
+ [[ -n "$OOT_DIR" ]] && cmd+=("M=$OOT_DIR")
cmd+=("clean")
"${cmd[@]}"
@@ -567,7 +594,11 @@ build_kernel() {
fi
cmd+=("-j$JOBS")
cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
- cmd+=("vmlinux")
+ if [[ -z "$OOT_DIR" ]]; then
+ cmd+=("vmlinux")
+ else
+ cmd+=("M=$OOT_DIR")
+ fi
cmd+=("modules")
"${cmd[@]}" \
@@ -579,13 +610,20 @@ build_kernel() {
find_objects() {
local opts=("$@")
- # Find root-level vmlinux.o and non-root-level .ko files,
- # excluding klp-tmp/ and hidden directories.
- find "$PWD" -mindepth 1 \
- \( -path "$TMP_DIR" -o -name ".*" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
- -type f "${opts[@]}" \
- \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
- -printf '%P\n'
+ if [[ -z "$OOT_DIR" ]]; then
+ # In-tree: find root-level vmlinux.o and non-root-level .ko files,
+ # excluding klp-tmp/ and hidden directories.
+ find "$PWD" -mindepth 1 \
+ \( -path "$TMP_DIR" -o -name ".*" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
+ -type f "${opts[@]}" \
+ \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
+ -printf '%P\n'
+ else
+ # OOT: find .ko at any depth under the module dir
+ find "$OOT_DIR" -path "$OOT_DIR/.git" -prune -o \
+ -type f "${opts[@]}" \
+ -name "*.ko" -printf '%P\n'
+ fi
}
# Copy all .o archives to $ORIG_DIR
@@ -597,10 +635,11 @@ copy_orig_objects() {
find_objects | mapfile -t files
+ local obj_root="${OOT_DIR:-$PWD}"
xtrace_save "copying original objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$PWD/$rel_file"
+ local file="$obj_root/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local orig_dir="$(dirname "$orig_file")"
@@ -633,10 +672,11 @@ copy_patched_objects() {
find_objects "${opts[@]}" | mapfile -t files
+ local obj_root="${OOT_DIR:-$PWD}"
xtrace_save "copying changed objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$PWD/$rel_file"
+ local file="$obj_root/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local patched_file="$PATCHED_DIR/$rel_file"
local patched_dir="$(dirname "$patched_file")"
@@ -717,6 +757,7 @@ diff_objects() {
cmd+=("klp")
cmd+=("diff")
(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
+ [[ -n "$OOT_DIR" ]] && cmd+=("--symvers" "$PWD/Module.symvers")
cmd+=("$orig_file")
cmd+=("$patched_file")
cmd+=("$out_file")
@@ -895,13 +936,16 @@ build_patch_module() {
process_args "$@"
do_init
+BUILD_TARGET="kernel"
+[[ -n "$OOT_DIR" ]] && BUILD_TARGET="module ${OOT_DIR##*/}"
+
if (( SHORT_CIRCUIT <= 2 )); then
status "Validating patch(es)"
validate_patches
fi
if (( SHORT_CIRCUIT <= 1 )); then
- status "Building original kernel"
+ status "Building original $BUILD_TARGET"
clean_kernel
build_kernel "original"
status "Copying original object files"
@@ -912,7 +956,7 @@ if (( SHORT_CIRCUIT <= 2 )); then
status "Fixing patch(es)"
fix_patches
apply_patches "--silent"
- status "Building patched kernel"
+ status "Building patched $BUILD_TARGET"
build_kernel "patched"
revert_patches
status "Copying patched object files"
--
2.55.0
next prev parent reply other threads:[~2026-08-26 19:50 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-26 19:49 [RFC PATCH v2 0/7] klp-build: OOT module support Joe Lawrence
2026-08-26 19:49 ` [RFC PATCH v2 1/7] objtool/klp: simplify read_exports file handling Joe Lawrence
2026-08-26 23:31 ` Song Liu
2026-08-26 19:49 ` [RFC PATCH v2 2/7] objtool/klp: add --symvers option to klp diff Joe Lawrence
2026-08-26 23:31 ` Song Liu
2026-08-26 19:49 ` [RFC PATCH v2 3/7] objtool/klp: allow special section entry size overrides Joe Lawrence
2026-08-27 19:17 ` Josh Poimboeuf
2026-08-26 19:49 ` [RFC PATCH v2 4/7] objtool: add target architecture to usage Joe Lawrence
2026-08-26 19:57 ` sashiko-bot
2026-08-27 19:23 ` Josh Poimboeuf
2026-08-26 19:49 ` Joe Lawrence [this message]
2026-08-26 20:00 ` [RFC PATCH v2 5/7] livepatch/klp-build: add basic out-of-tree module support sashiko-bot
2026-08-27 21:21 ` Josh Poimboeuf
2026-08-26 19:49 ` [RFC PATCH v2 6/7] livepatch/klp-build: add pre-built object support for advanced OOT workflows Joe Lawrence
2026-08-26 20:01 ` sashiko-bot
2026-08-27 21:45 ` Josh Poimboeuf
2026-08-26 19:50 ` [RFC PATCH v2 7/7] livepatch/klp-build: add validation for user-supplied OOT objects Joe Lawrence
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260826195000.455905-6-joe.lawrence@redhat.com \
--to=joe.lawrence@redhat.com \
--cc=jpoimboe@kernel.org \
--cc=laoar.shao@gmail.com \
--cc=live-patching@vger.kernel.org \
--cc=mbenes@suse.cz \
--cc=pmladek@suse.com \
--cc=song@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox