Michel Lind de7e84
From fc9c9f6cdd6fd9edc4db86d202b8e0d5e249c7ee Mon Sep 17 00:00:00 2001
54dc0a
From: Song Liu <song@kernel.org>
54dc0a
Date: Fri, 3 Feb 2023 10:28:16 -0800
Michel Lind de7e84
Subject: [PATCH 108/118] kpatch-build: support CONFIG_LTO_CLANG_THIN
54dc0a
54dc0a
Support CONFIG_LTO_CLANG_THIN with ld.lld --lto-obj-path option.
54dc0a
54dc0a
With CONFIG_LTO_CLANG_THIN, .o files are LLVM IR binary, so CDO doesn't
54dc0a
work on .o file. To solve this issue, we CDO the thinlto files generated
54dc0a
by the --lto-obj-path option. Clang LTO generates the thinlto files
54dc0a
after cross file inline, so they are good candidates for CDO. See [1] for
54dc0a
more discussions about this.
54dc0a
54dc0a
To achieve this, we need:
54dc0a
54dc0a
  1. kpatch-build to update kernel Makefile(s) so it generates thinlto
54dc0a
     files;
54dc0a
  2. kpatch-build and kpatch-cc to save the thinlto file properly;
54dc0a
  3. kpatch-build to feed these thinlto files to CDO;
54dc0a
  4. The user need to supply vmlinux.o, from which we generate the symtab
54dc0a
     file. We need this because GLOBAL symbols may be marked as LOCAL in
54dc0a
     LTO vmlinux;
54dc0a
54dc0a
[1] https://github.com/dynup/kpatch/issues/1320
54dc0a
54dc0a
Signed-off-by: Song Liu <song@kernel.org>
Michel Lind de7e84
(cherry picked from commit 3db1cfb91b9b2b7cf6c5564995c446567baa371a)
54dc0a
---
Michel Lind de7e84
 kpatch-build/kpatch-build | 53 ++++++++++++++++++++++++++++++++++-----
Michel Lind de7e84
 kpatch-build/kpatch-cc    | 18 +++++++++++++
Michel Lind de7e84
 2 files changed, 65 insertions(+), 6 deletions(-)
54dc0a
54dc0a
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
Michel Lind de7e84
index 643d85c..57b00c5 100755
54dc0a
--- a/kpatch-build/kpatch-build
54dc0a
+++ b/kpatch-build/kpatch-build
Michel Lind de7e84
@@ -1223,6 +1223,23 @@ if [[ "$CONFIG_LD_ORPHAN_WARN_LEVEL" == "error" ]]; then
Michel Lind de7e84
 	sed -i 's/--orphan-handling=[$](CONFIG_LD_ORPHAN_WARN_LEVEL)/--orphan-handling="warn"/g' "$KERNEL_SRCDIR/Makefile" || die
54dc0a
 fi
54dc0a
 
54dc0a
+if [[ -n "$CONFIG_LTO_CLANG" ]]; then
54dc0a
+	[[ -n "$CONFIG_LTO_CLANG_THIN" ]] || die "Non-thin LTO is not supported. Please enable CONFIG_LTO_CLANG_THIN"
54dc0a
+
54dc0a
+	# This is a heuristic: use -x to check vmlinux vs. vmlinux.o
54dc0a
+	[[ -x "$VMLINUX" ]] && die "For kernel with CONFIG_LTO_CLANG, please supply vmlinux.o instead of vmlinux via -v|--vmlinux option."
54dc0a
+
54dc0a
+	# update Makefile so that ld.lld generate vmlinux.o.thinlto.o* files for vmlinux.o
Michel Lind de7e84
+	backup_kernel_file "Makefile"
54dc0a
+	sed -i "s/--thinlto-cache-dir=\$(extmod_prefix).thinlto-cache/--lto-obj-path=vmlinux.o.thinlto.o/g" "$KERNEL_SRCDIR"/Makefile
54dc0a
+
54dc0a
+	# update scripts/Makefile.build so that ld.lld generate XX.o.thinlto.o* files for modules
Michel Lind de7e84
+	backup_kernel_file "scripts/Makefile.build"
54dc0a
+	sed -i "s/\$(ld_flags)/\$(ld_flags) --lto-obj-path=\$@.thinlto.o/g" "$KERNEL_SRCDIR"/scripts/Makefile.build
54dc0a
+
54dc0a
+	export KPATCH_CC_LTO=1
54dc0a
+fi
54dc0a
+
54dc0a
 if [[ -n "$CONFIG_CC_IS_CLANG" ]]; then
