The __LINE__ macro creates challenges for binary diffing.  When a .patch
file adds or removes lines, it shifts the line numbers for all code
below it.

This can cause the code generation of functions using __LINE__ to change
due to the line number constant being embedded in a MOV instruction,
despite there being no semantic difference.

Avoid such false positives by adding a fix-patch-lines script which can
be used to insert a #line directive in each patch hunk affecting the
line numbering.  This script will be used by klp-build, which will be
introduced in a subsequent patch.

Signed-off-by: Josh Poimboeuf <jpoim...@kernel.org>
---
 MAINTAINERS                       |  1 +
 scripts/livepatch/fix-patch-lines | 79 +++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
 create mode 100755 scripts/livepatch/fix-patch-lines

diff --git a/MAINTAINERS b/MAINTAINERS
index 0298d9570ca8..dd622368d74b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14006,6 +14006,7 @@ F:      include/linux/livepatch*.h
 F:     kernel/livepatch/
 F:     kernel/module/livepatch.c
 F:     samples/livepatch/
+F:     scripts/livepatch/
 F:     tools/testing/selftests/livepatch/
 
 LLC (802.2)
diff --git a/scripts/livepatch/fix-patch-lines 
b/scripts/livepatch/fix-patch-lines
new file mode 100755
index 000000000000..73c5e3dea46e
--- /dev/null
+++ b/scripts/livepatch/fix-patch-lines
@@ -0,0 +1,79 @@
+#!/usr/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Use #line directives to preserve original __LINE__ numbers across patches to
+# avoid unwanted compilation changes.
+
+BEGIN {
+       in_hunk = 0
+       skip    = 0
+}
+
+/^--- / {
+       skip = $2 !~ /\.(c|h)$/
+       print
+       next
+}
+
+/^@@/ {
+       if (skip) {
+               print
+               next
+       }
+
+       in_hunk = 1
+
+       # for @@ -1,3 +1,4 @@:
+       #   1: line number in old file
+       #   3: how many lines the hunk covers in old file
+       #   1: line number in new file
+       #   4: how many lines the hunk covers in new file
+
+       match($0, /^@@ -([0-9]+)(,([0-9]+))? \+([0-9]+)(,([0-9]+))? @@/, m)
+
+       # Set 'cur' to the old file's line number at the start of the hunk.  It
+       # gets incremented for every context line and every line removal, so
+       # that it always represents the old file's current line number.
+       cur = m[1]
+
+       # last = last line number of current hunk
+       last = cur + (m[3] ? m[3] : 1) - 1
+
+       need_line_directive = 0
+
+       print
+       next
+}
+
+{
+       if (skip || !in_hunk || $0 ~ /^\\ No newline at end of file/) {
+               print
+               next
+       }
+
+       # change line
+       if ($0 ~ /^[+-]/) {
+               # inject #line after this group of changes
+               need_line_directive = 1
+
+               if ($0 ~ /^-/)
+                       cur++
+
+               print
+               next
+       }
+
+       # If this is the first context line after a group of changes, inject
+       # the #line directive to force the compiler to correct the line
+       # numbering to match the original file.
+       if (need_line_directive) {
+               print "+#line " cur
+               need_line_directive = 0
+       }
+
+       if (cur == last)
+               in_hunk = 0
+
+       cur++
+       print
+}
-- 
2.49.0


Reply via email to