Thread (19 messages) flat view 19 messages, 5 authors, 14d ago
COOLING14d

[RFC PATCH v2 6/7] livepatch/klp-build: add pre-built object support for advanced OOT workflows

From: Joe Lawrence <joe.lawrence@redhat.com>
Date: 2026-08-26 19:50:19
Subsystem: live patching, the rest · Maintainers: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Petr Mladek, Linus Torvalds

Most real-world out-of-tree drivers use complicated, non-standard build
systems (DKMS, cmake, autoconf, proprietary toolchains) that cannot be
driven by klp-build's internal build stages.  Rather than attempting to
accommodate every third-party build system, let the user handle the
build process and supply the object pairs directly.

Enhance klp-build with --orig-dir and --patched-dir options that accept
directories of pre-built .o files.  The user is responsible for building
original and patched objects externally.  The only hard requirement is
that both builds use -ffunction-sections and -fdata-sections so that
objtool can identify changed functions at the object level.

From there, klp-build performs its binary comparison, symbol extraction,
and livepatch module assembly pipeline used for in-tree patches, only
with the build steps factored out.  External symbol ownership is
determined from the target kernel's Module.symvers (--symvers,
defaulting to $PWD/Module.symvers).

The new options are mutually exclusive with --oot-dir (which drives the
build itself) and --short-circuit (which is specific to the standard
build pipeline).

The workflow is as follows:

  # 1. Build OOT objects with -ffunction-sections and -fdata-sections
  mkdir -p /tmp/orig && cp $OOT/my_module.o /tmp/orig/

  # 2. (Optional) leverage the kernel's fix-patch-lines script to "undo"
  #    the effects of line number shift by the patch
  $KDIR/scripts/livepatch/fix-patch-lines $OOT/fix-overflow.patch \
      | recountdiff > /tmp/fixed.patch

  # 3. Apply patch, rebuild
  mkdir -p /tmp/patched && cp $OOT/my_module.o /tmp/patched/

  # 4. Generate the livepatch
  cd $KDIR
  ./scripts/livepatch/klp-build \
      --orig-dir /tmp/orig \
      --patched-dir /tmp/patched \
      --symvers $KDIR/Module.symvers \
      -o livepatch-my_module.ko