54dc0a
 	echo "WARNING: Clang support is experimental"
54dc0a
 fi
Michel Lind de7e84
@@ -1346,9 +1363,14 @@ if [[ -n "$CONFIG_MODVERSIONS" ]]; then
54dc0a
 	trace_on
54dc0a
 fi
54dc0a
 
54dc0a
+if [[ -n "$CONFIG_LTO_CLANG" ]]; then
54dc0a
+	DIFF_OBJS="$TEMPDIR/thinlto_objs"
54dc0a
+else
54dc0a
+	DIFF_OBJS="$TEMPDIR/changed_objs"
54dc0a
+fi
54dc0a
 # Read as words, no quotes.
54dc0a
 # shellcheck disable=SC2013
54dc0a
-for i in $(cat "$TEMPDIR/changed_objs")
54dc0a
+for i in $(cat "$DIFF_OBJS")
54dc0a
 do
54dc0a
 	mkdir -p "$TEMPDIR/patched/$(dirname "$i")" || die
54dc0a
 	cp -f "$BUILDDIR/$i" "$TEMPDIR/patched/$i" || die
Michel Lind de7e84
@@ -1377,7 +1399,8 @@ if [[ -z "$MODNAME" ]] ; then
54dc0a
 
54dc0a
 	MODNAME="$(module_name_string "$MODNAME")"
54dc0a
 fi
54dc0a
-FILES="$(cat "$TEMPDIR/changed_objs")"
54dc0a
+FILES="$(cat "$DIFF_OBJS")"
54dc0a
+
54dc0a
 cd "$TEMPDIR" || die
54dc0a
 mkdir output
54dc0a
 declare -a objnames
Michel Lind de7e84
@@ -1401,7 +1424,11 @@ for i in $FILES; do
54dc0a
 
54dc0a
 	mkdir -p "output/$(dirname "$i")"
54dc0a
 	cd "$BUILDDIR" || die
54dc0a
-	find_kobj "$i"
54dc0a
+	if [[ -n "$CONFIG_LTO_CLANG" ]] ; then
54dc0a
+		KOBJFILE=${i/.o.thinlto.o*/}
54dc0a
+	else
54dc0a
+		find_kobj "$i"
54dc0a
+	fi
54dc0a
 	cd "$TEMPDIR" || die
54dc0a
 	if [[ -e "orig/$i" ]]; then
54dc0a
 		if [[ -n $OOT_MODULE ]]; then
Michel Lind de7e84
@@ -1418,16 +1445,30 @@ for i in $FILES; do
54dc0a
 		else
54dc0a
 			KOBJFILE_NAME=$(basename "${KOBJFILE%.ko}")
54dc0a
 			KOBJFILE_NAME="${KOBJFILE_NAME//-/_}"
54dc0a
-			KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE"
54dc0a
+			if [[ -n "$CONFIG_LTO_CLANG" ]] ; then
54dc0a
+				KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE.ko"
54dc0a
+			else
54dc0a
+				KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE"
54dc0a
+			fi
54dc0a
 			SYMTAB="${KOBJFILE_PATH}.symtab"
54dc0a
 			SYMVERS_FILE="$BUILDDIR/Module.symvers"
54dc0a
 		fi
54dc0a
 
