Add a --show-first-changed option to identify where changed functions begin to diverge: - Parse 'objtool klp diff' output to find changed functions. - Run objtool again on each object with --debug-checksum=<funcs>. - Diff the per-instruction checksum debug output to locate the first differing instruction. This can be useful for quickly determining where and why a function changed.
Showing the changes to scripts/livepatch (1 of 1 file). Show all
Unified · Split
| @@ -20,7 +20,7 @@ set -o nounset · 19 unmodified lines | |||
| 20 | # This helps keep execution in pipes so pipefail+errexit can catch errors. | 20 | # This helps keep execution in pipes so pipefail+errexit can catch errors. |
| 21 | shopt -s lastpipe | 21 | shopt -s lastpipe |
| 22 | 22 | ||
| 23 | unset DEBUG_CLONE SKIP_CLEANUP XTRACE | 23 | unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE |
| 24 | 24 | ||
| 25 | REPLACE=1 | 25 | REPLACE=1 |
| 26 | SHORT_CIRCUIT=0 | 26 | SHORT_CIRCUIT=0 |
| @@ -114,6 +114,7 @@ Usage: $SCRIPT [OPTIONS] PATCH_FILE(s) · 87 unmodified lines | |||
| 114 | Generate a livepatch module. | 114 | Generate a livepatch module. |
| 115 | 115 | ||
| 116 | Options: | 116 | Options: |
| 117 | -f, --show-first-changed Show address of first changed instruction | ||
| 117 | -j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS] | 118 | -j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS] |
| 118 | -o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko] | 119 | -o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko] |
| 119 | --no-replace Disable livepatch atomic replace | 120 | --no-replace Disable livepatch atomic replace |
| @@ -141,8 +142,8 @@ process_args() { · 21 unmodified lines | |||
| 141 | local long | 142 | local long |
| 142 | local args | 143 | local args |
| 143 | 144 | ||
| 144 | short="hj:o:vdS:T" | 145 | short="hfj:o:vdS:T" |
| 145 | long="help,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" | 146 | long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" |
| 146 | 147 | ||
| 147 | args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { | 148 | args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { |
| 148 | echo; usage; exit | 149 | echo; usage; exit |
| @@ -155,6 +156,10 @@ process_args() { · 6 unmodified lines | |||
| 155 | usage | 156 | usage |
| 156 | exit 0 | 157 | exit 0 |
| 157 | ;; | 158 | ;; |
| 159 | -f | --show-first-changed) | ||
| 160 | DIFF_CHECKSUM=1 | ||
| 161 | shift | ||
| 162 | ;; | ||
| 158 | -j | --jobs) | 163 | -j | --jobs) |
| 159 | JOBS="$2" | 164 | JOBS="$2" |
| 160 | shift 2 | 165 | shift 2 |
| @@ -618,6 +623,7 @@ diff_objects() { · 457 unmodified lines | |||
| 618 | local orig_file="$rel_file" | 623 | local orig_file="$rel_file" |
| 619 | local patched_file="$PATCHED_DIR/$rel_file" | 624 | local patched_file="$PATCHED_DIR/$rel_file" |
| 620 | local out_file="$DIFF_DIR/$rel_file" | 625 | local out_file="$DIFF_DIR/$rel_file" |
| 626 | local filter=() | ||
| 621 | local cmd=() | 627 | local cmd=() |
| 622 | 628 | ||
| 623 | mkdir -p "$(dirname "$out_file")" | 629 | mkdir -p "$(dirname "$out_file")" |
| @@ -630,16 +636,80 @@ diff_objects() { · 6 unmodified lines | |||
| 630 | cmd+=("$patched_file") | 636 | cmd+=("$patched_file") |
| 631 | cmd+=("$out_file") | 637 | cmd+=("$out_file") |
| 632 | 638 | ||
| 639 | if [[ -v DIFF_CHECKSUM ]]; then | ||
| 640 | filter=("grep0") | ||
| 641 | filter+=("-Ev") | ||
| 642 | filter+=("DEBUG: .*checksum: ") | ||
| 643 | else | ||
| 644 | filter=("cat") | ||
| 645 | fi | ||
| 646 | |||
| 633 | ( | 647 | ( |
| 634 | cd "$ORIG_DIR" | 648 | cd "$ORIG_DIR" |
| 635 | "${cmd[@]}" \ | 649 | "${cmd[@]}" \ |
| 636 | 1> >(tee -a "$log") \ | 650 | 1> >(tee -a "$log") \ |
| 637 | 2> >(tee -a "$log" >&2) || \ | 651 | 2> >(tee -a "$log" | "${filter[@]}" >&2) || \ |
| 638 | die "objtool klp diff failed" | 652 | die "objtool klp diff failed" |
| 639 | ) | 653 | ) |
| 640 | done | 654 | done |
| 641 | } | 655 | } |
| 642 | 656 | ||
| 657 | # For each changed object, run objtool with --debug-checksum to get the | ||
| 658 | # per-instruction checksums, and then diff those to find the first changed | ||
| 659 | # instruction for each function. | ||
| 660 | diff_checksums() { | ||
| 661 | local orig_log="$ORIG_DIR/checksum.log" | ||
| 662 | local patched_log="$PATCHED_DIR/checksum.log" | ||
| 663 | local -A funcs | ||
| 664 | local cmd=() | ||
| 665 | local line | ||
| 666 | local file | ||
| 667 | local func | ||
| 668 | |||
| 669 | gawk '/\.o: changed function: / { | ||
| 670 | sub(/:$/, "", $1) | ||
| 671 | print $1, $NF | ||
| 672 | }' "$KLP_DIFF_LOG" | mapfile -t lines | ||
| 673 | |||
| 674 | for line in "${lines[@]}"; do | ||
| 675 | read -r file func <<< "$line" | ||
| 676 | if [[ ! -v funcs["$file"] ]]; then | ||
| 677 | funcs["$file"]="$func" | ||
| 678 | else | ||
| 679 | funcs["$file"]+=" $func" | ||
| 680 | fi | ||
| 681 | done | ||
| 682 | |||
| 683 | cmd=("$SRC/tools/objtool/objtool") | ||
| 684 | cmd+=("--checksum") | ||
| 685 | cmd+=("--link") | ||
| 686 | cmd+=("--dry-run") | ||
| 687 | |||
| 688 | for file in "${!funcs[@]}"; do | ||
| 689 | local opt="--debug-checksum=${funcs[$file]// /,}" | ||
| 690 | |||
| 691 | ( | ||
| 692 | cd "$ORIG_DIR" | ||
| 693 | "${cmd[@]}" "$opt" "$file" &> "$orig_log" || \ | ||
| 694 | ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" ) | ||
| 695 | |||
| 696 | cd "$PATCHED_DIR" | ||
| 697 | "${cmd[@]}" "$opt" "$file" &> "$patched_log" || \ | ||
| 698 | ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" ) | ||
| 699 | ) | ||
| 700 | |||
| 701 | for func in ${funcs[$file]}; do | ||
| 702 | diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \ | ||
| 703 | <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \ | ||
| 704 | | gawk '/^< DEBUG: / { | ||
| 705 | gsub(/:/, "") | ||
| 706 | printf "%s: %s: %s\n", $3, $5, $6 | ||
| 707 | exit | ||
| 708 | }' || true | ||
| 709 | done | ||
| 710 | done | ||
| 711 | } | ||
| 712 | |||
| 643 | # Build and post-process livepatch module in $KMOD_DIR | 713 | # Build and post-process livepatch module in $KMOD_DIR |
| 644 | build_patch_module() { | 714 | build_patch_module() { |
| 645 | local makefile="$KMOD_DIR/Kbuild" | 715 | local makefile="$KMOD_DIR/Kbuild" |
| @@ -743,6 +813,10 @@ fi · 97 unmodified lines | |||
| 743 | if (( SHORT_CIRCUIT <= 3 )); then | 813 | if (( SHORT_CIRCUIT <= 3 )); then |
| 744 | status "Diffing objects" | 814 | status "Diffing objects" |
| 745 | diff_objects | 815 | diff_objects |
| 816 | if [[ -v DIFF_CHECKSUM ]]; then | ||
| 817 | status "Finding first changed instructions" | ||
| 818 | diff_checksums | ||
| 819 | fi | ||
| 746 | fi | 820 | fi |
| 747 | 821 | ||
| 748 | if (( SHORT_CIRCUIT <= 4 )); then | 822 | if (( SHORT_CIRCUIT <= 4 )); then |