54dc0a
From 3db1cfb91b9b2b7cf6c5564995c446567baa371a Mon Sep 17 00:00:00 2001
54dc0a
From: Song Liu <song@kernel.org>
54dc0a
Date: Fri, 3 Feb 2023 10:28:16 -0800
54dc0a
Subject: [PATCH 102/107] 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>
54dc0a
---
54dc0a
 kpatch-build/kpatch-build | 59 +++++++++++++++++++++++++++++++++------
54dc0a
 kpatch-build/kpatch-cc    | 18 ++++++++++++
54dc0a
 2 files changed, 68 insertions(+), 9 deletions(-)
54dc0a
54dc0a
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
54dc0a
index 45e8b14..f712578 100755
54dc0a
--- a/kpatch-build/kpatch-build
54dc0a
+++ b/kpatch-build/kpatch-build
54dc0a
@@ -173,11 +173,11 @@ cleanup() {
54dc0a
 	# restore original vmlinux if it was overwritten by sourcedir build
54dc0a
 	[[ -e "$TEMPDIR/vmlinux" ]] && mv -f "$TEMPDIR/vmlinux" "$KERNEL_SRCDIR/"
54dc0a
 
54dc0a
-	# restore original link-vmlinux.sh if we updated it for the build
54dc0a
+	# restore any files that were modified for the build
54dc0a
 	[[ -e "$TEMPDIR/link-vmlinux.sh" ]] && mv -f "$TEMPDIR/link-vmlinux.sh" "$KERNEL_SRCDIR/scripts"
54dc0a
-
54dc0a
-	# restore original Makefile.modfinal if we updated it for the build
54dc0a
 	[[ -e "$TEMPDIR/Makefile.modfinal" ]] && mv -f "$TEMPDIR/Makefile.modfinal" "$KERNEL_SRCDIR/scripts"
54dc0a
+	[[ -e "$TEMPDIR/Makefile.build" ]] && mv -f "$TEMPDIR/Makefile.build" "$KERNEL_SRCDIR/scripts"
54dc0a
+	[[ -e "$TEMPDIR/Makefile" ]] && mv -f "$TEMPDIR/Makefile" "$KERNEL_SRCDIR"
54dc0a
 
54dc0a
 	[[ "$DEBUG" -eq 0 ]] && rm -rf "$TEMPDIR"
54dc0a
 	rm -rf "$RPMTOPDIR"
54dc0a
@@ -1054,6 +1054,23 @@ if [[ -n "$CONFIG_DEBUG_INFO_BTF" ]]; then
54dc0a
 	fi
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
54dc0a
+	cp -f "$KERNEL_SRCDIR/Makefile" "$TEMPDIR/Makefile" || die
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
54dc0a
+	cp -f "$KERNEL_SRCDIR/scripts/Makefile.build" "$TEMPDIR/Makefile.build" || die
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
54dc0a
@@ -1174,9 +1191,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
54dc0a
@@ -1205,7 +1227,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
54dc0a
@@ -1229,7 +1252,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
54dc0a
@@ -1246,16 +1273,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
54dc0a
 		"$TOOLSDIR"/create-diff-object $CDO_FLAGS "orig/$i" "patched/$i" "$KOBJFILE_NAME" \
54dc0a
@@ -1308,7 +1349,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
54dc0a
index 2a3d264..7e9e1be 100755
54dc0a
--- a/kpatch-build/kpatch-cc
54dc0a
+++ b/kpatch-build/kpatch-cc
54dc0a
@@ -79,6 +79,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
-- 
54dc0a
2.37.3
54dc0a