Lines Matching +full:patch +full:- +full:address

2 # SPDX-License-Identifier: GPL-2.0
14 set -o errtrace
15 set -o pipefail
16 set -o nounset
18 # Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'.
20 shopt -s lastpipe
27 shopt -o xtrace | grep -q 'on' && XTRACE=1
34 FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
38 TMP_DIR="$PWD/klp-tmp"
40 ORIG_DIR="$TMP_DIR/1-orig"
41 PATCHED_DIR="$TMP_DIR/2-patched"
42 ORIG_CSUM_DIR="$TMP_DIR/3-checksum-orig"
43 PATCHED_CSUM_DIR="$TMP_DIR/3-checksum-patched"
44 DIFF_DIR="$TMP_DIR/4-diff"
45 KMOD_DIR="$TMP_DIR/5-kmod"
54 read -r COLOR_RESET COLOR_BOLD COLOR_ERROR COLOR_WARN <<< ""
55 if [[ -t 1 && -t 2 ]]; then
75 echo -e "${COLOR_BOLD}$*${COLOR_RESET}"
79 echo -e "${COLOR_WARN}warning${COLOR_RESET}: $SCRIPT: $*" >&2
83 echo -e "${COLOR_ERROR}error${COLOR_RESET}: $SCRIPT: $*" >&2
87 declare -a STASHED_FILES
93 [[ ! -e "$file" ]] && die "no file to stash: $file"
95 mkdir -p "$STASH_DIR/$(dirname "$rel_file")"
96 cp -f "$file" "$STASH_DIR/$rel_file"
105 mv -f "$STASH_DIR/$file" "$PWD/$file" || warn "can't restore file: $file"
115 [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR"
132 -f, --show-first-changed Show address of first changed instruction
133 -j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS]
134 -o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko]
135 --no-replace Disable livepatch atomic replace
136 -v, --verbose Pass V=1 to kernel/module builds
139 -d, --debug Show symbol/reloc cloning decisions
140 -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
145 5|kmod Build patch module
146 -T, --keep-tmp Preserve tmp dir on exit
160 local patch
163 long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
165 args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
168 eval set -- "$args"
172 -h | --help)
176 -f | --show-first-changed)
180 -j | --jobs)
184 -o | --output)
192 --no-replace)
196 -v | --verbose)
200 -d | --debug)
205 -S | --short-circuit)
206 [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
214 *) die "invalid short-circuit step '$2'" ;;
218 -T | --keep-tmp)
222 --)
233 if [[ $# -eq 0 ]] && (( SHORT_CIRCUIT <= 2 )); then
241 for patch in "${PATCHES[@]}"; do
242 [[ -f "$patch" ]] || die "$patch doesn't exist"
248 [[ -v XTRACE ]] && set +x
253 [[ -v XTRACE ]] && set -x
262 [[ -v CONFIG_LIVEPATCH ]] || \
265 [[ -v CONFIG_KLP_BUILD ]] || \
268 [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] && \
271 [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] && \
274 [[ -v CONFIG_AS_IS_LLVM ]] && \
275 [[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
278 [[ -x "$OBJTOOL" ]] && "$OBJTOOL" klp 2>&1 | command grep -q "not implemented" && \
279 die "objtool not built with KLP support; install xxhash-devel/libxxhash-dev (version >= 0.8) and recompile"
284 # Only allow alphanumerics and '_' and '-' in the module name. Everything else
285 # is replaced with '-'. Also truncate to 55 chars so the full name + NUL
286 # terminator fits in the kernel's 56-byte module name array.
288 echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55
291 # If the module name wasn't specified on the cmdline with --output, give it a
292 # name based on the patch name.
294 [[ -v NAME ]] && return 0
296 if [[ "${#PATCHES[@]}" -eq 1 ]]; then
300 NAME="patch"
303 NAME="livepatch-$NAME"
309 # Hardcode the value printed by the localversion script to prevent patch
317 if [[ -n "$(make -s listnewconfig 2>/dev/null)" ]]; then
322 kernelrelease="$(make -s kernelrelease)"
323 [[ -z "$kernelrelease" ]] && die "failed to get kernel version"
325 sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
329 local patch="$1"
331 grep0 -E '^--- ' "$patch" \
332 | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
335 | sort -u
339 local patch="$1"
341 grep0 -E '^\+\+\+ ' "$patch" \
342 | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
345 | sort -u
349 local patch="$1"
351 { get_patch_input_files "$patch"; get_patch_output_files "$patch"; } \
352 | sort -u
356 local patch
358 for patch in "${PATCHES[@]}"; do
361 get_patch_files "$patch" | mapfile -t files
366 die "${patch}: unsupported patch to $file"
374 local patch="$1"
377 local drift_regex="with fuzz|offset [0-9]+ line"
381 [[ ! -f "$patch" ]] && die "$patch doesn't exist"
383 output=$(patch -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
384 if [[ "$status" -ne 0 ]]; then
386 die "$patch did not apply"
388 [[ -v VERBOSE ]] && echo "$output" >&2
389 warn "${patch} applied with fuzz"
392 APPLIED_PATCHES+=("$patch")
393 patch -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
397 local patch="$1"
400 patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
403 [[ "$p" == "$patch" ]] && continue
412 local patch
414 for patch in "${PATCHES[@]}"; do
415 apply_patch "$patch" "${extra_args[@]}"
422 for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do
436 # We're not yet smart enough to handle anything other than in-tree
438 [[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
441 [[ -f "$ORIG_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_DIR"
444 [[ -f "$PATCHED_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_DIR"
447 [[ -f "$ORIG_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_CSUM_DIR"
448 [[ -f "$PATCHED_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_CSUM_DIR"
451 [[ -f "$DIFF_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $DIFF_DIR"
454 (( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
455 mkdir -p "$TMP_DIR"
459 [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines"
460 command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)"
467 # Refresh the patch hunk headers, specifically the line numbers and counts.
469 local patch="$1"
474 rm -rf "$tmpdir"
475 mkdir -p "$tmpdir/a"
476 mkdir -p "$tmpdir/b"
478 # Get all source files affected by the patch
479 get_patch_input_files "$patch" | mapfile -t input_files
480 get_patch_output_files "$patch" | mapfile -t output_files
483 echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a"
486 apply_patch "$patch" "--silent"
487 echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
488 revert_patch "$patch"
490 # Diff 'a' and 'b' to make a clean patch
491 ( cd "$tmpdir" && diff -Nupr a b > "$patch" ) || true
501 rm -f "$TMP_DIR"/*.patch
506 local tmp_patch="$TMP_DIR/tmp.patch"
507 local patch="${PATCHES[$i]}"
510 new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")"
512 cp -f "$old_patch" "$tmp_patch"
518 rm -f "$tmp_patch"
527 cmd+=("--silent")
528 cmd+=("-j$JOBS")
541 # When a patch to a kernel module references a newly created unexported
558 if [[ -v VERBOSE ]]; then
561 cmd+=("-s")
563 cmd+=("-j$JOBS")
564 cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
569 1> >(tee -a "$log") \
570 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) \
577 # Find root-level vmlinux.o and non-root-level .ko files,
578 # excluding klp-tmp/ and .git/
579 find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
580 -type f "${opts[@]}" \
581 \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
582 -printf '%P\n'
589 rm -rf "$ORIG_DIR"
590 mkdir -p "$ORIG_DIR"
592 find_objects | mapfile -t files
601 [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file"
603 mkdir -p "$orig_dir"
604 cp -f "$file" "$orig_dir"
608 mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
619 rm -rf "$PATCHED_DIR"
620 mkdir -p "$PATCHED_DIR"
623 opts=("-newer")
626 find_objects "${opts[@]}" | mapfile -t files
636 [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file"
638 cmp -s "$orig_file" "$file" && continue
640 mkdir -p "$patched_dir"
641 cp -f "$file" "$patched_dir"
648 mv -f "$TMP_DIR/build.log" "$PATCHED_DIR"
659 local match_dir="${3:-}"
663 rm -rf "$dest_dir"
664 mkdir -p "$dest_dir"
666 find "$src_dir" -type f -name "*.o" | mapfile -t files
671 [[ -n "$match_dir" && ! -f "$match_dir/$rel" ]] && continue
673 mkdir -p "$(dirname "$dest")"
674 cp -f "$file" "$dest"
687 rm -rf "$DIFF_DIR"
688 mkdir -p "$DIFF_DIR"
690 find "$PATCHED_CSUM_DIR" -type f -name "*.o" | mapfile -t files
691 [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
693 [[ -v DEBUG_CLONE ]] && opts=("--debug")
704 mkdir -p "$(dirname "$out_file")"
714 if [[ -v DIFF_CHECKSUM ]]; then
716 filter+=("-Ev")
724 [[ -v VERBOSE ]] && echo "cd $ORIG_CSUM_DIR && ${cmd[*]}"
726 1> >(tee -a "$log") \
727 2> >(tee -a "$log" | "${filter[@]}" >&2) || \
735 # For each changed object, run "objtool klp checksum" with --debug-checksum to
736 # get the per-instruction checksums, and then diff those to find the first
741 local -A funcs
750 }' "$KLP_DIFF_LOG" | mapfile -t lines
753 read -r file func <<< "$line"
754 if [[ ! -v funcs["$file"] ]]; then
763 cmd+=("--dry-run")
766 local opt="--debug-checksum=${funcs[$file]// /,}"
779 local -a orig patched
780 paste <(grep0 -E "^DEBUG: .*checksum: $func " "$orig_log") \
781 <(grep0 -E "^DEBUG: .*checksum: $func " "$patched_log") |
782 while IFS= read -r line; do
783 read -ra orig <<< "${line%%$'\t'*}"
784 read -ra patched <<< "${line#*$'\t'}"
786 if [[ ${#patched[@]} -eq 0 ]]; then
787 printf "%s: %s: %s (removed)\n" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
789 elif [[ ${#orig[@]} -eq 0 ]]; then
790 printf "%s: %s: %s (added)\n" "${patched[1]%:}" "${patched[3]}" "${patched[-2]}"
794 [[ "${orig[-1]}" == "${patched[-1]}" ]] && continue
796 printf "%s: %s: %s" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
797 [[ "${orig[-2]}" != "${patched[-2]}" ]] && \
798 printf " (patched: %s)" "${patched[-2]}"
806 # Build and post-process livepatch module in $KMOD_DIR
815 rm -rf "$KMOD_DIR"
816 mkdir -p "$KMOD_DIR"
818 cp -f "$SCRIPT_DIR/init.c" "$KMOD_DIR"
820 echo "obj-m := $NAME.o" > "$makefile"
821 echo -n "$NAME-y := init.o" >> "$makefile"
823 find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files
824 [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
834 mkdir -p "$kmod_dir"
835 cp -f "$file" "$kmod_dir"
838 cp -f "$file" "${kmod_file}_shipped"
843 echo -n " $rel_file" >> "$makefile"
848 cflags=("-ffunction-sections")
849 cflags+=("-fdata-sections")
850 [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE")
853 if [[ -v VERBOSE ]]; then
856 cmd+=("-s")
858 cmd+=("-j$JOBS")
859 cmd+=("--directory=.")
865 1> >(tee -a "$log") \
866 2> >(tee -a "$log" >&2)
871 cp -f "$kmod_file" "$kmod_file.orig"
874 objcopy --remove-section=.BTF "$kmod_file"
877 "$OBJTOOL" klp post-link "$kmod_file" || die "objtool klp post-link failed"
879 cp -f "$kmod_file" "$OUTFILE"
889 status "Validating patch(es)"
902 status "Fixing patch(es)"
904 apply_patches "--silent"
922 if [[ -v DIFF_CHECKSUM ]]; then
929 status "Building patch module: $OUTFILE"