54dc0a
-		"$READELF" -s --wide "$KOBJFILE_PATH" > "$SYMTAB"
54dc0a
+		# With CONFIG_LTO_CLANG, multiple .thinlto files share a
54dc0a
+		# symtab file. Only generate the symtab file once.
54dc0a
+		[[ -e "$SYMTAB" ]] || "$READELF" --symbols --wide "$KOBJFILE_PATH" > "$SYMTAB"
54dc0a
 		if [[ "$ARCH" = "ppc64le" ]]; then
54dc0a
 			sed -ri 's/\s+\[<localentry>: 8\]//' "$SYMTAB"
54dc0a
 		fi
54dc0a
 
54dc0a
+		if [[ -n "$CONFIG_LTO_CLANG" ]] ; then
54dc0a
+			# skip .thinlto file that didn't change at all
54dc0a
+			diff "orig/$i" "patched/$i" 2> /dev/null && continue
54dc0a
+			# skip .thinlto file without any functions
54dc0a
+			num_func=$("$READELF" --symbols "orig/$i" | grep -c FUNC)
54dc0a
+			[[ $num_func -eq 0 ]] && continue
54dc0a
+		fi
54dc0a
+
54dc0a
 		# create-diff-object orig.o patched.o parent-name parent-symtab
54dc0a
 		#		     Module.symvers patch-mod-name output.o
Michel Lind de7e84
 		"$TOOLSDIR"/create-diff-object "${CDO_FLAGS[@]}" "orig/$i" "patched/$i" "$KOBJFILE_NAME" \
Michel Lind de7e84
@@ -1480,7 +1521,7 @@ fi
54dc0a
 cd "$TEMPDIR/output" || die
54dc0a
 # $KPATCH_LDFLAGS and result of find used as list, no quotes.
54dc0a
 # shellcheck disable=SC2086,SC2046
54dc0a
-"$LD" -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") 2>&1 | logger || die
54dc0a
+"$LD" -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o*") 2>&1 | logger || die
54dc0a
 
54dc0a
 if [[ "$USE_KLP" -eq 1 ]]; then
54dc0a
 	cp -f "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o || die
54dc0a
diff --git a/kpatch-build/kpatch-cc b/kpatch-build/kpatch-cc
Michel Lind de7e84
index 5456179..85189e1 100755
54dc0a
--- a/kpatch-build/kpatch-cc
54dc0a
+++ b/kpatch-build/kpatch-cc
Michel Lind de7e84
@@ -81,6 +81,24 @@ elif [[ "$TOOLCHAINCMD" =~ ^(.*-)?ld || "$TOOLCHAINCMD" =~ ^(.*-)?ld.lld ]] ; th
54dc0a
 					args+=(--warn-unresolved-symbols)
54dc0a
 					break
54dc0a
 					;;
54dc0a
+				*/.tmp_*.o)
54dc0a
+					# .tmp_*.o is used for single file modules.
54dc0a
+					# See "cmd_ld_single_m" in scripts/Makefile.build.
54dc0a
+					if [[ $KPATCH_CC_LTO -eq 1 ]] ; then
54dc0a
+						mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")"
54dc0a
+						cp "${obj/.tmp_/}".thinlto.o* "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")"
54dc0a
+						echo "${obj/.tmp_/}".thinlto.o* >> "$KPATCH_GCC_TEMPDIR/thinlto_objs"
54dc0a
+					fi
54dc0a
+					break
54dc0a
+					;;
54dc0a
+				*.o)
54dc0a
+					if [[ $KPATCH_CC_LTO -eq 1 ]] ; then
54dc0a
+						mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")"
54dc0a
+						cp "$obj".thinlto* "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")"
54dc0a
+						echo "$obj".thinlto.o* >> "$KPATCH_GCC_TEMPDIR/thinlto_objs"
54dc0a
+					fi
54dc0a
+					break
54dc0a
+					;;
54dc0a
 				*)
54dc0a
 					break
54dc0a
 					;;
54dc0a
-- 
Michel Lind de7e84
2.48.1
54dc0a