klp-build 18 KB

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