NOTE: klp-build does not verify that user-supplied objects were built
against the correct kernel headers or with compatible compiler options.
The resulting livepatch module is only as correct as the inputs
provided.

Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
 scripts/livepatch/klp-build | 110 +++++++++++++++++++++++++++++++++---
 1 file changed, 102 insertions(+), 8 deletions(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index b60f5a5da31e..e118f133e923 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -22,6 +22,9 @@ shopt -s lastpipe
 unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE XTRACE
 
 OOT_DIR=""
+USER_ORIG_DIR=""
+USER_PATCHED_DIR=""
+SYMVERS_PATH=""
 REPLACE=1
 SHORT_CIRCUIT=0
 JOBS="$(getconf _NPROCESSORS_ONLN)"
@@ -127,6 +130,7 @@ trap trap_err ERR
 __usage() {
 	cat <<EOF
 Usage: $SCRIPT [OPTIONS] PATCH_FILE(s)
+       $SCRIPT --orig-dir=<DIR> --patched-dir=<DIR> [OPTIONS]
 Generate a livepatch module.
 
 Options:
@@ -139,6 +143,9 @@ Options:
 Advanced Options:
    -d, --debug			Show symbol/reloc cloning decisions
        --oot-dir=<DIR>		Out-of-tree module source directory
+       --orig-dir=<DIR>		Directory of pre-built original .o files
+       --patched-dir=<DIR>	Directory of pre-built patched .o files
+       --symvers=<FILE>		Path to Module.symvers [default: \$PWD/Module.symvers]
    -S, --short-circuit=STEP	Start at build step (requires prior --keep-tmp)
 				   1|orig		Build original kernel (default)
 				   2|patched		Build patched kernel
@@ -162,7 +169,7 @@ process_args() {
 	local patch
 
 	short="hfj:o:vdS:T"
-	long="help,show-first-changed,jobs:,oot-dir:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
+	long="help,show-first-changed,jobs:,oot-dir:,orig-dir:,patched-dir:,symvers:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
 
 	args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
 		echo; usage; exit
@@ -208,6 +215,18 @@ process_args() {
 				OOT_DIR="$2"
 				shift 2
 				;;
+			--orig-dir)
+				USER_ORIG_DIR="$2"
+				shift 2
+				;;
+			--patched-dir)
+				USER_PATCHED_DIR="$2"
+				shift 2
+				;;
+			--symvers)
+				SYMVERS_PATH="$2"
+				shift 2
+				;;
 			-S | --short-circuit)
 				[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
 				keep_tmp=1
@@ -236,7 +255,18 @@ process_args() {
 		esac
 	done
 
-	if [[ $# -eq 0 ]] && (( SHORT_CIRCUIT <= 2 )); then
+	if [[ -n "$USER_ORIG_DIR" || -n "$USER_PATCHED_DIR" ]]; then
+		[[ -n "$USER_ORIG_DIR" && -n "$USER_PATCHED_DIR" ]] ||
+			die "--orig-dir and --patched-dir must both be specified"
+		[[ -v NAME ]] ||
+			die "--orig-dir/--patched-dir requires -o <output.ko>"
+		[[ -n "$OOT_DIR" ]] &&
+			die "--orig-dir/--patched-dir and --oot-dir are mutually exclusive"
+		(( SHORT_CIRCUIT > 0 )) &&
+			die "--short-circuit is not used with --orig-dir/--patched-dir"
+		[[ $# -gt 0 ]] &&
+			die "patch files are not used with --orig-dir/--patched-dir"
+	elif [[ $# -eq 0 ]] && (( SHORT_CIRCUIT <= 2 )); then
 		usage
 		exit 1
 	fi
@@ -456,7 +486,19 @@ validate_patches() {
 do_init() {
 	[[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
 
-	if [[ -n "$OOT_DIR" ]]; then
+	if [[ -n "$USER_ORIG_DIR" ]]; then
+		[[ -d "$USER_ORIG_DIR" ]] || die "directory not found: $USER_ORIG_DIR"
+		[[ -d "$USER_PATCHED_DIR" ]] || die "directory not found: $USER_PATCHED_DIR"
+		USER_ORIG_DIR="$(realpath "$USER_ORIG_DIR")"
+		USER_PATCHED_DIR="$(realpath "$USER_PATCHED_DIR")"
+		if [[ -n "$SYMVERS_PATH" ]]; then
+			SYMVERS_PATH="$(realpath "$SYMVERS_PATH")"
+			[[ -f "$SYMVERS_PATH" ]] || die "Module.symvers not found: $SYMVERS_PATH"
+		else
+			[[ -f "$PWD/Module.symvers" ]] ||
+				die "no Module.symvers in $PWD; use --symvers to specify"
+		fi
+	elif [[ -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" ]] ||
@@ -482,14 +524,19 @@ do_init() {
 	(( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
 	mkdir -p "$TMP_DIR"
 
+	validate_config
+	set_module_name
+	set_kernelversion
+
+	# Pre-built OOT init complete
+	if [[ -n "$USER_ORIG_DIR" ]]; then
+		return 0
+	fi
+
 	APPLIED_PATCHES=()
 
 	[[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines"
 	command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)"
-
-	validate_config
-	set_module_name
-	set_kernelversion
 }
 
 # Refresh the patch hunk headers, specifically the line numbers and counts.
@@ -757,7 +804,11 @@ diff_objects() {
 		cmd+=("klp")
 		cmd+=("diff")
 		(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
-		[[ -n "$OOT_DIR" ]] && cmd+=("--symvers" "$PWD/Module.symvers")
+		if [[ -n "$SYMVERS_PATH" ]]; then
+			cmd+=("--symvers" "$SYMVERS_PATH")
+		elif [[ -n "$OOT_DIR" || -n "$USER_ORIG_DIR" ]]; then
+			cmd+=("--symvers" "$PWD/Module.symvers")
+		fi
 		cmd+=("$orig_file")
 		cmd+=("$patched_file")
 		cmd+=("$out_file")
@@ -931,11 +982,54 @@ build_patch_module() {
 }
 
 
+setup_oot() {
+	local files=()
+	local rel
+
+	mkdir -p "$ORIG_DIR" "$PATCHED_DIR"
+
+	find "$USER_ORIG_DIR" -type f -name "*.o" -printf '%P\n' | mapfile -t files
+	[[ ${#files[@]} -gt 0 ]] || die "no .o files found in $USER_ORIG_DIR"
+	for rel in "${files[@]}"; do
+		[[ -f "$USER_PATCHED_DIR/$rel" ]] ||
+			die "$rel found in orig dir but missing from patched dir"
+		mkdir -p "$ORIG_DIR/$(dirname "$rel")"
+		mkdir -p "$PATCHED_DIR/$(dirname "$rel")"
+		cp -f "$USER_ORIG_DIR/$rel" "$ORIG_DIR/$rel"
+		cp -f "$USER_PATCHED_DIR/$rel" "$PATCHED_DIR/$rel"
+	done
+
+	touch "$ORIG_DIR/.complete"
+	touch "$PATCHED_DIR/.complete"
+}
+
+
 ################################################################################
 
 process_args "$@"
 do_init
 
+# User provided OOT original and patched object files can jump straight
+# to checksum + diff + build steps
+if [[ -n "$USER_ORIG_DIR" ]]; then
+	status "Setting up OOT objects"
+	setup_oot
+
+	status "Generating original checksums"
+	generate_checksums "$ORIG_DIR" "$ORIG_CSUM_DIR" "$PATCHED_DIR"
+	status "Generating patched checksums"
+	generate_checksums "$PATCHED_DIR" "$PATCHED_CSUM_DIR"
+
+	status "Diffing objects"
+	diff_objects
+
+	status "Building patch module: $OUTFILE"
+	build_patch_module
+
+	status "SUCCESS"
+	exit 0
+fi
+
 BUILD_TARGET="kernel"
 [[ -n "$OOT_DIR" ]] && BUILD_TARGET="module ${OOT_DIR##*/}"
 
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help