#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Build a livepatch module
# shellcheck disable=SC1090,SC2155,SC2164
if (( BASH_VERSINFO[0] < 4 || \
(BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
echo "error: this script requires bash 4.4+" >&2
exit 1
fi
set -o errtrace
set -o pipefail
set -o nounset
# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'.
# This helps keep execution in pipes so pipefail+ERR trap can catch errors.
shopt -s lastpipe
unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE XTRACE
REPLACE=1
SHORT_CIRCUIT=0
JOBS="$(getconf _NPROCESSORS_ONLN)"
shopt -o xtrace | grep -q 'on' && XTRACE=1
# Avoid removing the previous $TMP_DIR until args have been fully processed.
KEEP_TMP=1
SCRIPT="$(basename "$0")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
OBJTOOL="$PWD/tools/objtool/objtool"
CONFIG="$PWD/.config"
TMP_DIR="$PWD/klp-tmp"
ORIG_DIR="$TMP_DIR/1-orig"
PATCHED_DIR="$TMP_DIR/2-patched"
ORIG_CSUM_DIR="$TMP_DIR/3-checksum-orig"
PATCHED_CSUM_DIR="$TMP_DIR/3-checksum-patched"
DIFF_DIR="$TMP_DIR/4-diff"
KMOD_DIR="$TMP_DIR/5-kmod"
STASH_DIR="$TMP_DIR/stash"
TIMESTAMP="$TMP_DIR/timestamp"
PATCH_TMP_DIR="$TMP_DIR/tmp"
KLP_DIFF_LOG="$DIFF_DIR/diff.log"
# Terminal output colors
read -r COLOR_RESET COLOR_BOLD COLOR_ERROR COLOR_WARN <<< ""
if [[ -t 1 && -t 2 ]]; then
COLOR_RESET="\033[0m"
COLOR_BOLD="\033[1m"
COLOR_ERROR="\033[0;31m"
COLOR_WARN="\033[0;33m"
fi
grep0() {
# shellcheck disable=SC2317
command grep "$@" || true
}
# Because pipefail is enabled, the grep0 helper should be used instead of
# grep, otherwise a failed match can propagate to an error.
grep() {
echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2
exit 1
}
status() {
echo -e "${COLOR_BOLD}$*${COLOR_RESET}"
}
warn() {
echo -e "${COLOR_WARN}warning${COLOR_RESET}: $SCRIPT: $*" >&2
}
die() {
echo -e "${COLOR_ERROR}error${COLOR_RESET}: $SCRIPT: $*" >&2
exit 1
}
declare -a STASHED_FILES
stash_file() {
local file="$1"
local rel_file="${file#"$PWD"/}"
[[ ! -e "$file" ]] && die "no file to stash: $file"
mkdir -p "$STASH_DIR/$(dirname "$rel_file")"
cp -f "$file" "$STASH_DIR/$rel_file"
STASHED_FILES+=("$rel_file")
}
restore_files() {
local file
for file in "${STASHED_FILES[@]}"; do
mv -f "$STASH_DIR/$file" "$PWD/$file" || warn "can't restore file: $file"
done
STASHED_FILES=()
}
cleanup() {
set +o nounset
revert_patches
restore_files
[[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR"
return 0
}
trap_err() {
die "line ${BASH_LINENO[0]}: '$BASH_COMMAND'"
}
trap cleanup EXIT INT TERM HUP
trap trap_err ERR
__usage() {
cat <<EOF
Usage: $SCRIPT [OPTIONS] PATCH_FILE(s)
Generate a livepatch module.
Options:
-f, --show-first-changed Show address of first changed instruction
-j, --jobs=<jobs> Build jobs to run simultaneously [default: $JOBS]
-o, --output=<file.ko> Output file [default: livepatch-<patch-name>.ko]
--no-replace Disable livepatch atomic replace
-v, --verbose Pass V=1 to kernel/module builds
Advanced Options:
-d, --debug Show symbol/reloc cloning decisions
-S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
1|orig Build original kernel (default)
2|patched Build patched kernel
3|checksum Generate checksums
4|diff Diff objects
5|kmod Build patch module
-T, --keep-tmp Preserve tmp dir on exit
EOF
}
usage() {
__usage >&2
}
process_args() {
local keep_tmp=0
local short
local long
local args
local patch
short="hfj:o:vdS:T"
long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
echo; usage; exit
}
eval set -- "$args"
while true; do
case "$1" in
-h | --help)
usage
exit 0
;;
-f | --show-first-changed)
DIFF_CHECKSUM=1
shift
;;
-j | --jobs)
JOBS="$2"
shift 2
;;
-o | --output)
[[ "$2" != *.ko ]] && die "output filename should end with .ko"
OUTFILE="$2"
NAME="$(basename "$OUTFILE")"
NAME="${NAME%.ko}"
NAME="$(module_name_string "$NAME")"
shift 2
;;
--no-replace)
REPLACE=0
shift
;;
-v | --verbose)
VERBOSE=1
shift
;;
-d | --debug)
DEBUG_CLONE=1
keep_tmp=1
shift
;;
-S | --short-circuit)
[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tm