1#!/bin/bash 2# SPDX-License-Identifier: GPL-2.0 3# 4# Build a livepatch module 5 6# shellcheck disable=SC1090,SC2155 7 8if (( BASH_VERSINFO[0] < 4 || \ 9 (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then 10 echo "error: this script requires bash 4.4+" >&2 11 exit 1 12fi 13 14set -o errexit 15set -o errtrace 16set -o pipefail 17set -o nounset 18 19# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'. 20# This helps keep execution in pipes so pipefail+errexit can catch errors. 21shopt -s lastpipe 22 23unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE 24 25REPLACE=1 26SHORT_CIRCUIT=0 27JOBS="$(getconf _NPROCESSORS_ONLN)" 28VERBOSE="-s" 29shopt -o xtrace | grep -q 'on' && XTRACE=1 30 31# Avoid removing the previous $TMP_DIR until args have been fully processed. 32KEEP_TMP=1 33 34SCRIPT="$(basename "$0")" 35SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 36FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines" 37 38SRC="$(pwd)" 39OBJ="$(pwd)" 40 41CONFIG="$OBJ/.config" 42TMP_DIR="$OBJ/klp-tmp" 43 44ORIG_DIR="$TMP_DIR/orig" 45PATCHED_DIR="$TMP_DIR/patched" 46DIFF_DIR="$TMP_DIR/diff" 47KMOD_DIR="$TMP_DIR/kmod" 48 49STASH_DIR="$TMP_DIR/stash" 50TIMESTAMP="$TMP_DIR/timestamp" 51PATCH_TMP_DIR="$TMP_DIR/tmp" 52 53KLP_DIFF_LOG="$DIFF_DIR/diff.log" 54 55grep0() { 56 command grep "$@" || true 57} 58 59status() { 60 echo "$*" 61} 62 63warn() { 64 echo "error: $SCRIPT: $*" >&2 65} 66 67die() { 68 warn "$@" 69 exit 1 70} 71 72declare -a STASHED_FILES 73 74stash_file() { 75 local file="$1" 76 local rel_file="${file#"$SRC"/}" 77 78 [[ ! -e "$file" ]] && die "no file to stash: $file" 79 80 mkdir -p "$STASH_DIR/$(dirname "$rel_file")" 81 cp -f "$file" "$STASH_DIR/$rel_file" 82 83 STASHED_FILES+=("$rel_file") 84} 85 86restore_files() { 87 local file 88 89 for file in "${STASHED_FILES[@]}"; do 90 mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file" 91 done 92 93 STASHED_FILES=() 94} 95 96cleanup() { 97 set +o nounset 98 revert_patches "--recount" 99 restore_files 100 [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR" 101 return 0 102} 103 104trap_err() { 105 warn "line ${BASH_LINENO[0]}: '$BASH_COMMAND'" 106} 107 108trap cleanup EXIT INT TERM HUP 109trap trap_err ERR 110 111__usage() { 112 cat <<EOF 113Usage: $SCRIPT [OPTIONS] PATCH_FILE(s) 114Generate a livepatch module. 115 116Options: 117 -f, --show-first-changed Show address of first changed instruction 118 -j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS] 119 -o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko] 120 --no-replace Disable livepatch atomic replace 121 -v, --verbose Pass V=1 to kernel/module builds 122 123Advanced Options: 124 -d, --debug Show symbol/reloc cloning decisions 125 -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp) 126 1|orig Build original kernel (default) 127 2|patched Build patched kernel 128 3|diff Diff objects 129 4|kmod Build patch module 130 -T, --keep-tmp Preserve tmp dir on exit 131 132EOF 133} 134 135usage() { 136 __usage >&2 137} 138 139process_args() { 140 local keep_tmp=0 141 local short 142 local long 143 local args 144 145 short="hfj:o:vdS:T" 146 long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" 147 148 args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { 149 echo; usage; exit 150 } 151 eval set -- "$args" 152 153 while true; do 154 case "$1" in 155 -h | --help) 156 usage 157 exit 0 158 ;; 159 -f | --show-first-changed) 160 DIFF_CHECKSUM=1 161 shift 162 ;; 163 -j | --jobs) 164 JOBS="$2" 165 shift 2 166 ;; 167 -o | --output) 168 [[ "$2" != *.ko ]] && die "output filename should end with .ko" 169 OUTFILE="$2" 170 NAME="$(basename "$OUTFILE")" 171 NAME="${NAME%.ko}" 172 NAME="$(module_name_string "$NAME")" 173 shift 2 174 ;; 175 --no-replace) 176 REPLACE=0 177 shift 178 ;; 179 -v | --verbose) 180 VERBOSE="V=1" 181 shift 182 ;; 183 -d | --debug) 184 DEBUG_CLONE=1 185 keep_tmp=1 186 shift 187 ;; 188 -S | --short-circuit) 189 [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir" 190 keep_tmp=1 191 case "$2" in 192 1 | orig) SHORT_CIRCUIT=1; ;; 193 2 | patched) SHORT_CIRCUIT=2; ;; 194 3 | diff) SHORT_CIRCUIT=3; ;; 195 4 | mod) SHORT_CIRCUIT=4; ;; 196 *) die "invalid short-circuit step '$2'" ;; 197 esac 198 shift 2 199 ;; 200 -T | --keep-tmp) 201 keep_tmp=1 202 shift 203 ;; 204 --) 205 shift 206 break 207 ;; 208 *) 209 usage 210 exit 1 211 ;; 212 esac 213 done 214 215 if [[ $# -eq 0 ]]; then 216 usage 217 exit 1 218 fi 219 220 KEEP_TMP="$keep_tmp" 221 PATCHES=("$@") 222} 223 224# temporarily disable xtrace for especially verbose code 225xtrace_save() { 226 [[ -v XTRACE ]] && set +x 227 return 0 228} 229 230xtrace_restore() { 231 [[ -v XTRACE ]] && set -x 232 return 0 233} 234 235validate_config() { 236 xtrace_save "reading .config" 237 source "$CONFIG" || die "no .config file in $(dirname "$CONFIG")" 238 xtrace_restore 239 240 [[ -v CONFIG_LIVEPATCH ]] || \ 241 die "CONFIG_LIVEPATCH not enabled" 242 243 [[ -v CONFIG_KLP_BUILD ]] || \ 244 die "CONFIG_KLP_BUILD not enabled" 245 246 [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] && \ 247 die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported" 248 249 [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \ 250 die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" 251 252 [[ -v CONFIG_AS_IS_LLVM ]] && \ 253 [[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \ 254 die "Clang assembler version < 20 not supported" 255 256 return 0 257} 258 259# Only allow alphanumerics and '_' and '-' in the module name. Everything else 260# is replaced with '-'. Also truncate to 55 chars so the full name + NUL 261# terminator fits in the kernel's 56-byte module name array. 262module_name_string() { 263 echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55 264} 265 266# If the module name wasn't specified on the cmdline with --output, give it a 267# name based on the patch name. 268set_module_name() { 269 [[ -v NAME ]] && return 0 270 271 if [[ "${#PATCHES[@]}" -eq 1 ]]; then 272 NAME="$(basename "${PATCHES[0]}")" 273 NAME="${NAME%.*}" 274 else 275 NAME="patch" 276 fi 277 278 NAME="livepatch-$NAME" 279 NAME="$(module_name_string "$NAME")" 280 281 OUTFILE="$NAME.ko" 282} 283 284# Hardcode the value printed by the localversion script to prevent patch 285# application from appending it with '+' due to a dirty git working tree. 286set_kernelversion() { 287 local file="$SRC/scripts/setlocalversion" 288 local kernelrelease 289 290 stash_file "$file" 291 292 kernelrelease="$(cd "$SRC" && make syncconfig &>/dev/null && make -s kernelrelease)" 293 [[ -z "$kernelrelease" ]] && die "failed to get kernel version" 294 295 sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion 296} 297 298get_patch_files() { 299 local patch="$1" 300 301 grep0 -E '^(--- |\+\+\+ )' "$patch" \ 302 | gawk '{print $2}' \ 303 | sed 's|^[^/]*/||' \ 304 | sort -u 305} 306 307# Make sure git re-stats the changed files 308git_refresh() { 309 local patch="$1" 310 local files=() 311 312 [[ ! -e "$SRC/.git" ]] && return 313 314 get_patch_files "$patch" | mapfile -t files 315 316 ( 317 cd "$SRC" 318 git update-index -q --refresh -- "${files[@]}" 319 ) 320} 321 322check_unsupported_patches() { 323 local patch 324 325 for patch in "${PATCHES[@]}"; do 326 local files=() 327 328 get_patch_files "$patch" | mapfile -t files 329 330 for file in "${files[@]}"; do 331 case "$file" in 332 lib/*|*.S) 333 die "unsupported patch to $file" 334 ;; 335 esac 336 done 337 done 338} 339 340apply_patch() { 341 local patch="$1" 342 shift 343 local extra_args=("$@") 344 345 [[ ! -f "$patch" ]] && die "$patch doesn't exist" 346 347 ( 348 cd "$SRC" 349 350 # The sed strips the version signature from 'git format-patch', 351 # otherwise 'git apply --recount' warns. 352 sed -n '/^-- /q;p' "$patch" | 353 git apply "${extra_args[@]}" 354 ) 355 356 APPLIED_PATCHES+=("$patch") 357} 358 359revert_patch() { 360 local patch="$1" 361 shift 362 local extra_args=("$@") 363 local tmp=() 364 365 ( 366 cd "$SRC" 367 368 sed -n '/^-- /q;p' "$patch" | 369 git apply --reverse "${extra_args[@]}" 370 ) 371 git_refresh "$patch" 372 373 for p in "${APPLIED_PATCHES[@]}"; do 374 [[ "$p" == "$patch" ]] && continue 375 tmp+=("$p") 376 done 377 378 APPLIED_PATCHES=("${tmp[@]}") 379} 380 381apply_patches() { 382 local patch 383 384 for patch in "${PATCHES[@]}"; do 385 apply_patch "$patch" 386 done 387} 388 389revert_patches() { 390 local extra_args=("$@") 391 local patches=("${APPLIED_PATCHES[@]}") 392 393 for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do 394 revert_patch "${patches[$i]}" "${extra_args[@]}" 395 done 396 397 APPLIED_PATCHES=() 398} 399 400validate_patches() { 401 check_unsupported_patches 402 apply_patches 403 revert_patches 404} 405 406do_init() { 407 # We're not yet smart enough to handle anything other than in-tree 408 # builds in pwd. 409 [[ ! "$SRC" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" 410 [[ ! "$OBJ" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" 411 412 (( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR" 413 mkdir -p "$TMP_DIR" 414 415 APPLIED_PATCHES=() 416 417 [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines" 418 419 validate_config 420 set_module_name 421 set_kernelversion 422} 423 424# Refresh the patch hunk headers, specifically the line numbers and counts. 425refresh_patch() { 426 local patch="$1" 427 local tmpdir="$PATCH_TMP_DIR" 428 local files=() 429 430 rm -rf "$tmpdir" 431 mkdir -p "$tmpdir/a" 432 mkdir -p "$tmpdir/b" 433 434 # Get all source files affected by the patch 435 get_patch_files "$patch" | mapfile -t files 436 437 # Copy orig source files to 'a' 438 ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) 439 440 # Copy patched source files to 'b' 441 apply_patch "$patch" --recount 442 ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) 443 revert_patch "$patch" --recount 444 445 # Diff 'a' and 'b' to make a clean patch 446 ( cd "$tmpdir" && git diff --no-index --no-prefix a b > "$patch" ) || true 447} 448 449# Copy the patches to a temporary directory, fix their lines so as not to 450# affect the __LINE__ macro for otherwise unchanged functions further down the 451# file, and update $PATCHES to point to the fixed patches. 452fix_patches() { 453 local idx 454 local i 455 456 rm -f "$TMP_DIR"/*.patch 457 458 idx=0001 459 for i in "${!PATCHES[@]}"; do 460 local old_patch="${PATCHES[$i]}" 461 local tmp_patch="$TMP_DIR/tmp.patch" 462 local patch="${PATCHES[$i]}" 463 local new_patch 464 465 new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")" 466 467 cp -f "$old_patch" "$tmp_patch" 468 refresh_patch "$tmp_patch" 469 "$FIX_PATCH_LINES" "$tmp_patch" > "$new_patch" 470 refresh_patch "$new_patch" 471 472 PATCHES[i]="$new_patch" 473 474 rm -f "$tmp_patch" 475 idx=$(printf "%04d" $(( 10#$idx + 1 ))) 476 done 477} 478 479clean_kernel() { 480 local cmd=() 481 482 cmd=("make") 483 cmd+=("--silent") 484 cmd+=("-j$JOBS") 485 cmd+=("clean") 486 487 ( 488 cd "$SRC" 489 "${cmd[@]}" 490 ) 491} 492 493build_kernel() { 494 local log="$TMP_DIR/build.log" 495 local objtool_args=() 496 local cmd=() 497 498 objtool_args=("--checksum") 499 500 cmd=("make") 501 502 # When a patch to a kernel module references a newly created unexported 503 # symbol which lives in vmlinux or another kernel module, the patched 504 # kernel build fails with the following error: 505 # 506 # ERROR: modpost: "klp_string" [fs/xfs/xfs.ko] undefined! 507 # 508 # The undefined symbols are working as designed in that case. They get 509 # resolved later when the livepatch module build link pulls all the 510 # disparate objects together into the same kernel module. 511 # 512 # It would be good to have a way to tell modpost to skip checking for 513 # undefined symbols altogether. For now, just convert the error to a 514 # warning with KBUILD_MODPOST_WARN, and grep out the warning to avoid 515 # confusing the user. 516 # 517 cmd+=("KBUILD_MODPOST_WARN=1") 518 519 cmd+=("$VERBOSE") 520 cmd+=("-j$JOBS") 521 cmd+=("KCFLAGS=-ffunction-sections -fdata-sections") 522 cmd+=("OBJTOOL_ARGS=${objtool_args[*]}") 523 cmd+=("vmlinux") 524 cmd+=("modules") 525 526 ( 527 cd "$SRC" 528 "${cmd[@]}" \ 529 1> >(tee -a "$log") \ 530 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) 531 ) 532} 533 534find_objects() { 535 local opts=("$@") 536 537 # Find root-level vmlinux.o and non-root-level .ko files, 538 # excluding klp-tmp/ and .git/ 539 find "$OBJ" \( -path "$TMP_DIR" -o -path "$OBJ/.git" -o -regex "$OBJ/[^/][^/]*\.ko" \) -prune -o \ 540 -type f "${opts[@]}" \ 541 \( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \ 542 -printf '%P\n' 543} 544 545# Copy all .o archives to $ORIG_DIR 546copy_orig_objects() { 547 local files=() 548 549 rm -rf "$ORIG_DIR" 550 mkdir -p "$ORIG_DIR" 551 552 find_objects | mapfile -t files 553 554 xtrace_save "copying orig objects" 555 for _file in "${files[@]}"; do 556 local rel_file="${_file/.ko/.o}" 557 local file="$OBJ/$rel_file" 558 local file_dir="$(dirname "$file")" 559 local orig_file="$ORIG_DIR/$rel_file" 560 local orig_dir="$(dirname "$orig_file")" 561 562 [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" 563 564 mkdir -p "$orig_dir" 565 cp -f "$file" "$orig_dir" 566 done 567 xtrace_restore 568 569 mv -f "$TMP_DIR/build.log" "$ORIG_DIR" 570 touch "$TIMESTAMP" 571} 572 573# Copy all changed objects to $PATCHED_DIR 574copy_patched_objects() { 575 local files=() 576 local opts=() 577 local found=0 578 579 rm -rf "$PATCHED_DIR" 580 mkdir -p "$PATCHED_DIR" 581 582 # Note this doesn't work with some configs, thus the 'cmp' below. 583 opts=("-newer") 584 opts+=("$TIMESTAMP") 585 586 find_objects "${opts[@]}" | mapfile -t files 587 588 xtrace_save "copying changed objects" 589 for _file in "${files[@]}"; do 590 local rel_file="${_file/.ko/.o}" 591 local file="$OBJ/$rel_file" 592 local orig_file="$ORIG_DIR/$rel_file" 593 local patched_file="$PATCHED_DIR/$rel_file" 594 local patched_dir="$(dirname "$patched_file")" 595 596 [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" 597 598 cmp -s "$orig_file" "$file" && continue 599 600 mkdir -p "$patched_dir" 601 cp -f "$file" "$patched_dir" 602 found=1 603 done 604 xtrace_restore 605 606 (( found == 0 )) && die "no changes detected" 607 608 mv -f "$TMP_DIR/build.log" "$PATCHED_DIR" 609} 610 611# Diff changed objects, writing output object to $DIFF_DIR 612diff_objects() { 613 local log="$KLP_DIFF_LOG" 614 local files=() 615 local opts=() 616 617 rm -rf "$DIFF_DIR" 618 mkdir -p "$DIFF_DIR" 619 620 find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files 621 [[ ${#files[@]} -eq 0 ]] && die "no changes detected" 622 623 [[ -v DEBUG_CLONE ]] && opts=("--debug") 624 625 # Diff all changed objects 626 for file in "${files[@]}"; do 627 local rel_file="${file#"$PATCHED_DIR"/}" 628 local orig_file="$rel_file" 629 local patched_file="$PATCHED_DIR/$rel_file" 630 local out_file="$DIFF_DIR/$rel_file" 631 local filter=() 632 local cmd=() 633 634 mkdir -p "$(dirname "$out_file")" 635 636 cmd=("$SRC/tools/objtool/objtool") 637 cmd+=("klp") 638 cmd+=("diff") 639 (( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}") 640 cmd+=("$orig_file") 641 cmd+=("$patched_file") 642 cmd+=("$out_file") 643 644 if [[ -v DIFF_CHECKSUM ]]; then 645 filter=("grep0") 646 filter+=("-Ev") 647 filter+=("DEBUG: .*checksum: ") 648 else 649 filter=("cat") 650 fi 651 652 ( 653 cd "$ORIG_DIR" 654 "${cmd[@]}" \ 655 1> >(tee -a "$log") \ 656 2> >(tee -a "$log" | "${filter[@]}" >&2) || \ 657 die "objtool klp diff failed" 658 ) 659 done 660} 661 662# For each changed object, run objtool with --debug-checksum to get the 663# per-instruction checksums, and then diff those to find the first changed 664# instruction for each function. 665diff_checksums() { 666 local orig_log="$ORIG_DIR/checksum.log" 667 local patched_log="$PATCHED_DIR/checksum.log" 668 local -A funcs 669 local cmd=() 670 local line 671 local file 672 local func 673 674 gawk '/\.o: changed function: / { 675 sub(/:$/, "", $1) 676 print $1, $NF 677 }' "$KLP_DIFF_LOG" | mapfile -t lines 678 679 for line in "${lines[@]}"; do 680 read -r file func <<< "$line" 681 if [[ ! -v funcs["$file"] ]]; then 682 funcs["$file"]="$func" 683 else 684 funcs["$file"]+=" $func" 685 fi 686 done 687 688 cmd=("$SRC/tools/objtool/objtool") 689 cmd+=("--checksum") 690 cmd+=("--link") 691 cmd+=("--dry-run") 692 693 for file in "${!funcs[@]}"; do 694 local opt="--debug-checksum=${funcs[$file]// /,}" 695 696 ( 697 cd "$ORIG_DIR" 698 "${cmd[@]}" "$opt" "$file" &> "$orig_log" || \ 699 ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" ) 700 701 cd "$PATCHED_DIR" 702 "${cmd[@]}" "$opt" "$file" &> "$patched_log" || \ 703 ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" ) 704 ) 705 706 for func in ${funcs[$file]}; do 707 diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \ 708 <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \ 709 | gawk '/^< DEBUG: / { 710 gsub(/:/, "") 711 printf "%s: %s: %s\n", $3, $5, $6 712 exit 713 }' || true 714 done 715 done 716} 717 718# Build and post-process livepatch module in $KMOD_DIR 719build_patch_module() { 720 local makefile="$KMOD_DIR/Kbuild" 721 local log="$KMOD_DIR/build.log" 722 local kmod_file 723 local cflags=() 724 local files=() 725 local cmd=() 726 727 rm -rf "$KMOD_DIR" 728 mkdir -p "$KMOD_DIR" 729 730 cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" 731 732 echo "obj-m := $NAME.o" > "$makefile" 733 echo -n "$NAME-y := init.o" >> "$makefile" 734 735 find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files 736 [[ ${#files[@]} -eq 0 ]] && die "no changes detected" 737 738 for file in "${files[@]}"; do 739 local rel_file="${file#"$DIFF_DIR"/}" 740 local orig_file="$ORIG_DIR/$rel_file" 741 local orig_dir="$(dirname "$orig_file")" 742 local kmod_file="$KMOD_DIR/$rel_file" 743 local kmod_dir="$(dirname "$kmod_file")" 744 local cmd_file="$kmod_dir/.$(basename "$file").cmd" 745 746 mkdir -p "$kmod_dir" 747 cp -f "$file" "$kmod_dir" 748 749 # Tell kbuild this is a prebuilt object 750 cp -f "$file" "${kmod_file}_shipped" 751 752 # Make modpost happy 753 touch "$cmd_file" 754 755 echo -n " $rel_file" >> "$makefile" 756 done 757 758 echo >> "$makefile" 759 760 cflags=("-ffunction-sections") 761 cflags+=("-fdata-sections") 762 [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE") 763 764 cmd=("make") 765 cmd+=("$VERBOSE") 766 cmd+=("-j$JOBS") 767 cmd+=("--directory=.") 768 cmd+=("M=$KMOD_DIR") 769 cmd+=("KCFLAGS=${cflags[*]}") 770 771 # Build a "normal" kernel module with init.c and the diffed objects 772 ( 773 cd "$SRC" 774 "${cmd[@]}" \ 775 1> >(tee -a "$log") \ 776 2> >(tee -a "$log" >&2) 777 ) 778 779 kmod_file="$KMOD_DIR/$NAME.ko" 780 781 # Save off the intermediate binary for debugging 782 cp -f "$kmod_file" "$kmod_file.orig" 783 784 # Work around issue where slight .config change makes corrupt BTF 785 objcopy --remove-section=.BTF "$kmod_file" 786 787 # Fix (and work around) linker wreckage for klp syms / relocs 788 "$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool klp post-link failed" 789 790 cp -f "$kmod_file" "$OUTFILE" 791} 792 793 794################################################################################ 795 796process_args "$@" 797do_init 798 799if (( SHORT_CIRCUIT <= 1 )); then 800 status "Validating patch(es)" 801 validate_patches 802 status "Building original kernel" 803 clean_kernel 804 build_kernel 805 status "Copying original object files" 806 copy_orig_objects 807fi 808 809if (( SHORT_CIRCUIT <= 2 )); then 810 status "Fixing patch(es)" 811 fix_patches 812 apply_patches 813 status "Building patched kernel" 814 build_kernel 815 revert_patches 816 status "Copying patched object files" 817 copy_patched_objects 818fi 819 820if (( SHORT_CIRCUIT <= 3 )); then 821 status "Diffing objects" 822 diff_objects 823 if [[ -v DIFF_CHECKSUM ]]; then 824 status "Finding first changed instructions" 825 diff_checksums 826 fi 827fi 828 829if (( SHORT_CIRCUIT <= 4 )); then 830 status "Building patch module: $OUTFILE" 831 build_patch_module 832fi 833 834status "SUCCESS" 835