diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index a5473b0af..423e5400a 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -3240,19 +3240,35 @@ static bool is_expoline(struct kpatch_elf *kelf, char *name) static int function_ptr_rela(const struct rela *rela, struct kpatch_elf *kelf) { const struct rela *rela_toc = toc_rela(rela); + static int entry_offset = -1; bool funcptr = false; + if (entry_offset < 0) { + char *str = getenv("ENTRY_OFFSET_BYTES"); + if (str) + entry_offset = atoi(str); + else + entry_offset = 0; + } + if (rela_toc && rela_toc->sym->type == STT_FUNC && !rela_toc->sym->parent) { switch (kelf->arch) { case X86_64: - if (rela_toc->addend == (int)rela_toc->sym->sym.st_value && + if (rela_toc->addend + entry_offset == (int)rela_toc->sym->sym.st_value && rela->type == R_X86_64_32S) funcptr = true; break; case PPC64: - if (rela_toc->addend == (int)rela_toc->sym->sym.st_value && - (rela->type == R_PPC64_TOC16_HA || rela->type == R_PPC64_TOC16_LO_DS)) + if ((rela->type == R_PPC64_TOC16_HA || rela->type == R_PPC64_TOC16_LO_DS) && + (rela_toc->addend == (int)rela_toc->sym->sym.st_value)) { funcptr = true; + } else if (rela->type == R_PPC64_ADDR64) { + struct rela *entry; + entry = find_rela_by_offset(rela_toc->sym->sec->rela, + (unsigned int) (rela_toc->addend + rela_toc->sym->sym.st_value)); + if (entry && entry->type == R_PPC64_ENTRY) + funcptr = true; + } break; case S390: if (rela->type == R_390_GOTENT) diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index bc112fc98..59bcdd734 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -472,6 +472,31 @@ find_special_section_data() { return } +# Determine if this configuration uses the -fpatchable-function-entry=N[,M] +# compiler flag, for which create-diff-object will need to know any non-zero M +# NOPs value (i.e. to expect an entry point M NOPs instructions inside its +# function section.) +find_entry_offset() { + local entry_offset=0 + + case "$ARCH" in + "x86_64") + # arch/x86/Makefile: For CONFIG_CALL_PADDING, sets M=CONFIG_FUNCTION_PADDING_BYTES + if [[ -n "$CONFIG_CALL_PADDING" ]]; then + entry_offset="$CONFIG_FUNCTION_PADDING_BYTES" + fi + ;; + "ppc64le") + # arch/powerpc/Makefile: N/A + ;; + "s390x") + # arch/s390x/Makefile: N/A + ;; + esac + + export ENTRY_OFFSET_BYTES="$entry_offset" +} + # path of file, relative to dir # adapted from https://stackoverflow.com/a/24848739 relpath() { @@ -1262,6 +1287,7 @@ export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \ echo "Reading special section data" find_special_section_data +find_entry_offset if [[ $DEBUG -ge 4 ]]; then export KPATCH_GCC_DEBUG=1 diff --git a/test/integration/rhel-7.9/func-ptr-klp_reloc.patch b/test/integration/rhel-7.9/func-ptr-klp_reloc.patch new file mode 100644 index 000000000..e18ffef8d --- /dev/null +++ b/test/integration/rhel-7.9/func-ptr-klp_reloc.patch @@ -0,0 +1,17 @@ +--- src.old/fs/proc/uptime.c 2024-06-13 03:19:08.000000000 -0400 ++++ src/fs/proc/uptime.c 2025-03-25 09:08:38.206910364 -0400 +@@ -24,11 +24,12 @@ static int uptime_proc_show(struct seq_f + nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC; + idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem); + idle.tv_nsec = rem; +- seq_printf(m, "%lu.%02lu %lu.%02lu\n", ++ seq_printf(m, "%lu.%02lu %lu.%02lu uptime_proc_show=%p\n", + (unsigned long) uptime.tv_sec, + (uptime.tv_nsec / (NSEC_PER_SEC / 100)), + (unsigned long) idle.tv_sec, +- (idle.tv_nsec / (NSEC_PER_SEC / 100))); ++ (idle.tv_nsec / (NSEC_PER_SEC / 100)), ++ uptime_proc_show); + return 0; + } + diff --git a/test/integration/rhel-7.9/func-ptr-klp_reloc.test b/test/integration/rhel-7.9/func-ptr-klp_reloc.test new file mode 100755 index 000000000..6514a6e8a --- /dev/null +++ b/test/integration/rhel-7.9/func-ptr-klp_reloc.test @@ -0,0 +1,9 @@ +#!/bin/bash + +kpatch_value=$(cat /proc/uptime | grep -o 'uptime_proc_show=.*' | awk -F= '{print $2}') +kernel_value=$(awk '$NF=="uptime_proc_show" { print $1 }' /proc/kallsyms) + +if [[ "$kpatch_value" != "$kernel_value" ]]; then + echo "kpatch_value($kpatch_value) != kernel_value($kernel_value)" + exit 1 +fi diff --git a/test/integration/rhel-8.10/func-ptr-klp_reloc.patch b/test/integration/rhel-8.10/func-ptr-klp_reloc.patch new file mode 100644 index 000000000..08b4b2c6f --- /dev/null +++ b/test/integration/rhel-8.10/func-ptr-klp_reloc.patch @@ -0,0 +1,17 @@ +--- src.old/fs/proc/uptime.c 2025-03-20 06:57:19.000000000 -0400 ++++ src/fs/proc/uptime.c 2025-03-25 09:35:50.847697946 -0400 +@@ -25,11 +25,12 @@ static int uptime_proc_show(struct seq_f + + idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem); + idle.tv_nsec = rem; +- seq_printf(m, "%lu.%02lu %lu.%02lu\n", ++ seq_printf(m, "%lu.%02lu %lu.%02lu uptime_proc_show=%pK\n", + (unsigned long) uptime.tv_sec, + (uptime.tv_nsec / (NSEC_PER_SEC / 100)), + (unsigned long) idle.tv_sec, +- (idle.tv_nsec / (NSEC_PER_SEC / 100))); ++ (idle.tv_nsec / (NSEC_PER_SEC / 100)), ++ uptime_proc_show); + return 0; + } + diff --git a/test/integration/rhel-8.10/func-ptr-klp_reloc.test b/test/integration/rhel-8.10/func-ptr-klp_reloc.test new file mode 100755 index 000000000..6514a6e8a --- /dev/null +++ b/test/integration/rhel-8.10/func-ptr-klp_reloc.test @@ -0,0 +1,9 @@ +#!/bin/bash + +kpatch_value=$(cat /proc/uptime | grep -o 'uptime_proc_show=.*' | awk -F= '{print $2}') +kernel_value=$(awk '$NF=="uptime_proc_show" { print $1 }' /proc/kallsyms) + +if [[ "$kpatch_value" != "$kernel_value" ]]; then + echo "kpatch_value($kpatch_value) != kernel_value($kernel_value)" + exit 1 +fi diff --git a/test/integration/rhel-9.5/func-ptr-klp_reloc.patch b/test/integration/rhel-9.5/func-ptr-klp_reloc.patch new file mode 100644 index 000000000..a29677e2e --- /dev/null +++ b/test/integration/rhel-9.5/func-ptr-klp_reloc.patch @@ -0,0 +1,17 @@ +--- src.old/fs/proc/uptime.c 2025-03-25 09:35:31.468574086 -0400 ++++ src/fs/proc/uptime.c 2025-03-25 09:35:46.848570153 -0400 +@@ -29,11 +29,12 @@ static int uptime_proc_show(struct seq_f + + idle.tv_sec = div_u64_rem(idle_nsec, NSEC_PER_SEC, &rem); + idle.tv_nsec = rem; +- seq_printf(m, "%lu.%02lu %lu.%02lu\n", ++ seq_printf(m, "%lu.%02lu %lu.%02lu uptime_proc_show=%pK\n", + (unsigned long) uptime.tv_sec, + (uptime.tv_nsec / (NSEC_PER_SEC / 100)), + (unsigned long) idle.tv_sec, +- (idle.tv_nsec / (NSEC_PER_SEC / 100))); ++ (idle.tv_nsec / (NSEC_PER_SEC / 100)), ++ uptime_proc_show); + return 0; + } + diff --git a/test/integration/rhel-9.5/func-ptr-klp_reloc.test b/test/integration/rhel-9.5/func-ptr-klp_reloc.test new file mode 100755 index 000000000..6514a6e8a --- /dev/null +++ b/test/integration/rhel-9.5/func-ptr-klp_reloc.test @@ -0,0 +1,9 @@ +#!/bin/bash + +kpatch_value=$(cat /proc/uptime | grep -o 'uptime_proc_show=.*' | awk -F= '{print $2}') +kernel_value=$(awk '$NF=="uptime_proc_show" { print $1 }' /proc/kallsyms) + +if [[ "$kpatch_value" != "$kernel_value" ]]; then + echo "kpatch_value($kpatch_value) != kernel_value($kernel_value)" + exit 1 +fi